Skip to content

Commit

Permalink
[#27] check (#59)
Browse files Browse the repository at this point in the history
* [#27] check

* check - tests

* review fixes
  • Loading branch information
asiaziola authored Feb 14, 2021
1 parent 80acb7a commit e78c63a
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/controllers/GameController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class GameController {

if (isLegalMove) {
this.playSound(this.activeSquare, square);
this.gameEngine.movePiece(this.activeSquare, square);
this.gameEngine.movePiece(this.activeSquare, square, false);
this.boardView.render(this.gameEngine.board);
this.changePlayer();
}
Expand Down
14 changes: 11 additions & 3 deletions src/services/game-logic/Board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { Queen } from './pieces/Queen';
class Board {
public static BOARD_SIZE = 8;

private state: Array<Array<Piece | null>>;
private movesHistory: [Piece, Square][] = [];
public state: Array<Array<Piece | null>>;

constructor() {
this.state = new Array(Board.BOARD_SIZE);
Expand All @@ -27,9 +27,17 @@ class Board {
return this.state[square.row][square.column];
}

public movePiece(location: Square, destination: Square): void {
public checkAllSquares = (callback: any) => {
this.state.forEach((row) => {
row.forEach((square) => {
callback(square);
});
});
};

public movePiece(location: Square, destination: Square, potentialMove: boolean): void {
const piece = this.getPiece(location);
if (piece) this.movesHistory.push([piece, destination]);
if (!potentialMove && piece) this.movesHistory.push([piece, destination]);

this.state[location.row][location.column]?.move(destination);
this.state[destination.row][destination.column] = this.state[location.row][location.column];
Expand Down
70 changes: 58 additions & 12 deletions src/services/game-logic/GameEngine.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PieceNames, Constants } from '../../enums';
import { PieceNames, Constants, Colors } from '../../enums';
import { Square } from '../../models/Square';
import { Board } from './Board';
import { King } from './pieces/King';
import { Piece } from './pieces/Piece';

class GameEngine {
Expand All @@ -12,13 +13,61 @@ class GameEngine {

getLegalMoves = (square: Square): Square[] => {
const piece = this.board.getPiece(square);
let legalMoves = piece?.getPossibleMoves(this.board).filter(this.isOnBoard) ?? [];
let legalMoves = piece?.getPossibleMoves(this.board) ?? [];
if (piece?.name === PieceNames.KING) {
legalMoves = legalMoves.filter(
(move) => !this.isCastling(move, piece) || this.isCastlingLegal(move, piece)
);
}
return legalMoves.filter((destination) => !this.isOccupiedBySameColor(destination, piece));
return legalMoves
.filter((destination) => !this.isOccupiedBySameColor(destination, piece))
.filter((move) => this.moveNotResultWithCheck(move, piece, square));
};

moveNotResultWithCheck(move: Square, piece: Piece | null, square: Square): boolean {
const potentialMove = new Square(move.row, move.column);
const potentialPiece = this.board.getPiece(potentialMove);

this.movePiece(square, potentialMove, true);
piece!.hasMoved = true;
const isMovePossible = !this.isCheck(piece);

this.movePiece(potentialMove, square, true);
piece!.hasMoved = false;

if (potentialPiece)
this.board.state[potentialPiece.position.row][potentialPiece.position.column] = potentialPiece;

return isMovePossible;
}

private isCheck(piece: Piece | null): boolean {
const color = piece?.color === Colors.BLACK ? Colors.WHITE : Colors.BLACK;
let currentPlayerKing = this.getKingForCheck(piece)?.position;
const isChecked = this.isKingUnderCheck(color, currentPlayerKing);
return isChecked;
}

isKingUnderCheck(color: Colors, piecePosition: Square | undefined): boolean {
let checked = false;
this.board.checkAllSquares((square: Piece) => {
if (square && square.color === color) {
checked = square
.getPossibleMoves(this.board)
.some((move) => move.row === piecePosition?.row && move.column === piecePosition.column);
}
});
return checked;
}

getKingForCheck = (piece: Piece | null): King | null => {
let king = null;
this.board.checkAllSquares((square: Piece) => {
if (square && square?.color === piece?.color && square.name === PieceNames.KING) {
king = square;
}
});
return king;
};

public runSpecialRoutines(location: Square, destination: Square): void {
Expand All @@ -31,11 +80,11 @@ class GameEngine {
}
}

public movePiece(location: Square, destination: Square): void {
public movePiece(location: Square, destination: Square, potentialMove: boolean): void {
const piece = this.board.getPiece(location);
if (piece) {
this.runSpecialRoutines(piece?.position, destination);
this.board.movePiece(location, destination);
this.board.movePiece(location, destination, potentialMove);
}
}

Expand All @@ -52,12 +101,14 @@ class GameEngine {
if (destination.column - location.column === Constants.KINGSIDE_CASTLING) {
this.board.movePiece(
{ row: location.row, column: Constants.KINGSIDE_ROOK_COLUMN },
{ row: location.row, column: Constants.KINGSIDE_ROOK_DESTINATION_COLUMN }
{ row: location.row, column: Constants.KINGSIDE_ROOK_DESTINATION_COLUMN },
false
);
} else if (destination.column - location.column === Constants.QUEENSIDE_CASTLING) {
this.board.movePiece(
{ row: location.row, column: Constants.QUEENSIDE_ROOK_COLUMN },
{ row: location.row, column: Constants.QUEENSIDE_ROOK_DESTINATION_COLUMN }
{ row: location.row, column: Constants.QUEENSIDE_ROOK_DESTINATION_COLUMN },
false
);
}
}
Expand Down Expand Up @@ -87,11 +138,6 @@ class GameEngine {
return this.getLegalMoves(from).some(({ row, column }) => row === to.row && column === to.column);
}; // might be useful for 'check'

private isOnBoard = (square: Square): boolean => {
// here or in knight.ts (for now knights can get outside the board)
return square.row >= 0 && square.row < 8 && square.column >= 0 && square.column < 8;
};

private isOccupiedBySameColor = (square: Square, piece: Piece | null): boolean =>
this.board.getPiece(square)?.color === piece?.color;
}
Expand Down
7 changes: 5 additions & 2 deletions src/services/game-logic/pieces/King.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ class King extends Piece {
[1, -1]
];
if (!this.hasMoved) positions = [...positions, [0, 2], [0, -2]];
const [row, column] = positions;

const possibleMoves = positions.map(([row, column]) => ({
row: this.position.row + row,
column: this.position.column + column
}));
return possibleMoves;

const kingPossibleMovesOnBoard = possibleMoves.filter(
(move) => move.row >= 0 && move.row < 8 && move.column >= 0 && move.column < 8
);
return kingPossibleMovesOnBoard;
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/services/game-logic/pieces/Knight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class Knight extends Piece {
destination.push(new Square(row + possibleRowMoves[i], column + possibleColumnMoves[i]));
}

return destination;
const knightPossibleMoves = destination.filter((d) => d.row >= 0 && d.row < 8 && d.column >= 0 && d.column < 8);
return knightPossibleMoves;
}
}

Expand Down
11 changes: 11 additions & 0 deletions test/services/game-logic/Board.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,14 @@ describe('Testing Board.setup()', () => {
});
});
});

test(`should run function on all squares`, () => {
let arr = [];
const board = new Board();

board.checkAllSquares((square) => {
arr.push(square);
});

expect(arr.length).toBe(32);
});
36 changes: 36 additions & 0 deletions test/services/game-logic/GameEngine.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Square } from '../../../src/models/Square';
import { GameEngine } from '../../../src/services/game-logic/GameEngine';
import { Colors } from '../../../src/enums';
import { Board } from '../../../src/services/game-logic/Board';
import { King } from '../../../src/services/game-logic/pieces/King';

const gameEngine = new GameEngine();

describe('Testing GameEngine.getLegalMoves() while playing Pawn', () => {
test(`Pawn shall return 2 capturing moves `, () => {
Expand All @@ -16,6 +21,7 @@ describe('Testing GameEngine.getLegalMoves() while playing Pawn', () => {
// Test
expect(legalMoves).toEqual(expected);
});

test(`Extra En Passat move while playing white`, () => {
// Setup
const gameEngine = new GameEngine();
Expand All @@ -32,6 +38,7 @@ describe('Testing GameEngine.getLegalMoves() while playing Pawn', () => {
// Test
expect(legalMoves).toEqual(expected);
});

test(`White En Passat removes Black Pawn`, () => {
// Setup
const gameEngine = new GameEngine();
Expand All @@ -49,3 +56,32 @@ describe('Testing GameEngine.getLegalMoves() while playing Pawn', () => {
expect(removedPiece).toBe(null);
});
});

test(`should target king`, () => {
const piece = gameEngine.board.getPiece(new Square(1, 4));
const king = gameEngine.getKingForCheck(piece);
const expected = new Square(0, 4);

expect(king instanceof King).toBe(true);
expect(king.position).toStrictEqual(expected);
});

test(`should return true when check`, () => {
const color = Colors.BLACK;
const piece = new King({ column: 7, row: 3 }, Colors.WHITE);

const isKingChecked = gameEngine.isKingUnderCheck(color, piece.position);

expect(isKingChecked).toBe(true);
});

test(`should verify if oponent move result with check`, () => {
const board = new Board();
const square = { row: 6, column: 3 };
const move = new Square(4, 3);
const piece = board.getPiece(square);

const notCheck = gameEngine.moveNotResultWithCheck(move, piece, square);

expect(notCheck).toBe(true);
});
27 changes: 2 additions & 25 deletions test/services/game-logic/pieces/Knight.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,12 @@ import { Square } from '../../../../src/models/Square';
import { Knight } from '../../../../src/services/game-logic/pieces/Knight';

test.each([
[
Colors.WHITE,
7,
5,
[
new Square(9, 6),
new Square(8, 7),
new Square(6, 7),
new Square(5, 6),
new Square(5, 4),
new Square(6, 3),
new Square(8, 3),
new Square(9, 4)
]
],
[Colors.WHITE, 7, 5, [new Square(6, 7), new Square(5, 6), new Square(5, 4), new Square(6, 3)]],
[
Colors.BLACK,
3,
6,
[
new Square(5, 7),
new Square(4, 8),
new Square(2, 8),
new Square(1, 7),
new Square(1, 5),
new Square(2, 4),
new Square(4, 4),
new Square(5, 5)
]
[new Square(5, 7), new Square(1, 7), new Square(1, 5), new Square(2, 4), new Square(4, 4), new Square(5, 5)]
]
])(
'should return knights possible moves',
Expand Down

1 comment on commit e78c63a

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 Netlify deployed suspicious-johnson-b1e3a9 as production

https://suspicious-johnson-b1e3a9.netlify.app

Please sign in to comment.