import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; const BLACK = 0; const WHITE = 1; const PAWN = 0; const ROOK = 1; const KNIGHT = 2; const BISHOP = 3; const QUEEN = 4; const KING = 5; const Images = [ './white_pawn.svg', './white_rook.svg', './white_knight.svg', './white_bishop.svg', './white_queen.svg', './white_king.svg', './black_pawn.svg', './black_rook.svg', './black_knight.svg', './black_bishop.svg', './black_queen.svg', './black_king.svg', ]; 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) { const image = piece.color === WHITE ? piece.type : piece.type + 6; return Images[image]; } return null; } function Square(props) { return ( ); } class Piece { constructor(color, type) { this.color = color; this.type = type; this.passantable = false; } setType(type) { this.type = type; } } class Board extends React.Component { constructor(props) { super(props); this.state = { squares: this.reset(), blackIsNext: true, hand: { heldPiece: null, }, }; } setHand(hand) { this.setState({ squares: this.state.squares, blackIsNext: this.state.blackIsNext, hand: hand, }); } reset() { let whiteRow = [ new Piece(WHITE, ROOK), new Piece(WHITE, KNIGHT), new Piece(WHITE, BISHOP), new Piece(WHITE, QUEEN), new Piece(WHITE, KING), new Piece(WHITE, BISHOP), new Piece(WHITE, KNIGHT), new Piece(WHITE, ROOK), ]; let blackRow = [ new Piece(BLACK, ROOK), new Piece(BLACK, KNIGHT), new Piece(BLACK, BISHOP), new Piece(BLACK, QUEEN), new Piece(BLACK, KING), new Piece(BLACK, BISHOP), new Piece(BLACK, KNIGHT), new Piece(BLACK, ROOK) ]; let squares = whiteRow; for(var i = 0; i < 8; i++) { squares.push(new Piece(WHITE, PAWN)); } squares = squares.concat(Array(32).fill(null)); for(i = 0; i < 8; i++) { squares.push(new Piece(BLACK, PAWN)); } squares = squares.concat(blackRow); return squares; } getXandY(i) { let x = i % 8; let y = Math.floor(i / 8); return [x, y]; } pieceAt(x, y) { let i = x + (y * 8); if (i < 64 && x < 8 && y < 8 && x >= 0 && y >= 0) { return this.state.squares[x + (y * 8)]; } else { return null; } } whiteAt(x, y) { let square = this.pieceAt(x, y); if (square == null) { return false; } return isWhite(square); } blackAt(x, y) { let square = this.pieceAt(x, y); if (square == null) { return false; } return isBlack(square); } isEnemyOf(piece, x, y) { let pieceIsBlack = isBlack(piece); return pieceIsBlack ? this.whiteAt(x, y) : this.blackAt(x, y); } getValidMovesAt(piece, x, y) { var i; var moves = []; let tryAddMove = (x, y) => { if (x >= 0 && x < 8 && y >= 0 && y < 8 && this.pieceAt(x, y) == null) { moves.push({x: x, y: y}); // Keep searching return 0; } else if (this.isEnemyOf(piece, x, y)) { moves.push({x: x, y: y}); } // Stop searching return 1; }; if (isPawn(piece)) { let pieceIsBlack = isBlack(piece); let shift = pieceIsBlack ? -1 : 1; let startLine = pieceIsBlack ? 6 : 1; // Check for en passant let left = this.pieceAt(x - 1, y); let right = this.pieceAt(x + 1, y); if (left != null && left.passantable && left.color !== piece.color) { moves.push({x:x - 1, y:y + shift, passant: {x: x - 1, y: y}}) } if (right != null && right.passantable && right.color !== piece.color) { moves.push({x: x + 1, y: y + shift, passant: {x: x + 1, y: y}}) } // Pawn moving two spaces becomes en-passantable if (this.pieceAt(x, y + shift) == null) { moves.push({x: x, y: y + shift}); if (y === startLine && this.pieceAt(x, y + (shift * 2)) == null) { moves.push({x: x, y: y + (shift * 2), passantable: true}); } } [x + 1, x - 1].forEach(x => { if (x >= 0 && x < 8 && this.isEnemyOf(piece, x, y + shift)) { moves.push({x: x, y: y + shift}); } }); } else if (isRook(piece)) { // Down for (i = y + 1; i < 8; i++) { if (tryAddMove(x, i) !== 0) { break; } } // Up for (i = y - 1; i >= 0; i--) { if (tryAddMove(x, i) !== 0) { break; } } // Right for (i = x + 1; i < 8; i++) { if (tryAddMove(i, y) !== 0) { break; } } // Left for (i = x - 1; i >= 0; i--) { if (tryAddMove(i, y) !== 0) { break; } } } else if (isBishop(piece)) { // Down-Right for (i = 1; i < 8; i++) { if (tryAddMove(x + i, y + i) !== 0) { break; } } // Up-Right for (i = 1; i < 8; i++) { if (tryAddMove(x + i, y - i) !== 0) { break; } } // Down-Left for (i = 1; i < 8; i++) { if (tryAddMove(x - i, y + i) !== 0) { break; } } // Up-Left for (i = 1; i < 8; i++) { if (tryAddMove(x - i, y - i) !== 0) { break; } } } else if (isQueen(piece)) { let [rook, bishop] = isBlack(piece) ? [new Piece(BLACK, ROOK), new Piece(BLACK, BISHOP)] : [new Piece(WHITE, ROOK), new Piece(WHITE, BISHOP)]; moves = moves.concat(this.getValidMovesAt(rook, x, y)); moves = moves.concat(this.getValidMovesAt(bishop, x, y)); } else if (isKnight(piece)) { let deltas = [ [2, 1], [2, -1], [-2, 1], [-2, -1], [1, 2], [1, -2], [-1, 2], [-1, -2], ]; deltas.forEach(delta => tryAddMove(x + delta[0], y + delta[1])); } else if (isKing(piece)) { let deltas = [ [1, 1], [1, -1], [-1, 1], [-1, -1], [0, 1], [0, -1], [1, 0], [-1, 0] ]; deltas.forEach(delta => tryAddMove(x + delta[0], y + delta[1])); } return moves; } findIndex(piece) { for(var i = 0; i < this.squareCount(); i++) { let check = this.state.squares[i]; if(check && check.type === piece.type && check.color === piece.color) { return i; } } return null; } distanceBetween(i1, i2) { let [pos1X, pos1Y] = this.getXandY(i1); let [pos2X, pos2Y] = this.getXandY(i2); var a = pos1X - pos2X; a = a * a; var b = pos1Y - pos2Y; b = b * b; return Math.sqrt(a + b); } inCheck(piece) { var i; var kingPos = this.getXandY(this.findIndex(piece)); for(i = 0; i < this.squareCount(); i++) { let [x, y] = this.getXandY(i); if(this.isEnemyOf(piece, x, y)) { let moves = this.getValidMoves(i); for(var j = 0; j < moves.length; j++) { if(moves[j].x === kingPos[0] && moves[j].y === kingPos[1]) { return piece; } } } } return null; } whoInCheck() { let blackKing = this.inCheck(new Piece(BLACK, KING)); return blackKing ? blackKing : this.inCheck(new Piece(WHITE, KING)); } squareCount() { return this.state.squares.length; } squareAt(i) { return i >= 0 && i < 64 ? this.state.squares[i] : null; } checkmate() { let checkedKing = this.whoInCheck(); if (checkedKing != null) { // For each square for(var i = 0; i < this.squareCount(); i++) { let piece = this.squareAt(i); // If that piece is on the checked team if (piece != null && isBlack(piece) === isBlack(checkedKing)) { // Copy the board var board = new Board(); board.state.squares = this.state.squares.slice(); // For each move of the above piece let moves = board.getValidMoves(i) for(var j = 0; j < moves.length; j++) { let [x, y] = [moves[j].x, moves[j].y]; board.state.squares[x + (y * 8)] = board.state.squares[i]; board.state.squares[i] = null; let check = board.inCheck(checkedKing); if (check == null || check.color !== checkedKing.color) { return false; } } } } return true; } return false; } getValidMoves(source) { let [x, y] = this.getXandY(source); let piece = this.state.squares[source]; return this.getValidMovesAt(piece, x, y); } isValidMove(source, dest) { let [destX, destY] = this.getXandY(dest); for (var move of this.getValidMoves(source)) { let x = move.x; let y = move.y; if (destX === x && destY === y) { return move; } } return null; } isHoldingPiece() { return this.heldPiece() != null; } heldPiece() { return this.state.hand.heldPiece; } makeMove(from, to) { const squares = this.state.squares.slice(); let move = this.isValidMove(from, to) if (move) { if (move.passant) { squares[move.passant.x + (move.passant.y * 8)] = null; } // Remove existing passantable states for(var square of squares) { if (square) { square.passantable = false; } } if (move.passantable) { squares[from].passantable = true; } let y = this.getXandY(to)[1]; squares[to] = squares[from]; squares[from] = null; if (squares[to].type === PAWN && (y === 0 || y === 7)) { squares[to].setType(QUEEN); } this.setState({ squares: squares, blackIsNext: !this.state.blackIsNext, hand: { heldPiece: null, } }); return 0; } return 1; } handleClick(i) { if (this.checkmate()) { return; } if (this.isHoldingPiece()) { // Copy the board var board = new Board(); board.state.squares = this.state.squares.slice(); board.state.squares[i] = board.state.squares[this.heldPiece()]; board.state.squares[this.heldPiece()] = null; let moversKing = this.state.blackIsNext ? new Piece(BLACK, KING) : new Piece(WHITE, KING); if (board.inCheck(moversKing) != null) { return; } if (this.makeMove(this.heldPiece(), i) !== 0) { this.setHand({ heldPiece: null, }); } } else if (this.state.squares[i] != null) { let isSquareBlack = isBlack(this.state.squares[i]); if(isSquareBlack === this.state.blackIsNext) { this.setHand({ heldPiece: i, }); } } } renderSquare(i) { let plainBg = (i + (Math.floor(i / 8))) % 2 === 0 ? "white" : "#666"; let bgColor = this.heldPiece() === i ? "#5D98E6" : plainBg; return ( this.handleClick(i)} bgColor={bgColor} /> ); } row(row) { let i = row * 8; return (
{this.renderSquare(i)} {this.renderSquare(i + 1)} {this.renderSquare(i + 2)} {this.renderSquare(i + 3)} {this.renderSquare(i + 4)} {this.renderSquare(i + 5)} {this.renderSquare(i + 6)} {this.renderSquare(i + 7)}
); } render() { let checkMsg = this.whoInCheck() ? "Check! " : ""; let isCheckmate = this.checkmate(); var namedPlayer = isCheckmate ? !this.state.blackIsNext : this.state.blackIsNext let color = namedPlayer ? 'Black' : 'White'; const status = isCheckmate ? "Checkmate! " + color + " Wins!" : checkMsg + color + "'s Turn"; var texttext =

{status}

{this.row(0)} {this.row(1)} {this.row(2)} {this.row(3)} {this.row(4)} {this.row(5)} {this.row(6)} {this.row(7)}
; return ( texttext ); } } class Game extends React.Component { render() { return (
{/* status */}
    {/* TODO */}
); } } // ======================================== ReactDOM.render( , document.getElementById('root') );