Check-in Differences
Not logged in

Difference From:

[fc9286dad1] uum (user: kinaba, tags: redesign, date: 2012-07-15 12:11:33)

To:

[c88611bab8] horock solver (user: kinaba, tags: trunk, date: 2012-07-15 12:40:58)

Added maps/horock1.map version [7e25f07bad607ab0]

1 +#####L###### 2 +#....R....A# 3 +#..........# 4 +#\. #@\ ..# 5 +#\...#* ### 6 +#\.........# 7 +#\..*.@....# 8 +####@@. ..# 9 +#....#.. ### 10 +#....#..**.# 11 +#.1..# .\\# 12 +############ 13 + 14 +Trampoline A targets 1

Added maps/horock2.map version [2fb25272ef9314ea]

1 +################# 2 +#A .@@@@@@# 3 +# .. #*...*1# 4 +# .##########*## 5 +# ..@ \\\\#* # 6 +# ..# #####*.# 7 +# .##### * # 8 +#LR ....... . # 9 +################# 10 + 11 +Trampoline A targets 1 12 +Flooding 10 13 +Water 0

Added maps/horock3.map version [d80af0f6153d8208]

1 +################# 2 +# \\\\# * * # 3 +# ## !!#..@. #### 4 +#* @...####.....# 5 +#\\ \@...........# 6 +#\ # .\@ # #.######## 7 +#\ # ..\@ # .. # 8 +#\ # .. \@ # .. # 9 +#\ # *.. \@ ####### # 10 +#\ # ### \@ # #W # 11 +### # \@ # ##L# 12 +#...#@@**@@**\@ # 13 +#R ..........!\## 14 +################# 15 + 16 +Growth 25

Modified score_memo.txt from [95f0626c958d6d31] to [d46da9814cb8619d].

1 1 contest1 212! 2 2 contest2 280? 3 3 contest3 275! 4 4 contest4 561? 5 5 contest5 1281? 6 -contest6 737 // deadend trap 6 +contest6 737 7 7 contest7 867? 8 -contest8 1245 // tricky 8 +contest8 1245 9 9 contest9 3042? 10 -contest10 2076 // * on lambda, must move * first 11 -flood1 569 // too slow, because of 1-left danger lambda 10 +contest10 2076 11 +flood1 569 12 12 flood2 280? 13 -flood3 802 // too slow, drown 14 -flood4 970 // incorrect order of digging 13 +flood3 802 14 +flood4 970 15 15 flood5 561? 16 -trampoline1 291 // * on trampoline. must move * first 16 +trampoline1 291 17 17 trampoline2 1728? 18 -trampoline3 698 // * on trampoline target. must move * first. 18 +trampoline3 698 19 19 beard1 856? 20 -beard2 2792 // hutsu-ni muzui 21 -beard3 811 // tricky. must hurry to cut hige. 22 -beard4 677 // deadend trap 20 +beard2 2792 21 +beard3 811 22 +beard4 677

Modified src/driver.d from [3d15ff20e76164b8] to [cf90fae27c74ab2c].

6 6 // this(in Game g); 7 7 void on_game_changed(char c, in Game g, bool finished); 8 8 } 9 9 10 10 class Driver 11 11 { 12 12 this(Game g) { this.game = g; } 13 - this(File game_data) { this(new Game(game_data)); } 13 + this(File game_data) { this(Game.load(game_data)); } 14 14 15 15 void command(char c) 16 16 { 17 17 if( finished ) 18 18 return; 19 19 if( c == 'A' ) 20 20 aborted = true;

Modified src/game.d from [fc05481901940844] to [403db28cbc4609b7].

126 126 Pos robot; 127 127 Pos lift; 128 128 int waterproof; 129 129 Pos[char] tr_target; 130 130 Pos[][char] tr_source; 131 131 const(Hige) hige; 132 132 int razor; 133 + int collected_lambda; 134 + int total_lambda; 133 135 134 136 Map clone() const { return new Map(this); } 135 137 this(in Map m) { 136 138 foreach(s; m.data) 137 139 this.data ~= s.dup; 138 140 this.robot = m.robot.clone(); 139 141 this.lift = m.lift.clone(); 140 142 this.waterproof = m.waterproof; 141 143 this.tr_target = cast(Pos[char])m.tr_target; 142 144 this.tr_source = cast(Pos[][char])m.tr_source; 143 145 this.hige = m.hige.clone(); 144 146 this.razor = m.razor; 147 + this.collected_lambda = m.collected_lambda; 148 + this.total_lambda = m.total_lambda; 145 149 } 146 150 147 151 this(string[] raw_data, string[string] params, char[char] trampo) 148 152 { 149 153 int width = 0; 150 154 foreach(r; raw_data) 151 155 width = max(width, r.length); ................................................................................ 157 161 158 162 for(int y=1; y<=H; ++y) 159 163 for(int x=1; x<=W; ++x) { 160 164 if(this[y,x] == 'R') 161 165 this.robot = new Pos(y,x); 162 166 if(this[y,x] == 'L' || this[y,x] == 'O') 163 167 this.lift = new Pos(y,x); 168 + if(this[y,x] == '\\' || this[y,x] == '@') 169 + total_lambda++; 164 170 } 165 171 166 172 Pos[char] tr_pos; 167 173 for(int y=1; y<=H; ++y) 168 174 for(int x=1; x<=W; ++x) { 169 175 char c = this[y,x]; 170 176 if('1'<=c && c<='9' || 'A'<=c&&c<='I') ................................................................................ 234 240 for(int y=1; y<=H; ++y) 235 241 for(int x=1; x<=W; ++x) 236 242 if(this[y,x] == 'L' || this[y,x] == 'O') 237 243 return false; 238 244 return true; 239 245 } 240 246 241 - Tuple!(int,bool) command(char c, int turn) 247 + bool command(char c, int turn) 242 248 { 243 249 assert( this[robot] == 'R' ); 244 250 if(c=='R') return move( 0, +1, turn); 245 251 if(c=='L') return move( 0, -1, turn); 246 252 if(c=='U') return move(+1, 0, turn); 247 253 if(c=='D') return move(-1, 0, turn); 248 254 if(c=='W') return move( 0, 0, turn); 249 255 if(c=='S') return use_razor(turn); 250 256 assert(false); 251 257 } 252 258 253 - Tuple!(int, bool) use_razor(int turn) 259 + bool use_razor(int turn) 254 260 { 255 261 if(razor) { 256 262 razor--; 257 263 for(int dy=-1; dy<=+1; ++dy) 258 264 for(int dx=-1; dx<=+1; ++dx) 259 265 if(this[robot.y+dy,robot.x+dx] == 'W') 260 266 this[robot.y+dy,robot.x+dx] = ' '; 261 267 } 262 268 263 - bool dead = update(turn); 264 - return tuple(0,dead); 269 + return update(turn); 265 270 } 266 271 267 - Tuple!(int, bool) move(int dy, int dx, int turn) 272 + bool rocky(char c) { return c=='*' || c=='@'; } 273 + 274 + bool move(int dy, int dx, int turn) 268 275 { 269 276 int y = robot.y; 270 277 int x = robot.x; 271 - int lambda = 0; 272 278 if( '\\' == this[y+dy,x+dx] ) 273 - lambda++; 279 + collected_lambda++; 274 280 if( '!' == this[y+dy,x+dx] ) 275 281 razor++; 276 282 if( " \\!.O".count(this[y+dy,x+dx])==1 ) { 277 283 this[y,x]=' '; 278 284 this[y+dy,x+dx]='R'; 279 285 robot = new Pos(y+dy,x+dx); 280 - } else if(dy==0 && '*'==this[y+dy,x+dx] && ' '==this[y+dy*2,x+dx*2]) { 286 + } else if(dy==0 && rocky(this[y+dy,x+dx]) && ' '==this[y+dy*2,x+dx*2]) { 287 + char rock = this[y+dy,x+dx]; 281 288 this[y,x]=' '; 282 289 this[y+dy,x+dx]='R'; 283 - this[y+dy*2,x+dx*2]='*'; 290 + this[y+dy*2,x+dx*2]=rock; 284 291 robot = new Pos(y+dy,x+dx); 285 292 } else if('A'<=this[y+dy,x+dx] && this[y+dy,x+dx]<='I') { 286 293 this[y,x]=' '; 287 294 Pos tp = tr_target[this[y+dy,x+dx]]; 288 295 foreach(p; tr_source[this[tp]]) 289 296 this[p] = ' '; 290 297 this[tp] = 'R'; 291 298 robot = tp; 292 299 } 293 - bool dead = update(turn); 294 - return tuple(lambda,dead); 300 + return update(turn); 295 301 } 296 302 297 303 bool update(int turn) 298 304 { 299 305 bool dead = false; 300 306 301 307 char[][] next; 302 308 foreach(y,s; data) 303 309 next ~= s.dup; 304 310 305 311 ref char access(Pos p) { return next[H-p.y][p.x-1]; } 306 312 307 - bool lambda = false; 308 - for(int y=1; y<=H; ++y) 309 - for(int x=1; x<=W; ++x) 310 - lambda |= (this[y,x] == '\\'); 311 - 312 313 for(int y=1; y<=H; ++y) 313 314 for(int x=1; x<=W; ++x) { 314 315 Pos p = new Pos(y,x); 315 - if(this[p]=='*') { 316 + char rock = this[p]; 317 + if(rocky(this[p])) { 316 318 if(this[p.D]==' ') { 317 319 access(p) =' '; 318 - access(p.D)='*'; 320 + access(p.D)=(rock=='@'&&this[p.D.D]!=' ' ? '\\' : rock); 319 321 if(robot == p.D.D) 320 322 dead=true; 321 323 } 322 - else if((this[p.D]=='*' || this[p.D]=='\\') && this[p.R]==' ' && this[p.R.D]==' ') { 324 + else if((rocky(this[p.D]) || this[p.D]=='\\') && this[p.R]==' ' && this[p.R.D]==' ') { 323 325 access(p)=' '; 324 - access(p.R.D)='*'; 326 + access(p.R.D)=(rock=='@'&&this[p.R.D.D]!=' ' ? '\\' : rock); 325 327 if(robot == p.R.D.D) 326 328 dead=true; 327 329 } 328 - else if(this[p.D]=='*' && this[p.L]==' ' && this[p.L.D]==' ') { 330 + else if(rocky(this[p.D]) && this[p.L]==' ' && this[p.L.D]==' ') { 329 331 access(p)=' '; 330 - access(p.L.D)='*'; 332 + access(p.L.D)=(rock=='@'&&this[p.L.D.D]!=' ' ? '\\' : rock); 331 333 if(robot == p.L.D.D) 332 334 dead=true; 333 335 } 334 336 } 335 337 else if(this[p]=='L') { 336 - if(!lambda) 338 + if(collected_lambda == total_lambda) 337 339 access(p) = 'O'; 338 340 } 339 341 else if(this[p]=='W') { 340 342 if( hige.is_growing_turn(turn) ) 341 343 for(int dy=-1; dy<=+1; ++dy) 342 344 for(int dx=-1; dx<=+1; ++dx) 343 345 if(this[p.y+dy,p.x+dx] == ' ') ................................................................................ 349 351 } 350 352 } 351 353 352 354 //////////////////////////////////////////////////////////////////////////////// 353 355 354 356 class Game 355 357 { 356 -public: 357 - this(File input) 358 + mixin DeriveShow; 359 + 360 + static Game load(File input) 358 361 { 359 - // Read map data 360 - string[] map_data_lines; 362 + string[] raw_data; 363 + string[string] params; 364 + 365 + // Raw map data; read until empty line. 361 366 for(string line; !(line=input.readln().chomp()).empty; ) 362 - map_data_lines ~= line; 367 + raw_data ~= line; 363 368 364 - // H*W 365 - H_ = map_data_lines.length; 366 - W_ = 0; 367 - foreach(mdl; map_data_lines) 368 - W_ = max(W_, mdl.length); 369 - 370 - // Copy to modifiable buffer and adjust coordinates. 371 - raw_data_ = new char[][H_+1]; 372 - foreach(i,mdl; map_data_lines) { 373 - char[] buf = new char[mdl.length+1]; 374 - buf[0] = '#'; 375 - buf[1..$] = mdl[]; 376 - raw_data_[H_-i] = buf; 369 + // Additional commands; read until EOF. 370 + char[char] trampo; 371 + for(string line; !(line=input.readln()).empty; ) { 372 + string[] ss = line.split(); 373 + if( ss.length == 2 ) 374 + params[ss[0]] = ss[1]; 375 + if( ss.length == 4 && ss[0]=="Trampoline" && ss[2]=="targets" ) 376 + trampo[ss[1][0]] = ss[3][0]; 377 377 } 378 378 379 - // Detect objects 380 - for(int y=1; y<=H_; ++y) 381 - for(int x=1; x<raw_data_[y].length; ++x) 382 - { 383 - char c = raw_data_[y][x]; 384 - switch(c) 385 - { 386 - case '#': 387 - case '.': 388 - case ' ': 389 - break; 390 - case 'L': 391 - case 'O': 392 - lift_pos_ = new Pos(y,x); 393 - break; 394 - case 'A': .. case 'I': 395 - case '1': .. case '9': 396 - trampoline_pos_[c] = new Pos(y,x); 397 - break; 398 - case '!': 399 - razor_pos_ ~= new Pos(y,x); 400 - break; 401 - case '\\': 402 - lambda_pos_ ~= new Pos(y,x); 403 - break; 379 + return load(raw_data, params, trampo); 380 + } 381 + 382 + static Game load(string[] raw_data, string[string] params, char[char] trampo = null) 383 + { 384 + return new Game(raw_data, params, trampo); 385 + } 386 + 387 + this(string[] raw_data, string[string] params, char[char] trampo) 388 + { 389 + this.map = Map.load(raw_data, params, trampo); 390 + this.water = Water.load(params); 391 + } 392 + 393 + Game clone() const { return new Game(this); } 394 + this(in Game g) { 395 + map = g.map.clone(); 396 + water = g.water.clone(); 397 + turn = g.turn; 398 + dead = g.dead; 399 + cleared = g.cleared; 400 + under_water = g.under_water; 401 + } 402 + 403 + void command(char c) 404 + { 405 + assert(c != 'A'); 406 + if(dead || cleared) 407 + return; 404 408 405 - // Moving objects are erased from raw_data_ 406 - case 'R': 407 - robot_pos_ = new Pos(y,x); 408 - raw_data_[y][x] = ' '; 409 - break; 410 - case '*': 411 - case 'W': 412 - dynamic_objects_[new Pos(y,x)] = c; 413 - raw_data_[y][x] = ' '; 414 - if(c=='*') 415 - may_update_[new Pos(y,x)] = true; 416 - break; 417 - default: 418 - assert(false); 419 - } 409 + // TODO: clarify the event order 410 + bool dead_now = map.command(c, turn); 411 + if( map.cleared() ) { 412 + cleared = true; 413 + } 414 + else { 415 + if( dead_now ) 416 + dead = true; 417 + } 418 + if(!cleared) { 419 + if( map.robot.y <= water_level ) 420 + ++under_water; 421 + else 422 + under_water = 0; 423 + if( under_water > map.waterproof ) 424 + dead = true; 420 425 } 426 + turn += 1; 427 + } 421 428 422 - // Read other parameters 423 - for(string line; !(line=input.readln()).empty; ) 424 - { 425 - string[] ss = line.split(); 426 - if( ss.length == 2 ) 427 - switch(ss[0]) 428 - { 429 - case "Water": water_base_ = ss[1].to!int(); break; 430 - case "Flooding": water_pace_ = ss[1].to!int(); break; 431 - case "Waterproof": max_air_ = ss[1].to!int(); break; 432 - case "Growth": hige_pace_ = ss[1].to!int(); break; 433 - case "Razors": num_razor_ = ss[1].to!int(); break; 434 - default: assert(false); 435 - } 436 - if( ss.length == 4 && ss[0]=="Trampoline" && ss[2]=="targets" ) 437 - { 438 - char fr=ss[1][0], to=ss[3][0]; 439 - trampoline_[fr] = to; 440 - if(to !in trampoline_rev_) trampoline_rev_[to] = []; 441 - trampoline_rev_[to] ~= fr; 442 - } 443 - } 444 - 445 - air_left_ = max_air_; 446 - } 429 + Map map; 430 + Water water; 431 + int turn = 0; 432 + bool dead = false; 433 + int under_water = 0; 434 + bool cleared = false; 435 + // TODO: when adding members, take care of clone(). 436 + // TODO: fix this poor design. 447 437 448 438 @property const { 449 - int H() { return H_; } 450 - int W() { return W_; } 451 - char trampoline(char c) { return (c in trampoline_ ? trampoline_[c] : 0); } 452 - const(Pos)[] trampoline_rev(char c) { 453 - const(Pos)[] pp; 454 - if(c in trampoline_rev_) { 455 - foreach(ch; trampoline_rev_[c]) 456 - pp ~= trampoline_pos_[ch]; 457 - } 458 - return pp; 459 - } 460 - int water_level() { 461 - return water_pace_ ? water_base_ + turn_/water_pace_ : water_base_; 462 - } 463 - int water_until_rise() { 464 - return water_pace_ ? water_pace_ - turn_%water_pace_ : int.max; 465 - } 466 - int hige_until_rise() { 467 - return hige_pace_ ? hige_pace_ - turn_%hige_pace_ : int.max; 468 - } 469 - bool is_hige_turn() { 470 - return hige_pace_ ? turn_%hige_pace_ == hige_pace_-1 : false; 471 - } 472 - int hp() { return air_left_; } 473 - int num_razor() { return num_razor_; } 474 - bool cleared() { return cleared_; } 475 - bool dead() { return dead_; } 476 - long score() { return num_lambda_*(dead_ ? 25L : cleared_ ? 75L : 50L) - turn_; } 477 - const(Pos) robot() { return robot_pos_; } 478 - const(Pos) lift() { return lift_pos_; } 479 - Pos[] lambdas() { 480 - Pos[] pp; 481 - foreach(p; lambda_pos_) 482 - pp ~= p.clone(); 483 - return pp; 484 - } 485 - Pos[] razors() { 486 - Pos[] pp; 487 - foreach(p; razor_pos_) 488 - pp ~= p.clone(); 489 - return pp; 490 - } 491 - const(Pos)[] higes() { 492 - const(Pos)[] pp; 493 - foreach(p,c; dynamic_objects_) 494 - if(c=='W') 495 - pp ~= p; 496 - return pp; 497 - } 498 - } 499 - const { 500 - char opIndex(in Pos p) { return opIndex(p.y, p.x); } 501 - char opIndex(int y, int x) { return map_get(y, x); } 502 - } 503 - 504 -public: 505 - void command(char c) 506 - { 507 - if(dead || cleared) 508 - return; 509 - 510 - if(c == 'U') command_move(+1, 0); 511 - if(c == 'D') command_move(-1, 0); 512 - if(c == 'L') command_move(0, -1); 513 - if(c == 'R') command_move(0, +1); 514 - if(c == 'S') use_razor(); 515 - if(c == 'W') {} 516 - 517 - if(!cleared) 518 - { 519 - map_update(); 520 - water_update(); 521 - } 522 - turn_ ++; 523 - } 524 - 525 - void command_move(int dy, int dx) 526 - { 527 - int y = robot_pos_.y, x = robot_pos_.x; 528 - char c = this[y+dy, x+dx]; 529 - Pos p = new Pos(y+dy, x+dx); 530 - 531 - switch(c){ 532 - case 'O': 533 - cleared_ = true; 534 - move_robot_to(p); 535 - break; 536 - case '\\': 537 - take_lambda_at(p); 538 - move_robot_to(p); 539 - break; 540 - case '!': 541 - take_razor_at(p); 542 - move_robot_to(p); 543 - break; 544 - case 'A': .. case 'I': 545 - enter_trampoline_at(p, c); 546 - break; 547 - case ' ': 548 - case '.': 549 - move_robot_to(p); 550 - break; 551 - case '*': 552 - if(dy!=0 || this[y,x+dx*2]!=' ') 553 - break; 554 - move_robot_to(p); 555 - push_rock(p, new Pos(y,x+dx*2)); 556 - break; 557 - default: 558 - break; 559 - } 560 - } 561 - 562 - void use_razor() 563 - { 564 - if(num_razor_ == 0) 565 - return; 566 - num_razor_ --; 567 - 568 - for(int dy=-1; dy<=+1; ++dy) 569 - for(int dx=-1; dx<=+1; ++dx) if(dy||dx) 570 - { 571 - Pos p = new Pos(robot_pos_.y+dy, robot_pos_.x+dx); 572 - if(auto it = p in dynamic_objects_) 573 - if(*it == 'W') { 574 - something_gone(p); 575 - dynamic_objects_.remove(p); 576 - } 577 - } 578 - } 579 - 580 - void take_lambda_at(Pos p) 581 - { 582 - map_set_empty(p); 583 - num_lambda_ ++; 584 - lambda_pos_ = lambda_pos_.erase(p); 585 - } 586 - 587 - void take_razor_at(Pos p) 588 - { 589 - map_set_empty(p); 590 - num_razor_ ++; 591 - razor_pos_ = razor_pos_.erase(p); 592 - } 593 - 594 - void enter_trampoline_at(Pos p, char c) 595 - { 596 - char d = trampoline(c); 597 - foreach(cc; trampoline_rev_[d]) { 598 - Pos pp = trampoline_pos_[cc]; 599 - something_gone(pp); 600 - map_set_empty(pp); 601 - } 602 - move_robot_to(trampoline_pos_[d]); 603 - } 604 - 605 - void move_robot_to(Pos p) 606 - { 607 - something_gone(robot_pos_); 608 - map_set_empty(p.y, p.x); 609 - robot_pos_ = p; 610 - } 611 - 612 - void push_rock(Pos fr, Pos to) 613 - { 614 - dynamic_objects_.remove(fr); 615 - dynamic_objects_[to] = '*'; 616 - may_update_[to] = true; 617 - } 618 - 619 - void something_gone(Pos p) 620 - { 621 - for(int dy=0; dy<=+1; ++dy) 622 - for(int dx=-1; dx<=+1; ++dx) if(dy||dx) 623 - may_update_[new Pos(p.y+dy,p.x+dx)] = true; 624 - } 625 - 626 - void map_update() 627 - { 628 - Pos[] may_update_list; 629 - foreach(p,_; may_update_) 630 - if(this[p] == '*') 631 - may_update_list ~= p; 632 - may_update_ = null; 633 - 634 - if( is_hige_turn() ) 635 - foreach(p,c; dynamic_objects_) 636 - if(c == 'W') 637 - may_update_list ~= p; 638 - 639 - sort(may_update_list); 640 - char[Pos] to_be_written; 641 - foreach(p; may_update_list) 642 - if(is_hige_turn() && this[p]=='W') 643 - { 644 - for(int dy=-1; dy<=+1; ++dy) 645 - for(int dx=-1; dx<=+1; ++dx) { 646 - Pos q = new Pos(p.y+dy,p.x+dx); 647 - if( this[q] == ' ' ) 648 - to_be_written[q] = 'W'; 649 - } 650 - } 651 - else 652 - { 653 - int y = p.y; 654 - int x = p.x; 655 - char below = this[y-1,x]; 656 - // * 657 - // _ 658 - if(below==' ') { 659 - Pos q = new Pos(y-1,x); 660 - to_be_written[p] = ' '; 661 - to_be_written[q] = '*'; 662 - may_update_[q] = true; 663 - } 664 - // *_ *_ 665 - // *_ or \_ 666 - else if((below=='*'||below=='\\')&&this[y-1,x+1]==' '&&this[y,x+1]==' ') { 667 - Pos q = new Pos(y-1,x+1); 668 - to_be_written[p] = ' '; 669 - to_be_written[q] = '*'; 670 - may_update_[q] = true; 671 - } 672 - // _* 673 - // _* 674 - else if(below=='*'&&this[y-1,x-1]==' '&&this[y,x-1]==' ') { 675 - Pos q = new Pos(y-1,x-1); 676 - to_be_written[p] = ' '; 677 - to_be_written[q] = '*'; 678 - may_update_[q] = true; 679 - } 680 - } 681 - 682 - foreach(p,c; to_be_written) { 683 - dynamic_objects_[p] = c; 684 - if(c=='*' && p.y==robot_pos_.y+1 && p.x==robot_pos_.x) 685 - dead_ = true; 686 - } 687 - 688 - if(lambda_pos_.empty) 689 - raw_data_[lift_pos_.y][lift_pos_.x] = 'O'; 690 - } 691 - 692 - void water_update() 693 - { 694 - if( robot_pos_.y <= water_level() ) 695 - air_left_ --; 696 - else 697 - air_left_ = max_air_; 698 - if( air_left_ < 0 ) 699 - dead_ = true; 700 - } 701 - 702 -private: 703 - char map_get(int y, int x) const 704 - { 705 - if( y<1 || H<y || x<1 || W<x ) return '#'; 706 - Pos p = new Pos(y,x); 707 - if(p == robot_pos_) 708 - return 'R'; 709 - if(auto it = (p in dynamic_objects_)) 710 - return *it; 711 - if( x<0 || raw_data_[y].length<=x ) return ' '; 712 - return raw_data_[y][x]; 713 - } 714 - 715 - void map_set_empty(in Pos p) 716 - { 717 - return map_set_empty(p.y, p.x); 718 - } 719 - 720 - void map_set_empty(int y, int x) 721 - { 722 - if( y<1 || H<y || x<1 || W<x ) return; 723 - if( x<0 || raw_data_[y].length<=x ) return; 724 - raw_data_[y][x] = ' '; 725 - } 726 - 727 -public: 728 - Game clone() const { return new Game(this); } 729 - this(in Game g) { 730 - H_ = g.H_; 731 - W_ = g.W_; 732 - raw_data_ = new char[][g.raw_data_.length]; 733 - foreach(i,d; g.raw_data_) raw_data_[i] = d.dup; 734 - trampoline_pos_ = cast(Pos[char]) g.trampoline_pos_; 735 - razor_pos_ = (cast(Game)g).razor_pos_.dup; 736 - lambda_pos_ = (cast(Game)g).lambda_pos_.dup; 737 - lift_pos_ = g.lift_pos_.clone(); 738 - robot_pos_ = g.robot_pos_.clone(); 739 - dynamic_objects_ = dup(g.dynamic_objects_); 740 - trampoline_ = (cast(Game)g).trampoline_; 741 - trampoline_rev_ = (cast(Game)g).trampoline_rev_; 742 - water_base_ = g.water_base_; 743 - water_pace_ = g.water_pace_; 744 - max_air_ = g.max_air_; 745 - hige_pace_ = g.hige_pace_; 746 - num_razor_ = g.num_razor_; 747 - num_lambda_ = g.num_lambda_; 748 - turn_ = g.turn_; 749 - air_left_ = g.air_left_; 750 - cleared_ = g.cleared_; 751 - dead_ = g.dead_; 752 - may_update_ = dup(g.may_update_); 753 - } 754 - 755 - V[K] dup(V,K)(in V[K] aa) { 756 - V[K] aa2; 757 - foreach(k,v; aa) aa2[k] = v; 758 - return aa2; 759 - } 760 - 761 -private: 762 - int H_; 763 - int W_; 764 - char[][] raw_data_; 765 - Pos[char] trampoline_pos_; 766 - Pos[] razor_pos_; 767 - Pos[] lambda_pos_; 768 - Pos lift_pos_; 769 - Pos robot_pos_; 770 - char[Pos] dynamic_objects_; 771 - char[char] trampoline_; 772 - char[][char] trampoline_rev_; 773 - int water_base_ = 0; 774 - int water_pace_ = 0; 775 - int max_air_ = 10; 776 - int hige_pace_ = 25; 777 - int num_razor_ = 0; 778 - int num_lambda_ = 0; 779 - 780 - int turn_ = 0; 781 - int air_left_ = 0; 782 - bool cleared_ = false; 783 - bool dead_ = false; 784 - bool[Pos] may_update_; 439 + long score() { return map.collected_lambda*(dead ? 25L : cleared ? 75L : 50L) - turn; } 440 + int water_level() { return water.level(turn); } 441 + int water_until_rise() { return water.until_rise(turn); } 442 + int hige_until_rise() { return map.hige.until_rise(turn); } 443 + int hp() { return map.waterproof - under_water; } 444 + } 785 445 }

Modified src/gui.d from [e84cacd6262f93a7] to [9f0067d733f090c1].

4 4 import driver; 5 5 6 6 class GUI(Solver) : Form, GameObserver 7 7 { 8 8 this(in Game g) 9 9 { 10 10 this.solver = new Solver(g); 11 - setup_size(g.W, g.H); 11 + setup_size(g.map.W, g.map.H); 12 12 setup_resources(g); 13 13 draw(g); 14 14 } 15 15 16 16 private void delegate(char c) fn; 17 17 void set_fn(F)(F f) { this.fn = f; } 18 18 ................................................................................ 58 58 { 59 59 this.graphicContext = new MemoryGraphics(this.clientSize.width, this.clientSize.height); 60 60 this.setStyle(ControlStyles.OPAQUE, true); 61 61 this.font = new Font("MS Gothic", cell-2, GraphicsUnit.PIXEL); 62 62 this.backColor = Color(255,255,255); 63 63 this.colors['#'] = 64 64 this.colors['.'] = Color(255,191,127); 65 - this.colors['*'] = Color(255,127,127); 65 + this.colors['*'] = 66 + this.colors['@'] = Color(255,127,127); 66 67 this.colors['R'] = Color(128,128,0); 67 68 this.colors['d'] = Color(255,0,0); 68 69 this.colors['\\'] = 69 70 this.colors['L'] = 70 71 this.colors['O'] = Color(127,255,127); 71 72 this.colors['w'] = Color(204,229,255); 72 73 this.colors['W'] = 73 74 this.colors['!'] = Color(159,159,159); 74 75 foreach(char c; 'A'..'J') this.colors[c] = Color(142,142,255); 75 76 foreach(char c; '1'..':') this.colors[c] = Color(255,142,255); 76 77 this.render['#'] = "■"; 77 78 this.render['*'] = "✹"; 79 + this.render['@'] = "❁"; 78 80 this.render['.'] = "♒"; 79 81 this.render['\\'] = "λ"; 80 82 this.render['R'] = "☃"; 81 83 this.render['d'] = "☠"; 82 84 this.render['L'] = "☒"; 83 85 this.render['O'] = "☐"; 84 86 this.render['W'] = "ꔣ"; 85 87 this.render['!'] = "✄"; 86 - foreach(char c; 'A'..'J') this.render[c] = [cast(dchar)('☢'+g.trampoline(c)-'1')].to!string(); 88 + foreach(c,tp; g.map.tr_target) { 89 + char d = g.map[tp]; 90 + this.render[c] = [cast(dchar)('☢'+d-'1')].to!string(); 91 + } 87 92 foreach(char c; '1'..':') this.render[c] = [cast(dchar)('☢'+c-'1')].to!string(); 88 93 this.paint ~= (Control c, PaintEventArgs ev) { 89 94 graphicContext.copyTo(ev.graphics, Rect(0,0,this.clientSize.width,this.clientSize.height)); 90 95 }; 91 96 } 92 97 93 98 void draw(in Game g) ................................................................................ 99 104 graphicContext.fillRectangle(this.backColor, Rect(0,0,scrW,scrH)); 100 105 101 106 // Fill water. 102 107 int w = g.water_level(); 103 108 graphicContext.fillRectangle(this.colors['w'], Rect(0, scrH-cell*w-1, scrW, cell*w+1)); 104 109 105 110 // Paint map. 106 - for(int y=1; y<=g.H; ++y) 107 - for(int x=1; x<=g.W; ++x) { 111 + for(int y=1; y<=g.map.H; ++y) 112 + for(int x=1; x<=g.map.W; ++x) { 108 113 Rect r = Rect(cell*(x-1), scrH-cell*y, cell, cell); 109 - char c = g[y,x]; 114 + char c = g.map[y,x]; 110 115 if( c != ' ' ) { 111 116 if( c == 'R' && g.dead ) 112 117 c = 'd'; 113 118 graphicContext.drawText(this.render[c], font, this.colors[c], r); 114 119 } 115 120 } 116 121 117 122 // Update textual info. 118 123 this.text = .text( 119 124 "Score: ", g.score, 120 125 " Air: ", g.hp, 121 126 " Tide: ", g.water_until_rise, 122 127 " Wadler: ", g.hige_until_rise, 123 - " Razor: ", g.num_razor); 128 + " Razor: ", g.map.razor); 124 129 invalidate(); 125 130 } 126 131 127 132 private: 128 133 void setup_keyhandling() 129 134 { 130 135 noMessageFilter();

Modified src/output.d from [e2d0d7db868c3a44] to [045b845268918546].

31 31 override void on_game_changed(char c, in Game g, bool finished) 32 32 { 33 33 if(flushed) 34 34 return; 35 35 36 36 log ~= c; 37 37 score_log ~= g.score; 38 - if(finished || log.length+1==g.W*g.H) 38 + if(finished || log.length+1==g.map.W*g.map.H) 39 39 flush(); 40 40 } 41 41 42 42 private: 43 43 string log; 44 44 long[] score_log; 45 45 bool flushed;

Modified src/solver.d from [22c356ff1975e92f] to [8629f9c48aba56fd].

1 1 import util; 2 2 import game; 3 3 4 +bool rocky(char c){ return c=='*'||c=='@'; } 5 + 4 6 class Solver_0 5 7 { 6 8 this(in Game g) {} 7 9 char single_step() { return 'W'; } 8 10 void force(char c) {} 9 11 } 10 12 ................................................................................ 13 15 int wait_count = 0; 14 16 int choke_count = 0; 15 17 16 18 Game g; 17 19 this(in Game g) 18 20 { 19 21 this.g = g.clone(); 20 - forbidden_cell = new bool[][](g.H+2, g.W+2); 22 + forbidden_cell = new bool[][](g.map.H+2, g.map.W+2); 21 23 } 22 24 23 25 char single_step() 24 26 { 25 27 Tuple!(string,int) de = death_move(g); 26 28 char c = act(g, de[0], de[1]); 27 29 force(c); ................................................................................ 39 41 string death; 40 42 int choice = 0; 41 43 foreach(char c; "UDLRW") { 42 44 Game gg = g.clone(); 43 45 gg.command(c); 44 46 if( !gg.cleared && gg.dead ) 45 47 death ~= c; 46 - else if( gg.robot != g.robot ) 48 + else if( gg.map.robot != g.map.robot ) 47 49 choice++; 48 50 else if( c != 'W' ) // meaningless move 49 51 death ~= c; 50 52 } 51 53 return tuple(death, choice); 52 54 } 53 55 54 56 Tuple!(Pos, int)[] log; 55 57 bool[][] forbidden_cell; 56 58 57 59 char act(const(Game) g, string death, int breath) 58 60 { 59 - const Pos ro = g.robot; 60 - const Pos li = g.lift; 61 - Pos[] la = g.lambdas(); 61 + const Pos ro = g.map.robot; 62 + const Pos li = g.map.lift; 63 + Pos[] la = g.map.lambdas(); 62 64 sort!((Pos a,Pos b){ 63 65 int ad=abs(a.y-li.y)+abs(a.x-li.x); 64 66 int bd=abs(b.y-li.y)+abs(b.x-li.x); 65 67 return ad>bd;; 66 68 })(la); 67 - Pos[] ra = g.razors(); 68 - const(Pos)[] hi = g.higes(); 69 + Pos[] ra = g.map.razors(); 70 + const(Pos)[] hi = g.map.objects('W'); 69 71 70 72 Tuple!(char,int)[] cand; 71 73 char c = 'W'; 72 74 if( la.empty ) { 73 75 cand = search(g, ro, [li], death); 74 76 } else { 75 77 cand ~= search(g, ro, la~ra, death); 76 78 } 77 79 78 80 // 'higesori' mode 79 - if( !hi.empty && g.num_razor>0 ) { 81 + if( !hi.empty && g.map.razor>0 ) { 80 82 int his = 0; 81 83 for(int dy=-1; dy<=+1; ++dy) 82 84 for(int dx=-1; dx<=+1; ++dx) 83 - if(g[ro.y+dy,ro.x+dx] == 'W') 85 + if(g.map[ro.y+dy,ro.x+dx] == 'W') 84 86 his++; 85 87 86 88 if(his>=2 || his==hi.length) 87 89 cand = [tuple('S',int.max)]; 88 90 if(cand.empty) { 89 91 const(Pos)[] tgt; 90 - for(int y=1; y<=g.H; ++y) 91 - for(int x=1; x<=g.W; ++x) 92 - if(g[y,x]=='.'||g[y,x]==' ') { 92 + for(int y=1; y<=g.map.H; ++y) 93 + for(int x=1; x<=g.map.W; ++x) 94 + if(g.map[y,x]=='.'||g.map[y,x]==' ') { 93 95 his = 0; 94 96 for(int dy=-1; dy<=+1; ++dy) 95 97 for(int dx=-1; dx<=+1; ++dx) 96 - if(g[y+dy,x+dx] == 'W') 98 + if(g.map[y+dy,x+dx] == 'W') 97 99 his++; 98 100 if(his>=2) 99 101 tgt ~= new Pos(y,x); 100 102 } 101 103 cand ~= search(g, ro, tgt, death, true); 102 104 } 103 105 } 104 106 105 107 // 'dig' mode 106 108 if(cand.empty) { 107 109 const(Pos)[] tgt; 108 - for(int y=1; y<=g.H; ++y) 109 - for(int x=1; x<=g.W; ++x) 110 - if(g[y,x]=='.') 111 - if(g[y+1,x]=='*'||g[y+1,x-1]=='*'||g[y+1,x+1]=='*' 112 - ||g[y,x+1]=='*'||g[y,x-1]=='*') 110 + for(int y=1; y<=g.map.H; ++y) 111 + for(int x=1; x<=g.map.W; ++x) 112 + if(g.map[y,x]=='.') 113 + if(rocky(g.map[y+1,x])||rocky(g.map[y+1,x-1])||rocky(g.map[y+1,x+1]) 114 + ||rocky(g.map[y,x+1])||rocky(g.map[y,x-1])) 113 115 tgt ~= new Pos(y,x); 114 116 cand ~= search(g, ro, tgt, death, true); 115 117 } 116 118 117 119 if(cand.empty) { 118 120 choke_count++; 119 121 cand ~= tuple('W',int.max); ................................................................................ 133 135 } 134 136 } 135 137 136 138 if(c == 'W') 137 139 wait_count++; 138 140 else 139 141 wait_count = 0; 140 - if(choke_count >= g.H) 142 + if(choke_count >= g.map.H) 141 143 c = 'A'; 142 144 143 145 bool[char] choice; 144 146 foreach(t; cand) 145 147 choice[t[0]] = true; 146 148 log ~= tuple(ro.clone(), cast(int)choice.length); 147 149 if(log.length > 5) ................................................................................ 157 159 return c; 158 160 } 159 161 160 162 Tuple!(char,int)[] search(in Game g, in Pos s, in Pos[] gs, string death, bool danger_ok=false) 161 163 { 162 164 bool danger(int y, int x) 163 165 { 164 - if(g[y,x] == ' ' || g[y,x] == 'R') 166 + if(g.map[y,x] == ' ' || g.map[y,x] == 'R') 165 167 return false; 166 - if(g[y+1,x] == '*') 168 + if(rocky(g.map[y+1,x])) 169 + return true; 170 + if(rocky(g.map[y+1,x-1]) && (g.map[y,x-1]=='\\'||rocky(g.map[y,x-1])) && (g.map[y+1,x]==' '||g.map[y+1,x]=='R')) 167 171 return true; 168 - if(g[y+1,x-1]=='*' && (g[y,x-1]=='\\'||g[y,x-1]=='*') && (g[y+1,x]==' '||g[y+1,x]=='R')) 172 + if(rocky(g.map[y+1,x+1]) && rocky(g.map[y,x+1]) && (g.map[y+1,x]==' '||g.map[y+1,x]=='R')) 169 173 return true; 170 - if(g[y+1,x+1]=='*' && (g[y,x+1]=='*') && (g[y+1,x]==' '||g[y+1,x]=='R')) 174 + if(rocky(g.map[y,x-1]) && (g.map[y-1,x-1]=='\\'||rocky(g.map[y-1,x-1])) && (g.map[y-1,x]==' '||g.map[y-1,x]=='R')) 171 175 return true; 172 - if(g[y,x-1]=='*' && (g[y-1,x-1]=='\\'||g[y-1,x-1]=='*') && (g[y-1,x]==' '||g[y-1,x]=='R')) 173 - return true; 174 - if(g[y,x+1]=='*' && (g[y-1,x+1]=='*') && (g[y-1,x]==' '||g[y-1,x]=='R')) 176 + if(rocky(g.map[y,x+1]) && rocky(g.map[y-1,x+1]) && (g.map[y-1,x]==' '||g.map[y-1,x]=='R')) 175 177 return true; 176 178 return false; 177 179 } 178 180 179 181 // avoid directly below '*' 180 182 Tuple!(char,int)[] tryA() { 181 183 const(Pos)[] q; 182 184 foreach(p; gs) 183 185 if(!danger(p.y,p.x)) 184 186 q ~= p; 185 - bool[][] v = new bool[][](g.H+2, g.W+2); 187 + bool[][] v = new bool[][](g.map.H+2, g.map.W+2); 186 188 foreach(p; q) v[p.y][p.x]=true; 187 189 for(int step=1; q.length; ++step) { 188 190 Pos[] q2; 189 191 foreach(p; q) { 190 192 int[] yyy=[p.y-1,p.y+1,p.y,p.y]; 191 193 int[] xxx=[p.x,p.x,p.x-1,p.x+1]; 192 194 for(int i=0; i<yyy.length; ++i) { 193 195 int y = yyy[i]; 194 196 int x = xxx[i]; 195 - if('1'<=g[y,x]&&g[y,x]<='9') { 196 - foreach(ppp; g.trampoline_rev(g[y,x])) { 197 + if('1'<=g.map[y,x]&&g.map[y,x]<='9') { 198 + foreach(ppp; g.map.tr_source[g.map[y,x]]) { 197 199 yyy ~= ppp.y; 198 200 xxx ~= ppp.x; 199 201 } 200 202 continue; 201 203 } 202 204 if(v[y][x]) continue; 203 205 if(y==s.y && x==s.x && i<4) { 204 206 char c = "UDRL"[i]; 205 207 if( death.count(c) == 0 ) 206 208 return [tuple(c,step)]; 207 209 } else if(forbidden_cell[y][x]){ 208 - } else if(g[y,x]==' '||g[y,x]=='\\'||g[y,x]=='.'||g[y,x]=='!'||i>=4) { 210 + } else if(g.map[y,x]==' '||g.map[y,x]=='\\'||g.map[y,x]=='.'||g.map[y,x]=='!'||i>=4) { 209 211 if(danger(y,x)) 210 212 continue; 211 213 q2 ~= new Pos(y,x); 212 214 v[y][x]=true; 213 215 } 214 216 } 215 217 } ................................................................................ 218 220 return []; 219 221 } 220 222 221 223 // any empty space is my ground 222 224 Tuple!(char,int)[] tryB() { 223 225 const(Pos)[] q; 224 226 foreach(p; gs) q ~= p; 225 - bool[][] v = new bool[][](g.H+2, g.W+2); 227 + bool[][] v = new bool[][](g.map.H+2, g.map.W+2); 226 228 foreach(p; q) v[p.y][p.x]=true; 227 229 for(int step=10; q.length; ++step) { 228 230 Pos[] q2; 229 231 foreach(p; q) { 230 232 int[] yyy=[p.y-1,p.y+1,p.y,p.y]; 231 233 int[] xxx=[p.x,p.x,p.x-1,p.x+1]; 232 234 for(int i=0; i<yyy.length; ++i) { 233 235 int y = yyy[i]; 234 236 int x = xxx[i]; 235 - if('1'<=g[y,x]&&g[y,x]<='9') { 236 - foreach(ppp; g.trampoline_rev(g[y,x])) { 237 + if('1'<=g.map[y,x]&&g.map[y,x]<='9') { 238 + foreach(ppp; g.map.tr_source[g.map[y,x]]) { 237 239 yyy ~= ppp.y; 238 240 xxx ~= ppp.x; 239 241 } 240 242 continue; 241 243 } 242 244 if(v[y][x]) continue; 243 245 if(y==s.y && x==s.x && i<4) { 244 246 char c = "UDRL"[i]; 245 247 if( death.count(c) == 0 ) 246 248 return [tuple(c,step)]; 247 249 } else if(forbidden_cell[y][x]){ 248 - } else if(g[y,x]==' '||g[y,x]=='\\'||g[y,x]=='.'||g[y,x]=='!'||i>=4) { 250 + } else if(g.map[y,x]==' '||g.map[y,x]=='\\'||g.map[y,x]=='.'||g.map[y,x]=='!'||i>=4) { 249 251 q2 ~= new Pos(y,x); 250 252 v[y][x]=true; 251 253 } 252 254 } 253 255 } 254 256 q = q2; 255 257 } ................................................................................ 256 258 return []; 257 259 } 258 260 259 261 // push rocks! 260 262 Tuple!(char,int)[] tryC() { 261 263 const(Pos)[] q; 262 264 foreach(p; gs) q ~= p; 263 - bool[][] v = new bool[][](g.H+2, g.W+2); 265 + bool[][] v = new bool[][](g.map.H+2, g.map.W+2); 264 266 foreach(p; q) v[p.y][p.x]=true; 265 267 for(int step=20; q.length; ++step) { 266 268 Pos[] q2; 267 269 foreach(p; q) { 268 270 int[] yyy=[p.y-1,p.y+1,p.y,p.y]; 269 271 int[] xxx=[p.x,p.x,p.x-1,p.x+1]; 270 272 for(int i=0; i<yyy.length; ++i) { 271 273 int y = yyy[i]; 272 274 int x = xxx[i]; 273 - if(g[p] == '*') { 275 + if(rocky(g.map[p])) { 274 276 if(i>=4)continue; 275 277 if(y!=p.y)continue; 276 - if(g[y,p.x+(p.x-x)]!=' '&&g[y,p.x+(p.x-x)]!='R')continue; 278 + if(g.map[y,p.x+(p.x-x)]!=' '&&g.map[y,p.x+(p.x-x)]!='R')continue; 277 279 } 278 - if('1'<=g[y,x]&&g[y,x]<='9') { 279 - foreach(ppp; g.trampoline_rev(g[y,x])) { 280 + if('1'<=g.map[y,x]&&g.map[y,x]<='9') { 281 + foreach(ppp; g.map.tr_source[g.map[y,x]]) { 280 282 yyy ~= ppp.y; 281 283 xxx ~= ppp.x; 282 284 } 283 285 continue; 284 286 } 285 287 if(v[y][x]) continue; 286 288 if(y==s.y && x==s.x && i<4) { 287 289 char c = "UDRL"[i]; 288 290 if( death.count(c) == 0 ) 289 291 return [tuple(c,step)]; 290 292 } else if(forbidden_cell[y][x]){ 291 - } else if(g[y,x]==' '||g[y,x]=='\\'||g[y,x]=='.'||g[y,x]=='*'||g[y,x]=='!'||i>=4) { 293 + } else if(g.map[y,x]==' '||g.map[y,x]=='\\'||g.map[y,x]=='.'||rocky(g.map[y,x])||g.map[y,x]=='!'||i>=4) { 292 294 q2 ~= new Pos(y,x); 293 295 v[y][x]=true; 294 296 } 295 297 } 296 298 } 297 299 q = q2; 298 300 } ................................................................................ 314 316 make_plan(g); 315 317 } 316 318 317 319 Tuple!(Solver,string) run_sub_solver(in Game g) 318 320 { 319 321 string log; 320 322 auto s = new Solver(g); 321 - while(!g.cleared && !g.dead && plan.length<=g.H*g.W) { 323 + while(!g.cleared && !g.dead && plan.length<=g.map.H*g.map.W) { 322 324 char c = s.single_step(); 323 325 if( c == 'A' ) 324 326 break; 325 327 log ~= c; 326 328 } 327 329 while(log.length>0 && log[$-1]=='W') 328 330 log.length--;

Modified src/util.d from [b76be1f6ad977d56] to [41ba420d0c49ce8d].

4 4 public import std.range; 5 5 public import std.stdio; 6 6 public import std.string; 7 7 public import std.typecons; 8 8 public import std.math; 9 9 import std.c.stdlib; 10 10 11 -T[] erase(T,V)(T[] xs, V y) 12 -{ 13 - foreach(i,x; xs) 14 - if(x == y) 15 - return xs[0..i]~xs[i+1..$]; 16 - return xs; 17 -} 18 - 19 11 // To avoide the following ICE: 20 12 // src\phobos\std\algorithm.d(4552): 21 13 // Error: function std.algorithm.count!("a == b",string,char).count 22 14 // compiler error, parameter 'value', bugzilla 2962? 23 15 // Assertion failure: '0' on line 717 in file 'glue.c' 24 16 int count(T,V)(T[] a, V v) 25 17 {