用dart实现一个ASCII交互小动画
周末的时候想教小孩编程,想先引起孩子的兴趣,就想做一个小动画出来给她看看。之前给她玩过scratch的示例小猫,小猫可以通过简单的命令设置在屏幕上移动。就想用dart自己做一个,结果发现桌面端对图形的支持比较差,需要安装一堆额外的东西,或者采用flutter的app/web/desktop方案。简单研究了一下,都比较麻烦(主要也是当时用的电脑也不允许安装这些东西),就暂时放弃了,准备以后作为一个单独的课题进行研究。
于是就想,之前玩BBS时,经常有一些ASCII字符画,要不就在命令行的环境下,做一个简单的ASCII图画移动程序吧。然后在网上找了一幅画,又做了ASCII转码(网址:https://www.degraeve.com/img2txt.php),然后写了如下的程序。本次只做了平移,没有做旋转,后续再研究一下旋转的实现。平移的实现主要靠对数组的操作。
本次环境:
dart vm version: 2.10.4
console:
height = 30;
width = 118;
完整代码如下:
1 import 'dart:io';
2 import 'dart:math';
3
4 main() async {
5 var path = r'.\ascii\dog.txt';
6
7 var lines = await File(path).readAsLines();
8
9 display(AsciiImage(
10 'dog', List.generate(lines.length, (i) => lines[i].split(''))));
11 }
12
13 void display(AsciiImage image) {
14 Console.draw(image);
15 var cmd = _getCmd();
16 while (cmd != 'exit') {
17 var steps = _getSteps();
18 switch (cmd) {
19 case 'up':
20 image.moveUp(steps);
21 Console.draw(image);
22 break;
23 case 'down':
24 image.moveDown(steps);
25 Console.draw(image);
26 break;
27 case 'left':
28 image.moveLeft(steps);
29 Console.draw(image);
30 break;
31 case 'right':
32 image.moveRight(steps);
33 Console.draw(image);
34 break;
35 default:
36 break;
37 }
38 if (!image.isValid()) {
39 stdout.writeln('Aha~~~ the ${image.name} has disappeared! Game Over!');
40 return;
41 }
42 cmd = _getCmd();
43 }
44 }
45
46 class Console {
47 static const height = 30;
48 static const width = 118;
49
50 static void draw(AsciiImage image) {
51 var outImage = _needCut(image) ? image.cut(height, width) : image;
52 stdout.writeln(outImage.content);
53 }
54 }
55
56 bool _needCut(AsciiImage image) =>
57 image.height > Console.height || image.width > Console.width;
58
59 bool _isValidCmd(String cmd) =>
60 cmd == 'left' ||
61 cmd == 'right' ||
62 cmd == 'up' ||
63 cmd == 'down' ||
64 cmd == 'exit';
65
66 String _getCmd() {
67 stdout.write(
68 'Now try to move the dog! left, right, up or down. exit for quit: ');
69 var cmd = stdin.readLineSync().toLowerCase();
70 while (!_isValidCmd(cmd)) {
71 stdout.write('Invalid Command! left, right, up or down. exit for quit: ');
72 cmd = stdin.readLineSync().toLowerCase();
73 }
74 return cmd;
75 }
76
77 int _getSteps() {
78 var steps = -1;
79 while (steps < 0) {
80 stdout.write('Please input the steps(an integer >= 0): ');
81 try {
82 steps = int.parse(stdin.readLineSync());
83 } catch (e) {}
84 }
85 return steps;
86 }
87
88 class AsciiImage {
89 static const placeholder = ' ';
90
91 String name;
92
93 final List<List<String>> asciis;
94
95 AsciiImage(this.name, this.asciis);
96
97 AsciiImage.generateEmptyImage(this.name, int height, int width)
98 : asciis = List.generate(
99 height, (_) => List.filled(width, placeholder, growable: true));
100
101 int get height => asciis.length;
102
103 int get width => asciis.isEmpty ? 0 : asciis[0].length;
104
105 void moveLeft(int x) {
106 for (var line in asciis) line.removeRange(0, min(x, width));
107 }
108
109 void moveRight(int x) {
110 for (var line in asciis) line.insertAll(0, List.filled(x, placeholder));
111 }
112
113 void moveUp(int y) => asciis.removeRange(0, min(y, height));
114
115 void moveDown(int y) => asciis.insertAll(0,
116 List.generate(y, (_) => List.filled(width, placeholder, growable: true)));
117
118 // 裁剪,根据给定的高和宽,当图形移动超出屏幕显示范围时,需要裁剪
119 AsciiImage cut(int h, int w) => AsciiImage(
120 name,
121 List.generate(
122 min(height, h), (i) => asciis[i].sublist(0, min(width, w))));
123
124 // 字符画的本质是字符串,因此将其转换为字符串以便输出
125 String get content {
126 var buffer = StringBuffer();
127 for (var line in asciis) buffer.write('${line.join()}\n');
128 return buffer.toString();
129 }
130
131 bool isValid() => height > 0 && width > 0;
132 }
示例用的小狗ASCII文件内容dog.txt:
L#KKK#G.
.G EK
.i t,
W ;,
;f
D K G #
# ; f # .#t#
L L f K .
i # ; fDW
: E W ,
E f i K
#t D . .,
:: t# ii
#EG E K.
.; ED: , ,DWi
# # ##fG;
j ; L D
t; W #L#; EL
j, # W. ## ;t W
;. , ;#i K ;j
L i tW f LWD
D # # # Di
D i ,E :
, # t; .
. E ;i D
. t D #
f j # t
# # : L
, :t G #, D
# ## # , E
# .# i ,
;#t t;E j W
.K :# j D #
K ,#: . # K
K #f E D .#
# GDE#. f . K
t # Wtf .
;L;iG####WWEEEi ;i E
.
浙公网安备 33010602011771号