EMPTY is now its own "type" and "color" of piece
This allows many functions to be moved into the Piece class, since there doesn't need to be the same degree of null-checking. Pieces can be more directly compared to each other, as well, like with the new piece.isEnemyOf(piece) function
This commit is contained in:
parent
5ffdf0a7a1
commit
6cad21b79f
190
src/index.js
190
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,
|
||||
|
|
Loading…
Reference in New Issue