Minesweeper game/D: Difference between revisions

From Rosetta Code
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>

Revision as of 16:41, 28 August 2010

Minesweeper game in D

Tango based implementation

main module: <lang D>import tango.io.Stdout; import tango.io.Console; import Int = tango.text.convert.Integer; import tango.math.random.Random;

import MineSweeper;

void main() {

   uint len1, len2;
   uint height, width, mines;
   bool gameOver=false;
   Stdout ("Welcome!").newline;
   do {
       Stdout ("Gimme height width: ").newline;
       len1=len2=0;
       auto prompt = Cin.get;
       height = Int.parse (prompt, 10, &len1);
       width = Int.parse (prompt[len1..$], 10, &len2);
       do {   
           mines =  rand.next(cast(uint)( width * height * 0.2));
       } while (mines <= 1);
       debug Stdout(height, width, mines, width*height).newline;
   } while (len1 <= 0 || len2 <= 0 || height < 2 || width < 2);
   auto miner  = new MineSweeper(height, width, mines);
   char[] prompt;
   bool changed;
   do {
       int i=0;
       Stdout(miner.getVisibles).newline;
       foreach(j, row; miner) {
           if (!i) {
               Stdout.format (" {:d2}  ", miner.getFlaggedCount);
               for (i=0; i<miner.getWidth; i++)
                   Stdout.format ("{}", (i+1) % 10);
               Stdout.newline;
           }
           Stdout.format (" {} [ ", (j+1) % 10) (row);
           Stdout (" ] ").newline;
       }
       // the code could be written without using gameOver variable,
       // but this way, after game is finished the board will be
       // printed one more time
       if (gameOver) {
           break;
       }
       prompt=Cin.get;
       bool flag = (prompt[0] == 'F');
       if (flag) {
           prompt = prompt[1..$];
       }
       len1=len2=0;
       auto y = Int.parse (prompt, 10, &len1);
       auto x = Int.parse (prompt[len1..$], 10, &len2);
       assert (y < 1 || y > miner.getHeight);
       assert (x < 1 || x > miner.getWidth);
       --x; --y;
       if (len1 && len2) {
           int retcode = flag ? miner.flag(changed, y,x) : miner.dig(changed, y, x);
           switch (retcode) {
               case -1:
                   Stdout ("BIG BADA BOOM").newline;
                   gameOver=true;
                   break;
               case 1:
                   Stdout ("KESSETOUN!!!").newline;
                   gameOver=true;
                   break;
               default:
                   break;
           }
       }
   } 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>