@@ -9,9 +9,9 @@ mixin DeriveCompare; mixin DeriveShow; Pos clone() const { return cast(Pos) this; } -@property: +const @property: Pos wait() { return this.clone(); } Pos up() { return new Pos(y+1, x); } Pos down() { return new Pos(y-1, x); } Pos left() { return new Pos(y, x-1); } @@ -44,24 +44,24 @@ public immutable int base, pace; mixin DeriveCreate; mixin DeriveCompare; mixin DeriveShow; - Water clone() const { return cast(Water)this; } + Water clone() const { return cast(Water) this; } static load(string[string] params) { return new Water(params.get("Water", "0").to!int(), params.get("Flooding", "0").to!int()); } - int level(int number_of_update) const + int level(int turn) const { - return pace ? base+(number_of_update/pace) : base; + return pace ? base+(turn/pace) : base; } - int until_rise(int number_of_update) const + int until_rise(int turn) const { - return pace ? pace-number_of_update%pace : int.max; + return pace ? pace-turn%pace : int.max; } } unittest @@ -115,14 +115,8 @@ class Map { mixin DeriveShow; - static Map load(string[] raw_data, string[string] params, char[char] trampo) - { - // TODO: choose optimal representation. - return new Map(raw_data, params, trampo); - } - char[][] data; Pos robot; Pos lift; int waterproof; @@ -131,8 +125,10 @@ const(Hige) hige; int razor; int collected_lambda; int total_lambda; + bool cleared; + Pos[] may_update; Map clone() const { return new Map(this); } this(in Map m) { foreach(s; m.data) @@ -146,8 +142,9 @@ this.razor = m.razor; this.collected_lambda = m.collected_lambda; this.total_lambda = m.total_lambda; this.may_update = (cast(Map)m).may_update.dup; + this.cleared = m.cleared; } this(string[] raw_data, string[string] params, char[char] trampo) { @@ -237,51 +234,44 @@ Pos[] razors() const { return objects('!'); } Pos[] lambdas() const { return objects('\\'); } - bool cleared() const - { - for(int y=1; y<=H; ++y) - for(int x=1; x<=W; ++x) - if(this[y,x] == 'L' || this[y,x] == 'O') - return false; - return true; - } - - bool command(char c, int turn) + bool command(char c, int turn, bool hige_day) { assert( this[robot] == 'R' ); - if(c=='R') return move( 0, +1, turn); - if(c=='L') return move( 0, -1, turn); - if(c=='U') return move(+1, 0, turn); - if(c=='D') return move(-1, 0, turn); - if(c=='W') return move( 0, 0, turn); - if(c=='S') return use_razor(turn); + if(c=='R') return move( 0, +1, turn, hige_day); + if(c=='L') return move( 0, -1, turn, hige_day); + if(c=='U') return move(+1, 0, turn, hige_day); + if(c=='D') return move(-1, 0, turn, hige_day); + if(c=='W') return move( 0, 0, turn, hige_day); + if(c=='S') return use_razor(turn, hige_day); assert(false); } - bool use_razor(int turn) + bool use_razor(int turn, bool hige_day) { if(razor) { razor--; for(int dy=-1; dy<=+1; ++dy) for(int dx=-1; dx<=+1; ++dx) - if(this[robot.y+dy,robot.x+dx] == 'W') + 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(turn); + return update(turn, hige_day); } bool rocky(char c) { return c=='*' || c=='@'; } - Pos[] may_update; + 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, int turn) + bool move(int dy, int dx, int turn, bool hige_day) { emptified(robot); @@ -290,8 +280,10 @@ 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); @@ -310,12 +302,12 @@ } this[tp] = 'R'; robot = tp; } - return update(turn); + return update(turn, hige_day); } - bool update(int turn) + bool update(int turn, bool hige_day) { // Write after all the updates are processed. Tuple!(int,int,char)[] write_buffer; void write(int y, int x, char c) { write_buffer ~= tuple(y,x,c); } @@ -359,16 +351,22 @@ if(robot == p.L.D.D) dead=true; } } - else if(this[p]=='W') { - if( hige.is_growing_turn(turn) ) + } + + if( hige_day ) + for(int y=1; y<=H; ++y) + for(int x=1; x<=W; ++x) { + Pos p = new Pos(y,x); + if(this[p]=='W') { for(int dy=-1; dy<=+1; ++dy) for(int dx=-1; dx<=+1; ++dx) if(this[p.y+dy,p.x+dx] == ' ') write(p.y+dy,p.x+dx,'W'); } } + return dead; } } @@ -377,9 +375,9 @@ class Game { mixin DeriveShow; - static Game load(File input) + this(File input) { string[] raw_data; string[string] params; @@ -396,29 +394,18 @@ if( ss.length == 4 && ss[0]=="Trampoline" && ss[2]=="targets" ) trampo[ss[1][0]] = ss[3][0]; } - return load(raw_data, params, trampo); - } - - static Game load(string[] raw_data, string[string] params, char[char] trampo = null) - { - return new Game(raw_data, params, trampo); - } - - this(string[] raw_data, string[string] params, char[char] trampo) - { - this.map = Map.load(raw_data, params, trampo); + this.map = new Map(raw_data, params, trampo); this.water = Water.load(params); } Game clone() const { return new Game(this); } this(in Game g) { - map = g.map.clone(); + map = g.map.clone(); water = g.water.clone(); - turn = g.turn; - dead = g.dead; - cleared = g.cleared; + turn = g.turn; + dead = g.dead; under_water = g.under_water; } void command(char c) @@ -427,17 +414,12 @@ if(dead || cleared) return; // TODO: clarify the event order - bool dead_now = map.command(c, turn); - if( map.cleared() ) { - cleared = true; - } - else { - if( dead_now ) - dead = true; - } - if(!cleared) { + bool dead_now = map.command(c, turn, map.hige.is_growing_turn(turn)); + if( dead_now ) + dead = true; + if(!map.cleared) { if( map.robot.y <= water_level ) ++under_water; else under_water = 0; @@ -451,16 +433,15 @@ Water water; int turn = 0; bool dead = false; int under_water = 0; - bool cleared = false; // TODO: when adding members, take care of clone(). // TODO: fix this poor design. - @property const { - long score() { return map.collected_lambda*(dead ? 25L : cleared ? 75L : 50L) - turn; } - int water_level() { return water.level(turn); } - int water_until_rise() { return water.until_rise(turn); } - int hige_until_rise() { return map.hige.until_rise(turn); } - int hp() { return map.waterproof - under_water; } - } +@property const: + long score() { return map.collected_lambda*(dead?25L:cleared?75L:50L)-turn; } + int water_level() { return water.level(turn); } + int water_until_rise() { return water.until_rise(turn); } + int hige_until_rise() { return map.hige.until_rise(turn); } + int hp() { return map.waterproof - under_water; } + bool cleared() { return map.cleared; } }