Index: maps/static.map ================================================================== --- maps/static.map +++ maps/static.map @@ -5,6 +5,6 @@ \ \ \ \ \ -R \ +R \ Index: src/game.d ================================================================== --- src/game.d +++ src/game.d @@ -1,9 +1,36 @@ import util; //////////////////////////////////////////////////////////////////////////////// +bool is_spacy(char c) +{ + return c==' ' || c=='.' || c=='R' || c=='!' || c=='\\' || c=='O'; +} + +bool is_rocky(char c) +{ + return c=='*' || c=='@'; +} + +bool is_true_space(char c) +{ + return c==' '; +} + +bool is_trampoline_source(char c) +{ + return 'A'<=c && c<='I'; +} + +bool is_rocklambda(char c) +{ + return is_rocky(c) || c=='\\'; +} + +//////////////////////////////////////////////////////////////////////////////// + class Pos { public immutable int y, x; mixin DeriveCreate; mixin DeriveCompare; @@ -39,13 +66,15 @@ //////////////////////////////////////////////////////////////////////////////// class Water { - public immutable int base, pace; - mixin DeriveCreate; mixin DeriveShow; + +private: + immutable int base, pace; + mixin DeriveCreate; Water clone() const { return cast(Water) this; } static load(string[string] params) { return new Water(params.get("Water", "0").to!int(), @@ -85,13 +114,15 @@ //////////////////////////////////////////////////////////////////////////////// class Hige { - public immutable int pace; - mixin DeriveCreate; mixin DeriveShow; + +private: + immutable int pace; + mixin DeriveCreate; Hige clone() const { return cast(Hige)this; } static load(string[string] params) { return new Hige(params.get("Growth", "25").to!int()); @@ -110,17 +141,20 @@ //////////////////////////////////////////////////////////////////////////////// class Trampoline { - private immutable char[] target_of_; - private immutable char[][] source_of_; - private immutable Pos[] position_of_; - private immutable char[] source_list_; - private immutable char[] target_list_; mixin DeriveShow; + +private: + immutable char[] target_of_; + immutable char[][] source_of_; + immutable Pos[] position_of_; + immutable char[] source_list_; + immutable char[] target_list_; Trampoline clone() const { return cast(Trampoline) this; } + this(Map m, char[char] tramparam) { auto ta = new char['I'+1]; auto sr = new char[]['9'+1]; auto po = new Pos[max('I','9')+1]; @@ -146,11 +180,11 @@ position_of_ = cast(immutable) po; source_list_ = cast(immutable) sl; target_list_ = cast(immutable) tl; } -@property const: +public @property const: const(char[]) source_list() { return source_list_; } const(char[]) target_list() { return target_list_; } const(char[]) source_of(char c) { return source_of_[c]; } char target_of(char c) { return target_of_[c]; } Pos[] source_pos(char c) { @@ -166,34 +200,66 @@ class Map { mixin DeriveShow; - char[][] data; + private char[][] data; Pos robot; Pos lift; - int waterproof; - int razor; + private int waterproof; + private int collected_razor; int collected_lambda; int total_lambda; - bool cleared; - Pos[] may_update; + private bool cleared; + private Pos[] may_update; - Map clone() const { return new Map(this); } - this(in Map m) { + private Map clone() const { return new Map(this); } + private this(in Map m) { foreach(s; m.data) this.data ~= s.dup; - this.robot = m.robot.clone(); - this.lift = m.lift.clone(); + this.robot = m.robot.clone(); + this.lift = m.lift.clone(); this.waterproof = m.waterproof; - this.razor = m.razor; + this.collected_razor = m.collected_razor; this.collected_lambda = m.collected_lambda; - this.total_lambda = m.total_lambda; + this.total_lambda = m.total_lambda; + this.cleared = m.cleared; this.may_update = (cast(Map)m).may_update.dup; - this.cleared = m.cleared; } + const { + @property { + int H() { return data.length; } + int W() { return data[0].length; } + int num_razor() { return collected_razor; } + + Pos[] razors() { return objects('!'); } + Pos[] lambdas() { return objects('\\'); } + } + + Pos[] objects(char c) { + Pos[] ans; + for(int y=1; y<=H; ++y) + for(int x=1; x<=W; ++x) + if(this[y,x] == c) + ans ~= new Pos(y,x); + return ans; + } + + char opIndex(int y, int x) { + --y, --x; + if(y<0||H<=y||x<0||W<=x) + return '#'; + return data[H-1-y][x]; + } + + char opIndex(in Pos p) { + return this[p.y, p.x]; + } + } + +private: this(string[] raw_data, string[string] params, char[char] trampo) { int width = 0; foreach(r; raw_data) width = max(width, r.length); @@ -209,42 +275,20 @@ this.robot = new Pos(y,x); if(this[y,x] == 'L' || this[y,x] == 'O') this.lift = new Pos(y,x); if(this[y,x] == '\\' || this[y,x] == '@') total_lambda++; - if(this[y,x] == '*' || this[y,x] == '@') + if(is_rocky(this[y,x])) may_update ~= new Pos(y,x); } - this.waterproof = params.get("Waterproof", "5").to!int(); - this.razor = params.get("Razors", "0").to!int(); - } - - const @property { - int H() { return data.length; } - int W() { return data[0].length; } - } - - const { - char opIndex(int y, int x) - { - // Adjust coordinate to the spec. bottom-left is (1,1). - --y, --x; - if(y<0||H<=y||x<0||W<=x) - return '#'; - return data[H-1-y][x]; - } - - char opIndex(in Pos p) - { - return this[p.y, p.x]; - } + this.waterproof = params.get("Waterproof", "5").to!int(); + this.collected_razor = params.get("Razors", "0").to!int(); } void opIndexAssign(char c, int y, int x) { - // Adjust coordinate to the spec. bottom-left is (1,1). --y, --x; if(y<0||H<=y||x<0||W<=x) return; data[H-1-y][x] = c; } @@ -252,89 +296,85 @@ void opIndexAssign(char c, in Pos p) { this[p.y, p.x] = c; } - Pos[] objects(char c) const { - Pos[] ans; - for(int y=1; y<=H; ++y) - for(int x=1; x<=W; ++x) - if(this[y,x] == c) - ans ~= new Pos(y,x); - return ans; - } - - Pos[] razors() const { return objects('!'); } - Pos[] lambdas() const { return objects('\\'); } - bool command(char c, int turn, bool hige_day, in Trampoline tr) { - assert( this[robot] == 'R' ); - if(c=='R') return move( 0, +1, hige_day, tr); - if(c=='L') return move( 0, -1, hige_day, tr); - if(c=='U') return move(+1, 0, hige_day, tr); - if(c=='D') return move(-1, 0, hige_day, tr); - if(c=='W') return move( 0, 0, hige_day, tr); - if(c=='S') return use_razor(hige_day); - assert(false); + switch(c) + { + case 'R': return move( 0, +1, hige_day, tr); + case 'L': return move( 0, -1, hige_day, tr); + case 'U': return move(+1, 0, hige_day, tr); + case 'D': return move(-1, 0, hige_day, tr); + case 'W': return move( 0, 0, hige_day, tr); + case 'S': return use_razor(hige_day); + default: assert(false); + } } bool use_razor(bool hige_day) { - if(razor) { - razor--; + if(collected_razor > 0) + { + collected_razor--; for(int dy=-1; dy<=+1; ++dy) for(int dx=-1; dx<=+1; ++dx) if(this[robot.y+dy,robot.x+dx] == 'W') { emptified(new Pos(robot.y+dy,robot.x+dx)); this[robot.y+dy,robot.x+dx] = ' '; } } - return update(hige_day); } - bool rocky(char c) { return c=='*' || c=='@'; } - - void emptified(Pos p) { - for(int dy=0; dy<=+1; ++dy) - for(int dx=-1; dx<=+1; ++dx) - may_update ~= new Pos(p.y+dy, p.x+dx); - } + // Register a position that may become empty in the last turn. + void emptified(Pos p) + { + for(int dy=0; dy<=+1; ++dy) + for(int dx=-1; dx<=+1; ++dx) + may_update ~= new Pos(p.y+dy, p.x+dx); + } bool move(int dy, int dx, bool hige_day, in Trampoline tr) { - emptified(robot); - - int y = robot.y; - int x = robot.x; - if( '\\' == this[y+dy,x+dx] ) - collected_lambda++; - if( '!' == this[y+dy,x+dx] ) - razor++; - if( 'O' == this[y+dy,x+dx] ) - cleared = true; - if( " \\!.O".count(this[y+dy,x+dx])==1 ) { - this[y,x]=' '; - this[y+dy,x+dx]='R'; - robot = new Pos(y+dy,x+dx); - } else if(dy==0 && rocky(this[y+dy,x+dx]) && ' '==this[y+dy*2,x+dx*2]) { - char rock = this[y+dy,x+dx]; - this[y,x]=' '; - this[y+dy,x+dx]='R'; - this[y+dy*2,x+dx*2]=rock; - robot = new Pos(y+dy,x+dx); + Pos next = new Pos(robot.y+dy, robot.x+dx); + int y=robot.y, x=robot.x; + + if( '\\' == this[next] ) collected_lambda++; + if( '!' == this[next] ) collected_razor++; + if( 'O' == this[next] ) cleared = true; + + if( is_spacy(this[next]) ) + { + emptified(robot); + this[y,x] = ' '; + this[next] = 'R'; + robot = next; + } + else if(dy==0 && is_rocky(this[next]) && ' '==this[y+dy*2,x+dx*2]) + { + char rock = this[next]; + emptified(robot); + this[y,x] = ' '; + this[next] = 'R'; + this[y+dy*2,x+dx*2] = rock; + robot = next; may_update ~= new Pos(y+dy*2,x+dx*2); - } else if('A'<=this[y+dy,x+dx] && this[y+dy,x+dx]<='I') { - this[y,x]=' '; - Pos tp = tr.target_pos(this[y+dy,x+dx]); - foreach(p; tr.source_pos(this[tp])) { + } + else if(is_trampoline_source(this[next])) + { + emptified(robot); + this[y,x] = ' '; + Pos tp = tr.target_pos(this[next]); + foreach(p; tr.source_pos(this[tp])) + { emptified(p); this[p] = ' '; } this[tp] = 'R'; - robot = tp; + robot = tp; } return update(hige_day); } bool update(bool hige_day) @@ -345,11 +385,11 @@ void writep(Pos p, char c) { write_buffer ~= tuple(0+p.y,0+p.x,c); } scope(exit) { may_update.length = 0; foreach(wr; write_buffer) { this[wr[0],wr[1]] = wr[2]; - if(rocky(wr[2])) + if(is_rocky(wr[2])) may_update ~= new Pos(wr[0],wr[1]); if(wr[2]==' ') emptified(new Pos(wr[0], wr[1])); } } @@ -368,24 +408,24 @@ sort(may_update); foreach(p; may_update) { int y = p.y, x = p.x; char rock = this[p]; - if(rocky(this[p])) { + if(is_rocky(this[p])) { if(this[p.D]==' ') { writep(p, ' '); writep(p.D, (rock=='@'&&this[p.D.D]!=' ' ? '\\' : rock)); if(robot == p.D.D) dead=true; } - else if((rocky(this[p.D]) || this[p.D]=='\\') && this[p.R]==' ' && this[p.R.D]==' ') { + else if((is_rocky(this[p.D]) || this[p.D]=='\\') && this[p.R]==' ' && this[p.R.D]==' ') { writep(p, ' '); writep(p.R.D,(rock=='@'&&this[p.R.D.D]!=' ' ? '\\' : rock)); if(robot == p.R.D.D) dead=true; } - else if(rocky(this[p.D]) && this[p.L]==' ' && this[p.L.D]==' ') { + else if(is_rocky(this[p.D]) && this[p.L]==' ' && this[p.L.D]==' ') { writep(p, ' '); writep(p.L.D, (rock=='@'&&this[p.L.D.D]!=' ' ? '\\' : rock)); if(robot == p.L.D.D) dead=true; } Index: src/gui.d ================================================================== --- src/gui.d +++ src/gui.d @@ -119,11 +119,11 @@ this.text = .text( "Score: ", g.score, " Air: ", g.hp, " Tide: ", g.water_until_rise, " Wadler: ", g.hige_until_rise, - " Razor: ", g.map.razor); + " Razor: ", g.map.num_razor); invalidate(); } private: void setup_keyhandling(void delegate(char c) command) Index: src/solver.d ================================================================== --- src/solver.d +++ src/solver.d @@ -9,36 +9,10 @@ // this(in Game g); char single_step(); void force(char c); } - -bool is_spacy(char c) -{ - return c==' ' || c=='.' || c=='R' || c=='!' || c=='\\' || c=='O'; -} - -bool is_rocky(char c) -{ - return c=='*' || c=='@'; -} - -bool is_true_space(char c) -{ - return c==' '; -} - -bool is_trampoline_source(char c) -{ - return 'A'<=c && c<='I'; -} - -bool is_rocklambda(char c) -{ - return is_rocky(c) || c=='\\'; -} - Tuple!(string,int) death_move(in Game g) { // TODO: S string death; @@ -188,11 +162,11 @@ } else if( !la.empty ){ cand ~= search(g, ro, la~ra, death); } // 'higesori' mode - if( !hi.empty && g.map.razor>0 ) { + if( !hi.empty && g.map.num_razor>0 ) { int his = 0; for(int dy=-1; dy<=+1; ++dy) for(int dx=-1; dx<=+1; ++dx) if(g.map[ro.y+dy,ro.x+dx] == 'W') his++;