Minesweeper game/D: Difference between revisions
Content added Content deleted
(main driver) |
(→Minesweeper game in D: mainsweeper module) |
||
Line 4: | Line 4: | ||
main module: |
main module: |
||
<lang D> |
<lang D>import tango.io.Stdout; |
||
import tango.io.Stdout; |
|||
import tango.io.Console; |
import tango.io.Console; |
||
import Int = tango.text.convert.Integer; |
import Int = tango.text.convert.Integer; |
||
Line 39: | Line 38: | ||
int i=0; |
int i=0; |
||
Stdout(miner.getVisibles).newline; |
Stdout(miner.getVisibles).newline; |
||
foreach(j, row; miner) |
foreach(j, row; miner) { |
||
{ |
|||
if (!i) { |
if (!i) { |
||
Stdout.format (" {:d2} ", miner.getFlaggedCount); |
Stdout.format (" {:d2} ", miner.getFlaggedCount); |
||
Line 87: | Line 85: | ||
} while (prompt != "quit"); |
} while (prompt != "quit"); |
||
} |
|||
</lang> |
|||
MineSweeper module |
|||
<lang D>module MineSweeper; |
|||
import tango.math.random.Random; |
|||
import tango.math.random.engines.Twister; |
|||
import Int = tango.text.convert.Integer; |
|||
import tango.io.Stdout; |
|||
class MineSweeper { |
|||
class Field {/*{{{*/ |
|||
private { |
|||
enum State { UNKNOWN, VISIBLE, FLAGGED }; |
|||
State state; |
|||
bool hasBomb; |
|||
int value; |
|||
Field[] friends; |
|||
} |
|||
void addFriend(Field fr) { friends ~= fr; } |
|||
void addFriends(Field[] fr) { friends = fr; } |
|||
void mineArea() { hasBomb = true; } |
|||
bool isMined() { return hasBomb; } |
|||
bool isVisible() { return state==State.VISIBLE; } |
|||
private { |
|||
void updateValue() { |
|||
value = 0; |
|||
foreach (ref l; friends) |
|||
if (l.isMined) value++; |
|||
} |
|||
int dig() { |
|||
bool dangerousFriends=false; |
|||
if (state == State.VISIBLE) |
|||
return 0; |
|||
if (state == State.FLAGGED) { flag; return 0; } |
|||
state = State.VISIBLE; |
|||
if (hasBomb) |
|||
return -1; |
|||
this.outer.visibles++; |
|||
foreach (ref fr; friends) |
|||
if (fr.isMined) { dangerousFriends=true; break; } |
|||
if (!dangerousFriends) { |
|||
foreach (ref fr; friends) |
|||
if (! fr.isVisible) |
|||
fr.dig; |
|||
} |
|||
return 0; |
|||
} |
|||
void flag() { |
|||
if (state == State.VISIBLE) |
|||
return; |
|||
state ^= State.FLAGGED; |
|||
this.outer.flaggedCount += state - 1; // ;> |
|||
if (!hasBomb) |
|||
this.outer.visibles += state - 1; |
|||
} |
|||
} |
|||
char[] toString() { |
|||
const char[][] status = [ ".", "blurp", "?" ]; |
|||
return (state==State.VISIBLE?(hasBomb?"*":(value?Int.toString(value):"_")):status[state])~" "; |
|||
} |
|||
}/*}}}*/ |
|||
/* this is only wrapper for opCall(), |
|||
* why the board isn't created as array of rows instead of 2D array? |
|||
* simple, because opArray* methods would be necessary |
|||
* and that would give access to elements in |
|||
* foreach(blah; MineSweeperObj).. |
|||
*/ |
|||
class RowWrapper { |
|||
private { |
|||
Field[] row; |
|||
char[] row_str; |
|||
} |
|||
this(Field[] r) { row = r; row_str = new char[r.length]; } |
|||
char[] toString() { |
|||
foreach (uint a, b; row) |
|||
row_str[a] = b.toString[0]; |
|||
return row_str; |
|||
} |
|||
} |
|||
private { |
|||
Field[][] board; |
|||
RowWrapper[] rows; |
|||
int y, x, mines; |
|||
int flaggedCount; |
|||
int visibles; |
|||
} |
|||
this (int y = 10, int x = 10, int mines = 10) { |
|||
assert (x >= 2 && y >= 2, "wrong dimensions"); |
|||
board = new Field[][](y,x); |
|||
rows = new RowWrapper[](y); |
|||
for (auto j=0; j<y; j++) { |
|||
for (auto i=0; i<x; i++) |
|||
board[j][i] = new Field; |
|||
rows[j] = new RowWrapper(board[j]); |
|||
} |
|||
this.y = y; |
|||
this.x = x; |
|||
this.mines = mines; |
|||
createNeighborhood; |
|||
createLandMines; |
|||
updateValues; |
|||
} |
|||
private { |
|||
void createNeighborhood() { |
|||
this.mines = mines; |
|||
/* now don't look at the code below, |
|||
* it's evil :P |
|||
*/ |
|||
/* middle */ |
|||
for (auto j=1; j<y-1; j++) |
|||
for (auto i=1; i<x-1; i++) |
|||
board[j][i].addFriends( |
|||
[board[j-1][i-1], board[j-1][i], board[j-1][i+1], |
|||
board[j][i-1], board[j][i+1], |
|||
board[j+1][i-1], board[j+1][i], board[j+1][i+1]] ); |
|||
/* up */ |
|||
for (auto i=1; i<x-1; i++) |
|||
board[0][i].addFriends( |
|||
[board[0][i-1], board[0][i+1], |
|||
board[1][i-1], board[1][i], board[1][i+1]] ); |
|||
/* bottom */ |
|||
for (auto i=1; i<x-1; i++) |
|||
board[y-1][i].addFriends( |
|||
[board[y-1][i-1], board[y-1][i+1], |
|||
board[y-2][i-1], board[y-2][i], board[y-2][i+1]] ); |
|||
/*left*/ |
|||
for (auto j=1; j<y-1; j++) |
|||
board[j][0].addFriends( |
|||
[board[j-1][0], board[j-1][1], |
|||
board[j][1], |
|||
board[j+1][0], board[j+1][1]] ); |
|||
/*right*/ |
|||
for (auto j=1; j<y-1; j++) |
|||
board[j][x-1].addFriends( |
|||
[board[j-1][x-2], board[j-1][x-1], |
|||
board[j][x-2], |
|||
board[j+1][x-2], board[j+1][x-1]] ); |
|||
/* corners */ |
|||
board[0][0].addFriends([board[0][1], board[1][0], board[1][1]] ); |
|||
board[y-1][0].addFriends([board[y-2][0], board[y-2][1], board[y-1][1]] ); |
|||
board[0][x-1].addFriends([board[0][x-2], board[1][x-2], board[1][x-1]] ); |
|||
board[y-1][x-1].addFriends([board[y-2][x-2], board[y-2][x-1], board[y-1][x-2]] ); |
|||
} |
|||
void createLandMines() { |
|||
auto r = new RandomG!(Twister); |
|||
for (auto i=0; i<mines; i++) { |
|||
int j; |
|||
do j=r.next(x*y); while (board[j/x][j%x].hasBomb); |
|||
board[j/x][j%x].mineArea; |
|||
} |
|||
} |
|||
void updateValues() { |
|||
foreach (ref row; board) |
|||
foreach (ref field; row) |
|||
field.updateValue; |
|||
} |
|||
} |
|||
public { |
|||
/* flags given field |
|||
* returns 1: superb |
|||
* 0: continue game |
|||
*/ |
|||
int flag(out bool changed, int y, int x) { |
|||
if (y < 0 || y >= this.y || x < 0 || x >= this.x) |
|||
throw new Exception("flag out of range"); |
|||
if (board[y][x].isVisible) |
|||
return 0; |
|||
changed = true; |
|||
board[y][x].flag; |
|||
return (visibles == this.x*this.y - mines && flaggedCount <= mines) ? 1 : 0; |
|||
} |
|||
/* digs given field |
|||
* returns 1: superb |
|||
* 0: continue game (changed indicates if board has changed) |
|||
* -1: kthxbai |
|||
*/ |
|||
int dig(out bool changed, int y, int x) { |
|||
if (y < 0 || y >= this.y || x < 0 || x >= this.x) |
|||
throw new MSException("dig out of range"); |
|||
if (board[y][x].isVisible) |
|||
return 0; |
|||
changed=true; |
|||
/*returns 0 or -1 */ |
|||
auto ret = board[y][x].dig; |
|||
return (visibles == this.x*this.y - mines && flaggedCount <= mines)?1:ret; |
|||
} |
|||
int getFlaggedCount() { return flaggedCount; } |
|||
int getVisibles() { return visibles; } |
|||
int getWidth() { return x; } |
|||
int getHeight() { return y; } |
|||
int getMines() { return mines; } |
|||
int opApply(int delegate(ref uint, ref RowWrapper) dg) { |
|||
int ret; |
|||
foreach (uint r, row; rows) |
|||
if ( (ret = dg(r, row)) != 0 ) |
|||
break; |
|||
return ret; |
|||
} |
|||
} |
|||
} |
} |
||
</lang> |
</lang> |