Created
February 3, 2014 08:41
-
-
Save loonkwil/8780610 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
"use strict"; | |
var Stream = require("stream"), | |
util = require("util"); | |
function ConnectFour() { | |
Stream.Transform.apply(this, arguments); | |
}; | |
util.inherits(ConnectFour, Stream.Transform); | |
ConnectFour.prototype._transform = function _transform(chunk, encoding, callback) { | |
var that = this; | |
// Helpers | |
/** | |
* @param {string} str | |
* @return {string} | |
*/ | |
var addLeadingZeros = function(str) { | |
return (Math.pow(2, 48) + parseInt(str, 2)).toString(2).slice(1); | |
}; | |
/** | |
* @param {string} str | |
* @return {boolean} | |
*/ | |
var isEmpty = function(str) { | |
return !Boolean(+str); | |
}; | |
/** | |
* @param {string} str | |
* @param {function} fn | |
* @return {string} | |
*/ | |
var doWithEveryChar = function(str, fn) { | |
return str.split('').map(fn).join(''); | |
}; | |
/** | |
* @param {string} a | |
* @param {string} b | |
* @return {string} | |
*/ | |
var bitwiseAnd = function(a, b) { | |
return doWithEveryChar(a, function(value, index) { | |
return value & b[index]; | |
}); | |
}; | |
/** | |
* @param {string} a | |
* @param {string} b | |
* @return {string} | |
*/ | |
var bitwiseOr = function(a, b) { | |
return doWithEveryChar(a, function(value, index) { | |
return value | b[index]; | |
}); | |
}; | |
/** | |
* @param {string} str | |
* @param {number} num | |
* @return {string} | |
*/ | |
var rightShift = function(str, num) { | |
return addLeadingZeros(str.slice(0, -1 * num)); | |
}; | |
/** | |
* @param {string} board | |
* @return {boolean} | |
*/ | |
var hasWon = function(board) { | |
var d1, d2, h, v; | |
d1 = bitwiseAnd(board, rightShift(board, 6)); // diagonal \ | |
d2 = bitwiseAnd(board, rightShift(board, 8)); // diagonal / | |
h = bitwiseAnd(board, rightShift(board, 7)); // horizontal - | |
v = bitwiseAnd(board, rightShift(board, 1)); // vertical | | |
return ( | |
!isEmpty(bitwiseAnd(d1, rightShift(d1, 2 * 6))) || | |
!isEmpty(bitwiseAnd(d2, rightShift(d2, 2 * 8))) || | |
!isEmpty(bitwiseAnd(h, rightShift(h, 2 * 7))) || | |
!isEmpty(bitwiseAnd(v, rightShift(v, 2))) | |
); | |
}; | |
// Remove the unnecessarily characters | |
chunk = chunk.toString().replace(/\D/g, ''); | |
// Validate the move | |
if( !chunk ) { return callback(new Error()); } | |
// Concatenate the current chunk with the previous ones | |
that.prevData = that.prevData || ''; | |
that.prevData += chunk; | |
// More about the bitboards: http://en.wikipedia.org/wiki/Bitboard | |
var emptyBoard = addLeadingZeros(0); | |
var bitboards = [ emptyBoard, emptyBoard ]; | |
doWithEveryChar(that.prevData, function(oneStep, index) { | |
var boardIndex = index % 2; | |
var firstCellInTheRow = (oneStep - 1) * 7; | |
// Find out the index of the first empty cell | |
var allMoves = bitwiseOr(bitboards[0], bitboards[1]); | |
var firstEmptyCell = allMoves.indexOf('0', firstCellInTheRow); | |
// Mark the cell | |
var myBoard = bitboards[boardIndex]; | |
myBoard = myBoard.slice(0, firstEmptyCell) + | |
1 + myBoard.slice(firstEmptyCell + 1); | |
// Has won? | |
if( hasWon(myBoard) ) { | |
that.push(boardIndex ? 'b' : 'a'); | |
bitboards = [ emptyBoard, emptyBoard ]; | |
} | |
bitboards[boardIndex] = myBoard; | |
}); | |
callback(); | |
}; | |
module.exports = ConnectFour; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment