Check-in Differences
Not logged in

Difference From:

[7e9400ba3d] Scores. (user: kinaba, tags: trunk, date: 2012-07-15 02:26:34)

To:

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

Added maps/40x40.map version [06dc362ef16f425d]

1 +######################################## 2 +#L\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 3 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 4 +#.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*# 5 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 6 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 7 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 8 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 9 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 10 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 11 +#.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*# 12 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 13 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 14 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 15 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 16 +#.\.\.\.\.\.\.\.\.\W\.\.\.\.\.\.\.\.\.\# 17 +#.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*# 18 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 19 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 20 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 21 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 22 +#.\.\.\.\!\.\.\.\.\.\.\.\W\.\.\.\.\.\.\# 23 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 24 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 25 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 26 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 27 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 28 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 29 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 30 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 31 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\!\.\.# 32 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 33 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 34 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\# 35 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\!\.\.# 36 +#.\.\.\.\.\.\.\.\W\.\.\.\.\.\.\.\.\.\.\# 37 +#\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.# 38 +#.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.R# 39 +######################################## 40 + 41 +Flooding 80

Modified score_memo.txt from [486054e680f2488a] to [95f0626c958d6d31].

1 -contest1 212! DLRDDUULLLDDL 2 -contest2 45 RRRRUA 3 -contest3 275! LDDDRRRRDDLLLLLDRRURURRUR 4 -contest4 561? DRDRDRDRRLUURUUULURDDDLLLLLUURRDDRRRUUR 5 -contest5 1281? LLUURUUURRRRRRLUULDLLULLRRDRDDDDDDUURRURRDDDLLRRUUULLLLUUUULDDDDDRDDD 6 -contest6 737 RUULRRRRRRRRRRUUURUURUUULULLULLLLLLLLDDLDDDDDDUUUUUUUURRRDDRRRDA 7 -contest7 867? RDRRRDDLLLDDLLRRURURRRDDRRRLLLULL 8 -contest8 1245 RRRRRLLLLLUULLLDLLLLRRRRRURUUUUUUUUUULLLLUUULLLLRRDDDDDDLRDDDDLLRRUUULLRRUUUUUUURRRRRRRDDDDDRRRRRDRRDDDDLA 9 -contest9 1990 LRUURRRDUDUDULLLDDRRRRRRRRRUUURRRLDDRLDRRRRRRRRRRRUULLLDLULLRDDLLULULLLUULLRUULLLUULLLRRURUULDRRRRRUUUURLLLDLLRURRRRRDDDRRRRDDRRURRDDULLLLLLLUULLLDDDLLLLULLLLLLA 10 -contest10 2976 UUUUULLLLLLUUUUULLUULLLRDDUUUUUUURRRUDLLUUURRRDRRRRLLUURRRRLLLLLLLLLLLLLLLLLLLDDLLLDRULLLDUURLLDDDDDLRRRUULDDDDDLDDLDDDDRRRRRLLDDDRRRRULLLLURRRRRRRURRDDRDRRRRRRDDDRRUURRRLLLUA 11 -flood1 569 LLLLDDDRRRDRDRUUULLLLLURRRRRDRRA 12 -flood2 45 RRRRUA 13 -flood3 716 LLUURUUURRRRRRLUULDLLULLRRDRDDDDDDA 14 -flood4 970 DDDDLRRRRDDUUUUUUUULLLLLULLLLLLLLUUDRRURUULDRDLLLLLLDDDDLLRRRRRRUURRRRRRRRRRRRRRRUUUUUDDDDDDDDDRRRRDDDRRRRRRRUUURRLLDLLLLRRUUUULLLA 15 -flood5 561? DRDRDRDRRLUURUUULURDDDLLLLLUURRDDRRRUUR 16 -trampoline1 291 LDLLLLURRA 17 -trampoline2 1728? ULLLLLRRRRDRRULULRRLLLLLLLDLWWRULLLDDLLLURRLLUULURURRLLUULLDLLLDLLRRRRRR 18 -trampoline3 698 RRRRDDLDDDDDDRDRRRRRRRUUUURRUDRRRRUUURRRDDRRRRRRRRRRA 19 -beard1 856? RRRURRRDDULLLLLLDLDRURRDDDLLDDLUURDRSRRRRRUR 20 -beard2 2716 RRDDRRRRRRRRRRRRRRRRRRRRRRRRDDRDDLLDDDDLLLLLLLLLLLLLLLLLLLLLLLRUURUULDRRRRLDDRDRRUURRRUUULLLRRRUURRRDDULULLLLLDRRRDRLDRRLLDLLLLLLLLLUUA 21 -beard3 811 RRRRULULRRURRURRRLDDSLDDLDLDDLLLUULDDDDA 22 -beard4 677 LLRDDDDDDRRLLDDLLLLLLLLA 23 - 24 - 1 +contest1 212! 2 +contest2 280? 3 +contest3 275! 4 +contest4 561? 5 +contest5 1281? 6 +contest6 737 // deadend trap 7 +contest7 867? 8 +contest8 1245 // tricky 9 +contest9 3042? 10 +contest10 2076 // * on lambda, must move * first 11 +flood1 569 // too slow, because of 1-left danger lambda 12 +flood2 280? 13 +flood3 802 // too slow, drown 14 +flood4 970 // incorrect order of digging 15 +flood5 561? 16 +trampoline1 291 // * on trampoline. must move * first 17 +trampoline2 1728? 18 +trampoline3 698 // * on trampoline target. must move * first. 19 +beard1 856? 20 +beard2 2792 // hutsu-ni muzui 21 +beard3 811 // tricky. must hurry to cut hige. 22 +beard4 677 // deadend trap

Added src/Makefile version [01a4f7a0531dae71]

1 +# To build, the following packages are needed. 2 +# wget http://ftp.digitalmars.com/dmd_2.059-0_i386.deb 3 +# sudo apt-get install gcc gcc-multilib 4 +# sudo pkg -i dmd_2.059-0_i386.deb 5 + 6 +all: 7 + dmd -O -release -inline -oflifter cui_auto_main.d driver.d game.d output.d solver.d util.d

Added src/driver.d version [3d15ff20e76164b8]

1 +import game; 2 +import util; 3 + 4 +interface GameObserver 5 +{ 6 + // this(in Game g); 7 + void on_game_changed(char c, in Game g, bool finished); 8 +} 9 + 10 +class Driver 11 +{ 12 + this(Game g) { this.game = g; } 13 + this(File game_data) { this(new Game(game_data)); } 14 + 15 + void command(char c) 16 + { 17 + if( finished ) 18 + return; 19 + if( c == 'A' ) 20 + aborted = true; 21 + else 22 + game.command(c); 23 + foreach(ob; observers) 24 + ob.on_game_changed(c, game, finished); 25 + } 26 + 27 + T addObserver(T)() 28 + { 29 + T t = new T(game); 30 + observers ~= t; 31 + return t; 32 + } 33 + 34 + @property bool finished() 35 + { 36 + return game.cleared || game.dead || aborted; 37 + } 38 + 39 +private: 40 + Game game; 41 + GameObserver[] observers; 42 + bool aborted = false; 43 +} 44 + 45 +unittest 46 +{ 47 + static class MockGame : Game { 48 + this() { super(null,null,null); } 49 + string log; 50 + void command(char c) { log ~= c; } 51 + } 52 + static class MockObserver : GameObserver { 53 + string log; 54 + this(in Game g) {} 55 + void on_game_changed(char c, in Game g, bool fin) { log~=c; if(fin)log~="$"; } 56 + } 57 + 58 + auto g = new MockGame; 59 + auto d = new Driver(g); 60 + auto o = d.addObserver!MockObserver(); 61 + foreach(char c; "UDLRSAUDLRSA") 62 + d.command(c); 63 + assert(g.log == "UDLRS"); 64 + assert(o.log == "UDLRSA$"); 65 +}

Modified src/game.d from [5d6b45c159e125db] to [fc05481901940844].

45 45 mixin DeriveCreate; 46 46 mixin DeriveCompare; 47 47 mixin DeriveShow; 48 48 Water clone() const { return cast(Water)this; } 49 49 50 50 static load(string[string] params) 51 51 { 52 - return new Water( 53 - params.get("Water", "0").to!int(), 54 - params.get("Flooding", "0").to!int() 55 - ); 52 + return new Water(params.get("Water", "0").to!int(), 53 + params.get("Flooding", "0").to!int()); 56 54 } 57 55 58 56 int level(int number_of_update) const 59 57 { 60 58 return pace ? base+(number_of_update/pace) : base; 61 59 } 62 60 ................................................................................ 124 122 return new Map(raw_data, params, trampo); 125 123 } 126 124 127 125 char[][] data; 128 126 Pos robot; 129 127 Pos lift; 130 128 int waterproof; 131 - // TODO: immutable 132 129 Pos[char] tr_target; 133 130 Pos[][char] tr_source; 134 131 const(Hige) hige; 135 132 int razor; 136 133 137 134 Map clone() const { return new Map(this); } 138 135 this(in Map m) { ................................................................................ 352 349 } 353 350 } 354 351 355 352 //////////////////////////////////////////////////////////////////////////////// 356 353 357 354 class Game 358 355 { 359 - mixin DeriveShow; 360 - 361 - static Game load(File input) 356 +public: 357 + this(File input) 362 358 { 363 - string[] raw_data; 364 - string[string] params; 365 - 366 - // Raw map data; read until empty line. 359 + // Read map data 360 + string[] map_data_lines; 367 361 for(string line; !(line=input.readln().chomp()).empty; ) 368 - raw_data ~= line; 362 + map_data_lines ~= line; 369 363 370 - // Additional commands; read until EOF. 371 - char[char] trampo; 372 - for(string line; !(line=input.readln()).empty; ) { 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; 377 + } 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; 404 + 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 + } 420 + } 421 + 422 + // Read other parameters 423 + for(string line; !(line=input.readln()).empty; ) 424 + { 373 425 string[] ss = line.split(); 374 426 if( ss.length == 2 ) 375 - params[ss[0]] = ss[1]; 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 + } 376 436 if( ss.length == 4 && ss[0]=="Trampoline" && ss[2]=="targets" ) 377 - trampo[ss[1][0]] = ss[3][0]; 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 + } 447 + 448 + @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; 378 497 } 379 - 380 - return load(raw_data, params, trampo); 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); } 381 502 } 382 503 383 - static Game load(string[] raw_data, string[string] params, char[char] trampo = null) 384 - { 385 - return new Game(raw_data, params, trampo); 386 - } 387 - 388 - this(string[] raw_data, string[string] params, char[char] trampo) 389 - { 390 - this.map = Map.load(raw_data, params, trampo); 391 - this.water = Water.load(params); 392 - } 393 - 394 - Game clone() const { return new Game(this); } 395 - this(in Game g) { 396 - map = g.map.clone(); 397 - water = g.water.clone(); 398 - turn = g.turn; 399 - dead = g.dead; 400 - lambda = g.lambda; 401 - exit_bonus = g.exit_bonus; 402 - under_water = g.under_water; 403 - } 404 - 504 +public: 405 505 void command(char c) 406 506 { 407 507 if(dead || cleared) 408 508 return; 409 509 410 - if(c == 'A') 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) 411 518 { 412 - exit_bonus = 1; 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) 413 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 + } 414 577 } 578 + } 415 579 416 - // TODO: clarify the event order 417 - Tuple!(int,bool) ld = map.command(c, turn); 418 - if( map.cleared() ) { 419 - exit_bonus = 2; 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); 420 601 } 421 - else { 422 - lambda += ld[0]; 423 - if( ld[1] ) { 424 - dead = true; 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 + } 425 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; 426 686 } 427 - if( map.robot.y <= water_level ) 428 - ++under_water; 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_ --; 429 696 else 430 - under_water = 0; 431 - if( under_water > map.waterproof ) 432 - dead = true; 433 - turn += 1; 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; 434 759 } 435 760 436 - Map map; 437 - Water water; 438 - int turn = 0; 439 - bool dead = false; 440 - int lambda = 0; 441 - int exit_bonus = 0; 442 - int under_water = 0; 443 - // TODO: when adding members, take care of clone(). 444 - // TODO: fix this poor design. 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; 445 779 446 - @property const { 447 - long score() { return lambda*25L*(1+exit_bonus) - turn; } 448 - int water_level() { return water.level(turn); } 449 - int water_until_rise() { return water.until_rise(turn); } 450 - int hige_until_rise() { return map.hige.until_rise(turn); } 451 - bool cleared() { return exit_bonus>0; } 452 - int hp() { return map.waterproof - under_water; } 453 - long score_if_abort_now() { return lambda*25*(1+max(1,exit_bonus)) - turn; } 454 - } 780 + int turn_ = 0; 781 + int air_left_ = 0; 782 + bool cleared_ = false; 783 + bool dead_ = false; 784 + bool[Pos] may_update_; 455 785 }

Modified src/gui.d from [b071b446f6e834b9] to [e84cacd6262f93a7].

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.map.W, g.map.H); 11 + setup_size(g.W, g.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 ................................................................................ 79 79 this.render['\\'] = "λ"; 80 80 this.render['R'] = "☃"; 81 81 this.render['d'] = "☠"; 82 82 this.render['L'] = "☒"; 83 83 this.render['O'] = "☐"; 84 84 this.render['W'] = "ꔣ"; 85 85 this.render['!'] = "✄"; 86 - foreach(c,tp; g.map.tr_target) { 87 - char d = g.map[tp]; 88 - this.render[c] = [cast(dchar)('☢'+d-'1')].to!string(); 89 - } 86 + foreach(char c; 'A'..'J') this.render[c] = [cast(dchar)('☢'+g.trampoline(c)-'1')].to!string(); 90 87 foreach(char c; '1'..':') this.render[c] = [cast(dchar)('☢'+c-'1')].to!string(); 91 88 this.paint ~= (Control c, PaintEventArgs ev) { 92 89 graphicContext.copyTo(ev.graphics, Rect(0,0,this.clientSize.width,this.clientSize.height)); 93 90 }; 94 91 } 95 92 96 93 void draw(in Game g) ................................................................................ 102 99 graphicContext.fillRectangle(this.backColor, Rect(0,0,scrW,scrH)); 103 100 104 101 // Fill water. 105 102 int w = g.water_level(); 106 103 graphicContext.fillRectangle(this.colors['w'], Rect(0, scrH-cell*w-1, scrW, cell*w+1)); 107 104 108 105 // Paint map. 109 - for(int y=1; y<=g.map.H; ++y) 110 - for(int x=1; x<=g.map.W; ++x) { 106 + for(int y=1; y<=g.H; ++y) 107 + for(int x=1; x<=g.W; ++x) { 111 108 Rect r = Rect(cell*(x-1), scrH-cell*y, cell, cell); 112 - char c = g.map[y,x]; 109 + char c = g[y,x]; 113 110 if( c != ' ' ) { 114 111 if( c == 'R' && g.dead ) 115 112 c = 'd'; 116 113 graphicContext.drawText(this.render[c], font, this.colors[c], r); 117 114 } 118 115 } 119 116 120 117 // Update textual info. 121 118 this.text = .text( 122 119 "Score: ", g.score, 123 120 " Air: ", g.hp, 124 121 " Tide: ", g.water_until_rise, 125 122 " Wadler: ", g.hige_until_rise, 126 - " Razor: ", g.map.razor); 123 + " Razor: ", g.num_razor); 127 124 invalidate(); 128 125 } 129 126 130 127 private: 131 128 void setup_keyhandling() 132 129 { 133 130 noMessageFilter(); 134 131 this.keyDown ~= &my_keydown; 135 132 } 133 + 134 + void do_manual_command(char c) 135 + { 136 + solver.force(c); 137 + fn(c); 138 + } 136 139 137 140 void my_keydown(Control c, KeyEventArgs ev) 138 141 { 139 142 switch(ev.keyCode) 140 143 { 141 - case Keys.DOWN: fn('D'); break; 142 - case Keys.UP: fn('U'); break; 143 - case Keys.LEFT: fn('L'); break; 144 - case Keys.RIGHT: fn('R'); break; 145 - case Keys.W: fn('W'); break; 146 - case Keys.S: fn('S'); break; 147 - case Keys.A: fn('A'); break; 144 + case Keys.DOWN: do_manual_command('D'); break; 145 + case Keys.UP: do_manual_command('U'); break; 146 + case Keys.LEFT: do_manual_command('L'); break; 147 + case Keys.RIGHT: do_manual_command('R'); break; 148 + case Keys.W: do_manual_command('W'); break; 149 + case Keys.S: do_manual_command('S'); break; 150 + case Keys.A: do_manual_command('A'); break; 148 151 case Keys.G: fn(solver.single_step()); break; 149 152 default: break; 150 153 } 151 154 } 152 155 153 156 Solver solver; 154 157 }

Modified src/output.d from [62e6040714438eff] to [e2d0d7db868c3a44].

20 20 } 21 21 22 22 class GuardedOutput : GameObserver 23 23 { 24 24 this(in Game g) 25 25 { 26 26 setup_sigint_handling(); 27 - ideal_log ~= g.score_if_abort_now; 27 + score_log ~= g.score; 28 28 flushed = false; 29 29 } 30 30 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 - ideal_log ~= g.score_if_abort_now; 39 - if(finished || log.length+1==g.map.W*g.map.H) 38 + if(finished || log.length+1==g.W*g.H) 40 39 flush(); 41 40 } 42 41 43 42 private: 44 43 string log; 45 44 long[] score_log; 46 - long[] ideal_log; 47 45 bool flushed; 48 46 49 47 void flush() 50 48 { 51 - Tuple!(long, int, int) cand; 49 + if(flushed) 50 + return; 51 + 52 + Tuple!(long, int) cand; 52 53 cand[0] = long.min; 53 54 54 55 for(int i=0; i<score_log.length; ++i) 55 56 if(cand[0] < score_log[i]) 56 - cand = tuple(score_log[i],i,0); 57 - for(int i=0; i<ideal_log.length; ++i) 58 - if(cand[0] < ideal_log[i]) 59 - cand = tuple(ideal_log[i],i,1); 57 + cand = tuple(score_log[i],i); 60 58 61 - if(cand[2]==0) { 62 - string str = log[0..cand[1]+1]; 63 - std.c.stdio.printf("%.*s\n", str.length, str.ptr); 64 - } else { 65 - string str = log[0..cand[1]]; 66 - std.c.stdio.printf("%.*sA\n", str.length, str.ptr); 67 - } 59 + std.c.stdio.printf("%.*sA\n", cand[1], log.ptr); 68 60 std.c.stdio.fflush(std.c.stdio.stdout); 69 61 flushed = true; 70 62 } 71 63 72 64 private: 73 65 static __gshared GuardedOutput g_output; 74 66

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

1 1 import util; 2 2 import game; 3 3 4 4 class Solver_0 5 5 { 6 - this(const(Game) g) {} 6 + this(in Game g) {} 7 7 char single_step() { return 'W'; } 8 + void force(char c) {} 8 9 } 9 10 10 11 class Solver_1 11 12 { 12 13 int wait_count = 0; 13 14 int choke_count = 0; 14 15 15 16 Game g; 16 - this(const(Game) g) 17 + this(in Game g) 17 18 { 18 19 this.g = g.clone(); 19 - forbidden_cell = new bool[][](g.map.H+2, g.map.W+2); 20 + forbidden_cell = new bool[][](g.H+2, g.W+2); 20 21 } 21 22 22 23 char single_step() 23 24 { 24 25 Tuple!(string,int) de = death_move(g); 25 26 char c = act(g, de[0], de[1]); 26 - g.command(c); 27 + force(c); 27 28 return c; 28 29 } 30 + 31 + void force(char c) 32 + { 33 + if(c != 'A') 34 + g.command(c); 35 + } 29 36 30 37 Tuple!(string,int) death_move(const(Game) g) 31 38 { 32 39 string death; 33 40 int choice = 0; 34 41 foreach(char c; "UDLRW") { 35 42 Game gg = g.clone(); 36 43 gg.command(c); 37 44 if( !gg.cleared && gg.dead ) 38 45 death ~= c; 39 - else if( gg.map.robot != g.map.robot ) 46 + else if( gg.robot != g.robot ) 40 47 choice++; 41 48 else if( c != 'W' ) // meaningless move 42 49 death ~= c; 43 50 } 44 51 return tuple(death, choice); 45 52 } 46 53 47 54 Tuple!(Pos, int)[] log; 48 55 bool[][] forbidden_cell; 49 56 50 57 char act(const(Game) g, string death, int breath) 51 58 { 52 - const Pos ro = g.map.robot; 53 - const Pos[] la = g.map.lambdas(); 54 - const Pos[] ra = g.map.razors(); 55 - const Pos li = g.map.lift; 56 - const Pos[] hi = g.map.objects('W'); 59 + const Pos ro = g.robot; 60 + const Pos li = g.lift; 61 + Pos[] la = g.lambdas(); 62 + sort!((Pos a,Pos b){ 63 + int ad=abs(a.y-li.y)+abs(a.x-li.x); 64 + int bd=abs(b.y-li.y)+abs(b.x-li.x); 65 + return ad>bd;; 66 + })(la); 67 + Pos[] ra = g.razors(); 68 + const(Pos)[] hi = g.higes(); 57 69 58 70 Tuple!(char,int)[] cand; 59 71 char c = 'W'; 60 72 if( la.empty ) { 61 73 cand = search(g, ro, [li], death); 62 74 } else { 63 75 cand ~= search(g, ro, la~ra, death); 64 76 } 65 77 66 78 // 'higesori' mode 67 - if( !hi.empty && g.map.razor>0 ) { 79 + if( !hi.empty && g.num_razor>0 ) { 68 80 int his = 0; 69 81 for(int dy=-1; dy<=+1; ++dy) 70 82 for(int dx=-1; dx<=+1; ++dx) 71 - if(g.map[ro.y+dy,ro.x+dx] == 'W') 83 + if(g[ro.y+dy,ro.x+dx] == 'W') 72 84 his++; 73 85 74 86 if(his>=2 || his==hi.length) 75 87 cand = [tuple('S',int.max)]; 76 88 if(cand.empty) { 77 89 const(Pos)[] tgt; 78 - for(int y=1; y<=g.map.H; ++y) 79 - for(int x=1; x<=g.map.W; ++x) 80 - if(g.map[y,x]=='.'||g.map[y,x]==' ') { 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]==' ') { 81 93 his = 0; 82 94 for(int dy=-1; dy<=+1; ++dy) 83 95 for(int dx=-1; dx<=+1; ++dx) 84 - if(g.map[y+dy,x+dx] == 'W') 96 + if(g[y+dy,x+dx] == 'W') 85 97 his++; 86 98 if(his>=2) 87 99 tgt ~= new Pos(y,x); 88 100 } 89 101 cand ~= search(g, ro, tgt, death, true); 90 102 } 91 103 } 92 104 93 105 // 'dig' mode 94 106 if(cand.empty) { 95 107 const(Pos)[] tgt; 96 - for(int y=1; y<=g.map.H; ++y) 97 - for(int x=1; x<=g.map.W; ++x) 98 - if(g.map[y,x]=='.') 99 - if(g.map[y+1,x]=='*'||g.map[y+1,x-1]=='*'||g.map[y+1,x+1]=='*' 100 - ||g.map[y,x+1]=='*'||g.map[y,x-1]=='*') 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]=='*') 101 113 tgt ~= new Pos(y,x); 102 114 cand ~= search(g, ro, tgt, death, true); 103 115 } 104 116 105 117 if(cand.empty) { 106 118 choke_count++; 107 119 cand ~= tuple('W',int.max); ................................................................................ 121 133 } 122 134 } 123 135 124 136 if(c == 'W') 125 137 wait_count++; 126 138 else 127 139 wait_count = 0; 128 - if(choke_count >= g.map.H) 140 + if(choke_count >= g.H) 129 141 c = 'A'; 130 142 131 143 bool[char] choice; 132 144 foreach(t; cand) 133 145 choice[t[0]] = true; 134 146 log ~= tuple(ro.clone(), cast(int)choice.length); 135 147 if(log.length > 5) ................................................................................ 145 157 return c; 146 158 } 147 159 148 160 Tuple!(char,int)[] search(in Game g, in Pos s, in Pos[] gs, string death, bool danger_ok=false) 149 161 { 150 162 bool danger(int y, int x) 151 163 { 152 - if(g.map[y,x] == ' ' || g.map[y,x] == 'R') 164 + if(g[y,x] == ' ' || g[y,x] == 'R') 153 165 return false; 154 - if(g.map[y+1,x] == '*') 166 + if(g[y+1,x] == '*') 167 + 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')) 155 169 return true; 156 - if(g.map[y+1,x-1]=='*' && (g.map[y,x-1]=='\\'||g.map[y,x-1]=='*') && (g.map[y+1,x]==' '||g.map[y+1,x]=='R')) 170 + if(g[y+1,x+1]=='*' && (g[y,x+1]=='*') && (g[y+1,x]==' '||g[y+1,x]=='R')) 157 171 return true; 158 - if(g.map[y+1,x+1]=='*' && (g.map[y,x+1]=='*') && (g.map[y+1,x]==' '||g.map[y+1,x]=='R')) 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')) 159 173 return true; 160 - if(g.map[y,x-1]=='*' && (g.map[y-1,x-1]=='\\'||g.map[y-1,x-1]=='*') && (g.map[y-1,x]==' '||g.map[y-1,x]=='R')) 161 - return true; 162 - if(g.map[y,x+1]=='*' && (g.map[y-1,x+1]=='*') && (g.map[y-1,x]==' '||g.map[y-1,x]=='R')) 174 + if(g[y,x+1]=='*' && (g[y-1,x+1]=='*') && (g[y-1,x]==' '||g[y-1,x]=='R')) 163 175 return true; 164 176 return false; 165 177 } 166 178 167 179 // avoid directly below '*' 168 180 Tuple!(char,int)[] tryA() { 169 181 const(Pos)[] q; 170 182 foreach(p; gs) 171 183 if(!danger(p.y,p.x)) 172 184 q ~= p; 173 - bool[][] v = new bool[][](g.map.H+2, g.map.W+2); 185 + bool[][] v = new bool[][](g.H+2, g.W+2); 174 186 foreach(p; q) v[p.y][p.x]=true; 175 187 for(int step=1; q.length; ++step) { 176 188 Pos[] q2; 177 189 foreach(p; q) { 178 190 int[] yyy=[p.y-1,p.y+1,p.y,p.y]; 179 191 int[] xxx=[p.x,p.x,p.x-1,p.x+1]; 180 192 for(int i=0; i<yyy.length; ++i) { 181 193 int y = yyy[i]; 182 194 int x = xxx[i]; 183 - if('1'<=g.map[y,x]&&g.map[y,x]<='9') { 184 - foreach(ppp; g.map.tr_source[g.map[y,x]]) { 195 + if('1'<=g[y,x]&&g[y,x]<='9') { 196 + foreach(ppp; g.trampoline_rev(g[y,x])) { 185 197 yyy ~= ppp.y; 186 198 xxx ~= ppp.x; 187 199 } 188 200 continue; 189 201 } 190 202 if(v[y][x]) continue; 191 203 if(y==s.y && x==s.x && i<4) { 192 204 char c = "UDRL"[i]; 193 205 if( death.count(c) == 0 ) 194 206 return [tuple(c,step)]; 195 207 } else if(forbidden_cell[y][x]){ 196 - } else if(g.map[y,x]==' '||g.map[y,x]=='\\'||g.map[y,x]=='.'||g.map[y,x]=='!'||i>=4) { 208 + } else if(g[y,x]==' '||g[y,x]=='\\'||g[y,x]=='.'||g[y,x]=='!'||i>=4) { 197 209 if(danger(y,x)) 198 210 continue; 199 211 q2 ~= new Pos(y,x); 200 212 v[y][x]=true; 201 213 } 202 214 } 203 215 } ................................................................................ 206 218 return []; 207 219 } 208 220 209 221 // any empty space is my ground 210 222 Tuple!(char,int)[] tryB() { 211 223 const(Pos)[] q; 212 224 foreach(p; gs) q ~= p; 213 - bool[][] v = new bool[][](g.map.H+2, g.map.W+2); 225 + bool[][] v = new bool[][](g.H+2, g.W+2); 214 226 foreach(p; q) v[p.y][p.x]=true; 215 227 for(int step=10; q.length; ++step) { 216 228 Pos[] q2; 217 229 foreach(p; q) { 218 230 int[] yyy=[p.y-1,p.y+1,p.y,p.y]; 219 231 int[] xxx=[p.x,p.x,p.x-1,p.x+1]; 220 232 for(int i=0; i<yyy.length; ++i) { 221 233 int y = yyy[i]; 222 234 int x = xxx[i]; 223 - if('1'<=g.map[y,x]&&g.map[y,x]<='9') { 224 - foreach(ppp; g.map.tr_source[g.map[y,x]]) { 235 + if('1'<=g[y,x]&&g[y,x]<='9') { 236 + foreach(ppp; g.trampoline_rev(g[y,x])) { 225 237 yyy ~= ppp.y; 226 238 xxx ~= ppp.x; 227 239 } 228 240 continue; 229 241 } 230 242 if(v[y][x]) continue; 231 243 if(y==s.y && x==s.x && i<4) { 232 244 char c = "UDRL"[i]; 233 245 if( death.count(c) == 0 ) 234 246 return [tuple(c,step)]; 235 247 } else if(forbidden_cell[y][x]){ 236 - } else if(g.map[y,x]==' '||g.map[y,x]=='\\'||g.map[y,x]=='.'||g.map[y,x]=='!'||i>=4) { 248 + } else if(g[y,x]==' '||g[y,x]=='\\'||g[y,x]=='.'||g[y,x]=='!'||i>=4) { 237 249 q2 ~= new Pos(y,x); 238 250 v[y][x]=true; 239 251 } 240 252 } 241 253 } 242 254 q = q2; 243 255 } ................................................................................ 244 256 return []; 245 257 } 246 258 247 259 // push rocks! 248 260 Tuple!(char,int)[] tryC() { 249 261 const(Pos)[] q; 250 262 foreach(p; gs) q ~= p; 251 - bool[][] v = new bool[][](g.map.H+2, g.map.W+2); 263 + bool[][] v = new bool[][](g.H+2, g.W+2); 252 264 foreach(p; q) v[p.y][p.x]=true; 253 265 for(int step=20; q.length; ++step) { 254 266 Pos[] q2; 255 267 foreach(p; q) { 256 268 int[] yyy=[p.y-1,p.y+1,p.y,p.y]; 257 269 int[] xxx=[p.x,p.x,p.x-1,p.x+1]; 258 270 for(int i=0; i<yyy.length; ++i) { 259 271 int y = yyy[i]; 260 272 int x = xxx[i]; 261 - if(g.map[p] == '*') { 273 + if(g[p] == '*') { 262 274 if(i>=4)continue; 263 275 if(y!=p.y)continue; 264 - if(g.map[y,p.x+(p.x-x)]!=' '&&g.map[y,p.x+(p.x-x)]!='R')continue; 276 + if(g[y,p.x+(p.x-x)]!=' '&&g[y,p.x+(p.x-x)]!='R')continue; 265 277 } 266 - if('1'<=g.map[y,x]&&g.map[y,x]<='9') { 267 - foreach(ppp; g.map.tr_source[g.map[y,x]]) { 278 + if('1'<=g[y,x]&&g[y,x]<='9') { 279 + foreach(ppp; g.trampoline_rev(g[y,x])) { 268 280 yyy ~= ppp.y; 269 281 xxx ~= ppp.x; 270 282 } 271 283 continue; 272 284 } 273 285 if(v[y][x]) continue; 274 286 if(y==s.y && x==s.x && i<4) { 275 287 char c = "UDRL"[i]; 276 288 if( death.count(c) == 0 ) 277 289 return [tuple(c,step)]; 278 290 } else if(forbidden_cell[y][x]){ 279 - } else if(g.map[y,x]==' '||g.map[y,x]=='\\'||g.map[y,x]=='.'||g.map[y,x]=='*'||g.map[y,x]=='!'||i>=4) { 291 + } else if(g[y,x]==' '||g[y,x]=='\\'||g[y,x]=='.'||g[y,x]=='*'||g[y,x]=='!'||i>=4) { 280 292 q2 ~= new Pos(y,x); 281 293 v[y][x]=true; 282 294 } 283 295 } 284 296 } 285 297 q = q2; 286 298 } 287 299 return []; 288 300 } 289 301 return (danger_ok ? [] : tryA()) ~ tryB() ~ tryC(); 290 302 } 291 303 } 292 304 293 -alias Solver_1 MainSolver; 305 +class Solver_2(Solver) 306 +{ 307 + string plan; 308 + bool plan_broken = true; 309 + 310 + Game g; 311 + this(in Game g) 312 + { 313 + this.g = g.clone(); 314 + make_plan(g); 315 + } 316 + 317 + Tuple!(Solver,string) run_sub_solver(in Game g) 318 + { 319 + string log; 320 + auto s = new Solver(g); 321 + while(!g.cleared && !g.dead && plan.length<=g.H*g.W) { 322 + char c = s.single_step(); 323 + if( c == 'A' ) 324 + break; 325 + log ~= c; 326 + } 327 + while(log.length>0 && log[$-1]=='W') 328 + log.length--; 329 + return tuple(s, log); 330 + } 331 + 332 + void make_plan(in Game g) { 333 + plan_broken = false; 334 + Tuple!(Solver,string) x = run_sub_solver(g); 335 + plan = x[1]; 336 + if(x[0].g.cleared) 337 + return; 338 + modify_plan(g, x[0].g.score); 339 + } 340 + 341 + void modify_plan(in Game ini, long unmod) 342 + { 343 + int bp = max(0, (cast(int)plan.length)-10); 344 + Game g = ini.clone(); 345 + for(int i=0; i<bp; ++i) g.command(plan[i]); 346 + 347 + Tuple!(string,long) cand = tuple(plan, unmod); 348 + for(int i=bp; i<plan.length; ++i) { 349 + foreach(string c; ["U","D","L","R","UD","DU","LR","RL"]) 350 + if(c[0] != plan[i]) { 351 + Tuple!(string,long) zz = try_plan(c, g); 352 + if(cand[1]<zz[1]) 353 + cand = tuple(plan[0..i]~c~zz[0], zz[1]); 354 + } 355 + g.command(plan[i]); 356 + } 357 + plan = cand[0]; 358 + } 359 + 360 + Tuple!(string,long) try_plan(string c, in Game g) 361 + { 362 + Game gg = g.clone(); 363 + foreach(cc;c)gg.command(cc); 364 + Tuple!(Solver, string) x = run_sub_solver(gg); 365 + return tuple(x[1], x[0].g.score); 366 + } 367 + 368 + char single_step() { 369 + if(plan_broken) 370 + make_plan(g); 371 + if(plan.empty) 372 + return 'A'; 373 + char c = plan[0]; 374 + plan = plan[1..$]; 375 + g.command(c); 376 + return c; 377 + } 378 + 379 + void force(char c) { 380 + g.command(c); 381 + if(plan.length==0 || plan[0]!=c) { 382 + plan = ""; 383 + plan_broken = true; 384 + } 385 + else 386 + plan = plan[1..$]; 387 + } 388 +} 389 + 390 +alias Solver_2!(Solver_1) MainSolver; 391 +//alias Solver_1 MainSolver;

Modified src/util.d from [0665970b47ba63ed] to [b76be1f6ad977d56].

1 1 public import std.algorithm; 2 2 public import std.array; 3 3 public import std.conv; 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 +public import std.math; 8 9 import std.c.stdlib; 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 +// To avoide the following ICE: 20 +// src\phobos\std\algorithm.d(4552): 21 +// Error: function std.algorithm.count!("a == b",string,char).count 22 +// compiler error, parameter 'value', bugzilla 2962? 23 +// Assertion failure: '0' on line 717 in file 'glue.c' 24 +int count(T,V)(T[] a, V v) 25 +{ 26 + int cnt = 0; 27 + foreach(e; a) 28 + if(e == v) 29 + ++cnt; 30 + return cnt; 31 +} 9 32 10 33 void application_exit() 11 34 { 12 35 std.c.stdlib.exit(0); 13 36 } 14 37 15 38 template DeriveCreate()