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 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
| 'use strict' | |
| const fs = require('fs') | |
| // util function modified from github.com/bucaran/sget | |
| const 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))) | |
| } | |
| const range = n => Array(n + 1).join(1).split('').map((x, i) => i) | |
| function end(game) { | |
| const verticalEnd = range(game.width).some(i => { | |
| const column = game.board[i] || [] | |
| const max = { p: 0, val: 0 } | |
| return range(game.height).some(j => { | |
| const value = column[j] | |
| if (value && value === max.p) max.val++ | |
| else max.p = value, max.val = 1 | |
| return max.p && max.val >= game.win | |
| }) | |
| }) | |
| const horizontalEnd = range(game.height).some(j => { | |
| const max = { p: 0, val: 0 } | |
| return range(game.width).some(i => { | |
| const value = (game.board[i] || [])[j] | |
| if (value && value === max.p) max.val++ | |
| else max.p = value, max.val = 1 | |
| return max.p && max.val >= game.win | |
| }) | |
| }) | |
| const diagonalNW = range(game.width + game.height - 1).some(k => { | |
| // taking top and left edge | |
| let i = k < game.width ? k : 0 | |
| let j = k < game.width ? 0 : k - game.width + 1 | |
| const max = { p: 0, val: 0 } | |
| while(i < game.width && j < game.height) { | |
| const value = (game.board[i] || [])[j] | |
| if (value && value === max.p) max.val++ | |
| else max.p = value, max.val = 1 | |
| if (max.p && max.val >= game.win) return true | |
| i++, j++ | |
| } | |
| }) | |
| const diagonalNE = range(game.width + game.height - 1).some(k => { | |
| // taking top and right edge | |
| let i = k < game.width ? k : game.width - 1 | |
| let j = k < game.width ? 0 : k - game.width + 1 | |
| const max = { p: 0, val: 0 } | |
| while(i >= 0 && j < game.height) { | |
| const value = (game.board[i] || [])[j] | |
| if (value && value === max.p) max.val++ | |
| else max.p = value, max.val = 1 | |
| if (max.p && max.val >= game.win) return true | |
| i--, j++ | |
| } | |
| }) | |
| const coinCount = range(game.width).reduce((mem, i) => ( | |
| mem + range(game.height).reduce((deepMem, j) => ( | |
| deepMem + Boolean((game.board[i] || [])[j]) | |
| ), 0) | |
| ), 0) | |
| return (coinCount >= game.width * game.height && 'draw') || verticalEnd || horizontalEnd || diagonalNW || diagonalNE | |
| } | |
| function print(game) { | |
| console.log('\x1B[2J\x1B[0f') // Clear screen | |
| // x index | |
| console.log([' '].concat(range(game.width)).join(' ')); | |
| range(game.height).map(j => { | |
| console.log( | |
| j + ' ', // y index | |
| range(game.width).map(i => coin[(game.board[i] || [])[j] || 0] ).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(i, j, game) { | |
| if (isNaN(i) || isNaN(j)) return false | |
| if (i < 0 || i > game.width - 1) return false | |
| if (j < 0 || j > game.height - 1) return false | |
| if (game.currentPlayer < 1) return false | |
| if (!game.board[i]) game.board[i] = [] | |
| if (game.board[i][j]) return false | |
| return game.board[i][j] = game.currentPlayer | |
| } | |
| function parse(text, game) { | |
| const split = text.split(',').length < 2 ? text.split(' ') : text.split(',') | |
| return split.map(v => parseInt(v)) | |
| } | |
| function switchPlayer(game) { | |
| game.currentPlayer++ | |
| if (game.currentPlayer > 2) game.currentPlayer = 1 | |
| } | |
| const coin = { 0: '_', 1: 'X', 2: 'O' } | |
| ;(function(){ | |
| let endState; | |
| const game = { | |
| board: [], | |
| width: 3, | |
| height: 3, | |
| win: 3, | |
| currentPlayer: 1, | |
| } | |
| print(game) | |
| while(!(endState = end(game))) { | |
| const position = parse(readLineSync(`Player "${coin[game.currentPlayer]}" (x y):`), game) | |
| if(place(position[0], position[1], game)) { | |
| print(game) | |
| switchPlayer(game) | |
| } else { | |
| console.log('Invalid move') | |
| } | |
| } | |
| printEnd(endState, game.currentPlayer) | |
| })() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment