Skip to content

Commit 762c799

Browse files
author
TheDevConnor
committed
Added in checking logic!
1 parent a487011 commit 762c799

4 files changed

Lines changed: 162 additions & 103 deletions

File tree

tests/chess_engine/board.lx

Lines changed: 156 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
@module "board"
22

33
@use "string" as string
4-
@use "helper" as helper
54
@use "piece" as piece
65

6+
pub const Move -> struct {
7+
from_col: int,
8+
from_row: int,
9+
to_col: int,
10+
to_row: int
11+
};
12+
13+
pub const Board -> struct {
14+
starting_fen: *char,
15+
length_fen: int,
16+
squares: *char,
17+
18+
white_can_castle_kingside: int,
19+
white_can_castle_queenside: int,
20+
black_can_castle_kingside: int,
21+
black_can_castle_queenside: int,
22+
};
23+
724
#returns_ownership
825
pub const init_board -> fn (bd: *Board) *Board {
926
bd.squares = cast<*char>(alloc(8 * sizeof<*char>));
@@ -12,6 +29,11 @@ pub const init_board -> fn (bd: *Board) *Board {
1229
bd.starting_fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
1330
bd.length_fen = string::strlen(bd.starting_fen);
1431

32+
bd.white_can_castle_kingside = 1;
33+
bd.white_can_castle_queenside = 1;
34+
bd.black_can_castle_kingside = 1;
35+
bd.black_can_castle_queenside = 1;
36+
1537
loop [i: int = 0, row: int = 0, col: int = 0](i < bd.length_fen) : (++i) {
1638
let c: char = bd.starting_fen[i];
1739
if (c == '/') {
@@ -46,21 +68,124 @@ pub const parse_move -> fn (mv: *Move, move: *char) *Move {
4668
return mv;
4769
}
4870

49-
const is_check -> fn (bd: *Board, king_color: int, king_row: int, king_col: int) int {
50-
// 1. Locate the King (already provided by king_row, king_col)
71+
pub const validate_pawn_path -> fn (bd: *Board, mv: *Move, dr: int, dc: int) int {
72+
if (dc == 0 && (dr == -2 || dr == 2)) {
73+
let mid_row: int = (mv.from_row + mv.to_row) / 2;
74+
let mid: char = bd.squares[mid_row * 8 + mv.from_col];
75+
if (mid != '.') { return 0; }
76+
}
77+
return 1;
78+
}
79+
80+
pub const is_path_clear -> fn (bd: *Board, mv: *Move) int {
81+
let dc: int = mv.to_col - mv.from_col;
82+
let dr: int = mv.to_row - mv.from_row;
83+
84+
let step_c: int;
85+
if (dc == 0) { step_c = 0;
86+
} elif (dc > 0) { step_c = 1;
87+
} else { step_c = -1; }
88+
89+
let step_r: int;
90+
if (dr == 0) { step_r = 0;
91+
} elif (dr > 0) { step_r = 1;
92+
} else { step_r = -1; }
93+
94+
let curr_col: int = mv.from_col + step_c;
95+
let curr_row: int = mv.from_row + step_r;
96+
97+
loop (curr_col != mv.to_col || curr_row != mv.to_row) {
98+
if (bd.squares[curr_row * 8 + curr_col] != '.') {
99+
return 0;
100+
}
51101

52-
// 2. Iterate Through Opponent's Pieces
53-
// (Loop through all squares, identify opponent's pieces)
102+
curr_col = curr_col + step_c;
103+
curr_row = curr_row + step_r;
104+
}
105+
106+
return 1;
107+
}
54108

55-
// 3. Check for Attacks
56-
// (Inside the loop, for each opponent's piece,
57-
// call a helper function like 'can_piece_attack_square'
58-
// to see if it attacks king_row, king_col)
109+
pub const is_move_legal -> fn (rule: *PieceRules, bd: *Board, mv: *Move) int {
110+
let dc: int = mv.to_col - mv.from_col;
111+
let dr: int = mv.to_row - mv.from_row;
112+
if (piece::can_move_direction(rule, dc, dr) == 0) { return 0; }
113+
if (rule.can_slide == 1 && rule.can_jump == 0) {
114+
if (is_path_clear(bd, mv) == 0) { return 0; }
115+
}
116+
return 1;
117+
}
59118

60-
// 4. Return Check Status
61-
// (If any piece attacks, return true; otherwise, return false after the loop)
119+
pub const is_king_in_check -> fn (bd: *Board, is_white: int) int {
120+
let king_char: char;
121+
if (is_white == 1) { king_char = 'K'; }
122+
else { king_char = 'k'; }
123+
124+
let king_row: int = -1;
125+
let king_col: int = -1;
62126

63-
return 0;
127+
// Find the king position
128+
loop [i: int = 0](i < 64) : (++i) {
129+
if (bd.squares[i] == king_char) {
130+
king_row = i / 8;
131+
king_col = i % 8;
132+
break;
133+
}
134+
}
135+
136+
if (king_row == -1) {
137+
// Should never happen unless the king was captured (illegal position)
138+
output("Error: King not found on board!\n");
139+
return 1;
140+
}
141+
142+
// 2️⃣ Scan all enemy pieces to see if they can attack the king
143+
loop [i: int = 0](i < 64) : (++i) {
144+
let ch: char = bd.squares[i];
145+
if (ch == '.') { continue; }
146+
147+
// Skip same color pieces
148+
if (is_white == 1 && piece::is_white_piece(ch) == 1) { continue; }
149+
if (is_white == 0 && piece::is_black_piece(ch) == 1) { continue; }
150+
151+
let rule: *PieceRules = piece::get_rules(ch);
152+
if (rule == cast<*PieceRules>(0)) { continue; }
153+
154+
let from_row: int = i / 8;
155+
let from_col: int = i % 8;
156+
let dr: int = king_row - from_row;
157+
let dc: int = king_col - from_col;
158+
159+
// Special handling for pawn attacks
160+
if (ch == 'P' || ch == 'p') {
161+
// Pawns attack diagonally forward only
162+
if (ch == 'P' && dr == -1 && (dc == -1 || dc == 1)) { return 1; }
163+
if (ch == 'p' && dr == 1 && (dc == -1 || dc == 1)) { return 1; }
164+
continue;
165+
}
166+
167+
// Check if move direction matches the piece’s legal move
168+
let can_attack: int = piece::can_move_direction(rule, dc, dr);
169+
if (can_attack == 0) { continue; }
170+
171+
// Check path clearance for sliding pieces
172+
if (rule.can_slide == 1 && rule.can_jump == 0) {
173+
let mv: Move = {
174+
from_row: from_row,
175+
from_col: from_col,
176+
to_row: king_row,
177+
to_col: king_col
178+
};
179+
let clear: int = is_path_clear(bd, &mv);
180+
if (clear == 0) { continue; }
181+
}
182+
183+
// If we reach this point → piece can attack the king
184+
return 1;
185+
}
186+
187+
// 3️⃣ No attacks found → king is safe
188+
return 0;
64189
}
65190

66191
pub const update_board -> fn (bd: *Board, mv: *Move, white_to_move: *int, is_valid: *int) void {
@@ -101,7 +226,7 @@ pub const update_board -> fn (bd: *Board, mv: *Move, white_to_move: *int, is_val
101226
}
102227

103228
// Check capture legality
104-
if (piece::can_capture(target, is_white_piece, is_black_piece) == 0) {
229+
if (piece::can_capture(piece_char, target) == 0) {
105230
output("Error: Cannot capture your own piece.\n");
106231
*is_valid = 1;
107232
return;
@@ -121,23 +246,38 @@ pub const update_board -> fn (bd: *Board, mv: *Move, white_to_move: *int, is_val
121246
// Special handling for pawns
122247
if (piece_char == 'P' || piece_char == 'p') {
123248
if (piece::validate_pawn_move(rule, dc, dr, mv.from_row, target, is_white_piece) == 0 ||
124-
piece::validate_pawn_path(bd, mv, dr, dc) == 0) {
249+
validate_pawn_path(bd, mv, dr, dc) == 0) {
125250
output("Error: Illegal pawn move.\n");
126251
*is_valid = 1;
127252
return;
128253
}
129254
} else {
130-
if (piece::is_move_legal(rule, bd, mv) == 0) {
255+
if (is_move_legal(rule, bd, mv) == 0) {
131256
output("Error: Illegal move for this piece.\n");
132257
*is_valid = 1;
133258
return;
134259
}
135260
}
261+
262+
// Save old state
263+
let backup_from: char = bd.squares[from_index];
264+
let backup_to: char = bd.squares[to_index];
136265

137-
// Move the piece
266+
// Simulate move
138267
bd.squares[to_index] = piece_char;
139268
bd.squares[from_index] = '.';
140269

270+
// Check if own king is now in check
271+
let in_check: int = is_king_in_check(bd, -cast<int>(is_white_piece == 1));
272+
if (in_check == 1) {
273+
// Undo move
274+
bd.squares[from_index] = backup_from;
275+
bd.squares[to_index] = backup_to;
276+
output("Error: Move leaves king in check.\n");
277+
*is_valid = 1;
278+
return;
279+
}
280+
141281
// Flip turn
142282
if (*white_to_move == 1) {
143283
*white_to_move = 0;

tests/chess_engine/helper.lx

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

tests/chess_engine/main.lx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22

33
@use "terminal" as term
44
@use "string" as string
5-
@use "helper" as helper
65
@use "termfx" as termfx
76
@use "memory" as mem
87
@use "board" as board
98

10-
// TODO: add in castle, en passant logic, check logic
9+
// TODO: add in castle, en passant logic
1110

1211
pub const main -> fn () int {
1312
let bd: Board;

tests/chess_engine/piece.lx

Lines changed: 5 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
@module "piece"
22

3-
@use "helper" as helper
4-
53
pub const PieceRules -> struct {
64
piece_type: char,
75
can_jump: int,
@@ -58,19 +56,11 @@ pub const get_rules -> fn (piece: char) *PieceRules {
5856
return cast<*PieceRules>(0);
5957
}
6058

61-
pub const can_capture -> fn (target: char, white_piece: int, black_piece: int) int {
62-
if (target != '.') {
63-
let target_is_white: int = is_white_piece(target);
64-
let target_is_black: int = is_black_piece(target);
65-
66-
// Disallow capturing your own piece
67-
if ((white_piece == 1 && target_is_white == 1) ||
68-
(black_piece == 1 && target_is_black == 1)) {
69-
return 0;
70-
}
71-
}
72-
73-
return 1;
59+
pub const can_capture -> fn (attacker: char, target: char) int {
60+
if (target == '.') { return 1; }
61+
let atk_white: int = is_white_piece(attacker);
62+
let tgt_white: int = is_white_piece(target);
63+
return -cast<int>(atk_white != tgt_white);
7464
}
7565

7666
// Special pawn validation function (pawns have unique rules)
@@ -113,52 +103,6 @@ pub const validate_pawn_move -> fn (rule: *PieceRules, dc: int, dr: int,
113103
return 0;
114104
}
115105

116-
pub const validate_pawn_path -> fn (bd: *Board, mv: *Move, dr: int, dc: int) int {
117-
if (dc == 0 && (dr == -2 || dr == 2)) {
118-
let mid_row: int = (mv.from_row + mv.to_row) / 2;
119-
let mid: char = bd.squares[mid_row * 8 + mv.from_col];
120-
if (mid != '.') { return 0; }
121-
}
122-
return 1;
123-
}
124-
125-
const is_path_clear -> fn (bd: *Board, mv: *Move) int {
126-
let dc: int = mv.to_col - mv.from_col;
127-
let dr: int = mv.to_row - mv.from_row;
128-
129-
let step_c: int;
130-
if (dc == 0) {
131-
step_c = 0;
132-
} elif (dc > 0) {
133-
step_c = 1;
134-
} else {
135-
step_c = -1;
136-
}
137-
138-
let step_r: int;
139-
if (dr == 0) {
140-
step_r = 0;
141-
} elif (dr > 0) {
142-
step_r = 1;
143-
} else {
144-
step_r = -1;
145-
}
146-
147-
let curr_col: int = mv.from_col + step_c;
148-
let curr_row: int = mv.from_row + step_r;
149-
150-
loop (curr_col != mv.to_col || curr_row != mv.to_row) {
151-
if (bd.squares[curr_row * 8 + curr_col] != '.') {
152-
return 0;
153-
}
154-
155-
curr_col = curr_col + step_c;
156-
curr_row = curr_row + step_r;
157-
}
158-
159-
return 1;
160-
}
161-
162106
pub const can_move_direction -> fn (rule: *PieceRules, dc: int, dr: int) int {
163107
if (rule.can_slide == 1) {
164108
let step_c: int;
@@ -196,13 +140,3 @@ pub const can_move_direction -> fn (rule: *PieceRules, dc: int, dr: int) int {
196140
return 0;
197141
}
198142

199-
pub const is_move_legal -> fn (rule: *PieceRules, bd: *Board, mv: *Move) int {
200-
let dc: int = mv.to_col - mv.from_col;
201-
let dr: int = mv.to_row - mv.from_row;
202-
if (can_move_direction(rule, dc, dr) == 0) { return 0; }
203-
if (rule.can_slide == 1 && rule.can_jump == 0) {
204-
if (is_path_clear(bd, mv) == 0) { return 0; }
205-
}
206-
return 1;
207-
}
208-

0 commit comments

Comments
 (0)