Index: src/driver.d ================================================================== --- src/driver.d +++ src/driver.d @@ -8,11 +8,11 @@ } class Driver { this(Game g) { this.game = g; } - this(File game_data) { this(Game.load(game_data)); } + this(File game_data) { this(new Game(game_data)); } void command(char c) { if( finished ) return; Index: src/game.d ================================================================== --- src/game.d +++ src/game.d @@ -8,11 +8,11 @@ mixin DeriveCreate; 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); } Pos right() { return new Pos(y, x+1); } @@ -43,26 +43,26 @@ { 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 { @@ -114,16 +114,10 @@ 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; Pos[char] tr_target; @@ -130,10 +124,12 @@ Pos[][char] tr_source; 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) this.data ~= s.dup; @@ -145,10 +141,11 @@ this.hige = m.hige.clone(); 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) { int width = 0; @@ -236,53 +233,46 @@ } 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); int y = robot.y; @@ -289,10 +279,12 @@ 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]) { @@ -309,14 +301,14 @@ this[p] = ' '; } 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); } void writep(Pos p, char c) { write_buffer ~= tuple(0+p.y,0+p.x,c); } @@ -358,18 +350,24 @@ writep(p.L.D, (rock=='@'&&this[p.L.D.D]!=' ' ? '\\' : rock)); 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; } } //////////////////////////////////////////////////////////////////////////////// @@ -376,11 +374,11 @@ class Game { mixin DeriveShow; - static Game load(File input) + this(File input) { string[] raw_data; string[string] params; // Raw map data; read until empty line. @@ -395,31 +393,20 @@ params[ss[0]] = ss[1]; 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) { @@ -426,19 +413,14 @@ assert(c != 'A'); 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; if( under_water > map.waterproof ) @@ -450,17 +432,16 @@ Map map; 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; } }