Index: game.d ================================================================== --- game.d +++ game.d @@ -7,10 +7,11 @@ { public immutable int y, x; mixin DeriveCreate; mixin DeriveCompare; mixin DeriveShow; + Pos clone() { return this; } @property: Pos wait() { return this; } Pos up() { return new Pos(y+1, x); } Pos down() { return new Pos(y-1, x); } @@ -43,10 +44,11 @@ { public immutable int base, pace; mixin DeriveCreate; mixin DeriveCompare; mixin DeriveShow; + Water clone() { return this; } static load(string[string] params) { return new Water( params.get("Water", "0").to!int(), @@ -100,10 +102,18 @@ private { char[][] data; Pos robot; Pos lift; int waterproof; + } + Map clone() { return new Map(this); } + this(Map m) { + foreach(s; m.data) + this.data ~= s.dup; + this.robot = m.robot.clone(); + this.lift = m.lift.clone(); + this.waterproof = m.waterproof; } this(string[] raw_data, string[string] params) { int width = 0; @@ -284,17 +294,33 @@ { this.map = Map.load(raw_data, params); this.water = Water.load(params); this.output = new NilOutput; } + + Game clone() { return new Game(this); } + this(Game g) { + map = g.map.clone(); + water = g.water.clone(); + output = new NilOutput; + turn = g.turn; + dead = g.dead; + lambda = g.lambda; + exit_bonus = g.exit_bonus; + under_water = g.under_water; + } void set_output(Output o) { this.output = (o is null ? new NilOutput : o); } void command(char c) { if(dead || cleared) return; + scope(exit) { + if(dead || cleared) + output.flush(); + } this.output.command(c); if(c == 'A') { exit_bonus = 1; @@ -311,36 +337,38 @@ if( ld[1] ) { dead = true; } } if( map.robot.y <= water_level ) - ++under_warter; + ++under_water; else - under_warter = 0; - if( under_warter > map.waterproof ) + under_water = 0; + if( under_water > map.waterproof ) dead = true; turn += 1; } Map map; Water water; Output output; - int turn = 0; bool dead = false; int lambda = 0; int exit_bonus = 0; - int under_warter = 0; + int under_water = 0; + // TODO: when adding members, take care of clone(). + // TODO: fix this poor design. + @property { long score() { return lambda*25L*(1+exit_bonus) - turn; } int water_level() { return water.level(turn); } int water_until_rise() { return water.until_rise(turn); } bool cleared() { return exit_bonus>0; } - int hp() { return map.waterproof - under_warter; } + int hp() { return map.waterproof - under_water; } long score_if_abort_now() { return lambda*25*(1+max(1,exit_bonus)) - turn; } } } unittest { Game.load(["###","...","#RL"], ["xxx":"yyy"]); } Index: gui.d ================================================================== --- gui.d +++ gui.d @@ -110,9 +110,9 @@ } void main(string[] args) { auto g = Game.load(File(args[1])); - g.set_output(new StdOutput); + g.set_output(new GuardedOutput(g)); auto myForm = new GUI(g); Application.run(myForm); } Index: output.d ================================================================== --- output.d +++ output.d @@ -4,32 +4,77 @@ import std.c.stdlib; abstract class Output { void command(char c); + void flush(); } class NilOutput : Output { override void command(char c) {} + override void flush() {} } class StdOutput : Output { + override void command(char c) + { + write(c); + stdout.flush(); + } + override void flush() {} +} + +// TODO: clean it up. +__gshared Output g_output; + +class GuardedOutput : StdOutput +{ // Handle SIGINT: force abort and exit. static this() { signal(SIGINT, &sigint); } - extern(C) static void sigint(int) { - write("A"); - stdout.flush(); + + extern(C) static void sigint(int) + { + if(g_output !is null) + g_output.flush(); + else { + write("A"); + stdout.flush(); + } exit(0); } + Game g; + this(Game ini) { this.g = ini.clone(); ideal_log ~= g.score_if_abort_now; g_output = this; } + + string log; + long[] score_log; + long[] ideal_log; + override void command(char c) { - // TODO: optimize redundancy. - write(c); + g.command(c); + log ~= c; + score_log ~= g.score; + ideal_log ~= g.score_if_abort_now; + } + override void flush() + { + Tuple!(long, int, int) cand; + cand[0] = long.min; + foreach(int i, long s; score_log) + if(cand[0] < s) + cand = tuple(s,i,0); + foreach(int i, long s; ideal_log) + if(cand[0] < s) + cand = tuple(s,i,1); + if(cand[2]==0) + writeln(log[0..cand[1]+1]); + else + writeln(log[0..cand[1]]~"A"); stdout.flush(); } }