QuickChess/src/index.js

529 lines
12 KiB
JavaScript
Raw Normal View History

2020-12-27 20:11:50 -05:00
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
2020-12-29 16:38:52 -05:00
const BLACK = 0;
const WHITE = 1;
const PAWN = 0;
const ROOK = 1;
const KNIGHT = 2;
const BISHOP = 3;
const QUEEN = 4;
const KING = 5;
2020-12-27 23:58:51 -05:00
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) {
2020-12-29 16:38:52 -05:00
return piece != null && piece.color === BLACK;
}
function isWhite(piece) {
2020-12-29 16:38:52 -05:00
return piece != null && piece.color === WHITE;
}
function isPawn(piece) {
2020-12-29 16:38:52 -05:00
return piece != null && piece.type === PAWN;
}
function isRook(piece) {
2020-12-29 16:38:52 -05:00
return piece != null && piece.type === ROOK;
}
function isBishop(piece) {
2020-12-29 16:38:52 -05:00
return piece != null && piece.type === BISHOP;
}
function isQueen(piece) {
2020-12-29 16:38:52 -05:00
return piece != null && piece.type === QUEEN;
}
function isKnight(piece) {
2020-12-29 16:38:52 -05:00
return piece != null && piece.type === KNIGHT;
}
function isKing(piece) {
2020-12-29 16:38:52 -05:00
return piece != null && piece.type === KING;
}
function imageFromPiece(piece) {
if (piece) {
let image = piece.color === WHITE ? piece.type : piece.type + 6;
return Images[image];
}
return null;
}
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={{
2020-12-29 16:38:52 -05:00
backgroundImage: `url(${imageFromPiece(props.piece)})`,
2020-12-28 22:51:01 -05:00
backgroundSize: `100%`,
backgroundColor: props.bgColor,
}}
>
</button>
);
2020-12-27 20:11:50 -05:00
}
2020-12-29 16:38:52 -05:00
class Piece {
constructor(color, type) {
this.color = color;
this.type = type;
}
}
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(),
blackIsNext: true,
hand: {
heldPiece: null,
},
2020-12-27 23:58:51 -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() {
2020-12-29 16:38:52 -05:00
console.log("RESET");
2020-12-27 23:58:51 -05:00
let whiteRow = [
2020-12-29 16:38:52 -05:00
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),
2020-12-27 23:58:51 -05:00
];
let blackRow = [
2020-12-29 16:38:52 -05:00
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)
2020-12-27 23:58:51 -05:00
];
2020-12-29 16:38:52 -05:00
let squares = whiteRow;
squares = squares.concat(Array(8).fill(new Piece(WHITE, PAWN)));
2020-12-27 23:58:51 -05:00
squares = squares.concat(Array(32).fill(null));
2020-12-29 16:38:52 -05:00
squares = squares.concat(Array(8).fill(new Piece(BLACK, PAWN)));
squares = squares.concat(blackRow);
2020-12-27 23:58:51 -05:00
return squares;
}
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) {
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);
if (square == null) {
return false;
}
2020-12-28 22:51:01 -05:00
return isWhite(square);
}
blackAt(x, y) {
2020-12-28 22:51:01 -05:00
let square = this.pieceAt(x, y);
if (square == null) {
return false;
}
2020-12-28 22:51:01 -05:00
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) => {
2020-12-28 22:51:01 -05:00
if (this.pieceAt(x, y) == null) {
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-29 16:38:52 -05:00
[x + 1, x - 1].forEach(x => {
2020-12-28 18:01:05 -05:00
if (this.isEnemyOf(piece, x, y + shift)) {
moves.push([x, 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) ?
2020-12-29 16:38:52 -05:00
[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],
];
2020-12-29 16:38:52 -05:00
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]
];
2020-12-29 16:38:52 -05:00
deltas.forEach(delta => tryAddMove(x + delta[0], y + delta[1]));
}
return moves;
}
2020-12-29 16:38:52 -05:00
findIndex(piece) {
console.log("founding");
2020-12-29 08:25:17 -05:00
for(var i = 0; i < this.squareCount(); i++) {
2020-12-29 16:38:52 -05:00
let check = this.state.squares[i];
if(check && check.type === piece.type && check.color === piece.color) {
console.log("found");
return i;
2020-12-28 19:21:13 -05:00
}
}
2020-12-29 16:38:52 -05:00
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));
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() {
2020-12-29 16:38:52 -05:00
let blackKing = this.inCheck(new Piece(BLACK, KING));
return blackKing ? blackKing : this.inCheck(new Piece(WHITE, KING));
2020-12-29 08:25:17 -05:00
}
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.state.squares[to] = board.state.squares[from];
board.state.squares[from] = null;
2020-12-29 08:25:17 -05:00
if (board.inCheck(checkedKing) !== checkedKing) {
return false;
}
}
}
}
return true;
}
2020-12-28 19:21:13 -05:00
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);
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.setState({
squares: squares,
blackIsNext: !this.state.blackIsNext,
hand: {
heldPiece: null,
}
2020-12-29 08:25:17 -05:00
});
return 0;
}
return 1;
}
handleClick(i) {
if (this.checkmate()) {
return;
}
if (this.isHoldingPiece()) {
2020-12-29 08:25:17 -05:00
if (this.makeMove(this.heldPiece(), i) !== 0) {
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]);
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";
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
2020-12-29 16:38:52 -05:00
piece={this.state.squares[i]}
2020-12-27 23:58:51 -05:00
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! " : "";
let isCheckmate = this.checkmate();
var namedPlayer = isCheckmate ?
!this.state.blackIsNext : this.state.blackIsNext
let color = namedPlayer ? 'Black' : 'White';
const status = this.checkmate() ? "Checkmate! " + color + " Wins!" :
checkMsg + color + "'s Turn";
2020-12-27 20:11:50 -05:00
2020-12-27 23:58:51 -05:00
var texttext =
<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>
<div className="game-board">
<Board />
</div>
2020-12-27 20:11:50 -05:00
</div>
);
}
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);