Rev 40 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | // move.cpp |
2 | |||
3 | #include "common.h" |
||
4 | |||
5 | |||
6 | // handy macros |
||
7 | #define IS_VALID(li,co) (((li) >= 0) && ((li) < 8) && ((co) >= 0) && ((co) < 8)) |
||
8 | #define IS_FREE(li,co) (IS_VALID ((li), (co)) && (move->slots[(li)][(co)].part == PART_NONE)) |
||
9 | #define CAN_PLAY(li,co) (IS_VALID ((li), (co)) && ((move->slots[(li)][(co)].part == PART_NONE) || (move->slots[(li)][(co)].color != boardslot->color))) |
||
10 | |||
11 | |||
12 | // prototypes of local functions |
||
13 | static void AddPossibleMove (boardmove_t **possiblemoves, int *possiblemove_count, int color, int part, int source_line, int source_column, int target_line, int target_column, int promotion_type, bool has_captured, bool is_enpassant); |
||
14 | |||
15 | |||
16 | void Move_SetSlot (boardmove_t *move, int line, int column, int color, int part_type) |
||
17 | { |
||
18 | // this function populates a board's slot at the given line,column coordinates with the |
||
19 | // given part of the given color |
||
20 | |||
21 | move->slots[line][column].flags = 0; // reset flags |
||
22 | move->slots[line][column].color = color; // set part color |
||
23 | move->slots[line][column].part = part_type; // set part type |
||
24 | |||
25 | return; // finished, board slot is set |
||
26 | } |
||
27 | |||
28 | |||
29 | bool Move_IsKingThreatenedAtLocation (boardmove_t *move, int color, int at_line, int at_column, int *threat_line, int *threat_column) |
||
30 | { |
||
31 | // this function returns TRUE if the specified color is safe at the specified location. |
||
32 | // In case it is not, it returns FALSE and sets the threat's line and column parameters. |
||
33 | // Note the use of the threat_line and threat_column output parameters as iterator |
||
34 | // variables. |
||
35 | |||
36 | boardslot_t *boardslot; |
||
37 | int movement_direction; |
||
38 | int line; |
||
39 | int column; |
||
40 | int index_line; |
||
41 | int index_column; |
||
42 | |||
43 | // cycle through all the board |
||
44 | for (line = 0; line < 8; line++) |
||
45 | for (column = 0; column < 8; column++) |
||
46 | { |
||
47 | boardslot = &move->slots[line][column]; // quick access to grid slot |
||
48 | |||
49 | if ((boardslot->part == PART_NONE) || (boardslot->color == color)) |
||
50 | continue; // if this location is empty or ours, it doesn't threaten us |
||
51 | |||
52 | // update new possible threat position |
||
53 | *threat_line = line; |
||
54 | *threat_column = column; |
||
55 | |||
56 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
57 | ////////////////////////////////////////// PAWN ////////////////////////////////////////// |
||
58 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
59 | // is it a pawn ? (note: pawns can only threaten kings normally, never "en passant") |
||
60 | if (boardslot->part == PART_PAWN) |
||
61 | { |
||
62 | // figure out movement direction |
||
63 | if (boardslot->color == COLOR_WHITE) |
||
64 | movement_direction = 1; |
||
65 | else |
||
66 | movement_direction = -1; |
||
67 | |||
68 | // see if pawn can take our piece on either of its sides |
||
69 | if ((column > 0) && (line + movement_direction == at_line) && (column - 1 == at_column)) |
||
70 | return (true); // this part threatens us, it can take our piece on its left |
||
71 | else if ((column < 7) && (line + movement_direction == at_line) && (column + 1 == at_column)) |
||
72 | return (true); // this part threatens us, it can take our piece on its right |
||
73 | } |
||
74 | |||
75 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
76 | ////////////////////////////////////////// ROOK ////////////////////////////////////////// |
||
77 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
78 | // else is it a rook ? |
||
79 | else if (boardslot->part == PART_ROOK) |
||
80 | { |
||
81 | // is rook in the same column as our king ? |
||
82 | if (column == at_column) |
||
83 | { |
||
84 | // is our king above ? |
||
85 | if (at_line > line) |
||
86 | { |
||
87 | // see if rook can threaten our king by moving upwards |
||
88 | for (index_line = line + 1; index_line < 8; index_line++) |
||
89 | if (index_line == at_line) |
||
90 | return (true); // this part threatens us |
||
91 | else if (!IS_FREE (index_line, column)) |
||
92 | break; // if part can no longer move this way, stop searching |
||
93 | } |
||
94 | |||
95 | // else our king must be below |
||
96 | else |
||
97 | { |
||
98 | // see if rook can threaten our king by moving downwards |
||
99 | for (index_line = line - 1; index_line >= 0; index_line--) |
||
100 | if (index_line == at_line) |
||
101 | return (true); // this part threatens us |
||
102 | else if (!IS_FREE (index_line, column)) |
||
103 | break; // if part can no longer move this way, stop searching |
||
104 | } |
||
105 | } |
||
106 | |||
107 | // else is rook in the same line as our king ? |
||
108 | else if (line == at_line) |
||
109 | { |
||
110 | // is our king on the right ? |
||
111 | if (at_column > column) |
||
112 | { |
||
113 | // see if rook can threaten our king by moving right |
||
114 | for (index_column = column + 1; index_column < 8; index_column++) |
||
115 | if (index_column == at_column) |
||
116 | return (true); // this part threatens us |
||
117 | else if (!IS_FREE (line, index_column)) |
||
118 | break; // if part can no longer move this way, stop searching |
||
119 | } |
||
120 | |||
121 | // else our king must be on the left |
||
122 | else |
||
123 | { |
||
124 | // see if rook can threaten our king by moving left |
||
125 | for (index_column = column - 1; index_column >= 0; index_column--) |
||
126 | if (index_column == at_column) |
||
127 | return (true); // this part threatens us |
||
128 | else if (!IS_FREE (line, index_column)) |
||
129 | break; // if part can no longer move this way, stop searching |
||
130 | } |
||
131 | } |
||
132 | } |
||
133 | |||
134 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
135 | ///////////////////////////////////////// KNIGHT ///////////////////////////////////////// |
||
136 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
137 | // else is it a knight ? |
||
138 | else if (boardslot->part == PART_KNIGHT) |
||
139 | { |
||
140 | if ((column > 0) && (line < 6) && (line + 2 == at_line) && (column - 1 == at_column)) |
||
141 | return (true); // this part threatens us on its NNW move |
||
142 | else if ((column < 7) && (line < 6) && (line + 2 == at_line) && (column + 1 == at_column)) |
||
143 | return (true); // this part threatens us on its NNE move |
||
144 | else if ((column < 6) && (line < 7) && (line + 1 == at_line) && (column + 2 == at_column)) |
||
145 | return (true); // this part threatens us on its ENE move |
||
146 | else if ((column < 6) && (line > 0) && (line - 1 == at_line) && (column + 2 == at_column)) |
||
147 | return (true); // this part threatens us on its ESE move |
||
148 | else if ((column > 0) && (line > 1) && (line - 2 == at_line) && (column - 1 == at_column)) |
||
149 | return (true); // this part threatens us on its SSW move |
||
150 | else if ((column < 7) && (line > 1) && (line - 2 == at_line) && (column + 1 == at_column)) |
||
151 | return (true); // this part threatens us on its SSE move |
||
152 | else if ((column > 1) && (line < 7) && (line + 1 == at_line) && (column - 2 == at_column)) |
||
153 | return (true); // this part threatens us on its WNW move |
||
154 | else if ((column > 1) && (line > 0) && (line - 1 == at_line) && (column - 2 == at_column)) |
||
155 | return (true); // this part threatens us on its WSW move |
||
156 | } |
||
157 | |||
158 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
159 | ///////////////////////////////////////// BISHOP ///////////////////////////////////////// |
||
160 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
161 | // else is it a bishop ? |
||
162 | else if (boardslot->part == PART_BISHOP) |
||
163 | { |
||
164 | // is bishop in the same SWNE diagonal as our king ? |
||
165 | if (line - at_line == column - at_column) |
||
166 | { |
||
167 | // is our king NE ? |
||
168 | if (at_line > line) |
||
169 | { |
||
170 | // see how far bishop can move NE |
||
171 | for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++) |
||
172 | if ((index_line == at_line) && (index_column == at_column)) |
||
173 | return (true); // this part threatens us |
||
174 | else if (!IS_FREE (index_line, index_column)) |
||
175 | break; // if part can no longer move this way, stop searching |
||
176 | } |
||
177 | |||
178 | // else our king must be SW |
||
179 | else |
||
180 | { |
||
181 | // see how far bishop can move SW |
||
182 | for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--) |
||
183 | if ((index_line == at_line) && (index_column == at_column)) |
||
184 | return (true); // this part threatens us |
||
185 | else if (!IS_FREE (index_line, index_column)) |
||
186 | break; // if part can no longer move this way, stop searching |
||
187 | } |
||
188 | } |
||
189 | |||
190 | // else is bishop in the same SENW diagonal as our king ? |
||
191 | else if (line - at_line == -(column - at_column)) |
||
192 | { |
||
193 | // is our king NW ? |
||
194 | if (at_line > line) |
||
195 | { |
||
196 | // see how far bishop can move NW |
||
197 | for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--) |
||
198 | if ((index_line == at_line) && (index_column == at_column)) |
||
199 | return (true); // this part threatens us |
||
200 | else if (!IS_FREE (index_line, index_column)) |
||
201 | break; // if part can no longer move this way, stop searching |
||
202 | } |
||
203 | |||
204 | // else our king must be SE |
||
205 | else |
||
206 | { |
||
207 | // see how far bishop can move SE |
||
208 | for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++) |
||
209 | if ((index_line == at_line) && (index_column == at_column)) |
||
210 | return (true); // this part threatens us |
||
211 | else if (!IS_FREE (index_line, index_column)) |
||
212 | break; // if part can no longer move this way, stop searching |
||
213 | } |
||
214 | } |
||
215 | } |
||
216 | |||
217 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
218 | ///////////////////////////////////////// QUEEN ////////////////////////////////////////// |
||
219 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
220 | // else is it a queen ? |
||
221 | else if (boardslot->part == PART_QUEEN) |
||
222 | { |
||
223 | // is queen in the same column as our king ? |
||
224 | if (column == at_column) |
||
225 | { |
||
226 | // is our king above ? |
||
227 | if (at_line > line) |
||
228 | { |
||
229 | // see if queen can threaten our king by moving upwards |
||
230 | for (index_line = line + 1; index_line < 8; index_line++) |
||
231 | if (index_line == at_line) |
||
232 | return (true); // this part threatens us |
||
233 | else if (!IS_FREE (index_line, column)) |
||
234 | break; // if part can no longer move this way, stop searching |
||
235 | } |
||
236 | |||
237 | // else our king must be below |
||
238 | else |
||
239 | { |
||
240 | // see if queen can threaten our king by moving downwards |
||
241 | for (index_line = line - 1; index_line >= 0; index_line--) |
||
242 | if (index_line == at_line) |
||
243 | return (true); // this part threatens us |
||
244 | else if (!IS_FREE (index_line, column)) |
||
245 | break; // if part can no longer move this way, stop searching |
||
246 | } |
||
247 | } |
||
248 | |||
249 | // else is queen in the same line as our king ? |
||
250 | else if (line == at_line) |
||
251 | { |
||
252 | // is our king on the right ? |
||
253 | if (at_column > column) |
||
254 | { |
||
255 | // see if queen can threaten our king by moving right |
||
256 | for (index_column = column + 1; index_column < 8; index_column++) |
||
257 | if (index_column == at_column) |
||
258 | return (true); // this part threatens us |
||
259 | else if (!IS_FREE (line, index_column)) |
||
260 | break; // if part can no longer move this way, stop searching |
||
261 | } |
||
262 | |||
263 | // else our king must be on the left |
||
264 | else |
||
265 | { |
||
266 | // see if queen can threaten our king by moving left |
||
267 | for (index_column = column - 1; index_column >= 0; index_column--) |
||
268 | if (index_column == at_column) |
||
269 | return (true); // this part threatens us |
||
270 | else if (!IS_FREE (line, index_column)) |
||
271 | break; // if part can no longer move this way, stop searching |
||
272 | } |
||
273 | } |
||
274 | |||
275 | // else is queen in the same SWNE diagonal as our king ? |
||
276 | else if (line - at_line == column - at_column) |
||
277 | { |
||
278 | // is our king NE ? |
||
279 | if (at_line > line) |
||
280 | { |
||
281 | // see how far queen can move NE |
||
282 | for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++) |
||
283 | if ((index_line == at_line) && (index_column == at_column)) |
||
284 | return (true); // this part threatens us |
||
285 | else if (!IS_FREE (index_line, index_column)) |
||
286 | break; // if part can no longer move this way, stop searching |
||
287 | } |
||
288 | |||
289 | // else our king must be SW |
||
290 | else |
||
291 | { |
||
292 | // see how far queen can move SW |
||
293 | for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--) |
||
294 | if ((index_line == at_line) && (index_column == at_column)) |
||
295 | return (true); // this part threatens us |
||
296 | else if (!IS_FREE (index_line, index_column)) |
||
297 | break; // if part can no longer move this way, stop searching |
||
298 | } |
||
299 | } |
||
300 | |||
301 | // else is queen in the same SENW diagonal as our king ? |
||
302 | else if (line - at_line == -(column - at_column)) |
||
303 | { |
||
304 | // is our king NW ? |
||
305 | if (at_line > line) |
||
306 | { |
||
307 | // see how far queen can move NW |
||
308 | for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--) |
||
309 | if ((index_line == at_line) && (index_column == at_column)) |
||
310 | return (true); // this part threatens us |
||
311 | else if (!IS_FREE (index_line, index_column)) |
||
312 | break; // if part can no longer move this way, stop searching |
||
313 | } |
||
314 | |||
315 | // else our king must be SE |
||
316 | else |
||
317 | { |
||
318 | // see how far queen can move SE |
||
319 | for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++) |
||
320 | if ((index_line == at_line) && (index_column == at_column)) |
||
321 | return (true); // this part threatens us |
||
322 | else if (!IS_FREE (index_line, index_column)) |
||
323 | break; // if part can no longer move this way, stop searching |
||
324 | } |
||
325 | } |
||
326 | } |
||
327 | |||
328 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
329 | ////////////////////////////////////////// KING ////////////////////////////////////////// |
||
330 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
331 | // else is it a king ? |
||
332 | else if (boardslot->part == PART_KING) |
||
333 | { |
||
334 | if ((line < 7) && (column < 7) && (line + 1 == at_line) && (column + 1 == at_column)) |
||
335 | return (true); // this part threatens us on its NE move |
||
336 | else if ((line > 0) && (column < 7) && (line - 1 == at_line) && (column + 1 == at_column)) |
||
337 | return (true); // this part threatens us on its SE move |
||
338 | else if ((line < 7) && (column > 0) && (line + 1 == at_line) && (column - 1 == at_column)) |
||
339 | return (true); // this part threatens us on its NW move |
||
340 | else if ((line > 0) && (column > 0) && (line - 1 == at_line) && (column - 1 == at_column)) |
||
341 | return (true); // this part threatens us on its SW move |
||
342 | else if ((line < 7) && (line + 1 == at_line) && (column == at_column)) |
||
343 | return (true); // this part threatens us on an upwards move |
||
344 | else if ((line > 0) && (line - 1 == at_line) && (column == at_column)) |
||
345 | return (true); // this part threatens us on a downwards move |
||
346 | else if ((column < 7) && (line == at_line) && (column + 1 == at_column)) |
||
347 | return (true); // this part threatens us on a right move |
||
348 | else if ((column > 0) && (line == at_line) && (column - 1 == at_column)) |
||
349 | return (true); // this part threatens us on a left move |
||
350 | } |
||
351 | } |
||
352 | |||
353 | return (false); // this king looks safe at this location |
||
354 | } |
||
355 | |||
356 | |||
357 | bool Move_IsCheck (boardmove_t *move, int color) |
||
358 | { |
||
359 | // this function returns TRUE if the king of the specified color is under check |
||
360 | |||
361 | boardslot_t *boardslot; |
||
362 | int line; |
||
363 | int column; |
||
364 | int threat_line; |
||
365 | int threat_column; |
||
366 | |||
367 | // cycle through all the grid again and see if the king we want is in check |
||
368 | for (line = 0; line < 8; line++) |
||
369 | for (column = 0; column < 8; column++) |
||
370 | { |
||
371 | boardslot = &move->slots[line][column]; // quick access to grid slot |
||
372 | |||
373 | if ((boardslot->color != color) || (boardslot->part != PART_KING)) |
||
374 | continue; // if this slot is not our king, skip it |
||
375 | |||
376 | // is this king currently threatened ? |
||
377 | if (Move_IsKingThreatenedAtLocation (move, color, line, column, &threat_line, &threat_column)) |
||
378 | return (true); // yes, it is |
||
379 | else |
||
380 | return (false); // no, this king is safe |
||
381 | } |
||
382 | |||
383 | // code should never reach here (it would mean that no king is on the board) |
||
384 | |||
385 | return (false); // no king of such color found on board, no check possible, return FALSE |
||
386 | } |
||
387 | |||
388 | |||
389 | bool Move_IsStaleMate (boardmove_t *move, int color) |
||
390 | { |
||
391 | // this function returns TRUE if the specified color is stalemate (no valid move possible) |
||
392 | |||
393 | boardslot_t *boardslot; |
||
394 | int movement_direction; |
||
395 | int line; |
||
396 | int column; |
||
397 | int index_line; |
||
398 | int index_column; |
||
399 | |||
400 | // cycle through all the board and find our parts |
||
401 | for (line = 0; line < 8; line++) |
||
402 | for (column = 0; column < 8; column++) |
||
403 | { |
||
404 | boardslot = &move->slots[line][column]; // quick access to grid slot |
||
405 | |||
406 | if ((boardslot->part == PART_NONE) || (boardslot->color != color)) |
||
407 | continue; // if this location is empty or not ours, we aren't interested in it |
||
408 | |||
409 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
410 | ////////////////////////////////////////// PAWN ////////////////////////////////////////// |
||
411 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
412 | // is it a pawn ? |
||
413 | if (boardslot->part == PART_PAWN) |
||
414 | { |
||
415 | // figure out movement direction |
||
416 | if (boardslot->color == COLOR_WHITE) |
||
417 | movement_direction = 1; |
||
418 | else |
||
419 | movement_direction = -1; |
||
420 | |||
421 | // see if pawn can move forward |
||
422 | if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0))) |
||
423 | && (move->slots[line + movement_direction][column].part == PART_NONE) // target slot free |
||
424 | && !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column, color)) |
||
425 | return (false); // this move is possible |
||
426 | |||
427 | // see if pawn can take a piece on its left |
||
428 | if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0))) |
||
429 | && (column > 0) // has room |
||
430 | && (move->slots[line + movement_direction][column - 1].color != color) // target slot NOT our color |
||
431 | && (move->slots[line + movement_direction][column - 1].part != PART_NONE) // target slot occupied |
||
432 | && !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column - 1, color)) |
||
433 | return (false); // this move is possible |
||
434 | |||
435 | // see if pawn can take a piece on its right |
||
436 | if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0))) |
||
437 | && (column < 7) // has room |
||
438 | && (move->slots[line + movement_direction][column + 1].color != color) // target slot NOT our color |
||
439 | && (move->slots[line + movement_direction][column + 1].part != PART_NONE) // target slot occupied |
||
440 | && !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column + 1, color)) |
||
441 | return (false); // this move is possible |
||
442 | |||
443 | // if previous move was a pawn rush, see if pawn can take "en passant" |
||
444 | if ((move->part == PART_PAWN) // last move was a pawn |
||
445 | && (move->target[1] == move->source[1]) // pawn moved in column |
||
446 | && (abs (move->target[0] - move->source[0]) == 2) // pawn rushed |
||
447 | && (move->target[0] == line) // pawn is in line with us |
||
448 | && (move->target[1] - column == -1) // pawn is left to us |
||
449 | && !Move_IsColorInCheckAfterTestMoveEP (move, line, column, line + movement_direction, column - 1, move->target[0], move->target[1], color)) |
||
450 | return (false); // this move is possible |
||
451 | else if ((move->part == PART_PAWN) // last move was a pawn |
||
452 | && (move->target[1] == move->source[1]) // pawn moved in column |
||
453 | && (abs (move->target[0] - move->source[0]) == 2) // pawn rushed |
||
454 | && (move->target[0] == line) // pawn is in line with us |
||
455 | && (move->target[1] - column == 1) // pawn is right to us |
||
456 | && !Move_IsColorInCheckAfterTestMoveEP (move, line, column, line + movement_direction, column + 1, move->target[0], move->target[1], color)) |
||
457 | return (false); // this move is possible |
||
458 | } |
||
459 | |||
460 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
461 | ////////////////////////////////////////// ROOK ////////////////////////////////////////// |
||
462 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
463 | // else is it a rook ? |
||
464 | else if (boardslot->part == PART_ROOK) |
||
465 | { |
||
466 | // see if rook can move upwards |
||
467 | for (index_line = line + 1; index_line < 8; index_line++) |
||
468 | if (!CAN_PLAY (index_line, column)) |
||
469 | break; // if part can no longer move this way, stop searching |
||
470 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color)) |
||
471 | return (false); // this move is possible |
||
472 | |||
473 | // see if rook can move downwards |
||
474 | for (index_line = line - 1; index_line >= 0; index_line--) |
||
475 | if (!CAN_PLAY (index_line, column)) |
||
476 | break; // if part can no longer move this way, stop searching |
||
477 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color)) |
||
478 | return (false); // this move is possible |
||
479 | |||
480 | // see if rook can move right |
||
481 | for (index_column = column + 1; index_column < 8; index_column++) |
||
482 | if (!CAN_PLAY (line, index_column)) |
||
483 | break; // if part can no longer move this way, stop searching |
||
484 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color)) |
||
485 | return (false); // this move is possible |
||
486 | |||
487 | // see if rook can move left |
||
488 | for (index_column = column - 1; index_column >= 0; index_column--) |
||
489 | if (!CAN_PLAY (line, index_column)) |
||
490 | break; // if part can no longer move this way, stop searching |
||
491 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color)) |
||
492 | return (false); // this move is possible |
||
493 | } |
||
494 | |||
495 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
496 | ///////////////////////////////////////// KNIGHT ///////////////////////////////////////// |
||
497 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
498 | // else is it a knight ? |
||
499 | else if (boardslot->part == PART_KNIGHT) |
||
500 | { |
||
501 | // see if knight can move in either of his allowed directions NNW |
||
502 | if (CAN_PLAY (line + 2, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 2, column - 1, color)) |
||
503 | return (false); // knight can move NNW, we are not stalemate |
||
504 | else if (CAN_PLAY (line + 2, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 2, column + 1, color)) |
||
505 | return (false); // knight can move NNE, we are not stalemate |
||
506 | else if (CAN_PLAY (line + 1, column + 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column + 2, color)) |
||
507 | return (false); // knight can move ENE, we are not stalemate |
||
508 | else if (CAN_PLAY (line - 1, column + 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column + 2, color)) |
||
509 | return (false); // knight can move ESE, we are not stalemate |
||
510 | else if (CAN_PLAY (line - 2, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 2, column - 1, color)) |
||
511 | return (false); // knight can move SSW, we are not stalemate |
||
512 | else if (CAN_PLAY (line - 2, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 2, column + 1, color)) |
||
513 | return (false); // knight can move SSE, we are not stalemate |
||
514 | else if (CAN_PLAY (line + 1, column - 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column - 2, color)) |
||
515 | return (false); // knight can move WNW, we are not stalemate |
||
516 | else if (CAN_PLAY (line - 1, column - 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column - 2, color)) |
||
517 | return (false); // knight can move WSW, we are not stalemate |
||
518 | } |
||
519 | |||
520 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
521 | ///////////////////////////////////////// BISHOP ///////////////////////////////////////// |
||
522 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
523 | // else is it a bishop ? |
||
524 | else if (boardslot->part == PART_BISHOP) |
||
525 | { |
||
526 | // see if bishop can move NE |
||
527 | for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++) |
||
528 | if (!CAN_PLAY (index_line, index_column)) |
||
529 | break; // if part can no longer move this way, stop searching |
||
530 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
531 | return (false); // this move is possible |
||
532 | |||
533 | // see if bishop can move SE |
||
534 | for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++) |
||
535 | if (!CAN_PLAY (index_line, index_column)) |
||
536 | break; // if part can no longer move this way, stop searching |
||
537 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
538 | return (false); // this move is possible |
||
539 | |||
540 | // see if bishop can move NW |
||
541 | for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--) |
||
542 | if (!CAN_PLAY (index_line, index_column)) |
||
543 | break; // if part can no longer move this way, stop searching |
||
544 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
545 | return (false); // this move is possible |
||
546 | |||
547 | // see if bishop can move SW |
||
548 | for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--) |
||
549 | if (!CAN_PLAY (index_line, index_column)) |
||
550 | break; // if part can no longer move this way, stop searching |
||
551 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
552 | return (false); // this move is possible |
||
553 | } |
||
554 | |||
555 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
556 | ///////////////////////////////////////// QUEEN ////////////////////////////////////////// |
||
557 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
558 | // else is it a queen ? |
||
559 | else if (boardslot->part == PART_QUEEN) |
||
560 | { |
||
561 | // see if queen can move upwards |
||
562 | for (index_line = line + 1; index_line < 8; index_line++) |
||
563 | if (!CAN_PLAY (index_line, column)) |
||
564 | break; // if part can no longer move this way, stop searching |
||
565 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color)) |
||
566 | return (false); // this move is possible |
||
567 | |||
568 | // see if queen can move downwards |
||
569 | for (index_line = line - 1; index_line >= 0; index_line--) |
||
570 | if (!CAN_PLAY (index_line, column)) |
||
571 | break; // if part can no longer move this way, stop searching |
||
572 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color)) |
||
573 | return (false); // this move is possible |
||
574 | |||
575 | // see if queen can move right |
||
576 | for (index_column = column + 1; index_column < 8; index_column++) |
||
577 | if (!CAN_PLAY (line, index_column)) |
||
578 | break; // if part can no longer move this way, stop searching |
||
579 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color)) |
||
580 | return (false); // this move is possible |
||
581 | |||
582 | // see if queen can move left |
||
583 | for (index_column = column - 1; index_column >= 0; index_column--) |
||
584 | if (!CAN_PLAY (line, index_column)) |
||
585 | break; // if part can no longer move this way, stop searching |
||
586 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color)) |
||
587 | return (false); // this move is possible |
||
588 | |||
589 | // see if queen can move NE |
||
590 | for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++) |
||
591 | if (!CAN_PLAY (index_line, index_column)) |
||
592 | break; // if part can no longer move this way, stop searching |
||
593 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
594 | return (false); // this move is possible |
||
595 | |||
596 | // see if queen can move SE |
||
597 | for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++) |
||
598 | if (!CAN_PLAY (index_line, index_column)) |
||
599 | break; // if part can no longer move this way, stop searching |
||
600 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
601 | return (false); // this move is possible |
||
602 | |||
603 | // see if queen can move NW |
||
604 | for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--) |
||
605 | if (!CAN_PLAY (index_line, index_column)) |
||
606 | break; // if part can no longer move this way, stop searching |
||
607 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
608 | return (false); // this move is possible |
||
609 | |||
610 | // see if queen can move SW |
||
611 | for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--) |
||
612 | if (!CAN_PLAY (index_line, index_column)) |
||
613 | break; // if part can no longer move this way, stop searching |
||
614 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
615 | return (false); // this move is possible |
||
616 | } |
||
617 | |||
618 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
619 | ////////////////////////////////////////// KING ////////////////////////////////////////// |
||
620 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
621 | // else is it a king ? |
||
622 | else if (boardslot->part == PART_KING) |
||
623 | { |
||
624 | // see if king can move in either of his allowed directions |
||
625 | if (CAN_PLAY (line + 1, column) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column, color)) |
||
626 | return (false); // king can move up, we are not stalemate |
||
627 | else if (CAN_PLAY (line - 1, column) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column, color)) |
||
628 | return (false); // king can move down, we are not stalemate |
||
629 | else if (CAN_PLAY (line, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line, column + 1, color)) |
||
630 | return (false); // king can move right, we are not stalemate |
||
631 | else if (CAN_PLAY (line, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line, column - 1, color)) |
||
632 | return (false); // king can move left, we are not stalemate |
||
633 | else if (CAN_PLAY (line + 1, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column + 1, color)) |
||
634 | return (false); // king can move NE, we are not stalemate |
||
635 | else if (CAN_PLAY (line - 1, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column + 1, color)) |
||
636 | return (false); // king can move SE, we are not stalemate |
||
637 | else if (CAN_PLAY (line + 1, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column - 1, color)) |
||
638 | return (false); // king can move NW, we are not stalemate |
||
639 | else if (CAN_PLAY (line - 1, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column - 1, color)) |
||
640 | return (false); // king can move SW, we are not stalemate |
||
641 | } |
||
642 | } |
||
643 | |||
644 | return (true); // found no legal move, we are indeed stalemate |
||
645 | } |
||
646 | |||
647 | |||
648 | bool Move_IsMoveValid (boardmove_t *move, int from_line, int from_column, int to_line, int to_column) |
||
649 | { |
||
650 | // this function returns TRUE if the specified move is valid, FALSE otherwise |
||
651 | |||
652 | // FIXME: doesn't support castling testing so far! (even though it shouldn't be necessary) |
||
653 | |||
654 | boardslot_t *boardslot; |
||
655 | int movement_direction; |
||
656 | int index_line; |
||
657 | int index_column; |
||
658 | |||
659 | boardslot = &move->slots[from_line][from_column]; // quick access to grid slot |
||
660 | |||
661 | // consistency check |
||
662 | if (!IS_VALID (from_line, from_column) || !IS_VALID (to_line, to_column)) |
||
663 | return (false); // if movement is out of bounds, it's obviously invalid |
||
664 | |||
665 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
666 | ////////////////////////////////////////// PAWN ////////////////////////////////////////// |
||
667 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
668 | // is it a pawn ? |
||
669 | if (boardslot->part == PART_PAWN) |
||
670 | { |
||
671 | // figure out movement direction |
||
672 | if (boardslot->color == COLOR_WHITE) |
||
673 | movement_direction = 1; |
||
674 | else |
||
675 | movement_direction = -1; |
||
676 | |||
677 | // quick checks |
||
678 | if (abs (to_line - from_line) > 2) |
||
679 | return (false); // pawns cannot make moves longer than 2 rows |
||
680 | else if (abs (to_column - from_column) > 1) |
||
681 | return (false); // pawns cannot make moves aside more than 1 column |
||
682 | |||
683 | // do we want pawn to rush forward |
||
684 | // OR do we want pawn to move forward |
||
685 | // OR do we want pawn to take a piece on its left |
||
686 | // OR do we want pawn to take a piece on its right ? |
||
687 | if ((((from_line == 1) || (from_line == 6)) // pawn on its initial slot |
||
688 | && (from_line + 2 * movement_direction == to_line) && (from_column == to_column) // target position is the position we want |
||
689 | && (move->slots[from_line + movement_direction][to_column].part == PART_NONE) // intermediate slot free |
||
690 | && (move->slots[to_line][to_column].part == PART_NONE)) // target slot free |
||
691 | || ((((movement_direction == 1) && (from_line < 7)) || ((movement_direction == -1) && (from_line > 0))) // has room |
||
692 | && (from_line + movement_direction == to_line) && (from_column == to_column) // target position is the position we want |
||
693 | && (move->slots[to_line][to_column].part == PART_NONE)) // target slot free |
||
694 | || ((((movement_direction == 1) && (from_line < 7)) || ((movement_direction == -1) && (from_line > 0))) |
||
695 | && (from_column > 0) // has room |
||
696 | && (from_line + movement_direction == to_line) && (from_column - 1 == to_column) // target position is the position we want |
||
697 | && (move->slots[to_line][to_column].color != boardslot->color) // target slot NOT our color |
||
698 | && (move->slots[to_line][to_column].part != PART_NONE)) // target slot occupied |
||
699 | || ((((movement_direction == 1) && (from_line < 7)) || ((movement_direction == -1) && (from_line > 0))) |
||
700 | && (from_column < 7) // has room |
||
701 | && (from_line + movement_direction == to_line) && (from_column + 1 == to_column) // target position is the position we want |
||
702 | && (move->slots[to_line][to_column].color != boardslot->color) // target slot NOT our color |
||
703 | && (move->slots[to_line][to_column].part != PART_NONE))) // target slot occupied |
||
704 | { |
||
705 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
706 | return (true); // this move is possible |
||
707 | |||
708 | return (false); // else this pawn can't move in the claimed way (his king would be in check) |
||
709 | } |
||
710 | |||
711 | // if previous move was a pawn rush, see if pawn can take "en passant" |
||
712 | if (IS_VALID (move->target[0], move->target[1]) // last move is valid |
||
713 | && (move->part == PART_PAWN) // last move was a pawn |
||
714 | && (move->target[1] == move->source[1]) // pawn moved in column |
||
715 | && (abs (move->target[0] - move->source[0]) == 2) // pawn rushed |
||
716 | && (move->target[0] == from_line) // pawn is in line with us |
||
717 | && (move->target[1] - from_column == -1) // pawn is left to us |
||
718 | && (from_line + movement_direction == to_line) && (from_column - 1 == to_column) // target position is the position we want |
||
719 | && !Move_IsColorInCheckAfterTestMoveEP (move, from_line, from_column, to_line, to_column, move->target[0], move->target[1], boardslot->color)) |
||
720 | return (true); // this move is possible |
||
721 | else if (IS_VALID (move->target[0], move->target[1]) // last move is valid |
||
722 | && (move->part == PART_PAWN) // last move was a pawn |
||
723 | && (move->target[1] == move->source[1]) // pawn moved in column |
||
724 | && (abs (move->target[0] - move->source[0]) == 2) // pawn rushed |
||
725 | && (move->target[0] == from_line) // pawn is in line with us |
||
726 | && (move->target[1] - from_column == 1) // pawn is right to us |
||
727 | && (from_line + movement_direction == to_line) && (from_column + 1 == to_column) // target position is the position we want |
||
728 | && !Move_IsColorInCheckAfterTestMoveEP (move, from_line, from_column, to_line, to_column, move->target[0], move->target[1], boardslot->color)) |
||
729 | return (true); // this move is possible |
||
730 | |||
731 | return (false); // this pawn can't move in the claimed way |
||
732 | } |
||
733 | |||
734 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
735 | ////////////////////////////////////////// ROOK ////////////////////////////////////////// |
||
736 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
737 | // else is it a rook ? |
||
738 | else if (boardslot->part == PART_ROOK) |
||
739 | { |
||
740 | // quick checks |
||
741 | if ((to_column != from_column) && (to_line != from_line)) |
||
742 | return (false); // rooks can only move horizontally or vertically |
||
743 | |||
744 | // do we want the rook to move upwards ? |
||
745 | if (to_line > from_line) |
||
746 | { |
||
747 | // see if rook can move upwards |
||
748 | for (index_line = from_line + 1; index_line < 8; index_line++) |
||
749 | if (!CAN_PLAY (index_line, to_column)) |
||
750 | return (false); // if rook can no longer move this way, stop searching |
||
751 | else if (index_line == to_line) |
||
752 | { |
||
753 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
754 | return (true); // this move is possible |
||
755 | |||
756 | return (false); // else this rook can't move in the claimed way (its king would be in check) |
||
757 | } |
||
758 | else if (move->slots[index_line][to_column].part != PART_NONE) |
||
759 | return (false); // rook can take a part there BUT it's not the location we want |
||
760 | } |
||
761 | |||
762 | // else do we want the rook to move downwards ? |
||
763 | else if (to_line < from_line) |
||
764 | { |
||
765 | // see if rook can move downwards |
||
766 | for (index_line = from_line - 1; index_line >= 0; index_line--) |
||
767 | if (!CAN_PLAY (index_line, to_column)) |
||
768 | return (false); // if rook can no longer move this way, stop searching |
||
769 | else if (index_line == to_line) |
||
770 | { |
||
771 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
772 | return (true); // this move is possible |
||
773 | |||
774 | return (false); // else this rook can't move in the claimed way (its king would be in check) |
||
775 | } |
||
776 | else if (move->slots[index_line][to_column].part != PART_NONE) |
||
777 | return (false); // rook can take a part there BUT it's not the location we want |
||
778 | } |
||
779 | |||
780 | // else do we want the rook to move right ? |
||
781 | else if (to_column > from_column) |
||
782 | { |
||
783 | // see if rook can move right |
||
784 | for (index_column = from_column + 1; index_column < 8; index_column++) |
||
785 | if (!CAN_PLAY (to_line, index_column)) |
||
786 | return (false); // if rook can no longer move this way, stop searching |
||
787 | else if (index_column == to_column) |
||
788 | { |
||
789 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
790 | return (true); // this move is possible |
||
791 | |||
792 | return (false); // else this rook can't move in the claimed way (its king would be in check) |
||
793 | } |
||
794 | else if (move->slots[to_line][index_column].part != PART_NONE) |
||
795 | return (false); // rook can take a part there BUT it's not the location we want |
||
796 | } |
||
797 | |||
798 | // else do we want the rook to move left ? |
||
799 | else if (to_column < from_column) |
||
800 | { |
||
801 | // see if rook can move left |
||
802 | for (index_column = from_column - 1; index_column >= 0; index_column--) |
||
803 | if (!CAN_PLAY (to_line, index_column)) |
||
804 | return (false); // if rook can no longer move this way, stop searching |
||
805 | else if (index_column == to_column) |
||
806 | { |
||
807 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
808 | return (true); // this move is possible |
||
809 | |||
810 | return (false); // else this rook can't move in the claimed way (its king would be in check) |
||
811 | } |
||
812 | else if (move->slots[to_line][index_column].part != PART_NONE) |
||
813 | return (false); // rook can take a part there BUT it's not the location we want |
||
814 | } |
||
815 | |||
816 | return (false); // this rook can't move in the claimed way |
||
817 | } |
||
818 | |||
819 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
820 | ///////////////////////////////////////// KNIGHT ///////////////////////////////////////// |
||
821 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
822 | // else is it a knight ? |
||
823 | else if (boardslot->part == PART_KNIGHT) |
||
824 | { |
||
825 | // do we want to move that knight in one of the allowed directions ? |
||
826 | if (((from_line + 2 == to_line) && (from_column - 1 == to_column)) // NNW |
||
827 | || ((from_line + 2 == to_line) && (from_column + 1 == to_column)) // NNE |
||
828 | || ((from_line + 1 == to_line) && (from_column + 2 == to_column)) // ENE |
||
829 | || ((from_line - 1 == to_line) && (from_column + 2 == to_column)) // ESE |
||
830 | || ((from_line - 2 == to_line) && (from_column - 1 == to_column)) // SSW |
||
831 | || ((from_line - 2 == to_line) && (from_column + 1 == to_column)) // SSE |
||
832 | || ((from_line + 1 == to_line) && (from_column - 2 == to_column)) // WNW |
||
833 | || ((from_line - 1 == to_line) && (from_column - 2 == to_column))) // WSW |
||
834 | { |
||
835 | if (!CAN_PLAY (to_line, to_column)) |
||
836 | return (false); // if knight can't move there (out of board, or some of our parts there), return false |
||
837 | else if (Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
838 | return (false); // if knight would leave his king in check there, return false |
||
839 | |||
840 | return (true); // else this move is safe |
||
841 | } |
||
842 | |||
843 | return (false); // this knight can't move in the claimed way |
||
844 | } |
||
845 | |||
846 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
847 | ///////////////////////////////////////// BISHOP ///////////////////////////////////////// |
||
848 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
849 | // else is it a bishop ? |
||
850 | else if (boardslot->part == PART_BISHOP) |
||
851 | { |
||
852 | // quick checks |
||
853 | if (abs (to_column - from_column) != abs (to_line - from_line)) |
||
854 | return (false); // bishops can only move diagonally |
||
855 | |||
856 | // do we want to move the bishop NE ? |
||
857 | if ((to_line > from_line) && (to_column > from_column)) |
||
858 | { |
||
859 | // see if bishop can move NE |
||
860 | for (index_line = from_line + 1, index_column = from_column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++) |
||
861 | if (!CAN_PLAY (index_line, index_column)) |
||
862 | return (false); // if bishop can no longer move this way, stop searching |
||
863 | else if ((index_line == to_line) && (index_column == to_column)) |
||
864 | { |
||
865 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color)) |
||
866 | return (true); // this move is possible |
||
867 | |||
868 | return (false); // else this bishop can't move in the claimed way (his king would be in check) |
||
869 | } |
||
870 | else if (move->slots[index_line][index_column].part != PART_NONE) |
||
871 | return (false); // bishop can take a part there BUT it's not the location we want |
||
872 | } |
||
873 | |||
874 | // else do we want to move the bishop SE ? |
||
875 | else if ((to_line < from_line) && (to_column > from_column)) |
||
876 | { |
||
877 | // see if bishop can move SE |
||
878 | for (index_line = from_line - 1, index_column = from_column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++) |
||
879 | if (!CAN_PLAY (index_line, index_column)) |
||
880 | return (false); // if bishop can no longer move this way, stop searching |
||
881 | else if ((index_line == to_line) && (index_column == to_column)) |
||
882 | { |
||
883 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color)) |
||
884 | return (true); // this move is possible |
||
885 | |||
886 | return (false); // else this bishop can't move in the claimed way (his king would be in check) |
||
887 | } |
||
888 | else if (move->slots[index_line][index_column].part != PART_NONE) |
||
889 | return (false); // bishop can take a part there BUT it's not the location we want |
||
890 | } |
||
891 | |||
892 | // else do we want to move the bishop NW ? |
||
893 | else if ((to_line > from_line) && (to_column < from_column)) |
||
894 | { |
||
895 | // see if bishop can move NW |
||
896 | for (index_line = from_line + 1, index_column = from_column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--) |
||
897 | if (!CAN_PLAY (index_line, index_column)) |
||
898 | return (false); // if bishop can no longer move this way, stop searching |
||
899 | else if ((index_line == to_line) && (index_column == to_column)) |
||
900 | { |
||
901 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color)) |
||
902 | return (true); // this move is possible |
||
903 | |||
904 | return (false); // else this bishop can't move in the claimed way (his king would be in check) |
||
905 | } |
||
906 | else if (move->slots[index_line][index_column].part != PART_NONE) |
||
907 | return (false); // bishop can take a part there BUT it's not the location we want |
||
908 | } |
||
909 | |||
910 | // else do we want to move the bishop SW ? |
||
911 | else if ((to_line < from_line) && (to_column < from_column)) |
||
912 | { |
||
913 | // see if bishop can move SW |
||
914 | for (index_line = from_line - 1, index_column = from_column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--) |
||
915 | if (!CAN_PLAY (index_line, index_column)) |
||
916 | return (false); // if bishop can no longer move this way, stop searching |
||
917 | else if ((index_line == to_line) && (index_column == to_column)) |
||
918 | { |
||
919 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color)) |
||
920 | return (true); // this move is possible |
||
921 | |||
922 | return (false); // else this bishop can't move in the claimed way (his king would be in check) |
||
923 | } |
||
924 | else if (move->slots[index_line][index_column].part != PART_NONE) |
||
925 | return (false); // bishop can take a part there BUT it's not the location we want |
||
926 | } |
||
927 | |||
928 | return (false); // this bishop can't move in the claimed way |
||
929 | } |
||
930 | |||
931 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
932 | ///////////////////////////////////////// QUEEN ////////////////////////////////////////// |
||
933 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
934 | // else is it a queen ? |
||
935 | else if (boardslot->part == PART_QUEEN) |
||
936 | { |
||
937 | // quick checks |
||
938 | if ((to_column != from_column) && (to_line != from_line) |
||
939 | && (abs (to_column - from_column) != abs (to_line - from_line))) |
||
940 | return (false); // queens can only move horizontally, vertically or diagonally |
||
941 | |||
942 | // do we want to move that queen vertically ? |
||
943 | if (from_column == to_column) |
||
944 | { |
||
945 | // do we want to move her upwards ? |
||
946 | if (to_line > from_line) |
||
947 | { |
||
948 | // see if queen can move upwards |
||
949 | for (index_line = from_line + 1; index_line < 8; index_line++) |
||
950 | if (!CAN_PLAY (index_line, to_column)) |
||
951 | return (false); // if queen can no longer move this way, stop searching |
||
952 | else if (index_line == to_line) |
||
953 | { |
||
954 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
955 | return (true); // this move is possible |
||
956 | |||
957 | return (false); // else this queen can't move in the claimed way (her king would be in check) |
||
958 | } |
||
959 | else if (move->slots[index_line][to_column].part != PART_NONE) |
||
960 | return (false); // queen can take a part there BUT it's not the location we want |
||
961 | } |
||
962 | |||
963 | // else do we want to move her downwards ? |
||
964 | else if (to_line < from_line) |
||
965 | { |
||
966 | // see if queen can move downwards |
||
967 | for (index_line = from_line - 1; index_line >= 0; index_line--) |
||
968 | if (!CAN_PLAY (index_line, to_column)) |
||
969 | return (false); // if queen can no longer move this way, stop searching |
||
970 | else if (index_line == to_line) |
||
971 | { |
||
972 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
973 | return (true); // this move is possible |
||
974 | |||
975 | return (false); // else this queen can't move in the claimed way (her king would be in check) |
||
976 | } |
||
977 | else if (move->slots[index_line][to_column].part != PART_NONE) |
||
978 | return (false); // queen can take a part there BUT it's not the location we want |
||
979 | } |
||
980 | |||
981 | return (false); // this queen can't move in the claimed way |
||
982 | } |
||
983 | |||
984 | // else do we want to move that queen horizontally ? |
||
985 | else if (from_line == to_line) |
||
986 | { |
||
987 | // do we want this queen to move right ? |
||
988 | if (to_column > from_column) |
||
989 | { |
||
990 | // see if queen can move right |
||
991 | for (index_column = from_column + 1; index_column < 8; index_column++) |
||
992 | if (!CAN_PLAY (to_line, index_column)) |
||
993 | return (false); // if queen can no longer move this way, stop searching |
||
994 | else if (index_column == to_column) |
||
995 | { |
||
996 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
997 | return (true); // this move is possible |
||
998 | |||
999 | return (false); // else this queen can't move in the claimed way (her king would be in check) |
||
1000 | } |
||
1001 | else if (move->slots[to_line][index_column].part != PART_NONE) |
||
1002 | return (false); // queen can take a part there BUT it's not the location we want |
||
1003 | } |
||
1004 | |||
1005 | // else do we want this queen to move left ? |
||
1006 | else if (to_column < from_column) |
||
1007 | { |
||
1008 | // see if queen can move left |
||
1009 | for (index_column = from_column - 1; index_column >= 0; index_column--) |
||
1010 | if (!CAN_PLAY (to_line, index_column)) |
||
1011 | return (false); // if queen can no longer move this way, stop searching |
||
1012 | else if (index_column == to_column) |
||
1013 | { |
||
1014 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
1015 | return (true); // this move is possible |
||
1016 | |||
1017 | return (false); // else this queen can't move in the claimed way (her king would be in check) |
||
1018 | } |
||
1019 | else if (move->slots[to_line][index_column].part != PART_NONE) |
||
1020 | return (false); // queen can take a part there BUT it's not the location we want |
||
1021 | } |
||
1022 | |||
1023 | return (false); // this queen can't move in the claimed way |
||
1024 | } |
||
1025 | |||
1026 | // else do we want to move the queen NE ? |
||
1027 | else if ((to_line > from_line) && (to_column > from_column)) |
||
1028 | { |
||
1029 | // see if queen can move NE |
||
1030 | for (index_line = from_line + 1, index_column = from_column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++) |
||
1031 | if (!CAN_PLAY (index_line, index_column)) |
||
1032 | return (false); // if queen can no longer move this way, stop searching |
||
1033 | else if ((index_line == to_line) && (index_column == to_column)) |
||
1034 | { |
||
1035 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color)) |
||
1036 | return (true); // this move is possible |
||
1037 | |||
1038 | return (false); // else this queen can't move in the claimed way (her king would be in check) |
||
1039 | } |
||
1040 | else if (move->slots[index_line][index_column].part != PART_NONE) |
||
1041 | return (false); // queen can take a part there BUT it's not the location we want |
||
1042 | } |
||
1043 | |||
1044 | // else do we want to move the queen SE ? |
||
1045 | else if ((to_line < from_line) && (to_column > from_column)) |
||
1046 | { |
||
1047 | // see if queen can move SE |
||
1048 | for (index_line = from_line - 1, index_column = from_column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++) |
||
1049 | if (!CAN_PLAY (index_line, index_column)) |
||
1050 | return (false); // if queen can no longer move this way, stop searching |
||
1051 | else if ((index_line == to_line) && (index_column == to_column)) |
||
1052 | { |
||
1053 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color)) |
||
1054 | return (true); // this move is possible |
||
1055 | |||
1056 | return (false); // else this queen can't move in the claimed way (her king would be in check) |
||
1057 | } |
||
1058 | else if (move->slots[index_line][index_column].part != PART_NONE) |
||
1059 | return (false); // queen can take a part there BUT it's not the location we want |
||
1060 | } |
||
1061 | |||
1062 | // else do we want to move the queen NW ? |
||
1063 | else if ((to_line > from_line) && (to_column < from_column)) |
||
1064 | { |
||
1065 | // see if queen can move NW |
||
1066 | for (index_line = from_line + 1, index_column = from_column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--) |
||
1067 | if (!CAN_PLAY (index_line, index_column)) |
||
1068 | return (false); // if queen can no longer move this way, stop searching |
||
1069 | else if ((index_line == to_line) && (index_column == to_column)) |
||
1070 | { |
||
1071 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color)) |
||
1072 | return (true); // this move is possible |
||
1073 | |||
1074 | return (false); // else this queen can't move in the claimed way (her king would be in check) |
||
1075 | } |
||
1076 | else if (move->slots[index_line][index_column].part != PART_NONE) |
||
1077 | return (false); // queen can take a part there BUT it's not the location we want |
||
1078 | } |
||
1079 | |||
1080 | // else do we want to move the queen SW ? |
||
1081 | else if ((to_line < from_line) && (to_column < from_column)) |
||
1082 | { |
||
1083 | // see if queen can move SW |
||
1084 | for (index_line = from_line - 1, index_column = from_column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--) |
||
1085 | if (!CAN_PLAY (index_line, index_column)) |
||
1086 | return (false); // if queen can no longer move this way, stop searching |
||
1087 | else if ((index_line == to_line) && (index_column == to_column)) |
||
1088 | { |
||
1089 | if (!Move_IsColorInCheckAfterTestMove (move, from_line, from_column, index_line, index_column, boardslot->color)) |
||
1090 | return (true); // this move is possible |
||
1091 | |||
1092 | return (false); // else this queen can't move in the claimed way (her king would be in check) |
||
1093 | } |
||
1094 | else if (move->slots[index_line][index_column].part != PART_NONE) |
||
1095 | return (false); // queen can take a part there BUT it's not the location we want |
||
1096 | } |
||
1097 | |||
1098 | return (false); // this queen can't move in the claimed way |
||
1099 | } |
||
1100 | |||
1101 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1102 | ////////////////////////////////////////// KING ////////////////////////////////////////// |
||
1103 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1104 | // else is it a king ? |
||
1105 | else if (boardslot->part == PART_KING) |
||
1106 | { |
||
1107 | // do we want to move that king in one of the allowed directions ? |
||
1108 | if (((from_line + 1 == to_line) && (from_column == to_column)) // up |
||
1109 | || ((from_line - 1 == to_line) && (from_column == to_column)) // down |
||
1110 | || ((from_line == to_line) && (from_column + 1 == to_column)) // right |
||
1111 | || ((from_line == to_line) && (from_column - 1 == to_column)) // left |
||
1112 | || ((from_line + 1 == to_line) && (from_column + 1 == to_column)) // NE |
||
1113 | || ((from_line - 1 == to_line) && (from_column + 1 == to_column)) // SE |
||
1114 | || ((from_line + 1 == to_line) && (from_column - 1 == to_column)) // NW |
||
1115 | || ((from_line - 1 == to_line) && (from_column - 1 == to_column))) // SW |
||
1116 | { |
||
1117 | if (!CAN_PLAY (to_line, to_column)) |
||
1118 | return (false); // if king can't move there, return false |
||
1119 | else if (Move_IsColorInCheckAfterTestMove (move, from_line, from_column, to_line, to_column, boardslot->color)) |
||
1120 | return (false); // if king would be in check there, return false |
||
1121 | |||
1122 | return (true); // else this move is safe |
||
1123 | } |
||
1124 | |||
1125 | return (false); // if not, this king can't move in the claimed way |
||
1126 | } |
||
1127 | |||
1128 | return (false); // this move is not possible, else we'd have returned earlier |
||
1129 | } |
||
1130 | |||
1131 | |||
1132 | bool Move_FindRandomMove (boardmove_t *move, int color, boardmove_t *random_move) |
||
1133 | { |
||
1134 | // this function returns TRUE if it can find a random move (most of the time blunderous) |
||
1135 | // and sets its coordinates in the given output parameters |
||
1136 | |||
1137 | boardslot_t *boardslot; |
||
1138 | int movement_direction; |
||
1139 | int line; |
||
1140 | int column; |
||
1141 | int index_line; |
||
1142 | int index_column; |
||
1143 | boardmove_t *possiblemoves; // mallocated |
||
1144 | int possiblemove_count; |
||
1145 | int move_index; |
||
1146 | |||
1147 | // assume no possible move until told otherwise |
||
1148 | possiblemoves = NULL; |
||
1149 | possiblemove_count = 0; |
||
1150 | |||
1151 | // cycle through all the board and find our parts |
||
1152 | for (line = 0; line < 8; line++) |
||
1153 | for (column = 0; column < 8; column++) |
||
1154 | { |
||
1155 | boardslot = &move->slots[line][column]; // quick access to grid slot |
||
1156 | |||
1157 | if ((boardslot->part == PART_NONE) || (boardslot->color != color)) |
||
1158 | continue; // if this location is empty or not ours, we aren't interested in it |
||
1159 | |||
1160 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1161 | ////////////////////////////////////////// PAWN ////////////////////////////////////////// |
||
1162 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1163 | // is it a pawn ? |
||
1164 | if (boardslot->part == PART_PAWN) |
||
1165 | { |
||
1166 | // figure out movement direction |
||
1167 | if (boardslot->color == COLOR_WHITE) |
||
1168 | movement_direction = 1; |
||
1169 | else |
||
1170 | movement_direction = -1; |
||
1171 | |||
1172 | // see if pawn can move forward |
||
1173 | if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0))) |
||
1174 | && (move->slots[line + movement_direction][column].part == PART_NONE) // target slot free |
||
1175 | && !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column, color)) |
||
1176 | { |
||
1177 | if (((movement_direction == 1) && (line + movement_direction == 7)) |
||
1178 | || ((movement_direction == -1) && (line + movement_direction == 0))) |
||
1179 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column, PART_QUEEN, false, false); // save promotional move |
||
1180 | else |
||
1181 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column, PART_NONE, false, false); // save possible move |
||
1182 | } |
||
1183 | |||
1184 | // see if pawn can take a piece on its left |
||
1185 | if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0))) |
||
1186 | && (column > 0) // has room |
||
1187 | && (move->slots[line + movement_direction][column - 1].color != color) // target slot NOT our color |
||
1188 | && (move->slots[line + movement_direction][column - 1].part != PART_NONE) // target slot occupied |
||
1189 | && !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column - 1, color)) |
||
1190 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column - 1, PART_NONE, true, false); // save possible move |
||
1191 | |||
1192 | // see if pawn can take a piece on its right |
||
1193 | if ((((movement_direction == 1) && (line < 7)) || ((movement_direction == -1) && (line > 0))) |
||
1194 | && (column < 7) // has room |
||
1195 | && (move->slots[line + movement_direction][column + 1].color != color) // target slot NOT our color |
||
1196 | && (move->slots[line + movement_direction][column + 1].part != PART_NONE) // target slot occupied |
||
1197 | && !Move_IsColorInCheckAfterTestMove (move, line, column, line + movement_direction, column + 1, color)) |
||
1198 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column + 1, PART_NONE, true, false); // save possible move |
||
1199 | |||
1200 | // if previous move was a pawn rush, see if pawn can take "en passant" |
||
1201 | if ((move->part == PART_PAWN) // last move was a pawn |
||
1202 | && (move->target[1] == move->source[1]) // pawn moved in column |
||
1203 | && (abs (move->target[0] - move->source[0]) == 2) // pawn rushed |
||
1204 | && (move->target[0] == line) // pawn is in line with us |
||
1205 | && (move->target[1] - column == -1) // pawn is left to us |
||
1206 | && !Move_IsColorInCheckAfterTestMoveEP (move, line, column, line + movement_direction, column - 1, move->target[0], move->target[1], color)) |
||
1207 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column - 1, PART_NONE, true, true); // save possible move |
||
1208 | if ((move->part == PART_PAWN) // last move was a pawn |
||
1209 | && (move->target[1] == move->source[1]) // pawn moved in column |
||
1210 | && (abs (move->target[0] - move->source[0]) == 2) // pawn rushed |
||
1211 | && (move->target[0] == line) // pawn is in line with us |
||
1212 | && (move->target[1] - column == 1) // pawn is right to us |
||
1213 | && !Move_IsColorInCheckAfterTestMoveEP (move, line, column, line + movement_direction, column + 1, move->target[0], move->target[1], color)) |
||
1214 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_PAWN, line, column, line + movement_direction, column + 1, PART_NONE, true, true); // save possible move |
||
1215 | } |
||
1216 | |||
1217 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1218 | ////////////////////////////////////////// ROOK ////////////////////////////////////////// |
||
1219 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1220 | // else is it a rook ? |
||
1221 | else if (boardslot->part == PART_ROOK) |
||
1222 | { |
||
1223 | // see if rook can move upwards |
||
1224 | for (index_line = line + 1; index_line < 8; index_line++) |
||
1225 | if (!CAN_PLAY (index_line, column)) |
||
1226 | break; // if part can no longer move this way, stop searching |
||
1227 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color)) |
||
1228 | { |
||
1229 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_ROOK, line, column, index_line, column, PART_NONE, (move->slots[index_line][column].part != PART_NONE), false); // save possible move |
||
1230 | if (move->slots[index_line][column].part != PART_NONE) |
||
1231 | break; // this move is possible, but no further moves are possible in the same direction |
||
1232 | } |
||
1233 | |||
1234 | // see if rook can move downwards |
||
1235 | for (index_line = line - 1; index_line >= 0; index_line--) |
||
1236 | if (!CAN_PLAY (index_line, column)) |
||
1237 | break; // if part can no longer move this way, stop searching |
||
1238 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color)) |
||
1239 | { |
||
1240 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_ROOK, line, column, index_line, column, PART_NONE, (move->slots[index_line][column].part != PART_NONE), false); // save possible move |
||
1241 | if (move->slots[index_line][column].part != PART_NONE) |
||
1242 | break; // this move is possible, but no further moves are possible in the same direction |
||
1243 | } |
||
1244 | |||
1245 | // see if rook can move right |
||
1246 | for (index_column = column + 1; index_column < 8; index_column++) |
||
1247 | if (!CAN_PLAY (line, index_column)) |
||
1248 | break; // if part can no longer move this way, stop searching |
||
1249 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color)) |
||
1250 | { |
||
1251 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_ROOK, line, column, line, index_column, PART_NONE, (move->slots[line][index_column].part != PART_NONE), false); // save possible move |
||
1252 | if (move->slots[line][index_column].part != PART_NONE) |
||
1253 | break; // this move is possible, but no further moves are possible in the same direction |
||
1254 | } |
||
1255 | |||
1256 | // see if rook can move left |
||
1257 | for (index_column = column - 1; index_column >= 0; index_column--) |
||
1258 | if (!CAN_PLAY (line, index_column)) |
||
1259 | break; // if part can no longer move this way, stop searching |
||
1260 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color)) |
||
1261 | { |
||
1262 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_ROOK, line, column, line, index_column, PART_NONE, (move->slots[line][index_column].part != PART_NONE), false); // save possible move |
||
1263 | if (move->slots[line][index_column].part != PART_NONE) |
||
1264 | break; // this move is possible, but no further moves are possible in the same direction |
||
1265 | } |
||
1266 | } |
||
1267 | |||
1268 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1269 | ///////////////////////////////////////// KNIGHT ///////////////////////////////////////// |
||
1270 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1271 | // else is it a knight ? |
||
1272 | else if (boardslot->part == PART_KNIGHT) |
||
1273 | { |
||
1274 | // see if knight can move NNW |
||
1275 | if (CAN_PLAY (line + 2, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 2, column - 1, color)) |
||
1276 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line + 2, column - 1, PART_NONE, (move->slots[line + 2][column - 1].part != PART_NONE), false); // save possible move |
||
1277 | |||
1278 | // see if knight can move NNE |
||
1279 | if (CAN_PLAY (line + 2, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 2, column + 1, color)) |
||
1280 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line + 2, column + 1, PART_NONE, (move->slots[line + 2][column + 1].part != PART_NONE), false); // save possible move |
||
1281 | |||
1282 | // see if knight can move ENE |
||
1283 | if (CAN_PLAY (line + 1, column + 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column + 2, color)) |
||
1284 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line + 1, column + 2, PART_NONE, (move->slots[line + 1][column + 2].part != PART_NONE), false); // save possible move |
||
1285 | |||
1286 | // see if knight can move ESE |
||
1287 | if (CAN_PLAY (line - 1, column + 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column + 2, color)) |
||
1288 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line - 1, column + 2, PART_NONE, (move->slots[line - 1][column + 2].part != PART_NONE), false); // save possible move |
||
1289 | |||
1290 | // see if knight can move SSW |
||
1291 | if (CAN_PLAY (line - 2, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 2, column - 1, color)) |
||
1292 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line - 2, column - 1, PART_NONE, (move->slots[line - 2][column - 1].part != PART_NONE), false); // save possible move |
||
1293 | |||
1294 | // see if knight can move SSE |
||
1295 | if (CAN_PLAY (line - 2, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 2, column + 1, color)) |
||
1296 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line - 2, column + 1, PART_NONE, (move->slots[line - 2][column + 1].part != PART_NONE), false); // save possible move |
||
1297 | |||
1298 | // see if knight can move WNW |
||
1299 | if (CAN_PLAY (line + 1, column - 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column - 2, color)) |
||
1300 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line + 1, column - 2, PART_NONE, (move->slots[line + 1][column - 2].part != PART_NONE), false); // save possible move |
||
1301 | |||
1302 | // see if knight can move WSW |
||
1303 | if (CAN_PLAY (line - 1, column - 2) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column - 2, color)) |
||
1304 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KNIGHT, line, column, line - 1, column - 2, PART_NONE, (move->slots[line - 1][column - 2].part != PART_NONE), false); // save possible move |
||
1305 | } |
||
1306 | |||
1307 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1308 | ///////////////////////////////////////// BISHOP ///////////////////////////////////////// |
||
1309 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1310 | // else is it a bishop ? |
||
1311 | else if (boardslot->part == PART_BISHOP) |
||
1312 | { |
||
1313 | // see if bishop can move NE |
||
1314 | for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++) |
||
1315 | if (!CAN_PLAY (index_line, index_column)) |
||
1316 | break; // if part can no longer move this way, stop searching |
||
1317 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
1318 | { |
||
1319 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_BISHOP, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move |
||
1320 | if (move->slots[index_line][index_column].part != PART_NONE) |
||
1321 | break; // this move is possible, but no further moves are possible in the same direction |
||
1322 | } |
||
1323 | |||
1324 | // see if bishop can move SE |
||
1325 | for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++) |
||
1326 | if (!CAN_PLAY (index_line, index_column)) |
||
1327 | break; // if part can no longer move this way, stop searching |
||
1328 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
1329 | { |
||
1330 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_BISHOP, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move |
||
1331 | if (move->slots[index_line][index_column].part != PART_NONE) |
||
1332 | break; // this move is possible, but no further moves are possible in the same direction |
||
1333 | } |
||
1334 | |||
1335 | // see if bishop can move NW |
||
1336 | for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--) |
||
1337 | if (!CAN_PLAY (index_line, index_column)) |
||
1338 | break; // if part can no longer move this way, stop searching |
||
1339 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
1340 | { |
||
1341 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_BISHOP, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move |
||
1342 | if (move->slots[index_line][index_column].part != PART_NONE) |
||
1343 | break; // this move is possible, but no further moves are possible in the same direction |
||
1344 | } |
||
1345 | |||
1346 | // see if bishop can move SW |
||
1347 | for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--) |
||
1348 | if (!CAN_PLAY (index_line, index_column)) |
||
1349 | break; // if part can no longer move this way, stop searching |
||
1350 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
1351 | { |
||
1352 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_BISHOP, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move |
||
1353 | if (move->slots[index_line][index_column].part != PART_NONE) |
||
1354 | break; // this move is possible, but no further moves are possible in the same direction |
||
1355 | } |
||
1356 | } |
||
1357 | |||
1358 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1359 | ///////////////////////////////////////// QUEEN ////////////////////////////////////////// |
||
1360 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1361 | // else is it a queen ? |
||
1362 | else if (boardslot->part == PART_QUEEN) |
||
1363 | { |
||
1364 | // see if queen can move upwards |
||
1365 | for (index_line = line + 1; index_line < 8; index_line++) |
||
1366 | if (!CAN_PLAY (index_line, column)) |
||
1367 | break; // if part can no longer move this way, stop searching |
||
1368 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color)) |
||
1369 | { |
||
1370 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, column, PART_NONE, (move->slots[index_line][column].part != PART_NONE), false); // save possible move |
||
1371 | if (move->slots[index_line][column].part != PART_NONE) |
||
1372 | break; // this move is possible, but no further moves are possible in the same direction |
||
1373 | } |
||
1374 | |||
1375 | // see if queen can move downwards |
||
1376 | for (index_line = line - 1; index_line >= 0; index_line--) |
||
1377 | if (!CAN_PLAY (index_line, column)) |
||
1378 | break; // if part can no longer move this way, stop searching |
||
1379 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, column, color)) |
||
1380 | { |
||
1381 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, column, PART_NONE, (move->slots[index_line][column].part != PART_NONE), false); // save possible move |
||
1382 | if (move->slots[index_line][column].part != PART_NONE) |
||
1383 | break; // this move is possible, but no further moves are possible in the same direction |
||
1384 | } |
||
1385 | |||
1386 | // see if queen can move right |
||
1387 | for (index_column = column + 1; index_column < 8; index_column++) |
||
1388 | if (!CAN_PLAY (line, index_column)) |
||
1389 | break; // if part can no longer move this way, stop searching |
||
1390 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color)) |
||
1391 | { |
||
1392 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, line, index_column, PART_NONE, (move->slots[line][index_column].part != PART_NONE), false); // save possible move |
||
1393 | if (move->slots[line][index_column].part != PART_NONE) |
||
1394 | break; // this move is possible, but no further moves are possible in the same direction |
||
1395 | } |
||
1396 | |||
1397 | // see if queen can move left |
||
1398 | for (index_column = column - 1; index_column >= 0; index_column--) |
||
1399 | if (!CAN_PLAY (line, index_column)) |
||
1400 | break; // if part can no longer move this way, stop searching |
||
1401 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, line, index_column, color)) |
||
1402 | { |
||
1403 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, line, index_column, PART_NONE, (move->slots[line][index_column].part != PART_NONE), false); // save possible move |
||
1404 | if (move->slots[line][index_column].part != PART_NONE) |
||
1405 | break; // this move is possible, but no further moves are possible in the same direction |
||
1406 | } |
||
1407 | |||
1408 | // see if queen can move NE |
||
1409 | for (index_line = line + 1, index_column = column + 1; (index_line < 8) && (index_column < 8); index_line++, index_column++) |
||
1410 | if (!CAN_PLAY (index_line, index_column)) |
||
1411 | break; // if part can no longer move this way, stop searching |
||
1412 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
1413 | { |
||
1414 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move |
||
1415 | if (move->slots[index_line][index_column].part != PART_NONE) |
||
1416 | break; // this move is possible, but no further moves are possible in the same direction |
||
1417 | } |
||
1418 | |||
1419 | // see if queen can move SE |
||
1420 | for (index_line = line - 1, index_column = column + 1; (index_line >= 0) && (index_column < 8); index_line--, index_column++) |
||
1421 | if (!CAN_PLAY (index_line, index_column)) |
||
1422 | break; // if part can no longer move this way, stop searching |
||
1423 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
1424 | { |
||
1425 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move |
||
1426 | if (move->slots[index_line][index_column].part != PART_NONE) |
||
1427 | break; // this move is possible, but no further moves are possible in the same direction |
||
1428 | } |
||
1429 | |||
1430 | // see if queen can move NW |
||
1431 | for (index_line = line + 1, index_column = column - 1; (index_line < 8) && (index_column >= 0); index_line++, index_column--) |
||
1432 | if (!CAN_PLAY (index_line, index_column)) |
||
1433 | break; // if part can no longer move this way, stop searching |
||
1434 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
1435 | { |
||
1436 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move |
||
1437 | if (move->slots[index_line][index_column].part != PART_NONE) |
||
1438 | break; // this move is possible, but no further moves are possible in the same direction |
||
1439 | } |
||
1440 | |||
1441 | // see if queen can move SW |
||
1442 | for (index_line = line - 1, index_column = column - 1; (index_line >= 0) && (index_column >= 0); index_line--, index_column--) |
||
1443 | if (!CAN_PLAY (index_line, index_column)) |
||
1444 | break; // if part can no longer move this way, stop searching |
||
1445 | else if (!Move_IsColorInCheckAfterTestMove (move, line, column, index_line, index_column, color)) |
||
1446 | { |
||
1447 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_QUEEN, line, column, index_line, index_column, PART_NONE, (move->slots[index_line][index_column].part != PART_NONE), false); // save possible move |
||
1448 | if (move->slots[index_line][index_column].part != PART_NONE) |
||
1449 | break; // this move is possible, but no further moves are possible in the same direction |
||
1450 | } |
||
1451 | } |
||
1452 | |||
1453 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1454 | ////////////////////////////////////////// KING ////////////////////////////////////////// |
||
1455 | ////////////////////////////////////////////////////////////////////////////////////////// |
||
1456 | // else is it a king ? |
||
1457 | else if (boardslot->part == PART_KING) |
||
1458 | { |
||
1459 | // see if king can move up |
||
1460 | if (CAN_PLAY (line + 1, column) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column, color)) |
||
1461 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line + 1, column, PART_NONE, (move->slots[line + 1][column].part != PART_NONE), false); // save possible move |
||
1462 | |||
1463 | // see if king can move down |
||
1464 | if (CAN_PLAY (line - 1, column) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column, color)) |
||
1465 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line - 1, column, PART_NONE, (move->slots[line - 1][column].part != PART_NONE), false); // save possible move |
||
1466 | |||
1467 | // see if king can move right |
||
1468 | if (CAN_PLAY (line, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line, column + 1, color)) |
||
1469 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line, column + 1, PART_NONE, (move->slots[line][column + 1].part != PART_NONE), false); // save possible move |
||
1470 | |||
1471 | // see if king can move left |
||
1472 | if (CAN_PLAY (line, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line, column - 1, color)) |
||
1473 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line, column - 1, PART_NONE, (move->slots[line][column - 1].part != PART_NONE), false); // save possible move |
||
1474 | |||
1475 | // see if king can move NE |
||
1476 | if (CAN_PLAY (line + 1, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column + 1, color)) |
||
1477 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line + 1, column + 1, PART_NONE, (move->slots[line + 1][column + 1].part != PART_NONE), false); // save possible move |
||
1478 | |||
1479 | // see if king can move SE |
||
1480 | if (CAN_PLAY (line - 1, column + 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column + 1, color)) |
||
1481 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line - 1, column + 1, PART_NONE, (move->slots[line - 1][column + 1].part != PART_NONE), false); // save possible move |
||
1482 | |||
1483 | // see if king can move NW |
||
1484 | if (CAN_PLAY (line + 1, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line + 1, column - 1, color)) |
||
1485 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line + 1, column - 1, PART_NONE, (move->slots[line + 1][column - 1].part != PART_NONE), false); // save possible move |
||
1486 | |||
1487 | // see if king can move SW |
||
1488 | if (CAN_PLAY (line - 1, column - 1) && !Move_IsColorInCheckAfterTestMove (move, line, column, line - 1, column - 1, color)) |
||
1489 | AddPossibleMove (&possiblemoves, &possiblemove_count, boardslot->color, PART_KING, line, column, line - 1, column - 1, PART_NONE, (move->slots[line - 1][column - 1].part != PART_NONE), false); // save possible move |
||
1490 | } |
||
1491 | } |
||
1492 | |||
1493 | // now that all the table has been parsed, see if we have some possible moves |
||
1494 | |||
1495 | if (possiblemove_count == 0) |
||
1496 | return (false); // if none, return FALSE (it means that we are stalemate, but there's a faster function to check that) |
||
1497 | |||
1498 | move_index = rand () % possiblemove_count; // select a possible move at random |
||
1499 | memcpy (random_move, &possiblemoves[move_index], sizeof (boardmove_t)); // copy it into destination variable |
||
1500 | |||
1501 | SAFE_free ((void **) &possiblemoves); // free the possible moves array, we no longer need them |
||
1502 | return (true); // we did find some possible moves |
||
1503 | } |
||
1504 | |||
1505 | |||
1506 | int Move_CountPartsByColorAndType (boardmove_t *move, int color, int part_type) |
||
1507 | { |
||
1508 | // this function returns the amount of parts of the specified color and type left on board |
||
1509 | |||
1510 | int line; |
||
1511 | int column; |
||
1512 | int count; |
||
1513 | |||
1514 | count = 0; // assume none so far |
||
1515 | |||
1516 | // cycle through all the board... |
||
1517 | for (line = 0; line < 8; line++) |
||
1518 | for (column = 0; column < 8; column++) |
||
1519 | if ((move->slots[line][column].color == color) && (move->slots[line][column].part == part_type)) |
||
1520 | count++; // sum up all the parts of the same colour and type we find |
||
1521 | |||
1522 | return (count); // and return their quantity |
||
1523 | } |
||
1524 | |||
1525 | |||
1526 | bool Move_IsColorInCheckAfterTestMove (boardmove_t *move, int source_line, int source_column, int target_line, int target_column, int color) |
||
1527 | { |
||
1528 | // helper function to play a test move on a temporary board (which must have been previously allocated) |
||
1529 | |||
1530 | static boardmove_t temp_move; // declare this static so as not to allocate/free it continuously |
||
1531 | |||
1532 | memcpy (temp_move.slots, move->slots, sizeof (move->slots)); // have a copy of the table, then make the move |
||
1533 | memcpy (&temp_move.slots[target_line][target_column], &temp_move.slots[source_line][source_column], sizeof (boardslot_t)); |
||
1534 | memset (&temp_move.slots[source_line][source_column], 0, sizeof (boardslot_t)); // erase the source slot |
||
1535 | |||
1536 | return (Move_IsCheck (&temp_move, color)); // return whether the final board has the given color in check |
||
1537 | } |
||
1538 | |||
1539 | |||
1540 | bool Move_IsColorInCheckAfterTestMoveEP (boardmove_t *move, int source_line, int source_column, int target_line, int target_column, int clear_line, int clear_column, int color) |
||
1541 | { |
||
1542 | // helper function to play a test move on a temporary board (which must have been previously allocated) |
||
1543 | // En Passant version -- cleans the specified target before testing |
||
1544 | |||
1545 | static boardmove_t temp_move; // declare this static so as not to allocate/free it continuously |
||
1546 | |||
1547 | memcpy (temp_move.slots, move->slots, sizeof (move->slots)); // have a copy of the table, then make the move |
||
1548 | memcpy (&temp_move.slots[target_line][target_column], &temp_move.slots[source_line][source_column], sizeof (boardslot_t)); |
||
1549 | memset (&temp_move.slots[source_line][source_column], 0, sizeof (boardslot_t)); // erase the source slot |
||
1550 | |||
1551 | memset (&temp_move.slots[clear_line][clear_column], 0, sizeof (boardslot_t)); // erase the "en passant" target |
||
1552 | |||
1553 | return (Move_IsCheck (&temp_move, color)); // return whether the final board has the given color in check |
||
1554 | } |
||
1555 | |||
1556 | |||
1557 | void Move_DescribeInFEN (boardmove_t *move) |
||
1558 | { |
||
1559 | // convert a board and its part placements into a Forsyth Edwards notation, writing in the fen_string buffer |
||
1560 | |||
1561 | boardslot_t *slot; |
||
1562 | int line; |
||
1563 | int column; |
||
1564 | int free_slots; |
||
1565 | int length; |
||
1566 | |||
1567 | // first reset the string |
||
1568 | move->fen_string[0] = 0; |
||
1569 | |||
1570 | //////////////////////////////////////////////////////// |
||
1571 | // first part of the FEN notation is the parts placement |
||
1572 | |||
1573 | // cycle through each column, line after line, starting up left and going downwards right |
||
1574 | for (line = 7; line >= 0; line--) |
||
1575 | { |
||
1576 | free_slots = 0; // no free slot in that line yet |
||
1577 | |||
1578 | for (column = 0; column < 8; column++) |
||
1579 | { |
||
1580 | slot = &move->slots[line][column]; // quick access to current slot |
||
1581 | |||
1582 | if (slot->part == PART_ROOK) |
||
1583 | { |
||
1584 | // if there are free slots to mention, do it |
||
1585 | if (free_slots > 0) |
||
1586 | { |
||
1587 | length = wcslen (move->fen_string); // append the free slots count |
||
1588 | swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots); |
||
1589 | free_slots = 0; // reset the free slots count |
||
1590 | } |
||
1591 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"r" : (slot->color == COLOR_WHITE ? L"R": L"?"))); |
||
1592 | } |
||
1593 | else if (slot->part == PART_KNIGHT) |
||
1594 | { |
||
1595 | // if there are free slots to mention, do it |
||
1596 | if (free_slots > 0) |
||
1597 | { |
||
1598 | length = wcslen (move->fen_string); // append the free slots count |
||
1599 | swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots); |
||
1600 | free_slots = 0; // reset the free slots count |
||
1601 | } |
||
1602 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"n" : (slot->color == COLOR_WHITE ? L"N": L"?"))); |
||
1603 | } |
||
1604 | else if (slot->part == PART_BISHOP) |
||
1605 | { |
||
1606 | // if there are free slots to mention, do it |
||
1607 | if (free_slots > 0) |
||
1608 | { |
||
1609 | length = wcslen (move->fen_string); // append the free slots count |
||
1610 | swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots); |
||
1611 | free_slots = 0; // reset the free slots count |
||
1612 | } |
||
1613 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"b" : (slot->color == COLOR_WHITE ? L"B": L"?"))); |
||
1614 | } |
||
1615 | else if (slot->part == PART_QUEEN) |
||
1616 | { |
||
1617 | // if there are free slots to mention, do it |
||
1618 | if (free_slots > 0) |
||
1619 | { |
||
1620 | length = wcslen (move->fen_string); // append the free slots count |
||
1621 | swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots); |
||
1622 | free_slots = 0; // reset the free slots count |
||
1623 | } |
||
1624 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"q" : (slot->color == COLOR_WHITE ? L"Q": L"?"))); |
||
1625 | } |
||
1626 | else if (slot->part == PART_KING) |
||
1627 | { |
||
1628 | // if there are free slots to mention, do it |
||
1629 | if (free_slots > 0) |
||
1630 | { |
||
1631 | length = wcslen (move->fen_string); // append the free slots count |
||
1632 | swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots); |
||
1633 | free_slots = 0; // reset the free slots count |
||
1634 | } |
||
1635 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"k" : (slot->color == COLOR_WHITE ? L"K": L"?"))); |
||
1636 | } |
||
1637 | else if (slot->part == PART_PAWN) |
||
1638 | { |
||
1639 | // if there are free slots to mention, do it |
||
1640 | if (free_slots > 0) |
||
1641 | { |
||
1642 | length = wcslen (move->fen_string); // append the free slots count |
||
1643 | swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots); |
||
1644 | free_slots = 0; // reset the free slots count |
||
1645 | } |
||
1646 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), (slot->color == COLOR_BLACK ? L"p" : (slot->color == COLOR_WHITE ? L"P": L"?"))); |
||
1647 | } |
||
1648 | else |
||
1649 | free_slots++; // we found one free slot more |
||
1650 | |||
1651 | // are we at the end of a line ? |
||
1652 | if (column == 7) |
||
44 | pmbaty | 1653 | { |
1654 | // if there are free slots to mention, do it |
||
1655 | if (free_slots > 0) |
||
1656 | { |
||
1657 | length = wcslen (move->fen_string); // append the free slots count |
||
1658 | swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", free_slots); |
||
1659 | free_slots = 0; // reset the free slots count |
||
1660 | } |
||
1661 | if (line > 0) |
||
1662 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"/"); // at the end of each line, drop a slash (except on the last line) |
||
1663 | } |
||
1 | pmbaty | 1664 | } |
1665 | } |
||
1666 | |||
1667 | ////////////////////////////////////////////////////// |
||
1668 | // second part of the FEN notation is the side on move |
||
1669 | |||
1670 | // deduce the side to move according to last move's color |
||
1671 | if (move->color == COLOR_WHITE) |
||
1672 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L" b"); // black to move |
||
1673 | else |
||
1674 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L" w"); // white to move (this also catches the beginning of a game) |
||
1675 | |||
1676 | /////////////////////////////////////////////////////////////// |
||
1677 | // third part of the FEN notation is the castling possibilities |
||
1678 | |||
1679 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L" "); |
||
1680 | if (!(move->sides[COLOR_BLACK].longcastle_allowed | move->sides[COLOR_WHITE].longcastle_allowed | move->sides[COLOR_BLACK].shortcastle_allowed | move->sides[COLOR_WHITE].shortcastle_allowed)) |
||
1681 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"-"); // neither side can castle |
||
1682 | else |
||
1683 | { |
||
1684 | if (move->sides[COLOR_WHITE].shortcastle_allowed) |
||
1685 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"K"); // white can castle kingside |
||
1686 | if (move->sides[COLOR_WHITE].longcastle_allowed) |
||
1687 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"Q"); // white can castle queenside |
||
1688 | if (move->sides[COLOR_BLACK].shortcastle_allowed) |
||
1689 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"k"); // black can castle kingside |
||
1690 | if (move->sides[COLOR_BLACK].longcastle_allowed) |
||
1691 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"q"); // black can castle queenside |
||
1692 | } |
||
1693 | |||
1694 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
||
1695 | // fourth part of the FEN notation is the optional position for a pawn that can be taken en passant |
||
1696 | |||
40 | pmbaty | 1697 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L" "); // spacer |
1698 | |||
1 | pmbaty | 1699 | if ((move->part == PART_PAWN) // last move was a pawn |
1700 | && (move->target[1] == move->source[1]) // pawn moved in column |
||
1701 | && (abs (move->target[0] - move->source[0]) == 2)) // pawn rushed |
||
1702 | { |
||
1703 | // column |
||
1704 | if (move->source[1] == 0) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"a"); |
||
1705 | else if (move->source[1] == 1) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"b"); |
||
1706 | else if (move->source[1] == 2) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"c"); |
||
1707 | else if (move->source[1] == 3) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"d"); |
||
1708 | else if (move->source[1] == 4) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"e"); |
||
1709 | else if (move->source[1] == 5) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"f"); |
||
1710 | else if (move->source[1] == 6) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"g"); |
||
1711 | else if (move->source[1] == 7) wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"h"); |
||
1712 | else wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"?"); |
||
1713 | |||
1714 | // line (it's the line the pawn would be on if it had made a "normal" move) |
||
1715 | length = wcslen (move->fen_string); |
||
1716 | swprintf_s (&move->fen_string[length], WCHAR_SIZEOF (move->fen_string) - length, L"%d", 1 + (move->target[0] + move->source[0]) / 2); |
||
1717 | } |
||
40 | pmbaty | 1718 | else |
1719 | wcscat_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), L"-"); // last move was not a pawn rush |
||
1 | pmbaty | 1720 | |
1721 | return; // finished |
||
1722 | } |
||
1723 | |||
1724 | |||
1725 | bool Move_SetupFromFEN (boardmove_t *move, wchar_t *fen_string) |
||
1726 | { |
||
1727 | // this function sets up the given board according to the Forsyth-Edwards description fen_string makes of it |
||
1728 | |||
1729 | // FIXME : load fen, then load PGN, then back arrow. |
||
1730 | |||
1731 | int char_index; |
||
1732 | int length; |
||
1733 | int current_line; |
||
1734 | int current_column; |
||
1735 | int enpassant_line; |
||
1736 | int enpassant_column; |
||
1737 | |||
1738 | // reset the chess grid |
||
1739 | memset (&move->slots, 0, sizeof (move->slots)); |
||
1740 | |||
1741 | // reset the taken pieces for both sides |
||
1742 | SAFE_free ((void **) &move->sides[COLOR_WHITE].takenparts); // release memory space |
||
1743 | move->sides[COLOR_WHITE].takenpart_count = 0; |
||
1744 | SAFE_free ((void **) &move->sides[COLOR_BLACK].takenparts); // release memory space |
||
1745 | move->sides[COLOR_BLACK].takenpart_count = 0; |
||
1746 | |||
1747 | // reset the moves array comments and the moves array itself |
||
1748 | SAFE_free ((void **) &move->comment); |
||
1749 | |||
1750 | // DISallow both sides to castle, until told otherwise |
||
1751 | move->sides[COLOR_WHITE].shortcastle_allowed = false; |
||
1752 | move->sides[COLOR_WHITE].longcastle_allowed = false; |
||
1753 | move->sides[COLOR_BLACK].shortcastle_allowed = false; |
||
1754 | move->sides[COLOR_BLACK].longcastle_allowed = false; |
||
1755 | |||
1756 | // get the length of the FEN string |
||
1757 | length = wcslen (fen_string); |
||
1758 | |||
1759 | // now parse the board from top left to bottom right, placing parts on the fly |
||
1760 | current_line = 7; |
||
1761 | current_column = 0; |
||
1762 | for (char_index = 0; char_index < length; char_index++) |
||
1763 | { |
||
1764 | // is it a number ? |
||
1765 | if (iswdigit (fen_string[char_index])) |
||
1766 | { |
||
1767 | current_column += _wtoi (&fen_string[char_index]); // skip as many columns as needed |
||
1768 | if (current_column > 8) |
||
1769 | return (false); // consistency check: something's wrong with this notation, return FALSE |
||
1770 | } |
||
1771 | |||
1772 | // else is it a line skip ? |
||
1773 | else if (fen_string[char_index] == L'/') |
||
1774 | { |
||
1775 | // were we reading the last line ? |
||
1776 | if (current_line == 0) |
||
1777 | { |
||
1778 | char_index++; // skip this character |
||
1779 | break; // stop reading parts placement |
||
1780 | } |
||
1781 | |||
1782 | current_line--; // proceed to next line, decrescending |
||
1783 | current_column = 0; // and begin at the first column on that line |
||
1784 | } |
||
1785 | |||
1786 | // else is it a blank space ? meaning parts have been read |
||
1787 | else if (fen_string[char_index] == L' ') |
||
1788 | break; // stop reading parts placement |
||
1789 | |||
1790 | // else it's a part. Check first if the current line/column is valid |
||
1791 | else if (IS_VALID (current_line, current_column)) |
||
1792 | { |
||
1793 | if (fen_string[char_index] == L'r') |
||
1794 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_ROOK); // black rook |
||
1795 | else if (fen_string[char_index] == L'R') |
||
1796 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_ROOK); // white rook |
||
1797 | else if (fen_string[char_index] == L'n') |
||
1798 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_KNIGHT); // black knight |
||
1799 | else if (fen_string[char_index] == L'N') |
||
1800 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_KNIGHT); // white knight |
||
1801 | else if (fen_string[char_index] == L'b') |
||
1802 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_BISHOP); // black bishop |
||
1803 | else if (fen_string[char_index] == L'B') |
||
1804 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_BISHOP); // white bishop |
||
1805 | else if (fen_string[char_index] == L'q') |
||
1806 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_QUEEN); // black queen |
||
1807 | else if (fen_string[char_index] == L'Q') |
||
1808 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_QUEEN); // white queen |
||
1809 | else if (fen_string[char_index] == L'k') |
||
1810 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_KING); // black king |
||
1811 | else if (fen_string[char_index] == L'K') |
||
1812 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_KING); // white king |
||
1813 | else if (fen_string[char_index] == L'p') |
||
1814 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_PAWN); // black pawn |
||
1815 | else if (fen_string[char_index] == L'P') |
||
1816 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_PAWN); // white pawn |
||
1817 | |||
1818 | current_column++; // proceed to next column |
||
1819 | } |
||
1820 | else |
||
1821 | return (false); // invalid position, something's wrong with this notation, return FALSE |
||
1822 | } |
||
1823 | |||
1824 | // a space has been reached: next thing to read is the side on move |
||
1825 | char_index++; |
||
1826 | if (char_index >= length) |
||
1827 | return (false); // consistency check: something's wrong with this notation, return FALSE |
||
1828 | |||
1829 | if (towlower (fen_string[char_index]) == L'w') |
||
1830 | move->color = COLOR_BLACK; // white to move |
||
1831 | else if (towlower (fen_string[char_index]) == L'b') |
||
1832 | move->color = COLOR_WHITE; // black to move |
||
1833 | else |
||
1834 | return (false); // consistency check: something's wrong with this notation, return FALSE |
||
1835 | |||
1836 | // there should be a space after this |
||
1837 | char_index++; |
||
1838 | if ((char_index >= length) || (fen_string[char_index] != L' ')) |
||
1839 | return (false); // consistency check: something's wrong with this notation, return FALSE |
||
1840 | |||
1841 | // a space has been reached: next thing to read is the castling possibilities |
||
1842 | char_index++; |
||
1843 | if (char_index >= length) |
||
1844 | return (false); // consistency check: something's wrong with this notation, return FALSE |
||
1845 | for (; char_index < length; char_index++) |
||
1846 | { |
||
1847 | if (fen_string[char_index] == L'k') |
||
1848 | move->sides[COLOR_BLACK].shortcastle_allowed = true; // short castling allowed for black |
||
1849 | else if (fen_string[char_index] == L'K') |
||
1850 | move->sides[COLOR_WHITE].shortcastle_allowed = true; // short castling allowed for white |
||
1851 | else if (fen_string[char_index] == L'q') |
||
1852 | move->sides[COLOR_BLACK].longcastle_allowed = true; // long castling allowed for black |
||
1853 | else if (fen_string[char_index] == L'Q') |
||
1854 | move->sides[COLOR_WHITE].longcastle_allowed = true; // long castling allowed for white |
||
1855 | else if (fen_string[char_index] == L'-') |
||
1856 | continue; // no side can castle (explicitly) |
||
1857 | else if (fen_string[char_index] == L' ') |
||
1858 | break; // if blank space, stop reading castling possibilities |
||
1859 | } |
||
1860 | |||
1861 | // is there a free space after this ? |
||
1862 | if (char_index < length) |
||
1863 | { |
||
1864 | char_index++; // if so, skip it |
||
1865 | |||
1866 | // is there enough room for an en passant position AND is it specified ? |
||
1867 | if ((char_index + 2 <= length) && (fen_string[char_index] != L'-')) |
||
1868 | { |
||
1869 | // read column |
||
1870 | if (towlower (fen_string[char_index]) == L'a') enpassant_column = 0; |
||
1871 | else if (towlower (fen_string[char_index]) == L'b') enpassant_column = 1; |
||
1872 | else if (towlower (fen_string[char_index]) == L'c') enpassant_column = 2; |
||
1873 | else if (towlower (fen_string[char_index]) == L'd') enpassant_column = 3; |
||
1874 | else if (towlower (fen_string[char_index]) == L'e') enpassant_column = 4; |
||
1875 | else if (towlower (fen_string[char_index]) == L'f') enpassant_column = 5; |
||
1876 | else if (towlower (fen_string[char_index]) == L'g') enpassant_column = 6; |
||
1877 | else if (towlower (fen_string[char_index]) == L'h') enpassant_column = 7; |
||
1878 | else return (false); // consistency check: something's wrong with this notation, return FALSE |
||
1879 | |||
1880 | // read line |
||
1881 | enpassant_line = _wtoi (&fen_string[char_index + 1]) - 1; |
||
1882 | if ((enpassant_line != 2) && (enpassant_line != 5)) |
||
1883 | return (false); // consistency check: something's wrong with this notation, return FALSE |
||
1884 | |||
1885 | // setup move data |
||
1886 | move->part = PART_PAWN; |
||
1887 | if (enpassant_line == 2) |
||
1888 | { |
||
1889 | move->source[0] = 1; // rush from line 1 to line 3 |
||
1890 | move->target[0] = 3; |
||
1891 | move->color = COLOR_WHITE; |
||
1892 | } |
||
1893 | else |
||
1894 | { |
||
1895 | move->source[0] = 6; // rush from line 6 to line 4 |
||
1896 | move->target[0] = 4; |
||
1897 | move->color = COLOR_BLACK; |
||
1898 | } |
||
1899 | move->source[1] = enpassant_column; |
||
1900 | move->target[1] = enpassant_column; |
||
1901 | } |
||
1902 | } |
||
1903 | |||
1904 | // table was setup correctly, save FEN string in move structure |
||
1905 | wcscpy_s (move->fen_string, WCHAR_SIZEOF (move->fen_string), fen_string); |
||
1906 | return (true); // and return TRUE |
||
1907 | } |
||
1908 | |||
1909 | |||
1910 | bool Move_SetupFromStyle12 (boardmove_t *move, wchar_t *positions, int move_color, int pawnrush_column, |
||
1911 | bool can_white_castle_short, bool can_white_castle_long, bool can_black_castle_short, bool can_black_castle_long, wchar_t *pretty_movestring) |
||
1912 | { |
||
1913 | // this function sets up the given board according to the Style12 ICC/FICS description style12_string makes of it |
||
1914 | |||
1915 | int pos_index; |
||
1916 | int current_line; |
||
1917 | int current_column; |
||
1918 | |||
1919 | // reset the chess grid |
||
1920 | memset (&move->slots, 0, sizeof (move->slots)); |
||
1921 | |||
1922 | // now parse the line from left to right, placing parts on the fly |
||
1923 | for (current_line = 0; current_line < 8; current_line++) |
||
1924 | for (current_column = 0; current_column < 8; current_column++) |
||
1925 | { |
||
1926 | pos_index = (7 - current_line) * 8 + current_column; // compute position in line |
||
1927 | |||
1928 | if (positions[pos_index] == L'r') |
||
1929 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_ROOK); // black rook |
||
1930 | else if (positions[pos_index] == L'R') |
||
1931 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_ROOK); // white rook |
||
1932 | else if (positions[pos_index] == L'n') |
||
1933 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_KNIGHT); // black knight |
||
1934 | else if (positions[pos_index] == L'N') |
||
1935 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_KNIGHT); // white knight |
||
1936 | else if (positions[pos_index] == L'b') |
||
1937 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_BISHOP); // black bishop |
||
1938 | else if (positions[pos_index] == L'B') |
||
1939 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_BISHOP); // white bishop |
||
1940 | else if (positions[pos_index] == L'q') |
||
1941 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_QUEEN); // black queen |
||
1942 | else if (positions[pos_index] == L'Q') |
||
1943 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_QUEEN); // white queen |
||
1944 | else if (positions[pos_index] == L'k') |
||
1945 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_KING); // black king |
||
1946 | else if (positions[pos_index] == L'K') |
||
1947 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_KING); // white king |
||
1948 | else if (positions[pos_index] == L'p') |
||
1949 | Move_SetSlot (move, current_line, current_column, COLOR_BLACK, PART_PAWN); // black pawn |
||
1950 | else if (positions[pos_index] == L'P') |
||
1951 | Move_SetSlot (move, current_line, current_column, COLOR_WHITE, PART_PAWN); // white pawn |
||
1952 | } |
||
1953 | |||
1954 | // save move color |
||
1955 | move->color = move_color; |
||
1956 | |||
1957 | // allow or disallow both sides to castle, as told |
||
1958 | move->sides[COLOR_WHITE].shortcastle_allowed = can_white_castle_short; |
||
1959 | move->sides[COLOR_WHITE].longcastle_allowed = can_white_castle_long; |
||
1960 | move->sides[COLOR_BLACK].shortcastle_allowed = can_black_castle_short; |
||
1961 | move->sides[COLOR_BLACK].longcastle_allowed = can_black_castle_long; |
||
1962 | |||
1963 | // is the last move a pawn rush ? |
||
1964 | if (pawnrush_column != -1) |
||
1965 | { |
||
1966 | if ((pawnrush_column < 0) || (pawnrush_column > 7)) |
||
1967 | return (false); // consistency check: something's wrong with this notation, return FALSE |
||
1968 | |||
1969 | move->part = PART_PAWN; |
||
1970 | if (move->color == COLOR_WHITE) |
||
1971 | { |
||
1972 | move->source[0] = 1; // rush from line 1 to line 3 |
||
1973 | move->target[0] = 3; |
||
1974 | } |
||
1975 | else |
||
1976 | { |
||
1977 | move->source[0] = 6; // rush from line 6 to line 4 |
||
1978 | move->target[0] = 4; |
||
1979 | } |
||
1980 | move->source[1] = pawnrush_column; |
||
1981 | move->target[1] = pawnrush_column; |
||
1982 | } |
||
1983 | |||
1984 | // finally, save the FEN string with which we initialized this board |
||
1985 | Move_DescribeInFEN (move); |
||
1986 | return (true); // finished, no error encountered |
||
1987 | } |
||
1988 | |||
1989 | |||
1990 | static void AddPossibleMove (boardmove_t **possiblemoves, int *possiblemove_count, int color, int part, int source_line, int source_column, int target_line, int target_column, int promotion_type, bool has_captured, bool is_enpassant) |
||
1991 | { |
||
1992 | // helper function that resizes the given possiblemoves array and adds a possible move to it |
||
1993 | |||
1994 | // TODO: raise or clear the is_check and is_stalemate move flags in the returned move. |
||
1995 | // Not crucial as this function is only called by Board_FindRandomMove(), the result is then |
||
1996 | // translated in SAN and then fed to the chess engine to order a blunderous move. The move |
||
1997 | // is then played normally using Board_AppendMove() using source and target locations, and this |
||
1998 | // call does evaluate the actual move and set the flags correctly in the final moves array. |
||
1999 | |||
2000 | *possiblemoves = (boardmove_t *) SAFE_realloc (*possiblemoves, *possiblemove_count, (*possiblemove_count) + 1, sizeof (boardmove_t), true); |
||
2001 | (*possiblemoves)[*possiblemove_count].color = color; |
||
2002 | (*possiblemoves)[*possiblemove_count].part = part; |
||
2003 | (*possiblemoves)[*possiblemove_count].source[0] = source_line; |
||
2004 | (*possiblemoves)[*possiblemove_count].source[1] = source_column; |
||
2005 | (*possiblemoves)[*possiblemove_count].target[0] = target_line; |
||
2006 | (*possiblemoves)[*possiblemove_count].target[1] = target_column; |
||
2007 | (*possiblemoves)[*possiblemove_count].promotion_type = promotion_type; |
||
2008 | (*possiblemoves)[*possiblemove_count].has_captured = has_captured; |
||
2009 | (*possiblemoves)[*possiblemove_count].is_enpassant = is_enpassant; |
||
2010 | (*possiblemove_count)++; // possible moves array holds now one move more |
||
2011 | |||
2012 | return; // finished |
||
2013 | } |