Last active
March 23, 2016 22:27
-
-
Save bigomega/86c67796b032f8fc74e6 to your computer and use it in GitHub Desktop.
simple tic-tac-toe
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
'use strict' | |
const _utils = require('./_utils') | |
function getColumns(game) { return game.board } | |
function getRows(game) { return _utils.inverse2D(game.board) } | |
function getNWDiagonals(game) { | |
return _utils.range(game.width + game.height - 1).map(edge => { | |
// taking top and left edge | |
let i = edge < game.width ? edge : 0 | |
let j = edge < game.width ? 0 : edge - game.width + 1 | |
const arr = [] | |
while(i < game.width && j < game.height) { | |
arr.push(game.board[i][j]) | |
i++, j++ | |
} | |
return arr | |
}) | |
} | |
function getNEDiagonals(game) { | |
return _utils.range(game.width + game.height - 1).map(edge => { | |
// taking top and right edge | |
let i = edge < game.width ? edge : game.width - 1 | |
let j = edge < game.width ? 0 : edge - game.width + 1 | |
const arr = [] | |
while(i >= 0 && j < game.height) { | |
arr.push(game.board[i][j]) | |
i--, j++ | |
} | |
return arr | |
}) | |
} | |
function getItems(game) { | |
return getColumns(game).reduce((mem, column) => mem.concat(column), []) | |
} | |
module.exports = { | |
getColumns, | |
getRows, | |
getItems, | |
getNWDiagonals, | |
getNEDiagonals, | |
} |
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
'use strict' | |
const fs = require('fs') | |
// util function modified from github.com/bucaran/sget | |
module.exports.readLineSync = function(message) { | |
message = message || '' | |
const win32 = () => 'win32' === process.platform | |
const readSync = function(buffer) { | |
var fd = win32() ? process.stdin.fd : fs.openSync('/dev/stdin', 'rs') | |
var bytes = fs.readSync(fd, buffer, 0, buffer.length) | |
if (!win32()) fs.closeSync(fd) | |
return bytes | |
} | |
return (function(buffer) { | |
try { | |
process.stdout.write(message + ' ') | |
return buffer.toString(null, 0, readSync(buffer)) | |
} catch (e) { | |
throw e | |
} | |
}(new Buffer(256))) | |
} | |
module.exports.range = n => Array(n + 1).join(1).split('').map((x, i) => i) | |
module.exports.inverse2D = Arr => Arr[0].map((val, j) => Arr.map(column => column[j])) |
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
'use strict' | |
const _utils = require('./_utils') | |
const _boardUtil = require('./_board-utils') | |
function initiateBoard(game) { | |
_utils.range(game.width).forEach(i => { | |
game.board[i] = [] | |
_utils.range(game.height).forEach(j => game.board[i][j] = 0) | |
}) | |
} | |
function end(game) { | |
const checkWin = Arr2D => Arr2D.some(list => { | |
const max = { p: 0, val: 0 } | |
return list.some(value => { | |
if (value && value === max.p) max.val++ | |
else max.p = value, max.val = 1 | |
return max.p && max.val >= game.win | |
}) | |
}) | |
const verticalEnd = checkWin(_boardUtil.getColumns(game)) | |
const horizontalEnd = checkWin(_boardUtil.getRows(game)) | |
const diagonalNW = checkWin(_boardUtil.getNWDiagonals(game)) | |
const diagonalNE = checkWin(_boardUtil.getNEDiagonals(game)) | |
const coinCount = _boardUtil.getItems(game).reduce((mem, value) => mem + Boolean(value)) | |
const staleMate = coinCount >= game.width * game.height | |
return (staleMate && 'draw') || verticalEnd || horizontalEnd || diagonalNW || diagonalNE | |
} | |
function print(game) { | |
console.log('\x1B[2J\x1B[0f') // Clear screen | |
console.log([' '].concat(_utils.range(game.width)).join(' ')); | |
_boardUtil.getRows(game).forEach((row, i) => { | |
console.log(i + ' ', row.map(value => coin[value] ).join(' ')) | |
}) | |
console.log('') | |
} | |
function printEnd(state, currentPlayer) { | |
if(state === 'draw') console.log('Game over. The game ended in a draw.\n') | |
else console.log(`Game over. Player "${coin[currentPlayer]}" won.\n`) | |
} | |
function place(position, game) { | |
const x = position[0] | |
const y = position[1] | |
if (isNaN(x) || isNaN(y)) return false | |
if (x < 0 || x > game.width - 1) return false | |
if (y < 0 || y > game.height - 1) return false | |
if (game.currentPlayer < 1 || game.board[x][y]) return false | |
return game.board[x][y] = game.currentPlayer | |
} | |
function getInput(message, game) { | |
const text = _utils.readLineSync(message) | |
const split = text.split(',').length < 2 ? text.split(' ') : text.split(',') | |
return split.map(v => parseInt(v)) | |
} | |
function switchPlayer(game, reverse) { | |
if(reverse) game.currentPlayer -= 1 | |
else game.currentPlayer += 1 | |
return game.currentPlayer = game.currentPlayer < 1 ? game.playerCount : 1 | |
} | |
const coin = { 0: '_', 1: 'X', 2: 'O', 3: '#' } | |
;(function(){ | |
let endState; | |
const game = { | |
board: [], | |
width: 3, | |
height: 3, | |
win: 3, | |
playerCount: 2, | |
currentPlayer: 1, | |
} | |
initiateBoard(game) | |
print(game) | |
while(!(endState = end(game))) { | |
const position = getInput(`Player "${coin[game.currentPlayer]}" (x y):`, game) | |
if (place(position, game)) { | |
print(game) | |
switchPlayer(game) | |
} else { | |
console.log('Invalid move') | |
} | |
} | |
switchPlayer(game, true) | |
printEnd(endState, game.currentPlayer) | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment