Last active
May 18, 2017 20:57
-
-
Save smith-kyle/140dac4b3953ccdd5c8ba5c8b1bb9229 to your computer and use it in GitHub Desktop.
Minesweeper Console App
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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