RosettaCodeData/Task/Tic-tac-toe/D/tic-tac-toe.d
2023-07-01 13:44:08 -04:00

115 lines
3.4 KiB
D

import std.stdio, std.string, std.algorithm, std.conv, std.random,
std.ascii, std.array, std.range, std.math;
struct GameBoard {
dchar[9] board = "123456789";
enum : dchar { human = 'X', computer = 'O' }
enum Game { going, humanWins, computerWins, draw }
const pure nothrow @safe @nogc invariant() {
int nHuman = 0, nComputer = 0;
foreach (immutable i, immutable c; board)
if (c.isDigit)
assert(i == c - '1'); // In correct position?
else {
assert(c == human || c == computer);
(c == human ? nHuman : nComputer)++;
}
assert(abs(nHuman - nComputer) <= 1);
}
string toString() const pure {
return format("%(%-(%s|%)\n-+-+-\n%)", board[].chunks(3));
}
bool isAvailable(in int i) const pure nothrow @safe @nogc {
return i >= 0 && i < 9 && board[i].isDigit;
}
auto availablePositions() const pure nothrow @safe /*@nogc*/ {
return 9.iota.filter!(i => isAvailable(i));
}
Game winner() const pure nothrow @safe /*@nogc*/ {
static immutable wins = [[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6]];
foreach (immutable win; wins) {
immutable bw0 = board[win[0]];
if (bw0.isDigit)
continue; // Nobody wins on this one.
if (bw0 == board[win[1]] && bw0 == board[win[2]])
return bw0 == GameBoard.human ?
Game.humanWins :
Game.computerWins;
}
return availablePositions.empty ? Game.draw: Game.going;
}
bool isFinished() const pure nothrow @safe /*@nogc*/ {
return winner != Game.going;
}
int computerMove() const // Random move.
out(res) {
assert(res >= 0 && res < 9 && isAvailable(res));
} body {
// return availablePositions.array.choice;
return availablePositions.array[uniform(0, $)];
}
}
GameBoard playGame() {
GameBoard board;
bool playsHuman = true;
while (!board.isFinished) {
board.writeln;
int move;
if (playsHuman) {
do {
writef("Your move (available moves: %s)? ",
board.availablePositions.map!q{ a + 1 });
readf("%d\n", &move);
move--; // Zero based indexing.
if (move < 0)
return board;
} while (!board.isAvailable(move));
} else
move = board.computerMove;
assert(board.isAvailable(move));
writefln("\n%s chose %d", playsHuman ? "You" : "I", move + 1);
board.board[move] = playsHuman ? GameBoard.human :
GameBoard.computer;
playsHuman = !playsHuman; // Switch player.
}
return board;
}
void main() {
"Tic-tac-toe game player.\n".writeln;
immutable outcome = playGame.winner;
final switch (outcome) {
case GameBoard.Game.going:
"Game stopped.".writeln;
break;
case GameBoard.Game.humanWins:
"\nYou win!".writeln;
break;
case GameBoard.Game.computerWins:
"\nI win.".writeln;
break;
case GameBoard.Game.draw:
"\nDraw".writeln;
break;
}
}