Check-in [bee0596f0f]
Not logged in
Overview
SHA1 Hash:bee0596f0fcfe8d8ac2135382c34fb134c8abc40
Date: 2012-07-14 20:24:30
User: kinaba
Comment:Refactoring.
Timelines: family | ancestors | descendants | both | trunk
Diffs: redesign
Downloads: Tarball | ZIP archive
Other Links: files | file ages | manifest
Tags And Properties
Changes

Modified src/game.d from [7c15481493257083] to [cde4121762ca0c01].

1 1 import util; 2 -import output; 3 2 4 3 //////////////////////////////////////////////////////////////////////////////// 5 4 6 5 class Pos 7 6 { 8 7 public immutable int y, x; 9 8 mixin DeriveCreate; 10 9 mixin DeriveCompare; 11 10 mixin DeriveShow; 12 - Pos clone() { return this; } 11 + Pos clone() const { return new Pos(y, x); } 13 12 14 13 @property: 15 - Pos wait() { return this; } 14 + Pos wait() { return this.clone(); } 16 15 Pos up() { return new Pos(y+1, x); } 17 16 Pos down() { return new Pos(y-1, x); } 18 17 Pos left() { return new Pos(y, x-1); } 19 18 Pos right() { return new Pos(y, x+1); } 20 19 alias wait W,w; 21 20 alias up U,u; 22 21 alias down D,d; ................................................................................ 42 41 43 42 class Water 44 43 { 45 44 public immutable int base, pace; 46 45 mixin DeriveCreate; 47 46 mixin DeriveCompare; 48 47 mixin DeriveShow; 49 - Water clone() { return this; } 48 + Water clone() const { return new Water(base, pace); } 50 49 51 50 static load(string[string] params) 52 51 { 53 52 return new Water( 54 53 params.get("Water", "0").to!int(), 55 54 params.get("Flooding", "0").to!int() 56 55 ); 57 56 } 58 57 59 - int level(int number_of_update) 58 + int level(int number_of_update) const 60 59 { 61 60 return pace ? base+(number_of_update/pace) : base; 62 61 } 63 62 64 - int until_rise(int number_of_update) 63 + int until_rise(int number_of_update) const 65 64 { 66 65 return pace ? pace-number_of_update%pace : int.max; 67 66 } 68 67 } 69 68 70 69 unittest 71 70 { ................................................................................ 100 99 } 101 100 102 101 char[][] data; 103 102 Pos robot; 104 103 Pos lift; 105 104 int waterproof; 106 105 107 - Map clone() { return new Map(this); } 108 - this(Map m) { 106 + Map clone() const { return new Map(this); } 107 + this(const(Map) m) { 109 108 foreach(s; m.data) 110 109 this.data ~= s.dup; 111 110 this.robot = m.robot.clone(); 112 111 this.lift = m.lift.clone(); 113 112 this.waterproof = m.waterproof; 114 113 } 115 114 ................................................................................ 136 135 } 137 136 138 137 const @property { 139 138 int H() { return data.length; } 140 139 int W() { return data[0].length; } 141 140 } 142 141 143 - char opIndex(int y, int x) 144 - { 145 - // Adjust coordinate to the spec. bottom-left is (1,1). 146 - --y, --x; 147 - if(y<0||H<=y||x<0||W<=x) 148 - return '#'; 149 - return data[H-1-y][x]; 150 - } 142 + const { 143 + char opIndex(int y, int x) 144 + { 145 + // Adjust coordinate to the spec. bottom-left is (1,1). 146 + --y, --x; 147 + if(y<0||H<=y||x<0||W<=x) 148 + return '#'; 149 + return data[H-1-y][x]; 150 + } 151 151 152 - char opIndex(Pos p) 153 - { 154 - return this[p.y, p.x]; 152 + char opIndex(Pos p) 153 + { 154 + return this[p.y, p.x]; 155 + } 155 156 } 156 157 157 158 void opIndexAssign(char c, int y, int x) 158 159 { 159 160 // Adjust coordinate to the spec. bottom-left is (1,1). 160 161 --y, --x; 161 162 if(y<0||H<=y||x<0||W<=x) ................................................................................ 298 299 return new Game(raw_data, params); 299 300 } 300 301 301 302 this(string[] raw_data, string[string] params) 302 303 { 303 304 this.map = Map.load(raw_data, params); 304 305 this.water = Water.load(params); 305 - this.output = new NilOutput; 306 306 } 307 307 308 - Game clone() { return new Game(this); } 309 - this(Game g) { 308 + Game clone() const { return new Game(this); } 309 + this(const(Game) g) { 310 310 map = g.map.clone(); 311 311 water = g.water.clone(); 312 - output = new NilOutput; 313 312 turn = g.turn; 314 313 dead = g.dead; 315 314 lambda = g.lambda; 316 315 exit_bonus = g.exit_bonus; 317 316 under_water = g.under_water; 318 317 } 319 318 320 - void set_output(Output o) { this.output = (o is null ? new NilOutput : o); } 321 - 322 319 void command(char c) 323 320 { 324 321 if(dead || cleared) 325 322 return; 326 - scope(exit) { 327 - if(dead || cleared) 328 - output.flush(); 329 - } 330 - this.output.command(c); 331 323 332 324 if(c == 'A') 333 325 { 334 326 exit_bonus = 1; 335 327 return; 336 328 } 337 329 ................................................................................ 353 345 if( under_water > map.waterproof ) 354 346 dead = true; 355 347 turn += 1; 356 348 } 357 349 358 350 Map map; 359 351 Water water; 360 - Output output; 361 352 int turn = 0; 362 353 bool dead = false; 363 354 int lambda = 0; 364 355 int exit_bonus = 0; 365 356 int under_water = 0; 366 357 // TODO: when adding members, take care of clone(). 367 358 // TODO: fix this poor design. 368 359 369 - @property { 360 + @property const { 370 361 long score() { return lambda*25L*(1+exit_bonus) - turn; } 371 362 int water_level() { return water.level(turn); } 372 363 int water_until_rise() { return water.until_rise(turn); } 373 364 bool cleared() { return exit_bonus>0; } 374 365 int hp() { return map.waterproof - under_water; } 375 366 long score_if_abort_now() { return lambda*25*(1+max(1,exit_bonus)) - turn; } 376 367 } 377 368 } 378 369 379 370 unittest 380 371 { 381 372 Game.load(["###","...","#RL"], ["xxx":"yyy"]); 382 373 }

Modified src/gui.d from [8bca70d55515c665] to [e76b6b97ba56256b].

1 1 import dfl.all; 2 2 import util; 3 3 import game; 4 4 import output; 5 +import driver; 5 6 //import solver; 7 +pragma(lib, "dfl.lib"); 6 8 7 -class GUI : Form 9 +class GUI : Form, GameObserver 8 10 { 11 + bool on_game_changed(char c, const(Game) g, bool finished) { 12 + draw(gr, g); 13 + invalidate(); 14 + return false; 15 + } 16 + 9 17 private { 10 - Game g; 11 18 int cell; 12 19 int turn = 0; 13 20 14 21 Font font; 15 22 Color[char] colors; 16 23 string[char] render; 24 + void delegate(char c) fn; 17 25 } 18 26 19 - this(Game g) 27 + this(const(Game) g) 20 28 { 21 29 noMessageFilter(); 22 30 this.setStyle(ControlStyles.OPAQUE, true); 23 - this.g = g; 31 + this.fn = fn; 24 32 25 33 this.paint ~= &my_paint; 26 34 this.keyDown ~= &my_keydown; 27 35 28 36 this.formBorderStyle = FormBorderStyle.FIXED_DIALOG; 29 37 this.maximizeBox = false; 30 38 this.minimizeBox = false; 31 39 this.cell = min(1024/g.map.W, 640/g.map.H); 32 40 this.clientSize = Size(g.map.W*cell, g.map.H*cell); 33 - set_text(); 41 + 42 + const scrH = this.clientSize.height; 43 + const scrW = this.clientSize.width; 44 + this.gr = new MemoryGraphics(scrW, scrH); 34 45 35 46 // Resources 36 47 this.font = new Font("MS Gothic", cell-2, GraphicsUnit.PIXEL); 37 48 this.backColor = Color(255,255,255); 38 49 this.colors['#'] = 39 50 this.colors['.'] = Color(255,191,127); 40 51 this.colors['*'] = Color(255,127,127); ................................................................................ 49 60 this.render['*'] = "✹"; 50 61 this.render['.'] = "♒"; 51 62 this.render['\\'] = "λ"; 52 63 this.render['R'] = "☃"; 53 64 this.render['D'] = "☠"; 54 65 this.render['L'] = "☒"; 55 66 this.render['O'] = "☐"; 67 + draw(gr, g); 68 + } 69 + 70 + void set_fn(F)(F f) { this.fn = f; } 71 + 72 + void run() { 73 + Application.run(this); 56 74 } 57 75 58 76 private: 77 + Graphics gr; 78 + 59 79 void my_paint(Control, PaintEventArgs ev) 60 80 { 61 - const scrH = this.clientSize.height; 62 - const scrW = this.clientSize.width; 63 - Graphics gr = new MemoryGraphics(scrW, scrH, ev.graphics); 64 - scope(exit) { 65 - gr.copyTo(ev.graphics, Rect(0,0,scrW,scrH)); 66 - gr.dispose(); 67 - } 81 + gr.copyTo(ev.graphics, Rect(0,0,this.clientSize.width,this.clientSize.height)); 82 + } 68 83 84 + void draw(Graphics gr, const(Game) g) 85 + { 86 + int scrW = this.clientSize.width; 87 + int scrH = this.clientSize.height; 69 88 // Fill bg. 70 89 gr.fillRectangle(this.backColor, Rect(0,0,scrW,scrH)); 71 90 72 91 // Fill water. 73 92 int w = g.water_level(); 74 93 gr.fillRectangle(this.colors['W'], Rect(0, scrH-cell*w-1, scrW, cell*w+1)); 75 94 ................................................................................ 80 99 char c = g.map[y,x]; 81 100 if( c != ' ' ) { 82 101 if( c == 'R' && g.dead ) 83 102 c = 'D'; 84 103 gr.drawText(this.render[c], font, this.colors[c], r); 85 104 } 86 105 } 106 + 107 + set_text(g); 87 108 } 88 109 89 110 void my_keydown(Control c, KeyEventArgs ev) 90 111 { 91 112 switch(ev.keyCode) 92 113 { 93 - case Keys.DOWN: g.command('D'); break; 94 - case Keys.UP: g.command('U'); break; 95 - case Keys.LEFT: g.command('L'); break; 96 - case Keys.RIGHT: g.command('R'); break; 97 - case Keys.W: g.command('W'); break; 98 - case Keys.A: g.command('A'); break; 99 -// case Keys.G: solver.act(g); break; 114 + case Keys.DOWN: fn('D'); break; 115 + case Keys.UP: fn('U'); break; 116 + case Keys.LEFT: fn('L'); break; 117 + case Keys.RIGHT: fn('R'); break; 118 + case Keys.W: fn('W'); break; 119 + case Keys.A: fn('A'); break; 100 120 default: break; 101 121 } 102 - if(g.cleared) 103 - Application.exit(); 104 - invalidate(); 105 - set_text(); 106 122 } 107 123 108 - void set_text() { 124 + void set_text(const(Game) g) { 109 125 this.text = .text("Score: ", g.score, " Air: ", g.hp, " Tide: ", g.water_until_rise); 110 126 } 111 127 } 112 128 113 129 void main(string[] args) 114 130 { 115 - auto g = Game.load(File(args[1])); 116 - g.set_output(new GuardedOutput(g)); 117 - auto myForm = new GUI(g); 118 - Application.run(myForm); 131 + auto d = new Driver(File(args[1])); 132 + d.addObserver!(GuardedOutput)(); 133 + GUI g = d.addObserver!(GUI)(); 134 + g.set_fn(&d.command); 135 + g.run(); 119 136 }

Modified src/output.d from [1b458d7cd25e92dc] to [2ca374354297c908].

1 1 import util; 2 2 import game; 3 -import core.stdc.signal; 3 +import driver; 4 4 import std.c.stdlib; 5 +import core.stdc.signal; 5 6 6 -abstract class Output 7 +class NilOutput : GameObserver 7 8 { 8 - void command(char c); 9 - void flush(); 9 + this(const(Game) g) {} 10 + override bool on_game_changed(char c, const(Game) g, bool finished) {return false;} 10 11 } 11 12 12 -class NilOutput : Output 13 +class StdOutput : GameObserver 13 14 { 14 - override void command(char c) {} 15 - override void flush() {} 16 -} 17 - 18 -class StdOutput : Output 19 -{ 20 - override void command(char c) 15 + this(const(Game) g) {} 16 + override bool on_game_changed(char c, const(Game) g, bool finished) 21 17 { 22 - write(c); 18 + stdout.write(c); 23 19 stdout.flush(); 20 + return false; 24 21 } 25 - override void flush() {} 26 22 } 27 23 28 -// TODO: clean it up. 29 -__gshared Output g_output; 30 - 31 -class GuardedOutput : StdOutput 24 +class GuardedOutput : GameObserver 32 25 { 33 - // Handle SIGINT: force abort and exit. 34 - static this() 26 + this(const(Game) g) 27 + { 28 + setup_sigint_handling(); 29 + ideal_log ~= g.score_if_abort_now; 30 + } 31 + 32 + override bool on_game_changed(char c, const(Game) g, bool finished) 35 33 { 36 - signal(SIGINT, &sigint); 34 + log ~= c; 35 + score_log ~= g.score; 36 + ideal_log ~= g.score_if_abort_now; 37 + if(finished) 38 + flush(); 39 + return false; 37 40 } 38 41 39 - extern(C) static void sigint(int) 40 - { 41 - if(g_output !is null) 42 - g_output.flush(); 43 - else { 44 - write("A"); 45 - stdout.flush(); 46 - } 47 - exit(0); 48 - } 49 - 50 - Game g; 51 - this(Game ini) { this.g = ini.clone(); ideal_log ~= g.score_if_abort_now; g_output = this; } 52 - 42 +private: 53 43 string log; 54 44 long[] score_log; 55 45 long[] ideal_log; 56 46 57 - override void command(char c) 58 - { 59 - g.command(c); 60 - log ~= c; 61 - score_log ~= g.score; 62 - ideal_log ~= g.score_if_abort_now; 63 - } 64 - override void flush() 47 + void flush() 65 48 { 66 49 Tuple!(long, int, int) cand; 67 50 cand[0] = long.min; 68 - foreach(int i, long s; score_log) 69 - if(cand[0] < s) 70 - cand = tuple(s,i,0); 71 - foreach(int i, long s; ideal_log) 72 - if(cand[0] < s) 73 - cand = tuple(s,i,1); 74 - if(cand[2]==0) 75 - writeln(log[0..cand[1]+1]); 76 - else 77 - writeln(log[0..cand[1]]~"A"); 78 - stdout.flush(); 51 + 52 + for(int i=0; i<score_log.length; ++i) 53 + if(cand[0] < score_log[i]) 54 + cand = tuple(score_log[i],i,0); 55 + for(int i=0; i<ideal_log.length; ++i) 56 + if(cand[0] < ideal_log[i]) 57 + cand = tuple(ideal_log[i],i,1); 58 + 59 + if(cand[2]==0) { 60 + string str = log[0..cand[1]+1]; 61 + std.c.stdio.printf("%.*s\n", str.length, str.ptr); 62 + } else { 63 + string str = log[0..cand[1]]; 64 + std.c.stdio.printf("%.*sA\n", str.length, str.ptr); 65 + } 66 + std.c.stdio.fflush(std.c.stdio.stdout); 67 + } 68 + 69 +private: 70 + static __gshared GuardedOutput g_output; 71 + 72 + void setup_sigint_handling() 73 + { 74 + assert(g_output is null); 75 + g_output = this; 76 + extern(C) static void catch_sigint(int) { g_output.flush(); application_exit(); } 77 + core.stdc.signal.signal(SIGINT, &catch_sigint); 79 78 } 80 79 }

Modified src/util.d from [8d98b81c5616ea88] to [783554bf667412ed].

1 1 public import std.algorithm; 2 2 public import std.array; 3 3 public import std.conv; 4 4 public import std.range; 5 5 public import std.stdio; 6 6 public import std.string; 7 7 public import std.typecons; 8 +import std.c.stdlib; 9 + 10 +void application_exit() 11 +{ 12 + std.c.stdlib.exit(0); 13 +} 8 14 9 15 template DeriveCreate() 10 16 { 11 17 this(TS...)(TS params) 12 18 { 13 19 this.tupleof = params; 14 20 }