Last active
June 29, 2017 14:58
-
-
Save dan-cooke/83aafbe2845019424f29276a1517e7ce to your computer and use it in GitHub Desktop.
Chess Kata - WIP
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
// Returns an array of threats if the arrangement of | |
// the pieces is a check, otherwise false | |
var getMoves = function getMoves(p, board) { | |
var x = p.x; | |
var y = p.y; | |
var getPawnMoves = function getPawnMoves(p, x, y) { | |
if (p.owner == 0) { | |
//white | |
let moves = [{ | |
x: x + 1 > 7 ? undefined : x + 1, | |
y: y - 1 < 0 ? undefined : y - 1, | |
prevX: x, | |
prevY: y | |
}, { | |
x: x - 1 < 0 ? undefined: x - 1, | |
y: y - 1 < 0 ? undefeind: y - 1, | |
prevX: x, | |
prevY: y | |
}, | |
{ | |
x: x, | |
y: y -1 < 0 ? undefined : y -1, | |
prevX: x, | |
prevY: y | |
} | |
]; | |
if(p.y == 6){ | |
//double move possible | |
moves.push({ | |
x : x, | |
y: y - 2, | |
prevX: x, | |
prevY: y}) | |
} | |
return moves; | |
} else { | |
//black | |
let moves = [{ | |
x: x + 1 > 7 ? undefined : x + 1, | |
y: y + 1 > 7 ? undefined : y + 1, | |
prevX: x, | |
prevY: y | |
}, { | |
x: x - 1 < 0 ? undefined: x - 1, | |
y: y + 1 > 7 ? undefined : y + 1, | |
prevX: x, | |
prevY: y | |
}, | |
{ | |
x: x, | |
y: y -1 < 0 ? undefined : y -1, | |
prevX: x, | |
prevY: y | |
} | |
]; | |
if(p.y == 1){ | |
//double move possible | |
moves.push({x : x, y: y + 2, | |
prevX: x, | |
prevY: y}) | |
} | |
return moves; | |
} | |
}; | |
var getRookMoves = function getRookMoves(p, x, y) { | |
var moves = []; | |
//move 8 vert and 8 hor - remove any negatives or any greater than 7 | |
var availableDirections = { | |
UP: true, | |
LEFT: true, | |
RIGHT: true, | |
DOWN: true | |
}; | |
for (var i = 1; i < 8; i++) { | |
var newX = { | |
positive: x + i > 7 ? undefined : x + i, | |
negative: x - i < 0 ? undefined : x - i, | |
prevX: x, | |
prevY: y | |
}; | |
var newY = { | |
positive: y + i > 7 ? undefined : y + i, | |
negative: y - i < 0 ? undefined : y - i, | |
prevX: x, | |
prevY: y | |
}; | |
//move right | |
if (newX.positive && availableDirections.RIGHT) { | |
moves.push({ | |
x: newX.positive, | |
y: y, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (newX.negative && availableDirections.LEFT) { | |
moves.push({ | |
x: newX.negative, | |
y: y, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (newY.positive && availableDirections.DOWN) { | |
moves.push({ | |
x: x, | |
y: newY.positive, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (newY.negative && availableDirections.UP) { | |
moves.push({ | |
x: x, | |
y: newY.negative, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
availableDirections = getAvailableMovementDirections(p, newX, newY, availableDirections, board); | |
} | |
return moves; | |
}; | |
var getKnightMoves = function getKnightMoves(p, x, y) { | |
var moves = []; | |
if (!isPositionOccupied(board, { | |
x: x + 1, | |
y: y - 2 | |
}) || isOppositeKing(p, { | |
x: x + 1, | |
y: y - 2 | |
}, board)) { | |
moves.push({ | |
x: x + 1, | |
y: y - 2, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (!isPositionOccupied(board, { | |
x: x + 1, | |
y: y + 2 | |
}) || isOppositeKing(p, { | |
x: x + 1, | |
y: y + 2 | |
}, board)) { | |
moves.push({ | |
x: x + 1, | |
y: y + 2, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (!isPositionOccupied(board, { | |
x: x + 2, | |
y: y - 1 | |
}) || isOppositeKing(p, { | |
x: x + 2, | |
y: y - 1 | |
}, board)) { | |
moves.push({ | |
x: x + 2, | |
y: y - 1, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (!isPositionOccupied(board, { | |
x: x + 2, | |
y: y + 1 | |
}) || isOppositeKing(p, { | |
x: x + 2, | |
y: y + 1 | |
}, board)) { | |
moves.push({ | |
x: x + 2, | |
y: y + 1, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (!isPositionOccupied(board, { | |
x: x - 1, | |
y: y - 2 | |
}) || isOppositeKing(p, { | |
x: x - 1, | |
y: y - 2 | |
}, board)) { | |
moves.push({ | |
x: x - 1, | |
y: y - 2, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (!isPositionOccupied(board, { | |
x: x - 1, | |
y: y + 2 | |
}) || isOppositeKing(p, { | |
x: x - 1, | |
y: y + 2 | |
}, board)) { | |
moves.push({ | |
x: x - 1, | |
y: y + 2, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (!isPositionOccupied(board, { | |
x: x - 2, | |
y: y - 1 | |
}) || isOppositeKing(p, { | |
x: x - 2, | |
y: y - 1 | |
}, board)) { | |
moves.push({ | |
x: x - 2, | |
y: y - 1, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
if (!isPositionOccupied(board, { | |
x: x - 2, | |
y: y + 1 | |
}) || isOppositeKing(p, { | |
x: x - 2, | |
y: y + 1 | |
}, board)) { | |
moves.push({ | |
x: x - 2, | |
y: y + 1, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
return moves; | |
}; | |
var getQueenMoves = function getQueenMoves(p, x, y) { | |
var moves = []; | |
var availableDirections = { | |
UP: true, | |
LEFT: true, | |
RIGHT: true, | |
DOWN: true, | |
UP_LEFT: true, | |
UP_RIGHT: true, | |
DOWN_LEFT: true, | |
DOWN_RIGHT: true | |
}; | |
for (var i = 1; i < 8; i++) { | |
var newX = { | |
positive: x + i > 7 ? undefined : x + i, | |
negative: x - i < 0 ? undefined : x - i, | |
prevX: x, | |
prevY: y | |
}; | |
var newY = { | |
positive: y + i > 7 ? undefined : y + i, | |
negative: y - i < 0 ? undefined : y - i, | |
prevX: x, | |
prevY: y | |
}; | |
//move right | |
if (availableDirections.RIGHT && newX.positive) { | |
moves.push({ | |
x: newX.positive, | |
y: y, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move up and right | |
if (availableDirections.UP_RIGHT && newX.positive && newY.negative) { | |
moves.push({ | |
x: newX.positive, | |
y: newY.negative, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move up | |
if (availableDirections.UP && newY.negative) { | |
moves.push({ | |
x: x, | |
y: newY.negative, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move up and left | |
if (availableDirections.UP_LEFT && newY.negative && newX.negative) { | |
moves.push({ | |
x: newX.negative, | |
y: newY.negative, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move left | |
if (availableDirections.LEFT && newX.negative) { | |
moves.push({ | |
x: newX.negative, | |
y: y, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move left and down | |
if (availableDirections.DOWN_LEFT && newX.negative && newY.positive) { | |
moves.push({ | |
x: newX.negative, | |
y: newY.positive, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move down | |
if (availableDirections.DOWN && newY.positive) { | |
moves.push({ | |
x: x, | |
y: newY.positive, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move down and right | |
if (availableDirections.DOWN_RIGHT && newX.positive && newY.positive) { | |
moves.push({ | |
x: newX.positive, | |
y: newY.positive, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
availableDirections = getAvailableMovementDirections(p, newX, newY, availableDirections, board); | |
} | |
return moves; | |
}; | |
var getBishopMoves = function getBishopMoves(p, x, y) { | |
var moves = []; | |
var availableDirections = { | |
UP_LEFT: true, | |
UP_RIGHT: true, | |
DOWN_LEFT: true, | |
DOWN_RIGHT: true | |
}; | |
//move 8 vert and 8 hor - remove any negatives or any greater than 7 | |
for (var i = 1; i < 8; i++) { | |
var newX = { | |
positive: x + i > 7 ? undefined : x + i, | |
negative: x - i < 0 ? undefined : x - i | |
}; | |
var newY = { | |
positive: y + i > 7 ? undefined : y + i, | |
negative: y - i < 0 ? undefined : y - i | |
}; | |
//move up and right | |
if (availableDirections.UP_RIGHT && newX.positive && newY.negative) { | |
moves.push({ | |
x: newX.positive, | |
y: newY.negative, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move up and left | |
if (availableDirections.UP_LEFT && newY.negative && newX.negative) { | |
moves.push({ | |
x: newX.negative, | |
y: newY.negative, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move left and down | |
if (availableDirections.DOWN_LEFT && newX.negative && newY.positive) { | |
moves.push({ | |
x: newX.negative, | |
y: newY.positive, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move down and right | |
if (availableDirections.DOWN_RIGHT && newX.positive && newY.positive) { | |
moves.push({ | |
x: newX.positive, | |
y: newY.positive, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
availableDirections = getAvailableMovementDirections(p, newX, newY, availableDirections, board); | |
} | |
return moves; | |
}; | |
var getKingMoves = function getKingMoves(p, x, y) { | |
let moves = []; | |
var availableDirections = { | |
UP: true, | |
LEFT: true, | |
RIGHT: true, | |
DOWN: true, | |
UP_LEFT: true, | |
UP_RIGHT: true, | |
DOWN_LEFT: true, | |
DOWN_RIGHT: true | |
}; | |
var newX = { | |
positive: x + 1 > 7 ? undefined : x + 1, | |
negative: x - 1 < 0 ? undefined : x - 1 | |
}; | |
var newY = { | |
positive: y + 1 > 7 ? undefined : y + 1, | |
negative: y - 1 < 0 ? undefined : y - 1 | |
}; | |
//move right | |
if (availableDirections.RIGHT && newX.positive) { | |
moves.push({ | |
x: newX.positive, | |
y: y, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move up and right | |
if (availableDirections.UP_RIGHT && newX.positive && newY.negative) { | |
moves.push({ | |
x: newX.positive, | |
y: newY.negative, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move up | |
if (availableDirections.UP && newY.negative) { | |
moves.push({ | |
x: x, | |
y: newY.negative, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move up and left | |
if (availableDirections.UP_LEFT && newY.negative && newX.negative) { | |
moves.push({ | |
x: newX.negative, | |
y: newY.negative, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move left | |
if (availableDirections.LEFT && newX.negative) { | |
moves.push({ | |
x: newX.negative, | |
y: y, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move left and down | |
if (availableDirections.DOWN_LEFT && newX.negative && newY.positive) { | |
moves.push({ | |
x: newX.negative, | |
y: newY.positive, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move down | |
if (availableDirections.DOWN && newY.positive) { | |
moves.push({ | |
x: x, | |
y: newY.positive, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
//move down and right | |
if (availableDirections.DOWN_RIGHT && newX.positive && newY.positive) { | |
moves.push({ | |
x: newX.positive, | |
y: newY.positive, | |
prevX: x, | |
prevY: y | |
}); | |
} | |
return moves; | |
}; | |
switch (p.piece) { | |
case 'pawn': | |
return getPawnMoves(p, x, y); | |
case 'rook': | |
return getRookMoves(p, x, y); | |
case 'bishop': | |
return getBishopMoves(p, x, y); | |
case 'queen': | |
return getQueenMoves(p, x, y); | |
case 'knight': | |
return getKnightMoves(p, x, y); | |
case 'king': | |
return getKingMoves(p, x, y); | |
} | |
}; | |
function getAvailableMovementDirections(piece, newX, newY, directions, board) { | |
const x = piece.x; | |
const y = piece.y; | |
if (directions.RIGHT && isPositionOccupied(board, { | |
x: newX.positive, | |
y: y | |
})) { | |
directions.RIGHT = isOppositeKing(piece, { | |
x: newX.positive, | |
y: y | |
}, board) ? directions.RIGHT : false; | |
} | |
if (directions.LEFT && isPositionOccupied(board, { | |
x: newX.negative, | |
y: y | |
})) { | |
directions.LEFT = isOppositeKing(piece, { | |
x: newX.negative, | |
y: y | |
}, board) ? directions.LEFT : false; | |
} | |
if (directions.DOWN && isPositionOccupied(board, { | |
x: x, | |
y: newY.positive | |
})) { | |
directions.DOWN = isOppositeKing(piece, { | |
x: x, | |
y: newY.positive | |
}, board) ? directions.DOWN : false; | |
} | |
if (directions.UP && isPositionOccupied(board, { | |
x: x, | |
y: newY.negative | |
})) { | |
directions.UP = isOppositeKing(piece, { | |
x: x, | |
y: newY.negative | |
}, board) ? directions.UP : false; | |
} | |
if (directions.UP_RIGHT && isPositionOccupied(board, { | |
x: newX.positive, | |
y: newY.negative | |
})) { | |
directions.UP_RIGHT = isOppositeKing(piece, { | |
x: newX.positive, | |
y: newY.negative | |
}, board) ? directions.UP_RIGHT : false; | |
} | |
if (directions.UP_LEFT && isPositionOccupied(board, { | |
x: newX.negative, | |
y: newY.negative | |
})) { | |
directions.UP_LEFT = isOppositeKing(piece, { | |
x: newX.negative, | |
y: newY.negative | |
}, board) ? directions.UP_LEFT : false; | |
} | |
if (directions.DOWN_LEFT && isPositionOccupied(board, { | |
x: newX.negative, | |
y: newY.positive | |
})) { | |
directions.DOWN_LEFT = isOppositeKing(piece, { | |
x: newX.negative, | |
y: newY.positive | |
}, board) ? directions.DOWN_LEFT : false; | |
} | |
if (directions.DOWN_RIGHT && isPositionOccupied(board, { | |
x: newX.positive, | |
y: newY.positive | |
})) { | |
directions.DOWN_RIGHT = isOppositeKing(piece, { | |
x: newX.positive, | |
y: newY.positive | |
}, board) ? directions.DOWN_RIGHT : false; | |
} | |
return directions; | |
} | |
function getPiece(pieces, name, owner) { | |
return pieces.find(function(piece) { | |
return piece.owner == owner && piece.piece == name; | |
}); | |
} | |
function isPositionOccupied(pieces, pos) { | |
return pieces.findIndex(p => p.x == pos.x && p.y == pos.y) > -1; | |
} | |
function isOppositeKing(piece, position, board) { | |
let p = getPieceAtPosition(board, position); | |
return p.owner != piece.owner && p.piece == 'king'; | |
} | |
function getPieceAtPosition(pieces, pos) { | |
return pieces.find(p => p.x == pos.x && p.y == pos.y); | |
} | |
const isOpponentPiece = (piece, player) => { | |
return piece.owner != player; | |
} | |
const isValidPawnTake = (pawnPiece, attemptedMove) => { | |
return attemptedMove.x != pawnPiece.x; | |
} | |
const isValidPawnMove = (pawnPiece, attemptedMove) => { | |
return attemptedMove.x == pawnPiece.x; | |
} | |
const getEnPassantPawns = (pieces, player) => { | |
return pieces.filter(p => { | |
if(player == 0) { | |
//we are looking for pawns with y == 4 | |
return p.y == 4 && p.piece == 'pawn' && p.owner == 0; | |
} | |
else { | |
return p.y == 5 && p.piece == 'pawn' && p.owner == 1; | |
} | |
}) | |
} | |
const simulateMove = (board, piece, nextMove) => { | |
let newPiece = { | |
piece: piece.piece, | |
owner: piece.owner, | |
x: nextMove.x, | |
y: nextMove.y, | |
prevX: piece.x, | |
prevY: piece.y | |
}; | |
let newBoard = board.slice(); | |
//check if the move conflicts with another piece | |
if(isPositionOccupied(board, newPiece)){ | |
let conflictingPiece = getPieceAtPosition(board, newPiece); | |
//check if the piece can be taken by the player | |
if(isOpponentPiece(conflictingPiece, piece.owner)){ | |
//if it is a pawn attempting to take the piece, the move must be a valid pawn take not just a move | |
if(piece.piece == 'pawn' && !isValidPawnTake(piece, nextMove)) return; | |
//remove the opponent piece from the potential board | |
let confIndex = board.findIndex(p => p == conflictingPiece); | |
newBoard = [...board.slice(0, confIndex) , ...board.slice(confIndex +1 , board.length)]; | |
} | |
else { | |
//otherwise this is not a valid move and we should return the previous board | |
return; | |
} | |
} | |
else { | |
//if this piece a pawn - and its not a valid move | |
if(piece.piece == 'pawn' && !isValidPawnMove(piece, nextMove)){ | |
//is it a valid take? | |
if(isValidPawnTake(piece, nextMove)){ | |
//get enpassant pawns | |
let enPassantPawns = getEnPassantPawns(newBoard, piece.owner == 0 ? 1 : 0); | |
if (enPassantPawns && enPassantPawns.length > 0){ | |
//if the moving piece can reach the enPassant take position then this is a valid move | |
let enPassantTakePosition = enPassantPawns[0].owner == 0 ? | |
{ | |
x: enPassantPawns[0].x, | |
y: enPassantPawns[0].y + 1 | |
} : | |
{ | |
x: enPassantPawns[0].x, | |
y: enPassantPawns[0].y - 1 | |
} | |
if(newPiece.x == enPassantTakePosition.x && newPiece.y == enPassantTakePosition.y){ | |
//remove enpassant pawn from new board | |
let pawnIndex = newBoard.findIndex(p => p.x == enPassantPawns[0].x && p.y == enPassantPawns[0].y); | |
newBoard = [...newBoard.slice(0, pawnIndex), ...newBoard.slice(pawnIndex + 1, newBoard.length)]; | |
} | |
} | |
else { | |
//if there are no enpassant pawns then this is not a valid move | |
return; | |
} | |
} | |
else { | |
//if not return | |
return; | |
} | |
} | |
} | |
//add the movement to the board | |
let pieceIndex = newBoard.findIndex(p => p.x == piece.x && p.y == piece.y); | |
return[...newBoard.slice(0, pieceIndex), newPiece, ...newBoard.slice(pieceIndex + 1, newBoard.length)]; | |
} | |
function isCheck(pieces, player) { | |
var king = getPiece(pieces, 'king', player); | |
var isChecking = function isChecking(p, king) { | |
if (getMoves(p, pieces).findIndex(m => m.x == king.x && m.y == king.y) > -1) { | |
return true; | |
} else { | |
return false; | |
} | |
}; | |
var getCheckingPieces = function getCheckingPieces(candidatePieces) { | |
let checkingPieces = []; | |
candidatePieces.forEach(function(p) { | |
if (p == king) return; | |
if (isChecking(p, king)) { | |
checkingPieces.push(p); | |
} | |
}); | |
return checkingPieces && checkingPieces.length > 0 ? checkingPieces : false; | |
}; | |
return getCheckingPieces(pieces.filter(p => p.owner != player)); | |
} | |
const isMate = (pieces, player) => { | |
let king = getPiece(pieces, 'king', player); | |
let checkMate = true; | |
console.log(pieces); | |
//check all possible moves from king against isCheck function | |
let kingMoves = getMoves(king, pieces); | |
kingMoves.forEach(move => { | |
if (!checkMate) return; | |
let board = simulateMove(pieces, king, move); | |
if(!board) return; | |
//determine if this move is valid | |
let checkingPieces = isCheck(board, player); | |
if (!checkingPieces) { | |
checkMate = false; | |
return; | |
} | |
}); | |
//if its still a check mate - consider all the possible moves by every other piece | |
let playerPieces = pieces.filter(p => p.owner == player && p.piece != 'king'); | |
playerPieces.forEach(pPiece => { | |
if(!checkMate) return; | |
debugger; | |
//check every possible move for every piece to see if its a check | |
let possibleMoves = getMoves(pPiece, pieces); | |
possibleMoves.forEach(move => { | |
if(!checkMate) return; | |
let board = simulateMove(pieces, pPiece, move); | |
if(!board) return; | |
//determine if this move is valid | |
let checkingPieces = isCheck(board, player); | |
if (!checkingPieces) { | |
checkMate = false; | |
return; | |
} | |
}) | |
}); | |
return checkMate; | |
}; | |
let p = [ { piece: 'pawn', owner: 0, x: 6, y: 4 }, | |
{ piece: 'pawn', owner: 0, x: 5, y: 5 }, | |
{ piece: 'pawn', owner: 0, x: 3, y: 6 }, | |
{ piece: 'pawn', owner: 0, x: 4, y: 6 }, | |
{ piece: 'pawn', owner: 0, x: 7, y: 6 }, | |
{ piece: 'queen', owner: 0, x: 3, y: 7 }, | |
{ piece: 'king', owner: 0, x: 4, y: 7 }, | |
{ piece: 'bishop', owner: 0, x: 5, y: 7 }, | |
{ piece: 'knight', owner: 0, x: 6, y: 7 }, | |
{ piece: 'rook', owner: 0, x: 7, y: 7 }, | |
{ piece: 'queen', owner: 1, x: 7, y: 4, prevX: 3, prevY: 0 }, | |
{ piece: 'king', owner: 1, x: 4, y: 0 } ]; | |
isMate(p, 0); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment