Created
January 3, 2019 18:00
-
-
Save alessbell/05a708d18706b21f7ee6213ae3b0b69b to your computer and use it in GitHub Desktop.
This file contains 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 rl = readline.createInterface({ | |
input: process.stdin, | |
output: process.stdout, | |
}); | |
let board = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]; | |
let currentPlayer = 'x'; | |
const charMap = { | |
A: 0, | |
B: 1, | |
C: 2, | |
}; | |
// Utils | |
const boardReducer = (acc, curr = []) => [...acc, ...curr]; | |
const rowReducer = (acc, curr) => `${acc} | ${curr}`; | |
const clearConsole = () => process.stdout.write('\x1B[2J\x1B[3J\x1B[H'); | |
const gameOver = () => playerHasWon(board.reduce(boardReducer), currentPlayer) || isFull(); | |
const boardIncludes = char => board.reduce(boardReducer).includes(char); | |
const isEmpty = () => !boardIncludes('x') && !boardIncludes('o'); | |
const isFull = () => !boardIncludes(' '); | |
const printBoard = (board, iter = 11) => { | |
let boardStr = '\n'; | |
board.forEach( | |
(row, idx) => | |
(boardStr += ` ${row.reduce(rowReducer)}\n${ | |
idx + 1 !== board.length ? `${'-'.repeat(iter)}\n` : '' | |
}`) | |
); | |
return boardStr; | |
}; | |
const playerHasWon = (board, player) => { | |
let gameOver = false; | |
const winningCombinations = [ | |
[0, 1, 2], | |
[3, 4, 5], | |
[6, 7, 8], // horizontal | |
[0, 3, 6], | |
[1, 4, 7], | |
[2, 5, 8], // vertical | |
[0, 4, 8], | |
[2, 4, 6], // diagonal | |
]; | |
winningCombinations.forEach(c => { | |
if (board[c[0]] === player && board[c[1]] === player && board[c[2]] === player) { | |
gameOver = true; | |
} | |
}); | |
return gameOver; | |
}; | |
// Steps | |
const ChooseCharacter = { | |
prompt: () => `Human 1, would you like to be player x or player o?`, | |
validate: [ | |
{ | |
test: answer => answer.toLowerCase() === 'x' || answer.toLowerCase() === 'o', | |
err: `Please choose either x or o.`, | |
}, | |
], | |
onSuccess: answer => { | |
console.log(`Human 2, you're ${answer.toLowerCase() === 'x' ? 'o' : 'x'}.`); | |
}, | |
}; | |
const CoinToss = { | |
prompt: () => `Press enter to flip a coin and see who goes first...`, | |
validate: () => [{ test: () => true }], | |
onSuccess: () => { | |
if (Math.floor(Math.random() * 2) == 0) currentPlayer = 'o'; | |
console.log(`${currentPlayer}, you're up first!`); | |
}, | |
}; | |
const PlayGame = { | |
prompt: () => | |
`${currentPlayer}, where would you like to move?\n${printBoard( | |
[['A1', 'A2', 'A3'], ['B1', 'B2', 'B3'], ['C1', 'C2', 'C3']], | |
13 | |
)}\n${printBoard(board)}`, | |
validate: [ | |
{ | |
test: answer => | |
answer.length === 2 && | |
[1, 2, 3].includes(parseInt(answer[1])) && | |
Object.keys(charMap).includes(answer[0].toUpperCase()), | |
err: `Please enter a valid move.`, | |
}, | |
{ | |
test: answer => board[charMap[answer[0].toUpperCase()]][answer[1] - 1] === ' ', | |
err: `This space is taken, please try again.`, | |
}, | |
], | |
onSuccess: answer => { | |
let msg = ''; | |
const [row, column] = answer.split(''); | |
board[charMap[row.toUpperCase()]][column - 1] = currentPlayer; | |
if (gameOver()) msg = `Game over! Congratulations ${currentPlayer}.`; | |
if (isFull()) msg = `Tie game! Good effort, try again next time.`; | |
if (gameOver() || isFull()) { | |
process.stdout.write(`${msg} \n${printBoard(board)}`); | |
return process.stdin.destroy(); | |
} | |
// Next player's turn... | |
if (!isEmpty()) { | |
currentPlayer = currentPlayer === 'x' ? 'o' : 'x'; | |
} | |
}, | |
repeat: true, | |
}; | |
const steps = [ChooseCharacter, CoinToss, PlayGame]; | |
const pose = (q, i = 0) => { | |
rl.question(`${q.prompt()}\n\n`, answer => { | |
try { | |
for (const validator in q.validate) { | |
if (!q.validate[validator].test(answer)) { | |
throw q.validate[validator].err; | |
} | |
} | |
clearConsole(); | |
q.onSuccess(answer); | |
} catch (ex) { | |
clearConsole(); | |
process.stdout.write(`${ex}\n`); | |
return pose(q, i); | |
} | |
if (!q.repeat) i++; | |
if (!gameOver()) pose(steps[i], i); | |
}); | |
}; | |
clearConsole(); | |
process.stdout.write(`Hello! And welcome to Alessia's Tic Tac Toe game.\n`); | |
// Kick off game | |
pose(steps[0]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment