Overview
SHA1 Hash: | b6daa1efa137f6d1095ec06812f9474c40716095 |
---|---|
Date: | 2012-07-14 16:47:55 |
User: | kinaba |
Comment: | Modularized version. |
Timelines: | family | ancestors | descendants | both | trunk |
Diffs: | redesign |
Downloads: | Tarball | ZIP archive |
Other Links: | files | file ages | manifest |
Tags And Properties
- branch=trunk inherited from [16f0b5784f]
- sym-trunk inherited from [16f0b5784f]
Changes
Added game.d version [700e6bf10300d920]
> 1 import util; > 2 import output; > 3 > 4 //////////////////////////////////////////////////////////////////////////////// > 5 > 6 class Pos > 7 { > 8 public immutable int y, x; > 9 mixin DeriveCreate; > 10 mixin DeriveCompare; > 11 mixin DeriveShow; > 12 > 13 @property: > 14 Pos wait() { return this; } > 15 Pos up() { return new Pos(y+1, x); } > 16 Pos down() { return new Pos(y-1, x); } > 17 Pos left() { return new Pos(y, x-1); } > 18 Pos right() { return new Pos(y, x+1); } > 19 alias wait W,w; > 20 alias up U,u; > 21 alias down D,d; > 22 alias left L,l; > 23 alias right R,r; > 24 } > 25 > 26 unittest > 27 { > 28 assert( (new Pos(2,1)).U == new Pos(3,1) ); > 29 assert( (new Pos(0,1)).D == new Pos(-1,1) ); > 30 assert( (new Pos(2,1)).L == new Pos(2,0) ); > 31 assert( (new Pos(2,1)).R == new Pos(2,2) ); > 32 int[Pos] aa; > 33 aa[new Pos(1,2)] = 1; > 34 aa[new Pos(1,2)] = 2; > 35 aa[new Pos(2,1)] = 3; > 36 assert( aa.length==2 ); > 37 assert( aa[new Pos(1,2)]==2 ); > 38 } > 39 > 40 //////////////////////////////////////////////////////////////////////////////// > 41 > 42 class Water > 43 { > 44 public immutable int base, pace; > 45 mixin DeriveCreate; > 46 mixin DeriveCompare; > 47 mixin DeriveShow; > 48 > 49 static load(string[string] params) > 50 { > 51 return new Water( > 52 params.get("Water", "0").to!int(), > 53 params.get("Flooding", "0").to!int() > 54 ); > 55 } > 56 > 57 int level(int number_of_update) > 58 { > 59 return pace ? base+(number_of_update/pace) : base; > 60 } > 61 > 62 int until_rise(int number_of_update) > 63 { > 64 return pace ? pace-number_of_update%pace : int.max; > 65 } > 66 } > 67 > 68 unittest > 69 { > 70 Water w = new Water(1, 3); > 71 assert( 1 == w.level(0) ); > 72 assert( 1 == w.level(1) ); > 73 assert( 1 == w.level(2) ); > 74 assert( 2 == w.level(3) ); > 75 assert( 2 == w.level(4) ); > 76 assert( 2 == w.level(5) ); > 77 assert( 3 == w.level(6) ); > 78 > 79 w = new Water(1, 0); > 80 assert( 1 == w.level(0) ); > 81 assert( 1 == w.level(1) ); > 82 assert( 1 == w.level(2) ); > 83 assert( 1 == w.level(3) ); > 84 assert( 1 == w.level(4) ); > 85 assert( 1 == w.level(5) ); > 86 } > 87 > 88 //////////////////////////////////////////////////////////////////////////////// > 89 > 90 class Map > 91 { > 92 mixin DeriveShow; > 93 > 94 static Map load(string[] raw_data, string[string] params) > 95 { > 96 // TODO: choose optimal representation. > 97 return new Map(raw_data, params); > 98 } > 99 > 100 private { > 101 char[][] data; > 102 Pos robot; > 103 Pos lift; > 104 int waterproof; > 105 } > 106 > 107 this(string[] raw_data, string[string] params) > 108 { > 109 int width = 0; > 110 foreach(r; raw_data) > 111 width = max(width, r.length); > 112 foreach(r; raw_data) { > 113 this.data ~= r.dup; > 114 this.data[$-1].length = width; > 115 this.data[$-1][r.length..$] = ' '; > 116 } > 117 > 118 for(int y=1; y<=H; ++y) > 119 for(int x=1; x<=W; ++x) { > 120 if(this[y,x] == 'R') > 121 this.robot = new Pos(y,x); > 122 if(this[y,x] == 'L') > 123 this.lift = new Pos(y,x); > 124 } > 125 > 126 this.waterproof = params.get("Waterproof", "5").to!int(); > 127 } > 128 > 129 const @property { > 130 int H() { return data.length; } > 131 int W() { return data[0].length; } > 132 } > 133 > 134 char opIndex(int y, int x) > 135 { > 136 // Adjust coordinate to the spec. bottom-left is (1,1). > 137 --y, --x; > 138 if(y<0||H<=y||x<0||W<=x) > 139 return '#'; > 140 return data[H-1-y][x]; > 141 } > 142 > 143 char opIndex(Pos p) > 144 { > 145 return this[p.y, p.x]; > 146 } > 147 > 148 void opIndexAssign(char c, int y, int x) > 149 { > 150 // Adjust coordinate to the spec. bottom-left is (1,1). > 151 --y, --x; > 152 if(y<0||H<=y||x<0||W<=x) > 153 return; > 154 data[H-1-y][x] = c; > 155 } > 156 > 157 void opIndexAssign(char c, Pos p) > 158 { > 159 this[p.y, p.x] = c; > 160 } > 161 > 162 bool cleared() > 163 { > 164 for(int y=1; y<=H; ++y) > 165 for(int x=1; x<=W; ++x) > 166 if(this[y,x] == 'L' || this[y,x] == 'O') > 167 return false; > 168 return true; > 169 } > 170 > 171 Tuple!(int,bool) command(char c) > 172 { > 173 if(c=='R') return move( 0, +1); > 174 if(c=='L') return move( 0, -1); > 175 if(c=='U') return move(+1, 0); > 176 if(c=='D') return move(-1, 0); > 177 if(c=='W') return move( 0, 0); > 178 assert(false); > 179 } > 180 > 181 Tuple!(int, bool) move(int dy, int dx) > 182 { > 183 int y = robot.y; > 184 int x = robot.x; > 185 assert( this[robot] == 'R' ); > 186 int lambda = 0; > 187 bool dead = false; > 188 if( '\\' == this[y+dy,x+dx] ) > 189 lambda++; > 190 if( " \\.O".count(this[y+dy,x+dx])==1 ) { > 191 this[y,x]=' '; > 192 this[y+dy,x+dx]='R'; > 193 robot = new Pos(y+dy,x+dx); > 194 } else if(dy==0 && '*'==this[y+dy,x+dx] && ' '==this[y+dy*2,x+dx > 195 this[y,x]=' '; > 196 this[y+dy,x+dx]='R'; > 197 this[y+dy*2,x+dx*2]='*'; > 198 robot = new Pos(y+dy,x+dx); > 199 } > 200 if( update() ) > 201 dead = true; > 202 return tuple(lambda,dead); > 203 } > 204 > 205 bool update() > 206 { > 207 bool dead = false; > 208 > 209 char[][] next; > 210 foreach(y,s; data) > 211 next ~= s.dup; > 212 > 213 ref char access(Pos p) { return next[H-p.y][p.x-1]; } > 214 > 215 bool lambda = false; > 216 for(int y=1; y<=H; ++y) > 217 for(int x=1; x<=W; ++x) > 218 lambda |= (this[y,x] == '\\'); > 219 > 220 for(int y=1; y<=H; ++y) > 221 for(int x=1; x<=W; ++x) { > 222 Pos p = new Pos(y,x); > 223 if(this[p]=='*') { > 224 if(this[p.D]==' ') { > 225 access(p) =' '; > 226 access(p.D)='*'; > 227 if(robot == p.D.D) > 228 dead=true; > 229 } > 230 else if((this[p.D]=='*' || this[p.D]=='\\') && t > 231 access(p)=' '; > 232 access(p.R.D)='*'; > 233 if(robot == p.R.D.D) > 234 dead=true; > 235 } > 236 else if(this[p.D]=='*' && this[p.L]==' ' && this > 237 access(p)=' '; > 238 access(p.L.D)='*'; > 239 if(robot == p.L.D.D) > 240 dead=true; > 241 } > 242 } > 243 else if(this[p]=='L') { > 244 if(!lambda) > 245 access(p) = 'O'; > 246 } > 247 } > 248 data = next; > 249 return dead; > 250 } > 251 } > 252 > 253 //////////////////////////////////////////////////////////////////////////////// > 254 > 255 class Game > 256 { > 257 mixin DeriveShow; > 258 > 259 static Game load(File input) > 260 { > 261 string[] raw_data; > 262 string[string] params; > 263 > 264 // Raw map data; read until empty line. > 265 for(string line; !(line=input.readln().chomp()).empty; ) > 266 raw_data ~= line; > 267 > 268 // Additional commands; read until EOF. > 269 for(string line; !(line=input.readln()).empty; ) { > 270 string[] ss = line.split(); > 271 if( ss.length == 2 ) > 272 params[ss[0]] = ss[1]; > 273 } > 274 > 275 return load(raw_data, params); > 276 } > 277 > 278 static Game load(string[] raw_data, string[string] params) > 279 { > 280 return new Game(raw_data, params); > 281 } > 282 > 283 this(string[] raw_data, string[string] params) > 284 { > 285 this.map = Map.load(raw_data, params); > 286 this.water = Water.load(params); > 287 this.output = new NilOutput; > 288 } > 289 > 290 void set_output(Output o) { this.output = (o is null ? new NilOutput : o > 291 > 292 void command(char c) > 293 { > 294 if(dead || cleared) > 295 return; > 296 this.output.command(c); > 297 > 298 if(c == 'A') > 299 { > 300 exit_bonus = 1; > 301 return; > 302 } > 303 > 304 // TODO: clarify the event order > 305 Tuple!(int,bool) ld = map.command(c); > 306 if( map.cleared() ) { > 307 exit_bonus = 2; > 308 } > 309 else { > 310 lambda += ld[0]; > 311 if( ld[1] ) { > 312 dead = true; > 313 } > 314 } > 315 if( map.robot.y <= water_level ) > 316 ++under_warter; > 317 else > 318 under_warter = 0; > 319 if( under_warter > map.waterproof ) > 320 dead = true; > 321 turn += 1; > 322 } > 323 > 324 Map map; > 325 Water water; > 326 Output output; > 327 > 328 int turn = 0; > 329 bool dead = false; > 330 int lambda = 0; > 331 int exit_bonus = 0; > 332 int under_warter = 0; > 333 @property { > 334 int score() { return lambda*25*(1+exit_bonus) - turn; } > 335 int water_level() { return water.level(turn); } > 336 int water_until_rise() { return water.until_rise(turn); } > 337 bool cleared() { return exit_bonus>0; } > 338 int hp() { return map.waterproof - under_warter; } > 339 } > 340 } > 341 > 342 unittest > 343 { > 344 Game.load(["###","...","#RL"], ["xxx":"yyy"]); > 345 }
Added gui.d version [2332c804152f20b9]
> 1 import dfl.all; > 2 import util; > 3 import game; > 4 import output; > 5 > 6 class GUI : Form > 7 { > 8 private { > 9 Game g; > 10 int cell; > 11 int turn = 0; > 12 > 13 Font font; > 14 Color[char] colors; > 15 string[char] render; > 16 } > 17 > 18 this(Game g) > 19 { > 20 noMessageFilter(); > 21 this.setStyle(ControlStyles.OPAQUE, true); > 22 this.g = g; > 23 > 24 this.paint ~= &my_paint; > 25 this.keyDown ~= &my_keydown; > 26 > 27 const MAX_SIZE = 640; > 28 this.formBorderStyle = FormBorderStyle.FIXED_DIALOG; > 29 this.maximizeBox = false; > 30 this.minimizeBox = false; > 31 this.cell = MAX_SIZE / max(g.map.W, g.map.H); > 32 this.clientSize = Size(g.map.W*cell, g.map.H*cell); > 33 set_text(); > 34 > 35 // Resources > 36 this.font = new Font("MS Gothic", cell-2, GraphicsUnit.PIXEL); > 37 this.backColor = Color(255,255,255); > 38 this.colors['#'] = > 39 this.colors['.'] = Color(255,191,127); > 40 this.colors['*'] = Color(255,127,127); > 41 this.colors['R'] = Color(128,128,0); > 42 this.colors['D'] = Color(255,0,0); // Dead > 43 this.colors['\\'] = > 44 this.colors['L'] = > 45 this.colors['O'] = Color(127,255,127); > 46 this.colors['W'] = Color(204,229,255); // water > 47 > 48 this.render['#'] = "■"; > 49 this.render['*'] = "✹"; > 50 this.render['.'] = "♒"; > 51 this.render['\\'] = "λ"; > 52 this.render['R'] = "☃"; > 53 this.render['D'] = "☠"; > 54 this.render['L'] = "☒"; > 55 this.render['O'] = "☐"; > 56 } > 57 > 58 private: > 59 void my_paint(Control, PaintEventArgs ev) > 60 { > 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 } > 68 > 69 // Fill bg. > 70 gr.fillRectangle(this.backColor, Rect(0,0,scrW,scrH)); > 71 > 72 // Fill water. > 73 int w = g.water_level(); > 74 gr.fillRectangle(this.colors['W'], Rect(0, scrH-cell*w-1, scrW, > 75 > 76 // Paint map. > 77 for(int y=1; y<=g.map.H; ++y) > 78 for(int x=1; x<=g.map.W; ++x) { > 79 Rect r = Rect(cell*(x-1), scrH-cell*y, cell, cell); > 80 char c = g.map[y,x]; > 81 if( c != ' ' ) { > 82 if( c == 'R' && g.dead ) > 83 c = 'D'; > 84 gr.drawText(this.render[c], font, this.colors[c] > 85 } > 86 } > 87 } > 88 > 89 void my_keydown(Control c, KeyEventArgs ev) > 90 { > 91 switch(ev.keyCode) > 92 { > 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 default: break; > 100 } > 101 if(g.cleared) > 102 Application.exit(); > 103 invalidate(); > 104 set_text(); > 105 } > 106 > 107 void set_text() { > 108 this.text = .text("Score: ", g.score, " Air: ", g.hp, " Tide: ", > 109 } > 110 } > 111 > 112 void main(string[] args) > 113 { > 114 auto g = Game.load(File(args[1])); > 115 g.set_output(new StdOutput); > 116 auto myForm = new GUI(g); > 117 Application.run(myForm); > 118 }
Added output.d version [29c4bbd4331091b4]
> 1 import util; > 2 import game; > 3 import core.stdc.signal; > 4 import std.c.stdlib; > 5 > 6 abstract class Output > 7 { > 8 void command(char c); > 9 } > 10 > 11 class NilOutput : Output > 12 { > 13 override void command(char c) {} > 14 } > 15 > 16 class StdOutput : Output > 17 { > 18 // Handle SIGINT: force abort and exit. > 19 static this() > 20 { > 21 signal(SIGINT, &sigint); > 22 } > 23 extern(C) static void sigint(int) { > 24 write("A"); > 25 stdout.flush(); > 26 exit(0); > 27 } > 28 > 29 override void command(char c) > 30 { > 31 // TODO: optimize redundancy. > 32 write(c); > 33 stdout.flush(); > 34 } > 35 }