Differences From Artifact [d9bf3ea129c7cbc5]:
- File
src/game.d
- 2012-07-16 03:53:00 - part of checkin [971863e35a] on branch trunk - No-cloning death-move(). With several fixes to the simulator. (user: kinaba) [annotate]
To Artifact [6849c1cc0eb810a9]:
- File
src/game.d
- 2012-07-16 10:02:57 - part of checkin [b96971b0b6] on branch trunk - refactoring. (user: kinaba) [annotate]
1 1 import util;
2 2
3 3 ////////////////////////////////////////////////////////////////////////////////
4 4
5 +bool is_spacy(char c)
6 +{
7 + return c==' ' || c=='.' || c=='R' || c=='!' || c=='\\' || c=='O';
8 +}
9 +
10 +bool is_rocky(char c)
11 +{
12 + return c=='*' || c=='@';
13 +}
14 +
15 +bool is_true_space(char c)
16 +{
17 + return c==' ';
18 +}
19 +
20 +bool is_trampoline_source(char c)
21 +{
22 + return 'A'<=c && c<='I';
23 +}
24 +
25 +bool is_rocklambda(char c)
26 +{
27 + return is_rocky(c) || c=='\\';
28 +}
29 +
30 +////////////////////////////////////////////////////////////////////////////////
31 +
5 32 class Pos
6 33 {
7 34 public immutable int y, x;
8 35 mixin DeriveCreate;
9 36 mixin DeriveCompare;
10 37 mixin DeriveShow;
11 38 Pos clone() const { return cast(Pos) this; }
................................................................................
37 64 assert( aa[new Pos(1,2)]==2 );
38 65 }
39 66
40 67 ////////////////////////////////////////////////////////////////////////////////
41 68
42 69 class Water
43 70 {
44 - public immutable int base, pace;
45 - mixin DeriveCreate;
46 71 mixin DeriveShow;
72 +
73 +private:
74 + immutable int base, pace;
75 + mixin DeriveCreate;
47 76 Water clone() const { return cast(Water) this; }
48 77
49 78 static load(string[string] params)
50 79 {
51 80 return new Water(params.get("Water", "0").to!int(),
52 81 params.get("Flooding", "0").to!int());
53 82 }
................................................................................
83 112 assert( 1 == w.level(5) );
84 113 }
85 114
86 115 ////////////////////////////////////////////////////////////////////////////////
87 116
88 117 class Hige
89 118 {
90 - public immutable int pace;
91 - mixin DeriveCreate;
92 119 mixin DeriveShow;
120 +
121 +private:
122 + immutable int pace;
123 + mixin DeriveCreate;
93 124 Hige clone() const { return cast(Hige)this; }
94 125
95 126 static load(string[string] params)
96 127 {
97 128 return new Hige(params.get("Growth", "25").to!int());
98 129 }
99 130
................................................................................
108 139 }
109 140 }
110 141
111 142 ////////////////////////////////////////////////////////////////////////////////
112 143
113 144 class Trampoline
114 145 {
115 - private immutable char[] target_of_;
116 - private immutable char[][] source_of_;
117 - private immutable Pos[] position_of_;
118 - private immutable char[] source_list_;
119 - private immutable char[] target_list_;
120 146 mixin DeriveShow;
147 +
148 +private:
149 + immutable char[] target_of_;
150 + immutable char[][] source_of_;
151 + immutable Pos[] position_of_;
152 + immutable char[] source_list_;
153 + immutable char[] target_list_;
121 154 Trampoline clone() const { return cast(Trampoline) this; }
155 +
122 156 this(Map m, char[char] tramparam)
123 157 {
124 158 auto ta = new char['I'+1];
125 159 auto sr = new char[]['9'+1];
126 160 auto po = new Pos[max('I','9')+1];
127 161 char[] sl, tl;
128 162 foreach(fr,to; tramparam) {
................................................................................
144 178 target_of_ = cast(immutable) ta;
145 179 source_of_ = cast(immutable) sr;
146 180 position_of_ = cast(immutable) po;
147 181 source_list_ = cast(immutable) sl;
148 182 target_list_ = cast(immutable) tl;
149 183 }
150 184
151 -@property const:
185 +public @property const:
152 186 const(char[]) source_list() { return source_list_; }
153 187 const(char[]) target_list() { return target_list_; }
154 188 const(char[]) source_of(char c) { return source_of_[c]; }
155 189 char target_of(char c) { return target_of_[c]; }
156 190 Pos[] source_pos(char c) {
157 191 Pos[] ps;
158 192 foreach(s; source_of(c))
................................................................................
164 198
165 199 ////////////////////////////////////////////////////////////////////////////////
166 200
167 201 class Map
168 202 {
169 203 mixin DeriveShow;
170 204
171 - char[][] data;
205 + private char[][] data;
172 206 Pos robot;
173 207 Pos lift;
174 - int waterproof;
175 - int razor;
208 + private int waterproof;
209 + private int collected_razor;
176 210 int collected_lambda;
177 211 int total_lambda;
178 - bool cleared;
179 - Pos[] may_update;
212 + private bool cleared;
213 + private Pos[] may_update;
180 214
181 - Map clone() const { return new Map(this); }
182 - this(in Map m) {
215 + private Map clone() const { return new Map(this); }
216 + private this(in Map m) {
183 217 foreach(s; m.data)
184 218 this.data ~= s.dup;
185 - this.robot = m.robot.clone();
186 - this.lift = m.lift.clone();
219 + this.robot = m.robot.clone();
220 + this.lift = m.lift.clone();
187 221 this.waterproof = m.waterproof;
188 - this.razor = m.razor;
222 + this.collected_razor = m.collected_razor;
189 223 this.collected_lambda = m.collected_lambda;
190 - this.total_lambda = m.total_lambda;
224 + this.total_lambda = m.total_lambda;
225 + this.cleared = m.cleared;
191 226 this.may_update = (cast(Map)m).may_update.dup;
192 - this.cleared = m.cleared;
193 227 }
194 228
229 + const {
230 + @property {
231 + int H() { return data.length; }
232 + int W() { return data[0].length; }
233 + int num_razor() { return collected_razor; }
234 +
235 + Pos[] razors() { return objects('!'); }
236 + Pos[] lambdas() { return objects('\\'); }
237 + }
238 +
239 + Pos[] objects(char c) {
240 + Pos[] ans;
241 + for(int y=1; y<=H; ++y)
242 + for(int x=1; x<=W; ++x)
243 + if(this[y,x] == c)
244 + ans ~= new Pos(y,x);
245 + return ans;
246 + }
247 +
248 + char opIndex(int y, int x) {
249 + --y, --x;
250 + if(y<0||H<=y||x<0||W<=x)
251 + return '#';
252 + return data[H-1-y][x];
253 + }
254 +
255 + char opIndex(in Pos p) {
256 + return this[p.y, p.x];
257 + }
258 + }
259 +
260 +private:
195 261 this(string[] raw_data, string[string] params, char[char] trampo)
196 262 {
197 263 int width = 0;
198 264 foreach(r; raw_data)
199 265 width = max(width, r.length);
200 266 foreach(r; raw_data) {
201 267 this.data ~= r.dup;
................................................................................
207 273 for(int x=1; x<=W; ++x) {
208 274 if(this[y,x] == 'R')
209 275 this.robot = new Pos(y,x);
210 276 if(this[y,x] == 'L' || this[y,x] == 'O')
211 277 this.lift = new Pos(y,x);
212 278 if(this[y,x] == '\\' || this[y,x] == '@')
213 279 total_lambda++;
214 - if(this[y,x] == '*' || this[y,x] == '@')
280 + if(is_rocky(this[y,x]))
215 281 may_update ~= new Pos(y,x);
216 282 }
217 283
218 - this.waterproof = params.get("Waterproof", "5").to!int();
219 - this.razor = params.get("Razors", "0").to!int();
220 - }
221 -
222 - const @property {
223 - int H() { return data.length; }
224 - int W() { return data[0].length; }
225 - }
226 -
227 - const {
228 - char opIndex(int y, int x)
229 - {
230 - // Adjust coordinate to the spec. bottom-left is (1,1).
231 - --y, --x;
232 - if(y<0||H<=y||x<0||W<=x)
233 - return '#';
234 - return data[H-1-y][x];
235 - }
236 -
237 - char opIndex(in Pos p)
238 - {
239 - return this[p.y, p.x];
240 - }
284 + this.waterproof = params.get("Waterproof", "5").to!int();
285 + this.collected_razor = params.get("Razors", "0").to!int();
241 286 }
242 287
243 288 void opIndexAssign(char c, int y, int x)
244 289 {
245 - // Adjust coordinate to the spec. bottom-left is (1,1).
246 290 --y, --x;
247 291 if(y<0||H<=y||x<0||W<=x)
248 292 return;
249 293 data[H-1-y][x] = c;
250 294 }
251 295
252 296 void opIndexAssign(char c, in Pos p)
253 297 {
254 298 this[p.y, p.x] = c;
255 299 }
256 300
257 - Pos[] objects(char c) const {
258 - Pos[] ans;
259 - for(int y=1; y<=H; ++y)
260 - for(int x=1; x<=W; ++x)
261 - if(this[y,x] == c)
262 - ans ~= new Pos(y,x);
263 - return ans;
264 - }
265 -
266 - Pos[] razors() const { return objects('!'); }
267 - Pos[] lambdas() const { return objects('\\'); }
268 -
269 301 bool command(char c, int turn, bool hige_day, in Trampoline tr)
270 302 {
271 - assert( this[robot] == 'R' );
272 - if(c=='R') return move( 0, +1, hige_day, tr);
273 - if(c=='L') return move( 0, -1, hige_day, tr);
274 - if(c=='U') return move(+1, 0, hige_day, tr);
275 - if(c=='D') return move(-1, 0, hige_day, tr);
276 - if(c=='W') return move( 0, 0, hige_day, tr);
277 - if(c=='S') return use_razor(hige_day);
278 - assert(false);
303 + switch(c)
304 + {
305 + case 'R': return move( 0, +1, hige_day, tr);
306 + case 'L': return move( 0, -1, hige_day, tr);
307 + case 'U': return move(+1, 0, hige_day, tr);
308 + case 'D': return move(-1, 0, hige_day, tr);
309 + case 'W': return move( 0, 0, hige_day, tr);
310 + case 'S': return use_razor(hige_day);
311 + default: assert(false);
312 + }
279 313 }
280 314
281 315 bool use_razor(bool hige_day)
282 316 {
283 - if(razor) {
284 - razor--;
317 + if(collected_razor > 0)
318 + {
319 + collected_razor--;
285 320 for(int dy=-1; dy<=+1; ++dy)
286 321 for(int dx=-1; dx<=+1; ++dx)
287 322 if(this[robot.y+dy,robot.x+dx] == 'W') {
288 323 emptified(new Pos(robot.y+dy,robot.x+dx));
289 324 this[robot.y+dy,robot.x+dx] = ' ';
290 325 }
291 326 }
292 -
293 327 return update(hige_day);
294 328 }
295 329
296 - bool rocky(char c) { return c=='*' || c=='@'; }
297 -
298 - void emptified(Pos p) {
299 - for(int dy=0; dy<=+1; ++dy)
300 - for(int dx=-1; dx<=+1; ++dx)
301 - may_update ~= new Pos(p.y+dy, p.x+dx);
302 - }
330 + // Register a position that may become empty in the last turn.
331 + void emptified(Pos p)
332 + {
333 + for(int dy=0; dy<=+1; ++dy)
334 + for(int dx=-1; dx<=+1; ++dx)
335 + may_update ~= new Pos(p.y+dy, p.x+dx);
336 + }
303 337
304 338 bool move(int dy, int dx, bool hige_day, in Trampoline tr)
305 339 {
306 - emptified(robot);
307 -
308 - int y = robot.y;
309 - int x = robot.x;
310 - if( '\\' == this[y+dy,x+dx] )
311 - collected_lambda++;
312 - if( '!' == this[y+dy,x+dx] )
313 - razor++;
314 - if( 'O' == this[y+dy,x+dx] )
315 - cleared = true;
316 - if( " \\!.O".count(this[y+dy,x+dx])==1 ) {
317 - this[y,x]=' ';
318 - this[y+dy,x+dx]='R';
319 - robot = new Pos(y+dy,x+dx);
320 - } else if(dy==0 && rocky(this[y+dy,x+dx]) && ' '==this[y+dy*2,x+dx*2]) {
321 - char rock = this[y+dy,x+dx];
322 - this[y,x]=' ';
323 - this[y+dy,x+dx]='R';
324 - this[y+dy*2,x+dx*2]=rock;
325 - robot = new Pos(y+dy,x+dx);
340 + Pos next = new Pos(robot.y+dy, robot.x+dx);
341 + int y=robot.y, x=robot.x;
342 +
343 + if( '\\' == this[next] ) collected_lambda++;
344 + if( '!' == this[next] ) collected_razor++;
345 + if( 'O' == this[next] ) cleared = true;
346 +
347 + if( is_spacy(this[next]) )
348 + {
349 + emptified(robot);
350 + this[y,x] = ' ';
351 + this[next] = 'R';
352 + robot = next;
353 + }
354 + else if(dy==0 && is_rocky(this[next]) && ' '==this[y+dy*2,x+dx*2])
355 + {
356 + char rock = this[next];
357 + emptified(robot);
358 + this[y,x] = ' ';
359 + this[next] = 'R';
360 + this[y+dy*2,x+dx*2] = rock;
361 + robot = next;
326 362 may_update ~= new Pos(y+dy*2,x+dx*2);
327 - } else if('A'<=this[y+dy,x+dx] && this[y+dy,x+dx]<='I') {
328 - this[y,x]=' ';
329 - Pos tp = tr.target_pos(this[y+dy,x+dx]);
330 - foreach(p; tr.source_pos(this[tp])) {
363 + }
364 + else if(is_trampoline_source(this[next]))
365 + {
366 + emptified(robot);
367 + this[y,x] = ' ';
368 + Pos tp = tr.target_pos(this[next]);
369 + foreach(p; tr.source_pos(this[tp]))
370 + {
331 371 emptified(p);
332 372 this[p] = ' ';
333 373 }
334 374 this[tp] = 'R';
335 - robot = tp;
375 + robot = tp;
336 376 }
337 377 return update(hige_day);
338 378 }
339 379
340 380 bool update(bool hige_day)
341 381 {
342 382 // Write after all the updates are processed.
................................................................................
343 383 Tuple!(int,int,char)[] write_buffer;
344 384 void write(int y, int x, char c) { write_buffer ~= tuple(y,x,c); }
345 385 void writep(Pos p, char c) { write_buffer ~= tuple(0+p.y,0+p.x,c); }
346 386 scope(exit) {
347 387 may_update.length = 0;
348 388 foreach(wr; write_buffer) {
349 389 this[wr[0],wr[1]] = wr[2];
350 - if(rocky(wr[2]))
390 + if(is_rocky(wr[2]))
351 391 may_update ~= new Pos(wr[0],wr[1]);
352 392 if(wr[2]==' ')
353 393 emptified(new Pos(wr[0], wr[1]));
354 394 }
355 395 }
356 396
357 397 if(collected_lambda == total_lambda)
................................................................................
366 406 may_update ~= new Pos(y,x);
367 407 }
368 408
369 409 sort(may_update);
370 410 foreach(p; may_update) {
371 411 int y = p.y, x = p.x;
372 412 char rock = this[p];
373 - if(rocky(this[p])) {
413 + if(is_rocky(this[p])) {
374 414 if(this[p.D]==' ') {
375 415 writep(p, ' ');
376 416 writep(p.D, (rock=='@'&&this[p.D.D]!=' ' ? '\\' : rock));
377 417 if(robot == p.D.D)
378 418 dead=true;
379 419 }
380 - else if((rocky(this[p.D]) || this[p.D]=='\\') && this[p.R]==' ' && this[p.R.D]==' ') {
420 + else if((is_rocky(this[p.D]) || this[p.D]=='\\') && this[p.R]==' ' && this[p.R.D]==' ') {
381 421 writep(p, ' ');
382 422 writep(p.R.D,(rock=='@'&&this[p.R.D.D]!=' ' ? '\\' : rock));
383 423 if(robot == p.R.D.D)
384 424 dead=true;
385 425 }
386 - else if(rocky(this[p.D]) && this[p.L]==' ' && this[p.L.D]==' ') {
426 + else if(is_rocky(this[p.D]) && this[p.L]==' ' && this[p.L.D]==' ') {
387 427 writep(p, ' ');
388 428 writep(p.L.D, (rock=='@'&&this[p.L.D.D]!=' ' ? '\\' : rock));
389 429 if(robot == p.L.D.D)
390 430 dead=true;
391 431 }
392 432 }
393 433 else if(this[p]=='W') {