Skip to content

Commit bfb446d

Browse files
authored
Merge pull request #10 from mgierschdev/codex/add-king-check-and-checkmate-functionality
Add game state evaluation and expose to frontend
2 parents 0bc875e + 189c618 commit bfb446d

14 files changed

Lines changed: 5010 additions & 428 deletions

File tree

backend/src/main/java/com/backend/ApplicationStart.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static void main(String[] args) {
1919

2020
public static void startConsoleGame(){
2121
ChessGame chessGame = new ChessGame();
22-
while(chessGame.gameState != GameState.Checkmate){
22+
while(chessGame.getGameState() != GameState.Checkmate){
2323
Scanner in = new Scanner(System.in);
2424
System.out.println("Move... turn: " + chessGame.getTurn());
2525
chessGame.printBoard();

backend/src/main/java/com/backend/controllers/ChessController.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.backend.models.ChessPieceType;
77
import com.backend.models.Color;
88
import com.backend.models.Position;
9+
import com.backend.models.GameState;
910
import com.backend.models.requests.*;
1011
import com.backend.util.Log;
1112
import org.springframework.web.bind.annotation.*;
@@ -95,10 +96,12 @@ private void SetChessResponse(){
9596
chessGameResponse.gameStarted = false;
9697
chessGameResponse.turn = Color.None;
9798
chessGameResponse.chessboard = Chessboard.GetArrayBoard(Chessboard.GetInitMatrixBoard());
99+
chessGameResponse.gameState = GameState.Free;
98100
return;
99101
}
100102
chessGameResponse.id = requestCount.incrementAndGet();
101103
chessGameResponse.turn = chessGame.getTurn();
104+
chessGameResponse.gameState = chessGame.getGameState();
102105
chessGameResponse.chessboard = chessGame.getChessboard();
103106
chessGameResponse.capturedBlack = chessGame.getCaptured(Color.Black);
104107
chessGameResponse.capturedWhite = chessGame.getCaptured(Color.White);

backend/src/main/java/com/backend/domain/ChessGame.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.util.Set;
99

1010
public class ChessGame {
11-
public final GameState gameState;
11+
private GameState gameState;
1212

1313
private final Chessboard chessboard;
1414

@@ -68,12 +68,17 @@ public ChessPiece MoveController(Position a, Position b, ChessPieceType promotio
6868
}
6969
}
7070

71-
if (turn == Color.White) {
72-
turn = Color.Black;
71+
Color nextTurn = turn == Color.White ? Color.Black : Color.White;
72+
if (chessboard.isCheckmate(nextTurn)) {
73+
gameState = GameState.Checkmate;
74+
} else if (chessboard.isKingInCheck(nextTurn)) {
75+
gameState = GameState.Check;
7376
} else {
74-
turn = Color.White;
77+
gameState = GameState.Free;
7578
}
7679

80+
turn = nextTurn;
81+
7782
return chessPiece;
7883
}
7984

@@ -97,6 +102,10 @@ public Color getTurn() {
97102
return turn;
98103
}
99104

105+
public GameState getGameState(){
106+
return gameState;
107+
}
108+
100109
public Set<ChessPiece> getCaptured(Color color) {
101110
if (color == Color.White) {
102111
return takenWhite;

backend/src/main/java/com/backend/domain/Chessboard.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,77 @@ public Position getEnPassantTarget() {
138138
return enPassantTarget;
139139
}
140140

141+
public boolean isKingInCheck(Color color){
142+
Position kingPos = findKing(color);
143+
if(kingPos == null){
144+
return false;
145+
}
146+
Color opponent = getOpposite(color);
147+
for(int r = 0; r < board.length; r++){
148+
for(int c = 0; c < board[r].length; c++){
149+
ChessPiece piece = board[r][c];
150+
if(piece.color() == opponent){
151+
Position[] moves = getValidMoves(new Position(r,c));
152+
for(Position pos : moves){
153+
if(pos.row == kingPos.row && pos.col == kingPos.col){
154+
return true;
155+
}
156+
}
157+
}
158+
}
159+
}
160+
return false;
161+
}
162+
163+
public boolean isCheckmate(Color color){
164+
if(!isKingInCheck(color)){
165+
return false;
166+
}
167+
for(int r = 0; r < board.length; r++){
168+
for(int c = 0; c < board[r].length; c++){
169+
if(board[r][c].color() == color){
170+
Position from = new Position(r,c);
171+
Position[] moves = getValidMoves(from);
172+
for(Position to : moves){
173+
ChessPiece captured = simulateMove(from, to);
174+
boolean stillCheck = isKingInCheck(color);
175+
undoMove(from, to, captured);
176+
if(!stillCheck){
177+
return false;
178+
}
179+
}
180+
}
181+
}
182+
}
183+
return true;
184+
}
185+
186+
private ChessPiece simulateMove(Position from, Position to){
187+
ChessPiece moving = board[from.row][from.col];
188+
ChessPiece captured = board[to.row][to.col];
189+
board[to.row][to.col] = moving;
190+
board[from.row][from.col] = emptySpace;
191+
return captured;
192+
}
193+
194+
private void undoMove(Position from, Position to, ChessPiece captured){
195+
ChessPiece moving = board[to.row][to.col];
196+
board[from.row][from.col] = moving;
197+
board[to.row][to.col] = captured;
198+
}
199+
200+
private Position findKing(Color color){
201+
for(int r = 0; r < board.length; r++){
202+
for(int c = 0; c < board[r].length; c++){
203+
ChessPiece piece = board[r][c];
204+
if(piece.type() == ChessPieceType.King && piece.color() == color){
205+
return new Position(r,c);
206+
}
207+
}
208+
}
209+
return null;
210+
}
211+
141212
private boolean isValidPromotionType(ChessPieceType type) {
142213
return type == ChessPieceType.Queen ||
143214
type == ChessPieceType.Rock ||

backend/src/main/java/com/backend/models/requests/ChessGameResponse.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.backend.models.ChessPiece;
44
import com.backend.models.Color;
5+
import com.backend.models.GameState;
56

67
import java.util.Set;
78

@@ -13,4 +14,5 @@ public class ChessGameResponse {
1314
public Set<ChessPiece> capturedBlack;
1415
public ChessPieceResponse[] chessboard;
1516
public Color turn;
17+
public GameState gameState;
1618
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.backend.domain;
2+
3+
import com.backend.models.GameState;
4+
import com.backend.models.Position;
5+
import org.junit.jupiter.api.Test;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
9+
public class GameStateTest {
10+
11+
@Test
12+
public void testCheckScenario() {
13+
ChessGame game = new ChessGame();
14+
15+
// 1. f3 e5 2. e4 Qh4+
16+
game.MoveController(new Position(2, 6), new Position(3, 6)); // f2 -> f3
17+
game.MoveController(new Position(7, 5), new Position(5, 5)); // e7 -> e5
18+
game.MoveController(new Position(2, 5), new Position(4, 5)); // e2 -> e4
19+
game.MoveController(new Position(8, 4), new Position(4, 8)); // Qd8 -> h4
20+
21+
assertEquals(GameState.Check, game.getGameState());
22+
}
23+
24+
@Test
25+
public void testCheckmateScenario() {
26+
ChessGame game = new ChessGame();
27+
28+
// 1. f3 e5 2. g4 Qh4#
29+
game.MoveController(new Position(2, 6), new Position(3, 6)); // f2 -> f3
30+
game.MoveController(new Position(7, 5), new Position(5, 5)); // e7 -> e5
31+
game.MoveController(new Position(2, 7), new Position(4, 7)); // g2 -> g4
32+
game.MoveController(new Position(8, 4), new Position(4, 8)); // Qd8 -> h4
33+
34+
assertEquals(GameState.Checkmate, game.getGameState());
35+
}
36+
}
37+

frontend/jest.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const nextJest = require('next/jest');
2+
3+
const createJestConfig = nextJest({
4+
dir: './',
5+
});
6+
7+
const customJestConfig = {
8+
moduleDirectories: ['node_modules', '<rootDir>/'],
9+
testEnvironment: 'jsdom',
10+
};
11+
12+
module.exports = createJestConfig(customJestConfig);
13+

0 commit comments

Comments
 (0)