Skip to content

Instantly share code, notes, and snippets.

@dan-cooke
Last active June 29, 2017 14:58
Show Gist options
  • Save dan-cooke/83aafbe2845019424f29276a1517e7ce to your computer and use it in GitHub Desktop.
Save dan-cooke/83aafbe2845019424f29276a1517e7ce to your computer and use it in GitHub Desktop.
Chess Kata - WIP
// 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