2020-12-27 20:11:50 -05:00
|
|
|
import React from 'react';
|
|
|
|
import ReactDOM from 'react-dom';
|
|
|
|
import './index.css';
|
|
|
|
|
2020-12-27 23:58:51 -05:00
|
|
|
const Pieces = {
|
2020-12-28 15:12:35 -05:00
|
|
|
WhitePawn: 0,
|
|
|
|
WhiteRook: 1,
|
|
|
|
WhiteKnight: 2,
|
|
|
|
WhiteBishop: 3,
|
|
|
|
WhiteQueen: 4,
|
|
|
|
WhiteKing: 5,
|
|
|
|
|
|
|
|
BlackPawn: 6,
|
|
|
|
BlackRook: 7,
|
|
|
|
BlackKnight: 8,
|
|
|
|
BlackBishop: 9,
|
|
|
|
BlackQueen: 10,
|
|
|
|
BlackKing: 11
|
2020-12-27 23:58:51 -05:00
|
|
|
};
|
|
|
|
|
2020-12-28 15:12:35 -05:00
|
|
|
const Images = [
|
2020-12-28 16:41:13 -05:00
|
|
|
'./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',
|
2020-12-28 15:12:35 -05:00
|
|
|
];
|
|
|
|
|
|
|
|
function isBlack(piece) {
|
|
|
|
return piece >= Pieces.BlackPawn;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isWhite(piece) {
|
|
|
|
return !isBlack(piece);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isPawn(piece) {
|
|
|
|
return piece === Pieces.WhitePawn || piece === Pieces.BlackPawn;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isRook(piece) {
|
|
|
|
return piece === Pieces.WhiteRook || piece === Pieces.BlackRook;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isBishop(piece) {
|
|
|
|
return piece === Pieces.WhiteBishop || piece === Pieces.BlackBishop;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isQueen(piece) {
|
|
|
|
return piece === Pieces.WhiteQueen || piece === Pieces.BlackQueen;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isKnight(piece) {
|
|
|
|
return piece === Pieces.WhiteKnight || piece === Pieces.BlackKnight;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isKing(piece) {
|
|
|
|
return piece === Pieces.WhiteKing || piece === Pieces.BlackKing;
|
|
|
|
}
|
|
|
|
|
2020-12-27 23:58:51 -05:00
|
|
|
function Square(props) {
|
2020-12-28 22:51:01 -05:00
|
|
|
return (
|
|
|
|
<button
|
|
|
|
className="square"
|
|
|
|
onClick={props.onClick}
|
|
|
|
style={{
|
|
|
|
backgroundImage: `url(${Images[props.value]})`,
|
|
|
|
backgroundSize: `100%`,
|
|
|
|
backgroundColor: props.bgColor,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
</button>
|
|
|
|
);
|
2020-12-27 20:11:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
class Board extends React.Component {
|
2020-12-27 23:58:51 -05:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
squares: this.reset(),
|
2020-12-28 15:12:35 -05:00
|
|
|
blackIsNext: true,
|
|
|
|
hand: {
|
|
|
|
heldPiece: null,
|
|
|
|
},
|
2020-12-27 23:58:51 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-12-28 15:12:35 -05:00
|
|
|
setHand(hand) {
|
|
|
|
this.setState({
|
|
|
|
squares: this.state.squares,
|
|
|
|
blackIsNext: this.state.blackIsNext,
|
|
|
|
hand: hand,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-12-27 23:58:51 -05:00
|
|
|
reset() {
|
|
|
|
let whiteRow = [
|
2020-12-28 15:12:35 -05:00
|
|
|
Pieces.WhiteRook,
|
|
|
|
Pieces.WhiteKnight,
|
|
|
|
Pieces.WhiteBishop,
|
|
|
|
Pieces.WhiteQueen,
|
|
|
|
Pieces.WhiteKing,
|
|
|
|
Pieces.WhiteBishop,
|
|
|
|
Pieces.WhiteKnight,
|
|
|
|
Pieces.WhiteRook
|
2020-12-27 23:58:51 -05:00
|
|
|
];
|
|
|
|
let blackRow = [
|
2020-12-28 15:12:35 -05:00
|
|
|
Pieces.BlackRook,
|
|
|
|
Pieces.BlackKnight,
|
|
|
|
Pieces.BlackBishop,
|
|
|
|
Pieces.BlackQueen,
|
|
|
|
Pieces.BlackKing,
|
|
|
|
Pieces.BlackBishop,
|
|
|
|
Pieces.BlackKnight,
|
|
|
|
Pieces.BlackRook
|
2020-12-27 23:58:51 -05:00
|
|
|
];
|
|
|
|
let squares = whiteRow.slice();
|
|
|
|
squares = squares.concat(Array(8).fill(Pieces.WhitePawn));
|
|
|
|
squares = squares.concat(Array(32).fill(null));
|
|
|
|
squares = squares.concat(Array(8).fill(Pieces.BlackPawn));
|
|
|
|
squares = squares.concat(blackRow.slice());
|
|
|
|
return squares;
|
|
|
|
}
|
|
|
|
|
2020-12-28 15:12:35 -05:00
|
|
|
getXandY(i) {
|
|
|
|
let x = i % 8;
|
|
|
|
let y = Math.floor(i / 8);
|
|
|
|
return [x, y];
|
|
|
|
}
|
|
|
|
|
2020-12-28 22:51:01 -05:00
|
|
|
pieceAt(x, y) {
|
2020-12-28 15:12:35 -05:00
|
|
|
let i = x + (y * 8);
|
|
|
|
if (i < 64 && x < 8 && y < 8) {
|
|
|
|
return this.state.squares[x + (y * 8)];
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
whiteAt(x, y) {
|
2020-12-28 22:51:01 -05:00
|
|
|
let square = this.pieceAt(x, y);
|
2020-12-28 15:12:35 -05:00
|
|
|
if (square == null) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-12-28 22:51:01 -05:00
|
|
|
return isWhite(square);
|
2020-12-28 15:12:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
blackAt(x, y) {
|
2020-12-28 22:51:01 -05:00
|
|
|
let square = this.pieceAt(x, y);
|
2020-12-28 15:12:35 -05:00
|
|
|
if (square == null) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-12-28 22:51:01 -05:00
|
|
|
return isBlack(square);
|
2020-12-28 15:12:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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) => {
|
2020-12-28 22:51:01 -05:00
|
|
|
if (this.pieceAt(x, y) == null) {
|
2020-12-28 15:12:35 -05:00
|
|
|
moves.push([x, y]);
|
|
|
|
// Keep searching
|
|
|
|
return 0;
|
|
|
|
} else if (this.isEnemyOf(piece, x, y)) {
|
|
|
|
moves.push([x, y]);
|
|
|
|
}
|
|
|
|
// Stop searching
|
|
|
|
return 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
if (isPawn(piece)) {
|
|
|
|
let pieceIsBlack = isBlack(piece);
|
|
|
|
let shift = pieceIsBlack ? -1 : 1;
|
|
|
|
let startLine = pieceIsBlack ? 6 : 1;
|
|
|
|
|
2020-12-28 22:51:01 -05:00
|
|
|
if (this.pieceAt(x, y + shift) == null) {
|
2020-12-28 18:01:05 -05:00
|
|
|
moves.push([x, y + shift]);
|
2020-12-28 22:51:01 -05:00
|
|
|
if (y === startLine && this.pieceAt(x, y + (shift * 2)) == null) {
|
2020-12-28 18:01:05 -05:00
|
|
|
moves.push([x, y + (shift * 2)]);
|
|
|
|
}
|
2020-12-28 15:12:35 -05:00
|
|
|
}
|
2020-12-28 18:01:05 -05:00
|
|
|
[x + 1, x - 1].forEach((x) => {
|
|
|
|
if (this.isEnemyOf(piece, x, y + shift)) {
|
|
|
|
moves.push([x, y + shift]);
|
|
|
|
}
|
|
|
|
});
|
2020-12-28 15:12:35 -05:00
|
|
|
} 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) ?
|
|
|
|
[Pieces.BlackRook, Pieces.BlackBishop] :
|
|
|
|
[Pieces.WhiteRook, Pieces.WhiteBishop];
|
|
|
|
moves = moves.concat(this.getValidMovesAt(rook, x, y));
|
|
|
|
moves = moves.concat(this.getValidMovesAt(bishop, x, y));
|
|
|
|
} else if (isKnight(piece)) {
|
|
|
|
tryAddMove(x + 2, y + 1);
|
|
|
|
tryAddMove(x + 2, y - 1);
|
|
|
|
tryAddMove(x - 2, y + 1);
|
|
|
|
tryAddMove(x - 2, y - 1);
|
|
|
|
tryAddMove(x + 1, y + 2);
|
|
|
|
tryAddMove(x + 1, y - 2);
|
|
|
|
tryAddMove(x - 1, y + 2);
|
|
|
|
tryAddMove(x - 1, y - 2);
|
|
|
|
} else if (isKing(piece)) {
|
|
|
|
tryAddMove(x + 1, y + 1);
|
|
|
|
tryAddMove(x + 1, y - 1);
|
|
|
|
tryAddMove(x - 1, y + 1);
|
|
|
|
tryAddMove(x - 1, y - 1);
|
|
|
|
|
|
|
|
tryAddMove(x, y + 1);
|
|
|
|
tryAddMove(x, y - 1);
|
|
|
|
tryAddMove(x + 1, y);
|
|
|
|
tryAddMove(x - 1, y);
|
|
|
|
}
|
|
|
|
return moves;
|
|
|
|
}
|
|
|
|
|
2020-12-29 08:25:17 -05:00
|
|
|
inCheck(piece) {
|
|
|
|
var kingPos;
|
|
|
|
for(var i = 0; i < this.squareCount(); i++) {
|
|
|
|
if(this.state.squares[i] === piece) {
|
|
|
|
kingPos = this.getXandY(i);
|
2020-12-28 19:21:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-29 08:25:17 -05:00
|
|
|
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][0] === kingPos[0] && moves[j][1] === kingPos[1]) {
|
|
|
|
return piece;
|
|
|
|
}
|
2020-12-28 19:21:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-28 22:51:01 -05:00
|
|
|
return null;
|
2020-12-28 19:21:13 -05:00
|
|
|
}
|
|
|
|
|
2020-12-29 08:25:17 -05:00
|
|
|
whoInCheck() {
|
|
|
|
let blackKing = this.inCheck(Pieces.BlackKing);
|
|
|
|
return blackKing ? blackKing : this.inCheck(Pieces.WhiteKing);
|
|
|
|
}
|
|
|
|
|
|
|
|
squareCount() {
|
|
|
|
return this.state.squares.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
squareAt(i) {
|
|
|
|
return i >= 0 && i < 64 ? this.state.squares[i] : null;
|
|
|
|
}
|
|
|
|
|
2020-12-28 19:21:13 -05:00
|
|
|
checkmate() {
|
2020-12-29 08:25:17 -05:00
|
|
|
let checkedKing = this.whoInCheck();
|
|
|
|
if (checkedKing != null) {
|
|
|
|
// For piece in board
|
|
|
|
// If piece is on the checked team,
|
|
|
|
// Copy the board
|
|
|
|
// Make one of the piece's moves on the board
|
|
|
|
// Return false if the king is no longer in check
|
|
|
|
|
|
|
|
// For each square
|
|
|
|
console.log("Square count: " + this.squareCount());
|
|
|
|
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)) {
|
|
|
|
console.log("Trying moves for " + Images[piece])
|
|
|
|
// 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 [from, to] = moves[j];
|
|
|
|
board.makeMove(from, to);
|
|
|
|
if (board.inCheck(checkedKing) !== checkedKing) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-12-28 19:21:13 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-28 15:12:35 -05:00
|
|
|
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);
|
|
|
|
|
|
|
|
let validMoves = this.getValidMoves(source);
|
|
|
|
if (validMoves == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var move of this.getValidMoves(source)) {
|
|
|
|
let [x, y] = move;
|
|
|
|
if (destX === x && destY === y) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
isHoldingPiece() {
|
|
|
|
return this.heldPiece() != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
heldPiece() {
|
|
|
|
return this.state.hand.heldPiece;
|
|
|
|
}
|
|
|
|
|
2020-12-29 08:25:17 -05:00
|
|
|
makeMove(from, to) {
|
2020-12-27 23:58:51 -05:00
|
|
|
const squares = this.state.squares.slice();
|
2020-12-29 08:25:17 -05:00
|
|
|
if (this.isValidMove(from, to)) {
|
|
|
|
squares[to] = squares[from];
|
|
|
|
squares[from] = null;
|
|
|
|
this.setHand({
|
|
|
|
heldPiece: null,
|
|
|
|
});
|
|
|
|
this.setState({
|
|
|
|
squares: squares,
|
|
|
|
blackIsNext: !this.state.blackIsNext,
|
|
|
|
});
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
handleClick(i) {
|
2020-12-28 15:12:35 -05:00
|
|
|
if (this.isHoldingPiece()) {
|
2020-12-29 08:25:17 -05:00
|
|
|
if (this.makeMove(this.heldPiece(), i) !== 0) {
|
2020-12-28 15:12:35 -05:00
|
|
|
this.setHand({
|
|
|
|
heldPiece: null,
|
|
|
|
});
|
|
|
|
}
|
2020-12-29 08:25:17 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (this.state.squares[i] != null) {
|
|
|
|
let isSquareBlack = isBlack(this.state.squares[i]);
|
2020-12-28 15:12:35 -05:00
|
|
|
if(isSquareBlack === this.state.blackIsNext) {
|
|
|
|
this.setHand({
|
|
|
|
heldPiece: i,
|
|
|
|
});
|
|
|
|
}
|
2020-12-27 23:58:51 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-27 20:11:50 -05:00
|
|
|
renderSquare(i) {
|
2020-12-28 17:51:09 -05:00
|
|
|
let plainBg = (i + (Math.floor(i / 8))) % 2 === 0 ?
|
|
|
|
"white" : "#666";
|
2020-12-28 16:54:23 -05:00
|
|
|
let bgColor = this.heldPiece() === i ?
|
2020-12-28 17:51:09 -05:00
|
|
|
"#5D98E6" : plainBg;
|
2020-12-27 23:58:51 -05:00
|
|
|
return (
|
|
|
|
<Square
|
|
|
|
value={this.state.squares[i]}
|
|
|
|
onClick={() => this.handleClick(i)}
|
2020-12-28 22:51:01 -05:00
|
|
|
bgColor={bgColor}
|
2020-12-27 23:58:51 -05:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
row(row) {
|
|
|
|
let i = row * 8;
|
|
|
|
return (
|
|
|
|
<div className="board-row">
|
|
|
|
{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)}
|
|
|
|
</div>
|
|
|
|
);
|
2020-12-27 20:11:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2020-12-29 08:25:17 -05:00
|
|
|
let checkMsg = this.whoInCheck() ? "Check! " : "";
|
|
|
|
const status = this.checkmate() ? "CHECKMATE" :
|
2020-12-28 19:21:13 -05:00
|
|
|
checkMsg + (this.state.blackIsNext ? 'Black' : 'White') + "'s Turn";
|
2020-12-27 20:11:50 -05:00
|
|
|
|
2020-12-27 23:58:51 -05:00
|
|
|
var texttext =
|
2020-12-28 16:41:13 -05:00
|
|
|
<div style={{textAlign: `center`,}}>
|
|
|
|
<div className="status"><h1>{status}</h1></div>
|
2020-12-27 23:58:51 -05:00
|
|
|
{this.row(0)}
|
|
|
|
{this.row(1)}
|
|
|
|
{this.row(2)}
|
|
|
|
{this.row(3)}
|
|
|
|
{this.row(4)}
|
|
|
|
{this.row(5)}
|
|
|
|
{this.row(6)}
|
|
|
|
{this.row(7)}
|
2020-12-27 20:11:50 -05:00
|
|
|
</div>
|
2020-12-27 23:58:51 -05:00
|
|
|
;
|
|
|
|
|
|
|
|
return (
|
|
|
|
texttext
|
2020-12-27 20:11:50 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Game extends React.Component {
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div className="game">
|
|
|
|
<div className="game-info">
|
|
|
|
<div>{/* status */}</div>
|
|
|
|
<ol>{/* TODO */}</ol>
|
|
|
|
</div>
|
2020-12-28 16:41:13 -05:00
|
|
|
<div className="game-board">
|
|
|
|
<Board />
|
|
|
|
</div>
|
2020-12-27 20:11:50 -05:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ========================================
|
|
|
|
|
|
|
|
ReactDOM.render(
|
|
|
|
<Game />,
|
|
|
|
document.getElementById('root')
|
|
|
|
);
|