Check-in [b6daa1efa1]
Not logged in
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
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*2]) { 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]=='\\') && this[p.R]==' ' && this[p.R.D]==' ') { 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[p.L.D]==' ') { 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, cell*w+1)); 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], r); 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: ", g.water_until_rise); 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 +}