Skip to content

Instantly share code, notes, and snippets.

@smith-kyle
Last active May 18, 2017 20:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smith-kyle/140dac4b3953ccdd5c8ba5c8b1bb9229 to your computer and use it in GitHub Desktop.
Save smith-kyle/140dac4b3953ccdd5c8ba5c8b1bb9229 to your computer and use it in GitHub Desktop.
Minesweeper Console App
const readline = require('readline');
const BOMB = 1;
const UNSELECTED = 2;
const SELECTED = 3;
const isBomb = (board, [y, x]) => board[y][x] === BOMB;
const isSelected = (board, [y, x]) => board[y][x] === SELECTED;
const hasWon = board => !board.some(row => row.some(item => item === UNSELECTED));
const isValidCoord = (board, [y, x]) => y > -1
&& x > -1
&& board.length - 1 >= y
&& board[0].length >= x;
const bombSearchOffsets = [
[-1, -1],
[-1, 0],
[-1, 1],
[0, -1],
[0, 1],
[1, -1],
[1, 0],
[1, 1]
];
const getNumTouchingBombs = (board, [y, x]) => bombSearchOffsets
.map(([offsetY, offsetX]) => [offsetY + y, offsetX + x])
.filter(coords => isValidCoord(board, coords))
.reduce((sum, coord) => sum + (isBomb(board, coord) ? 1 : 0), 0);
const coordsToSymbol = (board, [y, x]) => {
switch (board[y][x]) {
case BOMB:
case UNSELECTED:
return '-';
case SELECTED:
return String(getNumTouchingBombs(board, [y, x]));
}
}
const boardToSymbols = board => {
const result = [];
for(let y = 0; y < board.length; y++) {
result.push([]);
for(let x = 0; x < board[0].length; x++) {
const som = coordsToSymbol(board, [y, x]);
result[y].push(som);
}
}
return result;
}
const printBoard = board => boardToSymbols(board)
.map(row => row.join(' '))
.forEach(row => console.log(row));
const selectItem = (board, [y, x]) => isSelected(board, [y, x])
? board
: [
...board.slice(0, y),
[...board[y].slice(0, x), SELECTED, ...board[y].slice(x + 1, board[y].length)],
...board.slice(y + 1, board.length)
];
const generateBoard = (width, height, difficulty) => [...new Array(height)].map(
() => [...new Array(width)].map(() => Math.random() < (1 - difficulty) ? UNSELECTED : BOMB)
);
const readNumber = prompt => {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
return new Promise(
resolve => rl.question(prompt, (answer) => {
rl.close();
resolve(Number(answer));
}));
}
const playRound = board => new Promise(resolve => Promise.resolve(printBoard(board))
.then(() => readNumber('Y: '))
.then(y => Promise.all([y, readNumber('X: ')]))
.then(([y, x]) => {
if (!isValidCoord(board, [y, x])) {
console.log('Invalid coordinate');
return resolve(playRound(board))
} else if (isBomb(board, [y, x])) {
return resolve('GAME OVER IT EXPLODED');
}
const newBoard = selectItem(board, [y, x]);
return hasWon(newBoard) ? resolve('YOU WIN!') : resolve(playRound(newBoard));
})
);
readNumber('Height: ')
.then(height => Promise.all([ height, readNumber('Width: ') ]))
.then(([height, width]) => Promise.all([ height, width, readNumber('Difficulty: ') ]))
.then(([height, width, difficulty]) => playRound(generateBoard(height, width, difficulty)))
.then((result) => console.log(result))
.catch(ex => console.log(ex))
.then(() => process.exit(0));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment