Minesweeper game/D: Difference between revisions
Content added Content deleted
No edit summary |
|||
Line 1: | Line 1: | ||
Tango based |
Tango and Phobos based implementations |
||
== |
== Tango == |
||
=== Main module === |
|||
<lang D>import tango.io.Stdout; |
<lang D>import tango.io.Stdout; |
||
import tango.io.Console; |
import tango.io.Console; |
||
Line 87: | Line 88: | ||
</lang> |
</lang> |
||
== Example plays == |
=== Example plays === |
||
=== won game === |
==== won game ==== |
||
Welcome! |
Welcome! |
||
Gimme height width: |
Gimme height width: |
||
Line 142: | Line 143: | ||
4 [ ______ ] |
4 [ ______ ] |
||
=== lost game === |
==== lost game ==== |
||
Welcome! |
Welcome! |
||
Gimme height width: |
Gimme height width: |
||
Line 165: | Line 166: | ||
4 [ _1.... ] |
4 [ _1.... ] |
||
== MineSweeper module == |
=== MineSweeper module === |
||
<lang D>module MineSweeper; |
<lang D>module MineSweeper; |
||
Line 400: | Line 401: | ||
return ret; |
return ret; |
||
} |
} |
||
} |
|||
} |
|||
</lang> |
|||
== Phobos == |
|||
=== Main module === |
|||
<lang D>module main; |
|||
import std.stdio; |
|||
import std.format; |
|||
import std.conv; |
|||
import std.array; |
|||
import std.string; |
|||
import core.thread; |
|||
import core.time; |
|||
import minesweeper; |
|||
extern(C) int kbhit(); |
|||
void main() { |
|||
PlayMinesweeper(); |
|||
while(!kbhit()) Thread.sleep(dur!"msecs"(250)); |
|||
} |
|||
void PlayMinesweeper() { |
|||
static string prompt = ":> "; |
|||
uint len1, height, width; |
|||
string curLine; |
|||
bool gameOver; |
|||
writeln("Welcome!"); |
|||
do { |
|||
write("Gimme height width:\n", prompt); |
|||
curLine = readln(); |
|||
//Non-member functions can be called with the first argument using dot notation. |
|||
//"curLine.formattedRead(" is the same as "formattedRead(curLine, ". |
|||
len1 = curLine.formattedRead("%s %s", &height, &width); |
|||
} while (len1 == 0 || height < 2 || width < 2); |
|||
auto gameBoard = new Board(height, width); |
|||
writeln(gameBoard); |
|||
do { |
|||
int row, column; |
|||
bool print, flag, clear; |
|||
do { |
|||
row = column = -1; |
|||
write(prompt); |
|||
curLine = readln(); |
|||
if(curLine.strip() == "quit") |
|||
return; |
|||
string[] tokens = curLine.split(); |
|||
flag = tokens[0] == "F"; |
|||
print = tokens[0] == "P"; |
|||
clear = tokens[0] == "C"; |
|||
if(print && tokens.length == 1) |
|||
{ |
|||
writeln(gameBoard); |
|||
continue; |
|||
} |
|||
try { |
|||
auto index = flag || print || clear; |
|||
row = to!int(tokens[index]) - 1; |
|||
column = to!int(tokens[index + 1]) - 1; |
|||
} |
|||
catch(Exception e) { |
|||
writeln("Invalid input"); |
|||
continue; |
|||
} |
|||
} while(row < 0 || row >= gameBoard.Rows || column < 0 || column >= gameBoard.Columns); |
|||
auto cell = gameBoard.getCell(row, column); |
|||
if(flag) cell.flag(); |
|||
else { |
|||
(clear ? &cell.clear : &cell.uncover)(); |
|||
gameOver = gameBoard.Status != Board.DEFAULT; |
|||
if(gameOver) |
|||
writeln((gameBoard.Status == Board.WIN) ? |
|||
"DEAR DIARY... JACKPOT!" : "BIG BADA BOOM"); |
|||
} |
|||
if(print || gameOver) writeln(gameBoard); |
|||
} while(!gameOver); |
|||
} |
|||
</lang> |
|||
=== Example plays === |
|||
==== won game ==== |
|||
Welcome! |
|||
Gimme height width: |
|||
:> 4 6 |
|||
00 123456 |
|||
1 [ ...... ] |
|||
2 [ ...... ] |
|||
3 [ ...... ] |
|||
4 [ ...... ] |
|||
:> P 1 1 |
|||
00 123456 |
|||
1 [ ] |
|||
2 [ 112121 ] |
|||
3 [ ...... ] |
|||
4 [ ...... ] |
|||
:> F 3 2 |
|||
:> F 3 4 |
|||
:> F 3 6 |
|||
:> 3 1 |
|||
:> C 2 4 |
|||
:> P |
|||
03 123456 |
|||
1 [ ] |
|||
2 [ 112121 ] |
|||
3 [ 1?2?3? ] |
|||
4 [ ...... ] |
|||
:> C 3 3 |
|||
:> 4 1 |
|||
:> P |
|||
03 123456 |
|||
1 [ ] |
|||
2 [ 112121 ] |
|||
3 [ 1?2?3? ] |
|||
4 [ 1122.. ] |
|||
:> 4 6 |
|||
DEAR DIARY... JACKPOT! |
|||
03 123456 |
|||
1 [ ] |
|||
2 [ 112121 ] |
|||
3 [ 1?2?3? ] |
|||
4 [ 1122.2 ] |
|||
==== lost game ==== |
|||
Welcome! |
|||
Gimme height width: |
|||
:> 4 6 |
|||
00 123456 |
|||
1 [ ...... ] |
|||
2 [ ...... ] |
|||
3 [ ...... ] |
|||
4 [ ...... ] |
|||
:> P 1 1 |
|||
00 123456 |
|||
1 [ 1..... ] |
|||
2 [ ...... ] |
|||
3 [ ...... ] |
|||
4 [ ...... ] |
|||
:> P 1 2 |
|||
00 123456 |
|||
1 [ 11.... ] |
|||
2 [ ...... ] |
|||
3 [ ...... ] |
|||
4 [ ...... ] |
|||
:> 2 3 |
|||
:> 1 3 |
|||
:> P |
|||
00 123456 |
|||
1 [ 11 1. ] |
|||
2 [ .1 11 ] |
|||
3 [ .321 ] |
|||
4 [ ...1 ] |
|||
:> 1 6 |
|||
BIG BADA BOOM |
|||
00 123456 |
|||
1 [ 11 1* ] |
|||
2 [ *1 11 ] |
|||
3 [ 2321 ] |
|||
4 [ 1**1 ] |
|||
=== MineSweeper module === |
|||
<lang D>module minesweeper; |
|||
import std.random; |
|||
import std.conv; |
|||
import std.format; |
|||
import std.algorithm; |
|||
import std.range; |
|||
//Convenience function. Similar to Python's str.format. |
|||
string format(Args...)(string fmt, Args args) |
|||
{ |
|||
auto writer = appender!string(""); |
|||
writer.formattedWrite(fmt, args); |
|||
return writer.data(); |
|||
} |
|||
class Board { |
|||
public: |
|||
static enum { DEFAULT, WIN, LOSS }; |
|||
this(int rows, int columns) { |
|||
this.rows = rows; |
|||
this.columns = columns; |
|||
cells = new typeof(cells)(rows, columns); |
|||
foreach(ref row; cells) |
|||
foreach(ref cell; row) |
|||
cell = new typeof(cell)(); |
|||
placeMines(); |
|||
setAdjacentCells(); |
|||
} |
|||
@property const uint Rows() { return rows; } |
|||
@property const uint Columns() { return columns; } |
|||
@property const uint Status() { return status; } |
|||
string toString() { |
|||
auto columnRange = iota(0, columns), rowRange = iota(0, rows); |
|||
auto accColumn = (string acc, uint column) { return acc ~ to!string(column + 1); }, |
|||
accLine = (string acc, uint row) { |
|||
auto accCell = (string acc, uint column) { return acc ~ getCell(row, column).toString(); }; |
|||
return acc ~ "\n%2s [ %s ]".format(row + 1, reduce!accCell("", columnRange)); |
|||
}; |
|||
return " %02s %s".format(numFlags, reduce!accColumn("", columnRange)) ~ reduce!accLine("", rowRange); |
|||
} |
|||
auto getCell(uint row, uint column) { |
|||
if((row >= rows) || (column >= columns)) |
|||
throw new Exception("Incorrect dimensions for cell retrieval!"); |
|||
return cells[row][column]; |
|||
} |
|||
private: |
|||
void setAdjacentCells() { |
|||
foreach(row; 0..rows) { |
|||
uint isTop = row == 0, |
|||
isBottom = row == (rows - 1); |
|||
foreach(column; 0..columns) { |
|||
uint isLeft = column == 0, |
|||
isRight = column == (columns - 1); |
|||
auto cell = cells[row][column]; |
|||
auto adjacent = new Cell[](0); |
|||
foreach(r; row - !isTop..row + 1 + !isBottom) |
|||
foreach(c; column - !isLeft..column + 1 + !isRight) |
|||
if((r != row) || (c != column)) { |
|||
adjacent ~= cells[r][c]; |
|||
cell.numAdjacentMines += adjacent.back.isMined; |
|||
} |
|||
cell.adjacent = adjacent; |
|||
} |
|||
} |
|||
} |
|||
void placeMines() { |
|||
auto randGen = Random(unpredictableSeed); |
|||
do { |
|||
numMines = cast(uint)(rows*columns*uniform(0.1, 0.2, randGen)); |
|||
} while (numMines <= 1); |
|||
foreach(i; 0..numMines) { |
|||
uint rand = 0; |
|||
Cell cell; |
|||
do { |
|||
rand = uniform(uint.min, rows * columns, randGen); |
|||
cell = cells[rand/columns][rand%columns]; |
|||
} while(cell.isMined); |
|||
cell.isMined = true; |
|||
} |
|||
} |
|||
void reveal() { |
|||
foreach(row; cells) |
|||
foreach(cell; row) { |
|||
cell.isFlagged = false; |
|||
cell.isUncovered = true; |
|||
} |
|||
} |
|||
Cell[][] cells; |
|||
const uint rows, columns; |
|||
uint numFlags, numMines, numUncovered, status; |
|||
public: |
|||
class Cell { |
|||
public: |
|||
void flag() { |
|||
if(isUncovered) return; |
|||
isFlagged = (isFlagged != true); |
|||
numFlags += isFlagged ? 1 : -1; |
|||
} |
|||
void uncover() { |
|||
if(isUncovered || isFlagged) return; |
|||
isUncovered = true; |
|||
if(isMined) { |
|||
status = LOSS; |
|||
reveal(); |
|||
return; |
|||
} |
|||
if(++numUncovered + numMines == rows * columns) |
|||
{ |
|||
status = WIN; |
|||
return; |
|||
} |
|||
if(numAdjacentMines > 0) |
|||
return; |
|||
foreach(cell; adjacent) |
|||
cell.uncover(); |
|||
} |
|||
void clear() { |
|||
if(!isUncovered) return; |
|||
uint numFlagged; |
|||
foreach(cell; adjacent) |
|||
numFlagged += cell.isFlagged; |
|||
if(numFlagged == numAdjacentMines) |
|||
foreach(cell; adjacent) if(status == DEFAULT) |
|||
cell.uncover(); |
|||
} |
|||
string toString() { |
|||
return isUncovered ? (isMined ? "*" : (numAdjacentMines ? to!string(numAdjacentMines) : " ")) : |
|||
(isFlagged ? "?" : "."); |
|||
} |
|||
private: |
|||
Cell[] adjacent; |
|||
bool isMined, isUncovered, isFlagged; |
|||
ushort numAdjacentMines; |
|||
} |
} |
||
} |
} |