diff --git a/src/index.js b/src/index.js index 0e3c9a2..e25783c 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,8 @@ const BISHOP = 3; const QUEEN = 4; const KING = 5; +const EMPTY = -1; + const Images = [ './white_pawn.svg', './white_rook.svg', @@ -51,40 +53,8 @@ function range(n) { return Array.from(Array(n).keys()); } -function isBlack(piece) { - return piece != null && piece.color === BLACK; -} - -function isWhite(piece) { - return piece != null && piece.color === WHITE; -} - -function isPawn(piece) { - return piece != null && piece.type === PAWN; -} - -function isRook(piece) { - return piece != null && piece.type === ROOK; -} - -function isBishop(piece) { - return piece != null && piece.type === BISHOP; -} - -function isQueen(piece) { - return piece != null && piece.type === QUEEN; -} - -function isKnight(piece) { - return piece != null && piece.type === KNIGHT; -} - -function isKing(piece) { - return piece != null && piece.type === KING; -} - function imageFromPiece(piece) { - if (piece) { + if (piece && piece.type >= 0) { const image = piece.color === WHITE ? piece.type : piece.type + 6; return Images[image]; } @@ -126,6 +96,50 @@ class Piece { return "Has made " + this.moves + " moves" } } + + isEmpty() { + return this.type === EMPTY; + } + + isFull() { + return !this.isEmpty(); + } + + isBlack() { + return this.color === BLACK; + } + + isWhite() { + return this.color === WHITE; + } + + isEnemyOf(piece) { + if (this.color === EMPTY || piece.color === EMPTY) { + return false; + } else { + return this.color !== piece.color; + } + } + + isFriendOf(piece) { + if (this.color === EMPTY || piece.color === EMPTY) { + return false; + } else { + return this.color === piece.color; + } + } + + is(type) { + return this.type === type; + } + + hasMoved() { + return this.moves !== 0; + } + + hasntMoved() { + return !this.hasMoved(); + } } class Board extends React.Component { @@ -183,7 +197,7 @@ class Board extends React.Component { case 'p': return new Piece(color, PAWN); default: - return null; + return new Piece(EMPTY, EMPTY); } }), }; @@ -243,17 +257,13 @@ class Board extends React.Component { const mainRow = [ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK]; function add(num, color, type) { for(let i = 0; i < num; i++) { - if(color != null && type != null) { - squares.push(new Piece(color, type)); - } else { - squares.push(null); - } + squares.push(new Piece(color, type)); } } mainRow.forEach(type => add(1, WHITE, type)); add(8, WHITE, PAWN); - add(32, null, null); + add(32, EMPTY, EMPTY); add(8, BLACK, PAWN); mainRow.forEach(type => add(1, BLACK, type)); @@ -286,7 +296,7 @@ class Board extends React.Component { return this.state.squares.length; } - squareAt(i) { + pieceAtIndex(i) { return i >= 0 && i < 64 ? this.state.squares[i] : null; } @@ -294,39 +304,19 @@ class Board extends React.Component { if (this.isValidXY(x, y)) { return this.state.squares[this.getIndex(x, y)]; } else { - return null; + return new Piece(EMPTY, EMPTY); } } - whiteAt(x, y) { - const square = this.pieceAt(x, y); - if (square == null) { - return false; - } - return isWhite(square); - } - - blackAt(x, y) { - const square = this.pieceAt(x, y); - if (square == null) { - return false; - } - return isBlack(square); - } - - isEnemyOf(piece, x, y) { - return isBlack(piece) ? this.whiteAt(x, y) : this.blackAt(x, y); - } - getValidMovesAt(piece, x, y) { let moves = []; const tryAddMove = (x, y) => { if (this.isValidXY(x, y)) { - if(this.pieceAt(x, y) == null) { + if(this.pieceAt(x, y).isEmpty()) { moves.push({x, y}); // Keep searching return 0; - } else if (this.isEnemyOf(piece, x, y)) { + } else if (piece.isEnemyOf(this.pieceAt(x, y))) { moves.push({x, y}); } // Stop searching @@ -341,70 +331,71 @@ class Board extends React.Component { } } - if (isPawn(piece)) { - const pieceIsBlack = isBlack(piece); + if (piece.is(PAWN)) { + const pieceIsBlack = piece.isBlack(); const shift = pieceIsBlack ? -1 : 1; const startLine = pieceIsBlack ? 6 : 1; // Check for en passant const left = this.pieceAt(x - 1, y); const right = this.pieceAt(x + 1, y); - if (left != null && left.passantable && left.color !== piece.color) { + if (left && left.passantable && left.isEnemyOf(piece)) { moves.push({x: x - 1, y: y + shift, passant: {x: x - 1, y}}) } - if (right != null && right.passantable && right.color !== piece.color) { + if (right && right.passantable && right.isEnemyOf(piece)) { moves.push({x: x + 1, y: y + shift, passant: {x: x + 1, y}}) } - if (this.pieceAt(x, y + shift) == null) { + if (this.pieceAt(x, y + shift).isEmpty()) { moves.push({x, y: y + shift}); // Pawn moving two spaces becomes en-passantable - if (y === startLine && this.pieceAt(x, y + (shift * 2)) == null) { + if (y === startLine && this.pieceAt(x, y + (shift * 2)).isEmpty()) { moves.push({x, y: y + (shift * 2), passantable: true}); } } [x + 1, x - 1].forEach(x => { - if (this.isValidXY(x, y + shift) && this.isEnemyOf(piece, x, y + shift)) { - moves.push({x, y: y + shift}); + const y2 = y + shift; + if (this.isValidXY(x, y2) && piece.isEnemyOf(this.pieceAt(x, y2))) { + moves.push({x, y: y2}); } }); - } else if (isRook(piece)) { + } else if (piece.is(ROOK)) { addBunch(n => {return x;}, n => {return y + n;}); addBunch(n => {return x;}, n => {return y - n;}); addBunch(n => {return x + n;}, n => {return y;}); addBunch(n => {return x - n;}, n => {return y;}); - } else if (isBishop(piece)) { + } else if (piece.is(BISHOP)) { addBunch(n => {return x + n;}, n => {return y + n;}); addBunch(n => {return x - n;}, n => {return y + n;}); addBunch(n => {return x + n;}, n => {return y - n;}); addBunch(n => {return x - n;}, n => {return y - n;}); - } else if (isQueen(piece)) { + } else if (piece.is(QUEEN)) { const [rook, bishop] = [new Piece(piece.color, ROOK), new Piece(piece.color, BISHOP)]; moves = moves.concat(this.getValidMovesAt(rook, x, y)); moves = moves.concat(this.getValidMovesAt(bishop, x, y)); - } else if (isKnight(piece)) { + } else if (piece.is(KNIGHT)) { [ [2, 1], [2, -1], [-2, 1], [-2, -1], [1, 2], [1, -2], [-1, 2], [-1, -2], ].forEach(delta => tryAddMove(x + delta[0], y + delta[1])); - } else if (isKing(piece)) { + } else if (piece.is(KING)) { [[1, 1], [1, -1], [-1, 1], [-1, -1], [0, 1], [0, -1], [1, 0], [-1, 0]] .forEach(delta => tryAddMove(x + delta[0], y + delta[1])); - if (piece.moves === 0) { + if (piece.hasntMoved()) { const kingIndex = this.findIndex(piece); const [x, y] = this.getXandY(kingIndex); let leftRook = this.pieceAt(0, y); - if(isRook(leftRook) && leftRook.moves === 0) { + if(leftRook.is(ROOK) && leftRook.hasntMoved()) { // Check if spaces between rook and king are empty - if(this.pieceAt(1, y) == null && - this.pieceAt(2, y) == null && - this.pieceAt(3, y) == null) { + if(this.pieceAt(1, y).isEmpty() && + this.pieceAt(2, y).isEmpty() && + this.pieceAt(3, y).isEmpty()) { // Check if between space puts king in check let board = this.clone(); board.state.squares[board.getIndex(x - 1, y)] = piece; - board.state.squares[kingIndex] = null; + board.state.squares[kingIndex].isEmpty(); if(board.inCheck(piece) == null) { moves.push({x: x - 2, y, castle: [x - 1, y]}); } @@ -412,14 +403,14 @@ class Board extends React.Component { } let rightRook = this.pieceAt(7, y); - if(isRook(rightRook) && rightRook.moves === 0) { + if(rightRook.is(ROOK) && rightRook.hasntMoved()) { // Check if spaces between rook and king are empty - if(this.pieceAt(5, y) == null && - this.pieceAt(6, y) == null) { + if(this.pieceAt(5, y).isEmpty() && + this.pieceAt(6, y).isEmpty()) { // Check if between space puts king in check let board = this.clone(); board.state.squares[board.getIndex(x + 1, y)] = piece; - board.state.squares[kingIndex] = null; + board.state.squares[kingIndex].isEmpty(); if(board.inCheck(piece) == null) { moves.push({x: x + 2, y, castle: [x + 1, y]}); } @@ -433,7 +424,7 @@ class Board extends React.Component { findIndex(piece) { for(let i = 0; i < this.squareCount(); i++) { const check = this.state.squares[i]; - if(check && check.type === piece.type && check.color === piece.color) { + if(check.type === piece.type && check.color === piece.color) { return i; } } @@ -457,8 +448,7 @@ class Board extends React.Component { const kingPos = this.getXandY(this.findIndex(piece)); for(let i = 0; i < this.squareCount(); i++) { - const [x, y] = this.getXandY(i); - if(this.isEnemyOf(piece, x, y)) { + if(piece.isEnemyOf(this.pieceAtIndex(i))) { const moves = this.getValidMoves(i); for(let j = 0; j < moves.length; j++) { if(moves[j].x === kingPos[0] && moves[j].y === kingPos[1]) { @@ -481,16 +471,16 @@ class Board extends React.Component { if (checkedKing != null) { // For each square for(let i = 0; i < this.squareCount(); i++) { - const piece = this.squareAt(i); + const piece = this.pieceAtIndex(i); // If that piece is on the checked team - if (piece != null && isBlack(piece) === isBlack(checkedKing)) { + if (piece != null && piece.isFriendOf(checkedKing)) { // For each move of the above piece const moves = this.getValidMoves(i) for(const move of moves) { // Copy the board const board = this.clone(); board.state.squares[board.getIndex(move.x, move.y)] = board.state.squares[i]; - board.state.squares[i] = null; + board.state.squares[i].isEmpty(); const check = board.inCheck(checkedKing); if (check == null || check.color !== checkedKing.color) { return false; @@ -507,7 +497,7 @@ class Board extends React.Component { getValidMoves(source) { const [x, y] = this.getXandY(source); - const piece = this.squareAt(source); + const piece = this.pieceAtIndex(source); return this.getValidMovesAt(piece, x, y); } @@ -536,7 +526,7 @@ class Board extends React.Component { const move = this.isValidMove(from, to) if (move) { if (move.passant) { - squares[this.getIndex(move.passant.x, move.passant.y)] = null; + squares[this.getIndex(move.passant.x, move.passant.y)] = new Piece(EMPTY, EMPTY); } if (move.castle) { // .castle holds the position where the rook should end up @@ -548,7 +538,7 @@ class Board extends React.Component { console.log([rookX, move.castle[1]]); squares[this.getIndex(move.castle[0], move.castle[1])] = squares[this.getIndex(rookX, move.castle[1])]; - squares[this.getIndex(rookX, move.castle[1])] = null; + squares[this.getIndex(rookX, move.castle[1])] = new Piece(EMPTY, EMPTY); } // Remove existing passantable states squares.forEach(square => { @@ -561,7 +551,7 @@ class Board extends React.Component { } const y = this.getXandY(to)[1]; squares[to] = squares[from]; - squares[from] = null; + squares[from] = new Piece(EMPTY, EMPTY); if (squares[to].type === PAWN && (y === 0 || y === 7)) { squares[to].setType(QUEEN); } @@ -586,7 +576,7 @@ class Board extends React.Component { // Copy the board let board = this.clone(); board.state.squares[i] = board.state.squares[board.heldPiece()]; - board.state.squares[this.heldPiece()] = null; + board.state.squares[this.heldPiece()] = new Piece(EMPTY, EMPTY); const moversKing = this.state.blackIsNext ? new Piece(BLACK, KING) : new Piece(WHITE, KING); @@ -598,8 +588,8 @@ class Board extends React.Component { heldPiece: null, }); } - } else if (this.state.squares[i] != null) { - const isSquareBlack = isBlack(this.state.squares[i]); + } else if (this.state.squares[i].isFull()) { + const isSquareBlack = this.state.squares[i].isBlack(); if(isSquareBlack === this.state.blackIsNext) { this.setHand({ heldPiece: i,