Chess Engine
C++ chess engine with movegen, bitboards, and Arduino-friendly docs
Loading...
Searching...
No Matches
make_move.cpp
Go to the documentation of this file.
1// makeMove.cpp
2
3#include "defs.h"
4#include <cstdio>
5#include "make_move.h"
6#include "../util/attack.h"
7#include "../util/setup.h"
8
9#define HASH_PCE(pce, sq) (board->posKey ^= (zobristPieceKeys[(pce)][(sq)]))
10#define HASH_CA (board->posKey ^= (zobristCastleKeys[(board->castlePerm)]))
11#define HASH_SIDE (board->posKey ^= (zobristSideKey))
12#define HASH_EP (board->posKey ^= (zobristPieceKeys[EMPTY][(board->enPas)]))
13
14const int CastlePerm[120] = {
15 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
16 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
17 15, 13, 15, 15, 15, 12, 15, 15, 14, 15,
18 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
19 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
20 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
21 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
22 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
23 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
24 15, 7, 15, 15, 15, 3, 15, 15, 11, 15,
25 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
26 15, 15, 15, 15, 15, 15, 15, 15, 15, 15
27};
28
29// ---------- internal helpers (same behavior, C++-style locals) ----------
30static void ClearPiece(const int sq, S_BOARD* board) {
31 ASSERT(SqOnBoard(sq));
33
34 const int piece = board->pieces[sq];
35 ASSERT(pieceValueid(piece));
36
37 const int color = pieceColor[piece];
38 ASSERT(SideValid(color));
39
40 HASH_PCE(piece, sq);
41
42 board->pieces[sq] = EMPTY;
43 board->material[color] -= pieceValue[piece];
44
45 if (isBigPiece[piece]) {
46 board->bigPce[color]--;
47 if (isMajorPiece[piece]) {
48 board->majPce[color]--;
49 } else {
50 board->minPce[color]--;
51 }
52 } else {
53 CLRBIT(board->pawns[color], SQ64(sq));
54 CLRBIT(board->pawns[BOTH], SQ64(sq));
55 }
56
57 int foundIndex = -1;
58 for (int i = 0; i < board->pceNum[piece]; ++i) {
59 if (board->pList[piece][i] == sq) {
60 foundIndex = i;
61 break;
62 }
63 }
64
65 ASSERT(foundIndex != -1);
66 ASSERT(foundIndex >= 0 && foundIndex < 10);
67
68 board->pceNum[piece]--;
69 board->pList[piece][foundIndex] = board->pList[piece][board->pceNum[piece]];
70}
71
72static void AddPiece(const int sq, S_BOARD* board, const int piece) {
73 ASSERT(pieceValueid(piece));
74 ASSERT(SqOnBoard(sq));
75
76 const int color = pieceColor[piece];
77 ASSERT(SideValid(color));
78
79 HASH_PCE(piece, sq);
80
81 board->pieces[sq] = piece;
82
83 if (isBigPiece[piece]) {
84 board->bigPce[color]++;
85 if (isMajorPiece[piece]) {
86 board->majPce[color]++;
87 } else {
88 board->minPce[color]++;
89 }
90 } else {
91 SETBIT(board->pawns[color], SQ64(sq));
92 SETBIT(board->pawns[BOTH], SQ64(sq));
93 }
94
95 board->material[color] += pieceValue[piece];
96 board->pList[piece][board->pceNum[piece]++] = sq;
97}
98
99static void MovePiece(const int from, const int to, S_BOARD* board) {
100 ASSERT(SqOnBoard(from));
101 ASSERT(SqOnBoard(to));
102
103 const int piece = board->pieces[from];
104 ASSERT(pieceValueid(piece));
105
106 const int color = pieceColor[piece];
107 ASSERT(SideValid(color));
108
109#ifdef DEBUG
110 bool updatedList = false;
111#endif
112
113 HASH_PCE(piece, from);
114 board->pieces[from] = EMPTY;
115
116 HASH_PCE(piece, to);
117 board->pieces[to] = piece;
118
119 if (!isBigPiece[piece]) {
120 CLRBIT(board->pawns[color], SQ64(from));
121 CLRBIT(board->pawns[BOTH], SQ64(from));
122 SETBIT(board->pawns[color], SQ64(to));
123 SETBIT(board->pawns[BOTH], SQ64(to));
124 }
125
126 for (int i = 0; i < board->pceNum[piece]; ++i) {
127 if (board->pList[piece][i] == from) {
128 board->pList[piece][i] = to;
129#ifdef DEBUG
130 updatedList = true;
131#endif
132 break;
133 }
134 }
135
136#ifdef DEBUG
137 ASSERT(updatedList);
138#endif
139}
140
141// ---------- public API (same behavior, C++-style locals) ----------
142int makeMove(S_BOARD* board, int move) {
144
145 const int from = FROMSQ(move);
146 const int to = TOSQ(move);
147 const int side = board->side;
148
149 ASSERT(SqOnBoard(from));
150 ASSERT(SqOnBoard(to));
151 ASSERT(SideValid(side));
152 ASSERT(pieceValueid(board->pieces[from]));
153 ASSERT(board->hisPly >= 0 && board->hisPly < MAXGAMEMOVES);
154 ASSERT(board->ply >= 0 && board->ply < MAXDEPTH);
155
156 board->history[board->hisPly].posKey = board->posKey;
157
158 if (move & MFLAGEP) {
159 if (side == WHITE) ClearPiece(to - 10, board);
160 else ClearPiece(to + 10, board);
161 } else if (move & MFLAGCA) {
162 switch (to) {
163 case C1: MovePiece(A1, D1, board); break;
164 case C8: MovePiece(A8, D8, board); break;
165 case G1: MovePiece(H1, F1, board); break;
166 case G8: MovePiece(H8, F8, board); break;
167 default: ASSERT(FALSE); break;
168 }
169 }
170
171 if (board->enPas != NO_SQ) HASH_EP;
172 HASH_CA;
173
174 board->history[board->hisPly].move = move;
175 board->history[board->hisPly].fiftyMove = board->fiftyMove;
176 board->history[board->hisPly].enPas = board->enPas;
177 board->history[board->hisPly].castlePerm = board->castlePerm;
178
179 board->castlePerm &= CastlePerm[from];
180 board->castlePerm &= CastlePerm[to];
181 board->enPas = NO_SQ;
182
183 HASH_CA;
184
185 const int captured = CAPTURED(move);
186 board->fiftyMove++;
187
188 if (captured != EMPTY) {
189 ASSERT(pieceValueid(captured));
190 ClearPiece(to, board);
191 board->fiftyMove = 0;
192 }
193
194 board->hisPly++;
195 board->ply++;
196
197 ASSERT(board->hisPly >= 0 && board->hisPly < MAXGAMEMOVES);
198 ASSERT(board->ply >= 0 && board->ply < MAXDEPTH);
199
200 if (isPawn[board->pieces[from]]) {
201 board->fiftyMove = 0;
202 if (move & MFLAGPS) {
203 if (side == WHITE) {
204 board->enPas = from + 10;
205 ASSERT(rankIndex120[board->enPas] == RANK_3);
206 } else {
207 board->enPas = from - 10;
208 ASSERT(rankIndex120[board->enPas] == RANK_6);
209 }
210 HASH_EP;
211 }
212 }
213
214 MovePiece(from, to, board);
215
216 const int promoted = PROMOTED(move);
217 if (promoted != EMPTY) {
218 ASSERT(pieceValueid(promoted) && !isPawn[promoted]);
219 ClearPiece(to, board);
220 AddPiece(to, board, promoted);
221 }
222
223 if (isKing[board->pieces[to]]) {
224 board->KingSq[board->side] = to;
225 }
226
227 board->side ^= 1;
228 HASH_SIDE;
229
231
232 if (isSquareAttacked(board->KingSq[side], board->side, board)) {
233 takeMove(board);
234 return FALSE;
235 }
236
237 return TRUE;
238}
239
240void takeMove(S_BOARD* board) {
242
243 board->hisPly--;
244 board->ply--;
245
246 ASSERT(board->hisPly >= 0 && board->hisPly < MAXGAMEMOVES);
247 ASSERT(board->ply >= 0 && board->ply < MAXDEPTH);
248
249 const int move = board->history[board->hisPly].move;
250 const int from = FROMSQ(move);
251 const int to = TOSQ(move);
252
253 ASSERT(SqOnBoard(from));
254 ASSERT(SqOnBoard(to));
255
256 if (board->enPas != NO_SQ) HASH_EP;
257 HASH_CA;
258
259 board->castlePerm = board->history[board->hisPly].castlePerm;
260 board->fiftyMove = board->history[board->hisPly].fiftyMove;
261 board->enPas = board->history[board->hisPly].enPas;
262
263 if (board->enPas != NO_SQ) HASH_EP;
264 HASH_CA;
265
266 board->side ^= 1;
267 HASH_SIDE;
268
269 if (MFLAGEP & move) {
270 if (board->side == WHITE) AddPiece(to - 10, board, bP);
271 else AddPiece(to + 10, board, wP);
272 } else if (MFLAGCA & move) {
273 switch (to) {
274 case C1: MovePiece(D1, A1, board); break;
275 case C8: MovePiece(D8, A8, board); break;
276 case G1: MovePiece(F1, H1, board); break;
277 case G8: MovePiece(F8, H8, board); break;
278 default: ASSERT(FALSE); break;
279 }
280 }
281
282 MovePiece(to, from, board);
283
284 if (isKing[board->pieces[from]]) {
285 board->KingSq[board->side] = from;
286 }
287
288 const int captured = CAPTURED(move);
289 if (captured != EMPTY) {
290 ASSERT(pieceValueid(captured));
291 AddPiece(to, board, captured);
292 }
293
294 if (PROMOTED(move) != EMPTY) {
295 ASSERT(pieceValueid(PROMOTED(move)) && !isPawn[PROMOTED(move)]);
296 ClearPiece(from, board);
297 AddPiece(from, board, (pieceColor[PROMOTED(move)] == WHITE ? wP : bP));
298 }
299
301}
302
303void makeNullMove(S_BOARD* board) {
305 ASSERT(!isSquareAttacked(board->KingSq[board->side], board->side ^ 1, board));
306
307 board->ply++;
308 board->history[board->hisPly].posKey = board->posKey;
309
310 if (board->enPas != NO_SQ) HASH_EP;
311
312 board->history[board->hisPly].move = NOMOVE;
313 board->history[board->hisPly].fiftyMove = board->fiftyMove;
314 board->history[board->hisPly].enPas = board->enPas;
315 board->history[board->hisPly].castlePerm = board->castlePerm;
316 board->enPas = NO_SQ;
317
318 board->side ^= 1;
319 board->hisPly++;
320 HASH_SIDE;
321
323 ASSERT(board->hisPly >= 0 && board->hisPly < MAXGAMEMOVES);
324 ASSERT(board->ply >= 0 && board->ply < MAXDEPTH);
325}
326
327void takeNullMove(S_BOARD* board) {
329
330 board->hisPly--;
331 board->ply--;
332
333 if (board->enPas != NO_SQ) HASH_EP;
334
335 board->castlePerm = board->history[board->hisPly].castlePerm;
336 board->fiftyMove = board->history[board->hisPly].fiftyMove;
337 board->enPas = board->history[board->hisPly].enPas;
338
339 if (board->enPas != NO_SQ) HASH_EP;
340 board->side ^= 1;
341 HASH_SIDE;
342
344 ASSERT(board->hisPly >= 0 && board->hisPly < MAXGAMEMOVES);
345 ASSERT(board->ply >= 0 && board->ply < MAXDEPTH);
346}
bool isSquareAttacked(const int square, const int side, const S_BOARD *pos)
Determines if a given square is attacked by a given side.
Definition attack.cpp:9
bool isBoardStateValid(const S_BOARD *pos)
Performs a full internal consistency check of the board state.
Definition board.cpp:28
bool isMajorPiece[13]
Definition board_data.cpp:9
bool isKing[13]
int pieceColor[13]
Color of each piece type.
bool isBigPiece[13]
Lookup tables for piece attributes.
Definition board_data.cpp:8
bool isPawn[13]
int pieceValue[13]
Material value of each piece type.
#define MAXDEPTH
Definition defs.h:33
#define TOSQ(m)
Definition defs.h:165
@ EMPTY
Definition defs.h:40
@ bP
Definition defs.h:40
@ wP
Definition defs.h:40
@ WHITE
Definition defs.h:44
@ BOTH
Definition defs.h:44
#define CAPTURED(m)
Definition defs.h:166
#define MFLAGCA
Definition defs.h:171
#define NOMOVE
Definition defs.h:176
#define MAXGAMEMOVES
Definition defs.h:31
#define MFLAGEP
Definition defs.h:169
#define CLRBIT(bb, sq)
Definition defs.h:184
#define SQ64(sq120)
Definition defs.h:182
@ RANK_6
Definition defs.h:42
@ RANK_3
Definition defs.h:42
#define MFLAGPS
Definition defs.h:170
#define FROMSQ(m)
Definition defs.h:164
#define ASSERT(n)
Definition defs.h:14
#define PROMOTED(m)
Definition defs.h:167
#define SETBIT(bb, sq)
Definition defs.h:185
@ D1
Definition defs.h:47
@ F1
Definition defs.h:47
@ C8
Definition defs.h:54
@ A1
Definition defs.h:47
@ H1
Definition defs.h:47
@ NO_SQ
Definition defs.h:54
@ G8
Definition defs.h:54
@ G1
Definition defs.h:47
@ F8
Definition defs.h:54
@ H8
Definition defs.h:54
@ C1
Definition defs.h:47
@ D8
Definition defs.h:54
@ A8
Definition defs.h:54
@ FALSE
Definition defs.h:57
@ TRUE
Definition defs.h:57
void takeNullMove(S_BOARD *board)
Undo the last null move.
int makeMove(S_BOARD *board, int move)
Make a move; returns non-zero if legal (king not left in check).
#define HASH_EP
Definition make_move.cpp:12
const int CastlePerm[120]
Castling permission mask by board square (mailbox 120).
Definition make_move.cpp:14
static void AddPiece(const int sq, S_BOARD *board, const int piece)
Definition make_move.cpp:72
static void ClearPiece(const int sq, S_BOARD *board)
Definition make_move.cpp:30
void makeNullMove(S_BOARD *board)
Make a null move (side to move toggles, no pieces moved).
#define HASH_PCE(pce, sq)
Definition make_move.cpp:9
#define HASH_SIDE
Definition make_move.cpp:11
#define HASH_CA
Definition make_move.cpp:10
void takeMove(S_BOARD *board)
Undo the last made move.
static void MovePiece(const int from, const int to, S_BOARD *board)
Definition make_move.cpp:99
int rankIndex120[BRD_SQ_NUM]
Definition setup.cpp:27
int pList[13][10]
Definition defs.h:127
int pieces[BRD_SQ_NUM]
Definition defs.h:102
int castlePerm
Definition defs.h:114
int pceNum[13]
Definition defs.h:118
int majPce[2]
Definition defs.h:120
int enPas
Definition defs.h:108
U64 pawns[3]
Definition defs.h:103
int KingSq[2]
Definition defs.h:105
int fiftyMove
Definition defs.h:109
int bigPce[2]
Definition defs.h:119
U64 posKey
Definition defs.h:116
int side
Definition defs.h:107
int hisPly
Definition defs.h:112
int ply
Definition defs.h:111
int material[2]
Definition defs.h:122
int minPce[2]
Definition defs.h:121
S_UNDO history[MAXGAMEMOVES]
Definition defs.h:124
U64 posKey
Definition defs.h:96
int enPas
Definition defs.h:94
int castlePerm
Definition defs.h:93
int move
Definition defs.h:92
int fiftyMove
Definition defs.h:95
int pieceValueid(const int pce)
Checks if the given piece code is a valid piece (non-empty).
Definition validate.cpp:46
int SqOnBoard(const int sq)
Checks if a given square index refers to a valid on-board square.
Definition validate.cpp:29
int SideValid(const int side)
Checks if the given side identifier is valid.
Definition validate.cpp:34