Skip to content

Commit acb64c3

Browse files
committed
Rules: enforce pass-through safety for castling; fix knight check offsets; correct checkmate/stalemate endgame logic; UI: lowercase f in coord header
1 parent 596d05e commit acb64c3

File tree

4 files changed

+95
-78
lines changed

4 files changed

+95
-78
lines changed

.idea/gradle.xml

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/main/kotlin/dev/tabansi/chess/Board.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ object Board {
7171
row--
7272
println()
7373
}
74-
println("\t a\t b\t c\t d\t e\t F\t g\t h\n\n")
74+
println("\t a\t b\t c\t d\t e\t f\t g\t h\n\n")
7575
}
7676

7777
private fun printLegend(row: Int) {

src/main/kotlin/dev/tabansi/chess/Game.kt

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -192,20 +192,31 @@ class Game {
192192
println("\n\nCheck!!!")
193193
displayBoard()
194194
}
195-
println(
196-
if (turnTracker % 2 == 0)
197-
"\nPlayer Two has won!!!"
198-
else
199-
"\nPlayer One has won!!!"
200-
)
195+
if (gameOver()) {
196+
println(
197+
if (turnTracker % 2 == 0)
198+
"\nPlayer Two has won!!!"
199+
else
200+
"\nPlayer One has won!!!"
201+
)
202+
} else {
203+
println("\nStalemate. It's a draw.")
204+
}
201205
}
202206

203207
private fun gameOver(): Boolean {
204208
val currentPlayer = if (turnTracker % 2 == 0) player1 else player2
205-
return currentPlayer.getMoves().isEmpty()
209+
val noMoves = currentPlayer.getMoves().isEmpty()
210+
if (!noMoves) return false
211+
return currentPlayer.king.isInCheck()
206212
}
207213

208-
private fun stalemate(): Boolean = player1.getMoves().isEmpty() && player2.getMoves().isEmpty()
214+
private fun stalemate(): Boolean {
215+
val currentPlayer = if (turnTracker % 2 == 0) player1 else player2
216+
val noMoves = currentPlayer.getMoves().isEmpty()
217+
if (!noMoves) return false
218+
return !currentPlayer.king.isInCheck()
219+
}
209220

210221
private fun confirmInput(input: String): Boolean {
211222
if (input == "undo") return true
@@ -214,17 +225,7 @@ class Game {
214225
return true
215226
}
216227

217-
private fun labelToCoord(label: String): Pair<Int, Int> {
218-
val col = label[0].code - 'a'.code
219-
val row = 8 - label[1].toString().toInt()
220-
return Pair(row, col)
221-
}
222-
223-
private fun coordToLabel(x: Int, y: Int): String {
224-
val col = (y + 'a'.code).toChar()
225-
val row = 8 - x
226-
return "$col$row".uppercase()
227-
}
228+
// Coordinate helpers are defined once at top-level in dev.tabansi.chess.Main.kt
228229

229230
private fun promotePawn(x: Int, y: Int, player: Player) {
230231
val options = mapOf(

src/main/kotlin/dev/tabansi/chess/pieces/King.kt

Lines changed: 74 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -43,33 +43,49 @@ class King(currentPoint: BoardSpace, player: Player): Piece(currentPoint, player
4343
undoMove(EMPTY)
4444

4545
if (player.player == PLAYER_1) {
46-
if (!player.king.isInCheck() && y < 6 && isEmptySpace(x, y + 2) && currentPoint == BoardSpace(
47-
7,
48-
4
49-
) && !hasMoved && board[7][7] == ROOK + PLAYER_1
46+
if (
47+
!player.king.isInCheck() &&
48+
y < 6 &&
49+
isEmptySpace(x, y + 2) &&
50+
currentPoint == BoardSpace(7, 4) &&
51+
!hasMoved &&
52+
board[7][7] == ROOK + PLAYER_1
5053
) {
5154
val rook = player.getPiece(BoardSpace(7, 7)) as Rook
5255
if (!rook.hasMoved) {
53-
doMove(BoardSpace(7, 6))
54-
rook.doMove(BoardSpace(7, 5))
56+
// Verify pass-through square (7,5) is safe
57+
doMove(BoardSpace(7, 5))
58+
val pathSafe = !player.king.isInCheck()
59+
undoMove(EMPTY)
60+
if (pathSafe) {
61+
// Now verify final square (7,6)
62+
doMove(BoardSpace(7, 6))
63+
rook.doMove(BoardSpace(7, 5))
64+
if (!player.king.isInCheck()) availableMoves.add(BoardSpace(7, 6))
65+
undoMove(EMPTY)
66+
rook.undoMove(EMPTY)
67+
}
5568
}
56-
if (!player.king.isInCheck()) availableMoves.add(BoardSpace(7, 6))
57-
undoMove(EMPTY)
58-
rook.undoMove(EMPTY)
5969
}
6070
} else if (!player.king.isInCheck() && y < 6 && isEmptySpace(x, y + 2) && currentPoint == BoardSpace(
61-
0,
62-
4
63-
) && !hasMoved && board[0][7] == ROOK + PLAYER_2
71+
0,
72+
4
73+
) && !hasMoved && board[0][7] == ROOK + PLAYER_2
6474
) {
6575
val rook = player.getPiece(BoardSpace(0, 7)) as Rook
6676
if (!rook.hasMoved) {
67-
doMove(BoardSpace(0, 6))
68-
rook.doMove(BoardSpace(0, 5))
77+
// Verify pass-through square (0,5) is safe
78+
doMove(BoardSpace(0, 5))
79+
val pathSafe = !player.king.isInCheck()
80+
undoMove(EMPTY)
81+
if (pathSafe) {
82+
doMove(BoardSpace(0, 6))
83+
rook.doMove(BoardSpace(0, 5))
84+
if (!player.king.isInCheck()) availableMoves.add(BoardSpace(0, 6))
85+
undoMove(EMPTY)
86+
rook.undoMove(EMPTY)
87+
}
6988
}
70-
if (!player.king.isInCheck()) availableMoves.add(BoardSpace(0, 6))
71-
undoMove(EMPTY)
72-
rook.undoMove(EMPTY)
7389
}
7490
}
7591

@@ -78,35 +94,50 @@ class King(currentPoint: BoardSpace, player: Player): Piece(currentPoint, player
7894
if (!player.king.isInCheck()) availableMoves.add(BoardSpace(x, y - 1))
7995
undoMove(EMPTY)
8096
if (player.player == PLAYER_1) {
81-
if (!player.king.isInCheck() && y > 1 && isEmptySpace(x, y - 2) && currentPoint == BoardSpace(
82-
7,
83-
4
84-
) && !hasMoved && board[7][0] == ROOK + PLAYER_1
97+
if (
98+
!player.king.isInCheck() &&
99+
y > 1 &&
100+
isEmptySpace(x, y - 2) &&
101+
currentPoint == BoardSpace(7, 4) &&
102+
!hasMoved &&
103+
board[7][0] == ROOK + PLAYER_1
85104
) {
86105
val rook = player.getPiece(BoardSpace(7, 0)) as Rook
87106
if (!rook.hasMoved) {
88-
doMove(BoardSpace(7, 2))
89-
rook.doMove(BoardSpace(7, 3))
107+
// Verify pass-through square (7,3) is safe
108+
doMove(BoardSpace(7, 3))
109+
val pathSafe = !player.king.isInCheck()
110+
undoMove(EMPTY)
111+
if (pathSafe) {
112+
doMove(BoardSpace(7, 2))
113+
rook.doMove(BoardSpace(7, 3))
114+
if (!player.king.isInCheck()) availableMoves.add(BoardSpace(7, 2))
115+
undoMove(EMPTY)
116+
rook.undoMove(EMPTY)
117+
}
90118
}
91-
if (!player.king.isInCheck()) availableMoves.add(BoardSpace(7, 2))
92-
undoMove(EMPTY)
93-
rook.undoMove(EMPTY)
94119
}
95120
} else if (!player.king.isInCheck() && y > 1 && isEmptySpace(x, y - 2) && currentPoint == BoardSpace(
96-
0,
97-
4
98-
) && !hasMoved && board[0][0] == ROOK + PLAYER_2
121+
0,
122+
4
123+
) && !hasMoved && board[0][0] == ROOK + PLAYER_2
99124
) {
100-
val rook = player.getPiece(BoardSpace(0, 0)) as Rook
101-
if (!rook.hasMoved) {
125+
val rook = player.getPiece(BoardSpace(0, 0)) as Rook
126+
if (!rook.hasMoved) {
127+
// Verify pass-through square (0,3) is safe
128+
doMove(BoardSpace(0, 3))
129+
val pathSafe = !player.king.isInCheck()
130+
undoMove(EMPTY)
131+
if (pathSafe) {
102132
doMove(BoardSpace(0, 2))
103133
rook.doMove(BoardSpace(0, 3))
134+
if (!player.king.isInCheck()) availableMoves.add(BoardSpace(0, 2))
135+
undoMove(EMPTY)
136+
rook.undoMove(EMPTY)
104137
}
105-
if (!player.king.isInCheck()) availableMoves.add(BoardSpace(0, 2))
106-
undoMove(EMPTY)
107-
rook.undoMove(EMPTY)
108138
}
109139
}
140+
}
110141

111142
if (x < 7 && y < 7 && isEmptySpace(x + 1, y + 1)) {
112143
doMove(BoardSpace(x + 1, y + 1))
@@ -312,22 +343,22 @@ class King(currentPoint: BoardSpace, player: Player): Piece(currentPoint, player
312343
if (y > 0 && player.player != board[x][y - 1].takeLast(1) && board[x][y - 1].startsWith(KING))
313344
return true
314345

315-
//Checks if knight is in the way of the king.
316-
if (x < 7 && y < 6 && player.player != board[x + 1][y + 2].takeLast(1) && board[x + 1][y + 2].startsWith(KNIGHT))
346+
//Checks if a knight is attacking the king (8 possible L-shapes)
347+
if (x <= 6 && y <= 5 && player.player != board[x + 1][y + 2].takeLast(1) && board[x + 1][y + 2].startsWith(KNIGHT))
317348
return true
318-
if (x > 0 && y < 6 && player.player != board[x - 1][y + 2].takeLast(1) && board[x - 1][y + 2].startsWith(KNIGHT))
349+
if (x >= 1 && y <= 5 && player.player != board[x - 1][y + 2].takeLast(1) && board[x - 1][y + 2].startsWith(KNIGHT))
319350
return true
320-
if (x < 6 && y > 0 && player.player != board[x + 1][y - 1].takeLast(1) && board[x + 1][y - 1].startsWith(KNIGHT))
351+
if (x <= 5 && y >= 1 && player.player != board[x + 2][y - 1].takeLast(1) && board[x + 2][y - 1].startsWith(KNIGHT))
321352
return true
322-
if (x < 6 && y < 7 && player.player != board[x + 2][y + 1].takeLast(1) && board[x + 2][y + 1].startsWith(KNIGHT))
353+
if (x <= 5 && y <= 6 && player.player != board[x + 2][y + 1].takeLast(1) && board[x + 2][y + 1].startsWith(KNIGHT))
323354
return true
324-
if (x < 7 && y > 1 && player.player != board[x + 1][y - 2].takeLast(1) && board[x + 1][y - 2].startsWith(KNIGHT))
355+
if (x <= 6 && y >= 2 && player.player != board[x + 1][y - 2].takeLast(1) && board[x + 1][y - 2].startsWith(KNIGHT))
325356
return true
326-
if (x > 0 && y > 1 && player.player != board[x - 1][y - 2].takeLast(1) && board[x - 1][y - 2].startsWith(KNIGHT))
357+
if (x >= 1 && y >= 2 && player.player != board[x - 1][y - 2].takeLast(1) && board[x - 1][y - 2].startsWith(KNIGHT))
327358
return true
328-
if (x > 1 && y > 0 && player.player != board[x - 2][y - 1].takeLast(1) && board[x - 2][y - 1].startsWith(KNIGHT))
359+
if (x >= 2 && y >= 1 && player.player != board[x - 2][y - 1].takeLast(1) && board[x - 2][y - 1].startsWith(KNIGHT))
329360
return true
330-
if (x > 1 && y < 7 && player.player != board[x - 2][y + 1].takeLast(1) && board[x - 2][y + 1].startsWith(KNIGHT))
361+
if (x >= 2 && y <= 6 && player.player != board[x - 2][y + 1].takeLast(1) && board[x - 2][y + 1].startsWith(KNIGHT))
331362
return true
332363

333364
//Checks if pawn is in the way of the king.

0 commit comments

Comments
 (0)