1 import std.algorithm;
2 import std.array;
3 import std.conv;
4 import std.stdio;
5 import std.string;
6 import std.typecons;
7 import core.stdc.signal;
8 import core.stdc.stdlib;
9 import dfl.all;
10
11 class Map
12 {
13 private char[][] data;
14 bool dead = false;
15 bool cleared = false;
16 int water = 0;
17 int flooding = 0;
18 int water_proof = 10;
19 int underwater = 0;
20 int flooding_counter = 0;
21
22 this(File input)
23 {
24 string line;
25 while( (line=input.readln().chomp()).length )
26 data ~= line.dup;
27
28 int width = 0;
29 foreach(s; data)
30 width = max(width, s.length);
31
32 // space padding and sentinels
33 foreach(ref s; data) {
34 int p = s.length;
35 s.length = width;
36 s[p..$] = ' ';
37 s = '#' ~ s ~ '#';
38 }
39
40 // vertical sentinel
41 char[] sen = new char[width+2];
42 sen[] = '#';
43 data = sen.dup ~ data ~ sen;
44
45 // flooding
46 water = H-1;
47 while( (line=input.readln()).length ) {
48 string[] ss = line.split();
49 if(ss.length==2 && ss[0]=="Water")
50 water = H-1 - ss[1].to!int();
51 else if(ss.length==2 && ss[0]=="Flooding")
52 flooding = ss[1].to!int();
53 else if(ss.length==2 && ss[0]=="Waterproof")
54 water_proof = ss[1].to!int();
55 }
56 }
57
58 @property const
59 {
60 int W() { return data[0].length; }
61 int H() { return data.length; }
62 string toString() {
63 string result;
64 foreach(i,s; data) {
65 if(i) result ~= '\n';
66 result ~= s.idup;
67 }
68 return result;
69 }
70 }
71
72 int command_R() { if(dead)return 0; write("R"); return move(0, +1); }
73 int command_L() { if(dead)return 0; write("L"); return move(0, -1); }
74 int command_U() { if(dead)return 0; write("U"); return move(-1, 0); }
75 int command_D() { if(dead)return 0; write("D"); return move(+1, 0); }
76 int wait() { if(dead)return 0; update(); write("W"); return -1; }
77 int abort() { if(dead)return 0; cleared=true; write("A"); return gained*25; }
78
79 int move(int dy, int dx) {
80 foreach(y,s; data)
81 foreach(x,c; s)
82 if(c == 'R')
83 return move(dy, dx, y, x);
84 assert(false);
85 }
86
87 int gained = 0; // TODO: atode naosu
88 int move(int dy, int dx, int y, int x) {
89 if(dead)
90 return 0;
91 int score = 0;
92 if(data[y+dy][x+dx]=='\\') {
93 score += 25;
94 ++gained;
95 }
96 if(data[y+dy][x+dx]=='O') {
97 score += gained*50;
98 cleared = true;
99 }
100
101 if(data[y+dy][x+dx]==' ' || data[y+dy][x+dx]=='.'
102 || data[y+dy][x+dx]=='\\' || data[y+dy][x+dx]=='O') {
103 data[y][x]=' ';
104 data[y+dy][x+dx]='R';
105 } else if(dy==0 && data[y+dy][x+dx]=='*' && data[y+2*dy][x+2*dx]==' ') {
106 data[y][x]=' ';
107 data[y+dy][x+dx]='R';
108 data[y+2*dy][x+2*dx]='*';
109 }
110 update();
111 return score-1;
112 }
113
114 void update() {
115 char[][] next;
116 foreach(y,s; data)
117 next ~= s.dup;
118
119 bool lambda = false;
120 for(int y=1; y+1<H; ++y)
121 for(int x=1; x+1<W; ++x)
122 lambda |= (data[y][x] == '\\');
123
124 for(int y=H-2; y>=1; --y)
125 for(int x=1; x+1<W; ++x) {
126 if(data[y][x]=='*') {
127 if(data[y+1][x]==' ') {
128 next[y][x]=' ';
129 next[y+1][x]='*';
130 if(next[y+2][x]=='R')
131 dead=true;
132 }
133 else if(data[y+1][x]=='*' && data[y][x+1]==' ' && data[y+1][x+1]==' ') {
134 next[y][x]=' ';
135 next[y+1][x+1]='*';
136 if(next[y+2][x+1]=='R')
137 dead=true;
138 }
139 else if(data[y+1][x]=='*' && data[y][x-1]==' ' && data[y+1][x-1]==' ') {
140 next[y][x]=' ';
141 next[y+1][x-1]='*';
142 if(next[y+2][x-1]=='R')
143 dead=true;
144 }
145 else if(data[y+1][x]=='\\' && data[y][x+1]==' ' && data[y+1][x+1]==' ') {
146 next[y][x]=' ';
147 next[y+1][x+1]='*';
148 if(next[y+2][x+1]=='R')
149 dead=true;
150 }
151 }
152 else if(data[y][x]=='L') {
153 if(!lambda)
154 next[y][x] = 'O';
155 }
156 }
157 data = next;
158
159 if(flooding) {
160 bool wa = false;
161 for(int y=water; y+1<H; ++y)
162 for(int x=1; x+1<W; ++x)
163 if(data[y][x]=='R') {
164 wa = true;
165 underwater++;
166 if(underwater > water_proof)
167 dead = true;
168 }
169 flooding_counter ++;
170 if(flooding_counter == flooding) {
171 flooding_counter = 0;
172 water --;
173 }
174 }
175 }
176
177 int clever()
178 {
179 if(dead)
180 return 0;
181 int sy,sx;
182 int[] ly,lx;
183 int oy,ox;
184 for(int y=0; y<H; ++y)
185 for(int x=0; x<W; ++x)
186 if(data[y][x]=='R')
187 sy=y, sx=x;
188 else if(data[y][x]=='\\')
189 ly~=y, lx~=x;
190 else if(data[y][x]=='O')
191 oy=y, ox=x;
192 if(ly.length==0) {
193 auto r = search(sy,sx,oy,ox);
194 switch(r[0]) {
195 case 'D': return command_D();
196 case 'U': return command_U();
197 case 'L': return command_L();
198 case 'R': return command_R();
199 case 'A': return abort();
200 default: return wait();
201 }
202 } else {
203 Tuple!(char,int)[] cand;
204 for(int i=0; i<ly.length; ++i) {
205 auto r = search(sy,sx,ly[i],lx[i]);
206 cand ~= r;
207 }
208 sort!((Tuple!(char,int) c1, Tuple!(char,int) c2){
209 if(c1[1] != c2[1])
210 return c1[1] < c2[1];
211 return c1[0] < c2[0];
212 })(cand);
213 switch(cand[0][0]) {
214 case 'D': return command_D();
215 case 'U': return command_U();
216 case 'L': return command_L();
217 case 'R': return command_R();
218 case 'A': return abort();
219 default: return wait();
220 }
221 }
222 return wait();
223 }
224 Tuple!(char,int) search(int sy, int sx, int oy, int ox)
225 {
226 alias Tuple!(int,"y",int,"x") Pt;
227 Pt[] q = [Pt(oy,ox)];
228 bool[][] v = new bool[][](H,W);
229 for(int step=1; q.length; ++step) {
230 Pt[] q2;
231 foreach(p; q) {
232 int[] dy=[-1,+1,0,0];
233 int[] dx=[0,0,-1,+1];
234 for(int i=0; i<4; ++i) {
235 int y = p.y+dy[i];
236 int x = p.x+dx[i];
237 if(v[y][x]) continue;
238 if(y==sy && x==sx) {
239 if(i==0) return tuple('D',step);
240 if(i==1) return tuple('U',step);
241 if(i==2) return tuple('R',step);
242 if(i==3) return tuple('L',step);
243 } else if(data[y][x]==' '||data[y][x]=='\\') {
244 q2 ~= Pt(y,x);
245 v[y][x]=true;
246 } else if(data[y][x]=='.' && data[y-1][x]!='*') {
247 q2 ~= Pt(y,x);
248 v[y][x]=true;
249 }
250 }
251 }
252 q = q2;
253 }
254 q = [Pt(oy,ox)];
255 v = new bool[][](H,W);
256 for(int step=1<<10; q.length; ++step) {
257 Pt[] q2;
258 foreach(p; q) {
259
260 int[] dy=[-1,+1,0,0];
261 int[] dx=[0,0,-1,+1];
262 for(int i=0; i<4; ++i) {
263 int y = p.y+dy[i];
264 int x = p.x+dx[i];
265 if(v[y][x]) continue;
266 if(y==sy && x==sx) {
267 if(i==0) return tuple('D',step);
268 if(i==1) return tuple('U',step);
269 if(i==2) return tuple('R',step);
270 if(i==3) return tuple('L',step);
271 } else if(data[y][x]==' '||data[y][x]=='\\') {
272 q2 ~= Pt(y,x);
273 v[y][x]=true;
274 } else if(data[y][x]=='.'/* && data[y-1][x]!='*'*/) {
275 q2 ~= Pt(y,x);
276 v[y][x]=true;
277 }
278 }
279 }
280 q = q2;
281 }
282 return tuple('A',int.max);
283 }
284 }
285
286 class MyForm : Form
287 {
288 Map m;
289 int score;
290
291 this(Map m)
292 {
293 noMessageFilter();
294 this.m = m;
295 this.text = .text("Score: ", score, " air[",m.water_proof-m.underwater,"]");
296 this.keyDown ~= &myKey;
297 this.score = 0;
298 }
299 override void onResize(EventArgs ev) {
300 invalidate();
301 }
302 override void onPaint(PaintEventArgs ev)
303 {
304 int Z = min(this.clientSize.width/(m.W-2), this.clientSize.height/(m.H-2));
305 Font font = new Font("MS Gothic", Z-4);
306 Graphics g = ev.graphics;
307 g.fillRectangle(Color(0,233,255), Rect(0,Z*(m.water-1),this.clientSize.width,this.clientSize.height-(Z*(m.water-1))));
308 for(int y=1; y+1<m.H; ++y)
309 for(int x=1; x+1<m.W; ++x) {
310 if(m.data[y][x]=='*') {
311 g.drawText("岩", font, Color(0,0,0), Rect((x-1)*Z, (y-1)*Z, Z, Z));
312 }
313 if(m.data[y][x]=='\\') {
314 g.drawText("λ", font, Color(0,255,0), Rect((x-1)*Z, (y-1)*Z, Z, Z));
315 }
316 if(m.data[y][x]=='R') {
317 if(m.dead)
318 g.drawText("Я", font, Color(255,0,0), Rect((x-1)*Z, (y-1)*Z, Z, Z));
319 else
320 g.drawText("R", font, Color(128,128,0), Rect((x-1)*Z, (y-1)*Z, Z, Z));
321 }
322 if(m.data[y][x]=='L') {
323 g.drawText("扉", font, Color(255,255,0), Rect((x-1)*Z, (y-1)*Z, Z, Z));
324 }
325 if(m.data[y][x]=='O') {
326 g.drawText("外", font, Color(255,255,0), Rect((x-1)*Z, (y-1)*Z, Z, Z));
327 }
328 if(m.data[y][x]=='#') {
329 g.drawText("#", font, Color(0,0,0), Rect((x-1)*Z, (y-1)*Z, Z, Z));
330 }
331 if(m.data[y][x]=='.') {
332 g.drawText("・", font, Color(128,40,0), Rect((x-1)*Z, (y-1)*Z, Z, Z));
333 }
334 }
335 }
336 void myKey(Control c, KeyEventArgs ev)
337 {
338 switch(ev.keyCode)
339 {
340 case Keys.DOWN:
341 score += m.command_D();
342 stdout.flush();
343 break;
344 case Keys.UP:
345 score += m.command_U();
346 stdout.flush();
347 break;
348 case Keys.LEFT:
349 score += m.command_L();
350 stdout.flush();
351 break;
352 case Keys.RIGHT:
353 score += m.command_R();
354 stdout.flush();
355 break;
356 case Keys.W:
357 score += m.wait();
358 stdout.flush();
359 break;
360 case Keys.A:
361 score += m.abort();
362 stdout.flush();
363 break;
364 case Keys.G:
365 score += m.clever();
366 stdout.flush();
367 break;
368 default:
369 break;
370 }
371 if(m.cleared) {
372 writeln();
373 writeln("Score: ", score);
374 Application.exit();
375 }
376 this.text = .text("Score: ", score, " air[",m.water_proof-m.underwater,"]");
377 invalidate();
378 }
379 }
380
381 extern(C) {
382 void sigint(int) {
383 write("A");
384 stdout.flush();
385 exit(0);
386 }
387 }
388
389 void main(string[] args)
390 {
391 signal(SIGINT, &sigint);
392
393 Form myForm = new MyForm(new Map(File(args[1])));
394 Application.run(myForm);
395 }