Minesweeper game/D: Difference between revisions

Updated minesweeper_main1 D module
(→‎failed game: +won game)
(Updated minesweeper_main1 D module)
 
(13 intermediate revisions by 3 users not shown)
Line 1:
Phobos and Tango based implementationimplementations
 
== Main modulePhobos ==
=== minesweeper_main1 module ===
<lang d>module minesweeper_main1;
 
import std.stdio, std.conv, std.array, std.string, std.typecons,
minesweeper1;
 
Tuple!(uint,"height", uint,"width") getBoardSides() {
while (true) {
write("Give me height width (2 2 or more): ");
const parts = readln().split();
if (parts.length != 2) {
writeln("Required: number number");
continue;
}
 
try {
immutable height = to!uint(parts[0]);
immutable width = to!uint(parts[1]);
 
if (height < 2 || width < 2) {
writeln("Minimal board sides: 2 2");
continue;
}
 
return typeof(return)(height, width);
} catch (ConvException e) {
writeln("Invalid input. Required: number number");
}
}
}
 
 
void main() {
enum Action { uncover, flag, clear }
static immutable string prompt =
"\n[Flag toggle|Clear] nRow nCol (or Quit): ";
 
writeln("Welcome to Minesweeper Game!");
immutable sides = getBoardSides();
auto board = new GameBoard(sides.height, sides.width);
writeln("Mines to find: ", board.nMines);
 
while (true) {
int row, column;
Action action;
 
while (true) {
writeln(board);
row = column = -1;
write(prompt);
auto parts = readln().toLower().split();
if (parts.empty)
continue;
 
if (parts[0] == "quit" || parts[0] == "q")
return;
action = Action.uncover;
if (parts[0] == "flag" || parts[0] == "f")
action = Action.flag;
else if (parts[0] == "clear" || parts[0] == "c")
action = Action.clear;
if (action != Action.uncover)
parts.popFront();
if (parts.length != 2) {
writeln("Invalid input.");
continue;
}
 
try {
row = to!int(parts[0]) - 1;
column = to!int(parts[1]) - 1;
} catch (ConvException e) {
writeln("Invalid input.");
continue;
}
 
if (row < 0 || row >= board.nRows ||
column < 0 || column >= board.nColumns) {
writeln("Invalid row col bounds.");
continue;
}
 
break;
}
 
GameBoard.Cell cell = board[row, column];
 
if (action == Action.flag) {
cell.flag();
} else {
(action == Action.clear ? &cell.clear : &cell.uncover)();
if (board.state != GameBoard.State.ongoing) {
writeln((board.state == GameBoard.State.win) ?
"You Win!" : "BIG BADA BOOM!");
writeln(board);
break;
}
}
}
}</lang>
 
=== minesweeper1 module ===
<lang d>module minesweeper1;
 
import std.random, std.conv, std.string, std.array, std.range;
 
final class GameBoard {
public:
enum State { ongoing, win, loss }
 
this(in int n_rows_, in int n_columns_)
in {
assert(n_rows_ > 1 && n_columns_ > 1);
} body {
this.n_rows = n_rows_;
this.n_columns = n_columns_;
cells = new typeof(cells)(n_rows, n_columns);
 
foreach (row; cells)
foreach (ref cell; row)
cell = new typeof(cell)();
placeMines();
setAdjacentCells();
}
 
@property uint nRows() pure nothrow const { return n_rows; }
@property uint nColumns() pure nothrow const { return n_columns; }
@property State state() pure nothrow const { return status; }
@property uint nMines() pure nothrow const { return n_mines; }
 
inout opIndex(in uint row, in uint column) const pure nothrow
in {
assert(row < n_rows && column < n_columns,
"Incorrect dimensions for cell retrieval!");
} body {
return cells[row][column];
}
 
override string toString() const {
string result = format("N. flags: %d\n %(%d%)",
n_flags, iota(1, n_columns + 1));
 
foreach (immutable r; 0 .. n_rows) {
string row;
foreach (immutable c; 0 .. n_columns)
row ~= this[r, c].toString();
result ~= format("\n%2d [%s]", r + 1, row);
}
 
return result;
}
 
private:
void placeMines() {
n_mines = cast(uint)(n_rows * n_columns * uniform(0.1, 0.2));
n_mines++;
 
foreach (immutable i; 0 .. n_mines) {
while (true) {
auto cell = cells[uniform(0, n_rows)]
[uniform(0, n_columns)];
if (!cell.isMined) {
cell.isMined = true;
break;
}
}
}
}
 
void setAdjacentCells() pure nothrow {
foreach (immutable row; 0 .. n_rows) {
immutable uint isTop = row == 0,
isBottom = row == (n_rows - 1);
 
foreach (immutable column; 0 .. n_columns) {
immutable uint isLeft = column == 0,
isRight = column == (n_columns - 1);
auto cell = cells[row][column];
 
foreach (immutable r; row - !isTop
.. row + 1 + !isBottom)
foreach (immutable c; column - !isLeft
.. column + 1 + !isRight)
if (r != row || c != column) {
cell.adjacents ~= cells[r][c];
cell.numAdjacentMines +=
cell.adjacents.back.isMined;
}
}
}
}
 
void revealAll() pure nothrow {
foreach (row; cells)
foreach (cell; row) {
cell.isFlagged = false;
cell.isUncovered = true;
}
}
 
immutable uint n_rows, n_columns;
Cell[][] cells;
uint n_flags, n_mines, n_uncovered;
State status;
 
public:
final class Cell {
public:
void uncover() pure nothrow {
if (isUncovered || isFlagged)
return;
isUncovered = true;
 
if (isMined) {
status = State.loss;
revealAll();
return;
}
n_uncovered++;
if (n_uncovered + n_mines == n_rows * n_columns) {
status = State.win;
return;
}
if (numAdjacentMines > 0)
return;
 
foreach (cell; adjacents)
cell.uncover();
}
 
void flag() pure nothrow {
if (isUncovered)
return;
isFlagged = isFlagged != true;
n_flags += isFlagged ? +1 : -1;
}
 
void clear() pure nothrow {
if (!isUncovered)
return;
 
uint numFlagged;
foreach (cell; adjacents)
numFlagged += cell.isFlagged;
 
if (numFlagged == numAdjacentMines &&
status == State.ongoing)
foreach (cell; adjacents)
cell.uncover();
}
 
override string toString() const pure nothrow {
if (isUncovered) {
if (isMined)
return "*";
else
return numAdjacentMines
? text(numAdjacentMines)
: " ";
} else {
return isFlagged ? "?" : ".";
}
}
 
private:
Cell[] adjacents;
bool isMined, isUncovered, isFlagged;
uint numAdjacentMines;
}
}
 
void main(){}</lang>
 
=== Example plays ===
==== lost game ====
<pre>Welcome to Minesweeper Game!
Give me height width (2 2 or more): 4 6
Mines to find: 5
N. flags: 0
123456
1 [......]
2 [......]
3 [......]
4 [......]
 
[Flag toggle|Clear] nRow nCol (or Quit): 1 1
N. flags: 0
123456
1 [2.....]
2 [......]
3 [......]
4 [......]
 
[Flag toggle|Clear] nRow nCol (or Quit): 1 2
N. flags: 0
123456
1 [23....]
2 [......]
3 [......]
4 [......]
 
[Flag toggle|Clear] nRow nCol (or Quit): 1 3
BIG BADA BOOM!
N. flags: 0
123456
1 [23*1 ]
2 [**3321]
3 [222**1]
4 [ 1221]</pre>
 
== Tango ==
=== Main module ===
<lang D>import tango.io.Stdout;
import tango.io.Console;
Line 23 ⟶ 335:
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);
Line 52 ⟶ 364:
break;
}
Stdout (":>").flush;
prompt=Cin.get;
 
Line 86 ⟶ 399:
</lang>
 
=== ExampleMineSweeper playsmodule ===
 
=== won game ===
Welcome!
Gimme height width:
4 6
00 123456
1 [ ...... ]
2 [ ...... ]
3 [ ...... ]
4 [ ...... ]
2 2
00 123456
1 [ ...... ]
2 [ .1.... ]
3 [ ...... ]
4 [ ...... ]
3 3
00 123456
1 [ ...... ]
2 [ .11121 ]
3 [ 11____ ]
4 [ ______ ]
F 2 1
01 123456
1 [ ...... ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
1 3
01 123456
1 [ ..1... ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
1 5
01 123456
1 [ ..1.2. ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
1 1
01 123456
1 [ 1.1.2. ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
1 2
KESSETOUN!!!
01 123456
1 [ 111.2. ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
 
=== lost game ===
Welcome!
Gimme height width:
4 6
00 123456
1 [ ...... ]
2 [ ...... ]
3 [ ...... ]
4 [ ...... ]
1 1
00 123456
1 [ ___1.. ]
2 [ ___1.. ]
3 [ _112.. ]
4 [ _1.... ]
1 5
BIG BADA BOOM
00 123456
1 [ ___1*. ]
2 [ ___1.. ]
3 [ _112.. ]
4 [ _1.... ]
 
== MineSweeper module ==
<lang D>module MineSweeper;
 
Line 181 ⟶ 416:
int value;
Field[] friends;
}
 
void addFriend(Field fr) { friends ~= fr; }
Line 402 ⟶ 637:
}
</lang>
 
=== Example plays ===
 
==== won game ====
Welcome!
Gimme height width:
:> 4 6
00 123456
1 [ ...... ]
2 [ ...... ]
3 [ ...... ]
4 [ ...... ]
:> 2 2
00 123456
1 [ ...... ]
2 [ .1.... ]
3 [ ...... ]
4 [ ...... ]
:> 3 3
00 123456
1 [ ...... ]
2 [ .11121 ]
3 [ 11____ ]
4 [ ______ ]
:> F 2 1
01 123456
1 [ ...... ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
:> 1 3
01 123456
1 [ ..1... ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
:> 1 5
01 123456
1 [ ..1.2. ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
:> 1 1
01 123456
1 [ 1.1.2. ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
:> 1 2
KESSETOUN!!!
01 123456
1 [ 111.2. ]
2 [ ?11121 ]
3 [ 11____ ]
4 [ ______ ]
 
==== lost game ====
Welcome!
Gimme height width:
:> 4 6
00 123456
1 [ ...... ]
2 [ ...... ]
3 [ ...... ]
4 [ ...... ]
:> 1 1
00 123456
1 [ ___1.. ]
2 [ ___1.. ]
3 [ _112.. ]
4 [ _1.... ]
:> 1 5
BIG BADA BOOM
00 123456
1 [ ___1*. ]
2 [ ___1.. ]
3 [ _112.. ]
4 [ _1.... ]