ADDED src/cui_auto_main.d Index: src/cui_auto_main.d ================================================================== --- src/cui_auto_main.d +++ src/cui_auto_main.d @@ -0,0 +1,13 @@ +import util; +import game; +import output; +import driver; +import solver; + +void main(string[] args) +{ + Driver d = new Driver(stdin); + d.addObserver!(GuardedOutput)(); + Solver s = d.addObserver!(Solver)(); + s.run(&d.command); +} Index: src/game.d ================================================================== --- src/game.d +++ src/game.d @@ -167,20 +167,20 @@ void opIndexAssign(char c, Pos p) { this[p.y, p.x] = c; } - Pos[] lambdas() { + Pos[] lambdas() const { Pos[] ans; for(int y=1; y<=H; ++y) for(int x=1; x<=W; ++x) if(this[y,x] == '\\') ans ~= new Pos(y,x); return ans; } - bool cleared() + 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; Index: src/gui.d ================================================================== --- src/gui.d +++ src/gui.d @@ -1,112 +1,113 @@ import dfl.all; import util; import game; -import output; import driver; -//import solver; -pragma(lib, "dfl.lib"); -class GUI : Form, GameObserver +class GUI(Solver) : Form, GameObserver { - bool on_game_changed(char c, const(Game) g, bool finished) { - draw(gr, g); - invalidate(); - return false; + this(const(Game) g) + { + this.solver = new Solver(g); + setup_size(g.map.W, g.map.H); + setup_resources(); + setup_keyhandling(); + draw(g); + } + + private void delegate(char c) fn; + void set_fn(F)(F f) { this.fn = f; } + + void run() + { + Application.run(this); } - private { - int cell; - int turn = 0; - - Font font; - Color[char] colors; - string[char] render; - void delegate(char c) fn; + override void on_game_changed(char c, const(Game) g, bool finished) + { + draw(g); } - this(const(Game) g) - { - noMessageFilter(); - this.setStyle(ControlStyles.OPAQUE, true); - this.fn = fn; +private: + int cell; - this.paint ~= &my_paint; - this.keyDown ~= &my_keydown; - + void setup_size(int W, int H) + { this.formBorderStyle = FormBorderStyle.FIXED_DIALOG; this.maximizeBox = false; this.minimizeBox = false; - this.cell = min(1024/g.map.W, 640/g.map.H); - this.clientSize = Size(g.map.W*cell, g.map.H*cell); + this.cell = min(1024/W, 640/H); + this.clientSize = Size(W*cell, H*cell); + } - const scrH = this.clientSize.height; - const scrW = this.clientSize.width; - this.gr = new MemoryGraphics(scrW, scrH); + Font font; + Color[char] colors; + string[char] render; + Graphics graphicContext; - // Resources + void setup_resources() + { + this.graphicContext = new MemoryGraphics(this.clientSize.width, this.clientSize.height); + this.setStyle(ControlStyles.OPAQUE, true); this.font = new Font("MS Gothic", cell-2, GraphicsUnit.PIXEL); this.backColor = Color(255,255,255); this.colors['#'] = this.colors['.'] = Color(255,191,127); this.colors['*'] = Color(255,127,127); this.colors['R'] = Color(128,128,0); - this.colors['D'] = Color(255,0,0); // Dead + this.colors['D'] = Color(255,0,0); this.colors['\\'] = this.colors['L'] = this.colors['O'] = Color(127,255,127); - this.colors['W'] = Color(204,229,255); // water - + this.colors['W'] = Color(204,229,255); this.render['#'] = "■"; this.render['*'] = "✹"; this.render['.'] = "♒"; this.render['\\'] = "λ"; this.render['R'] = "☃"; this.render['D'] = "☠"; this.render['L'] = "☒"; this.render['O'] = "☐"; - draw(gr, g); - } - - void set_fn(F)(F f) { this.fn = f; } - - void run() { - Application.run(this); + this.paint ~= (Control c, PaintEventArgs ev) { + graphicContext.copyTo(ev.graphics, Rect(0,0,this.clientSize.width,this.clientSize.height)); + }; } -private: - Graphics gr; - - void my_paint(Control, PaintEventArgs ev) - { - gr.copyTo(ev.graphics, Rect(0,0,this.clientSize.width,this.clientSize.height)); - } - - void draw(Graphics gr, const(Game) g) + void draw(const(Game) g) { int scrW = this.clientSize.width; int scrH = this.clientSize.height; + // Fill bg. - gr.fillRectangle(this.backColor, Rect(0,0,scrW,scrH)); + graphicContext.fillRectangle(this.backColor, Rect(0,0,scrW,scrH)); // Fill water. int w = g.water_level(); - gr.fillRectangle(this.colors['W'], Rect(0, scrH-cell*w-1, scrW, cell*w+1)); + graphicContext.fillRectangle(this.colors['W'], Rect(0, scrH-cell*w-1, scrW, cell*w+1)); // Paint map. for(int y=1; y<=g.map.H; ++y) for(int x=1; x<=g.map.W; ++x) { Rect r = Rect(cell*(x-1), scrH-cell*y, cell, cell); char c = g.map[y,x]; if( c != ' ' ) { if( c == 'R' && g.dead ) c = 'D'; - gr.drawText(this.render[c], font, this.colors[c], r); + graphicContext.drawText(this.render[c], font, this.colors[c], r); } } - set_text(g); + // Update textual info. + this.text = .text("Score: ", g.score, " Air: ", g.hp, " Tide: ", g.water_until_rise); + invalidate(); + } + +private: + void setup_keyhandling() + { + noMessageFilter(); + this.keyDown ~= &my_keydown; } void my_keydown(Control c, KeyEventArgs ev) { switch(ev.keyCode) @@ -115,22 +116,12 @@ case Keys.UP: fn('U'); break; case Keys.LEFT: fn('L'); break; case Keys.RIGHT: fn('R'); break; case Keys.W: fn('W'); break; case Keys.A: fn('A'); break; + case Keys.G: fn(solver.single_step()); break; default: break; } } - void set_text(const(Game) g) { - this.text = .text("Score: ", g.score, " Air: ", g.hp, " Tide: ", g.water_until_rise); - } -} - -void main(string[] args) -{ - auto d = new Driver(File(args[1])); - d.addObserver!(GuardedOutput)(); - GUI g = d.addObserver!(GUI)(); - g.set_fn(&d.command); - g.run(); + Solver solver; } ADDED src/gui_main.d Index: src/gui_main.d ================================================================== --- src/gui_main.d +++ src/gui_main.d @@ -0,0 +1,15 @@ +import gui; +import output; +import driver; +import solver; +import std.stdio; +pragma(lib, "dfl.lib"); + +void main(string[] args) +{ + Driver d = new Driver(stdin); + d.addObserver!(GuardedOutput)(); + auto g = d.addObserver!(GUI!Solver_1)(); + g.set_fn(&d.command); + g.run(); +} Index: src/output.d ================================================================== --- src/output.d +++ src/output.d @@ -1,25 +1,23 @@ import util; import game; import driver; -import std.c.stdlib; import core.stdc.signal; class NilOutput : GameObserver { this(const(Game) g) {} - override bool on_game_changed(char c, const(Game) g, bool finished) {return false;} + override void on_game_changed(char c, const(Game) g, bool finished) {} } class StdOutput : GameObserver { this(const(Game) g) {} - override bool on_game_changed(char c, const(Game) g, bool finished) + override void on_game_changed(char c, const(Game) g, bool finished) { stdout.write(c); stdout.flush(); - return false; } } class GuardedOutput : GameObserver { @@ -27,18 +25,17 @@ { setup_sigint_handling(); ideal_log ~= g.score_if_abort_now; } - override bool on_game_changed(char c, const(Game) g, bool finished) + override void on_game_changed(char c, const(Game) g, bool finished) { log ~= c; score_log ~= g.score; ideal_log ~= g.score_if_abort_now; if(finished) flush(); - return false; } private: string log; long[] score_log; Index: src/solver.d ================================================================== --- src/solver.d +++ src/solver.d @@ -1,104 +1,125 @@ import util; import game; -import output; +import driver; -int g_wc = 0; - -void act(Game g) +/* +interface Solver { - Pos ro = g.map.robot; - Pos[] la = g.map.lambdas(); - Pos li = g.map.lift; + this(const(Game) g); + char single_step(); +} +*/ - char c = 'W'; - if( la.empty ) { - auto r = search(g, ro, li); - c = r[0]; - } else { - Tuple!(char,int)[] cand; - foreach(lam; la) - cand ~= search(g, ro, lam); - sort!((Tuple!(char,int) c1, Tuple!(char,int) c2){ - if(c1[1] != c2[1]) - return c1[1] < c2[1]; - return c1[0] < c2[0]; - })(cand); - c = cand[0][0]; - } - if(c=='W') { - g_wc++; - if(g_wc > 10) - c = 'A'; - } - else - g_wc = 0; - g.command(c); +class Solver_0 +{ + this(const(Game) g) {} + char single_step() { return 'W'; } } -Tuple!(char,int) search(Game g, Pos s, Pos o) +class Solver_1 { - Pos[] q = [o]; - bool[][] v = new bool[][](g.map.H+2, g.map.W+2); - for(int step=1; q.length; ++step) { - Pos[] q2; - foreach(p; q) { - int[] dy=[-1,+1,0,0]; - int[] dx=[0,0,-1,+1]; - for(int i=0; i<4; ++i) { - int y = p.y+dy[i]; - int x = p.x+dx[i]; - if(v[y][x]) continue; - if(y==s.y && x==s.x) { - if(i==0) return tuple('U',step); - if(i==1) return tuple('D',step); - if(i==2) return tuple('R',step); - if(i==3) return tuple('L',step); - } else if(g.map[y,x]==' '||g.map[y,x]=='\\') { - q2 ~= new Pos(y,x); - v[y][x]=true; - } else if(g.map[y,x]=='.' && g.map[y-1,x]!='*') { - q2 ~= new Pos(y,x); - v[y][x]=true; - } - } + int g_wc = 0; + + Game g; + this(const(Game) g) + { + this.g = g.clone(); + } + + char single_step() + { + char c = act(g); + g.command(c); + return c; + } + + char act(const(Game) g) + { + const Pos ro = g.map.robot; + const Pos[] la = g.map.lambdas(); + const Pos li = g.map.lift; + + char c = 'W'; + if( la.empty ) { + auto r = search(g, ro, li); + c = r[0]; + } else { + Tuple!(char,int)[] cand; + foreach(lam; la) + cand ~= search(g, ro, lam); + sort!((Tuple!(char,int) c1, Tuple!(char,int) c2){ + if(c1[1] != c2[1]) + return c1[1] < c2[1]; + return c1[0] < c2[0]; + })(cand); + c = cand[0][0]; + } + if(c=='W') { + g_wc++; + if(g_wc > 10) + c = 'A'; } - q = q2; + else + g_wc = 0; + return c; } - q = [o]; - v = new bool[][](g.map.H+2, g.map.W+2); - for(int step=1000; q.length; ++step) { - Pos[] q2; - foreach(p; q) { - int[] dy=[-1,+1,0,0]; - int[] dx=[0,0,-1,+1]; - for(int i=0; i<4; ++i) { - int y = p.y+dy[i]; - int x = p.x+dx[i]; - if(v[y][x]) continue; - if(y==s.y && x==s.x) { - if(i==0) return tuple('U',step); - if(i==1) return tuple('D',step); - if(i==2) return tuple('R',step); - if(i==3) return tuple('L',step); - } else if(g.map[y,x]==' '||g.map[y,x]=='\\') { - q2 ~= new Pos(y,x); - v[y][x]=true; - } else if(g.map[y,x]=='.'/* && g[y-1,x]!='*'*/) { - q2 ~= new Pos(y,x); - v[y][x]=true; + + Tuple!(char,int) search(in Game g, in Pos s, in Pos o) + { + const(Pos)[] q = [o]; + bool[][] v = new bool[][](g.map.H+2, g.map.W+2); + for(int step=1; q.length; ++step) { + Pos[] q2; + foreach(p; q) { + int[] dy=[-1,+1,0,0]; + int[] dx=[0,0,-1,+1]; + for(int i=0; i<4; ++i) { + int y = p.y+dy[i]; + int x = p.x+dx[i]; + if(v[y][x]) continue; + if(y==s.y && x==s.x) { + if(i==0) return tuple('U',step); + if(i==1) return tuple('D',step); + if(i==2) return tuple('R',step); + if(i==3) return tuple('L',step); + } else if(g.map[y,x]==' '||g.map[y,x]=='\\') { + q2 ~= new Pos(y,x); + v[y][x]=true; + } else if(g.map[y,x]=='.' && g.map[y-1,x]!='*') { + q2 ~= new Pos(y,x); + v[y][x]=true; + } + } + } + q = q2; + } + q = [o]; + v = new bool[][](g.map.H+2, g.map.W+2); + for(int step=1000; q.length; ++step) { + Pos[] q2; + foreach(p; q) { + int[] dy=[-1,+1,0,0]; + int[] dx=[0,0,-1,+1]; + for(int i=0; i<4; ++i) { + int y = p.y+dy[i]; + int x = p.x+dx[i]; + if(v[y][x]) continue; + if(y==s.y && x==s.x) { + if(i==0) return tuple('U',step); + if(i==1) return tuple('D',step); + if(i==2) return tuple('R',step); + if(i==3) return tuple('L',step); + } else if(g.map[y,x]==' '||g.map[y,x]=='\\') { + q2 ~= new Pos(y,x); + v[y][x]=true; + } else if(g.map[y,x]=='.'/* && g[y-1,x]!='*'*/) { + q2 ~= new Pos(y,x); + v[y][x]=true; + } } } + q = q2; } - q = q2; + return tuple('W', int.max); } - return tuple('W', int.max); -} - -void main(string[] args) -{ - auto g = Game.load(stdin); - g.set_output(new GuardedOutput(g)); - - while(!g.dead && !g.cleared) - act(g); }