143 } |
154 } |
144 |
155 |
145 addmove(gamestate, move); |
156 addmove(gamestate, move); |
146 } |
157 } |
147 |
158 |
148 static _Bool validate_move_rules(GameState *gamestate, Move *move) { |
159 static int validate_move_rules(GameState *gamestate, Move *move) { |
149 /* validate indices (don't trust opponent) */ |
160 /* validate indices (don't trust opponent) */ |
150 if (!chkidx(move)) { |
161 if (!chkidx(move)) { |
151 return 0; |
162 return INVALID_POSITION; |
152 } |
163 } |
153 |
164 |
154 /* must move */ |
165 /* must move */ |
155 if (move->fromfile == move->tofile && move->fromrow == move->torow) { |
166 if (move->fromfile == move->tofile && move->fromrow == move->torow) { |
156 return 0; |
167 return INVALID_MOVE_SYNTAX; |
157 } |
168 } |
158 |
169 |
159 /* does piece exist */ |
170 /* does piece exist */ |
160 if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) |
171 if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK)) |
161 != (move->piece&(PIECE_MASK|COLOR_MASK))) { |
172 != (move->piece&(PIECE_MASK|COLOR_MASK))) { |
162 return 0; |
173 return INVALID_POSITION; |
163 } |
174 } |
164 |
175 |
165 /* can't capture own pieces */ |
176 /* can't capture own pieces */ |
166 if ((mdst(gamestate->board, move) & COLOR_MASK) |
177 if ((mdst(gamestate->board, move) & COLOR_MASK) |
167 == (move->piece & COLOR_MASK)) { |
178 == (move->piece & COLOR_MASK)) { |
168 return 0; |
179 return RULES_VIOLATED; |
169 } |
180 } |
170 |
181 |
171 /* must capture, if and only if destination is occupied */ |
182 /* must capture, if and only if destination is occupied */ |
172 if ((mdst(gamestate->board, move) == 0 && move->capture) || |
183 if ((mdst(gamestate->board, move) == 0 && move->capture) || |
173 (mdst(gamestate->board, move) != 0 && !move->capture)) { |
184 (mdst(gamestate->board, move) != 0 && !move->capture)) { |
174 return 0; |
185 return INVALID_MOVE_SYNTAX; |
175 } |
186 } |
176 |
187 |
177 /* validate individual rules */ |
188 /* validate individual rules */ |
|
189 _Bool chkrules; |
178 switch (move->piece & PIECE_MASK) { |
190 switch (move->piece & PIECE_MASK) { |
179 case PAWN: |
191 case PAWN: |
180 return pawn_chkrules(gamestate, move) && |
192 chkrules = pawn_chkrules(gamestate, move) && |
181 !pawn_isblocked(gamestate, move); |
193 !pawn_isblocked(gamestate, move); |
|
194 break; |
182 case ROOK: |
195 case ROOK: |
183 return rook_chkrules(move) && |
196 chkrules = rook_chkrules(move) && |
184 !rook_isblocked(gamestate, move); |
197 !rook_isblocked(gamestate, move); |
|
198 break; |
185 case KNIGHT: |
199 case KNIGHT: |
186 return knight_chkrules(move); /* knight is never blocked */ |
200 chkrules = knight_chkrules(move); /* knight is never blocked */ |
|
201 break; |
187 case BISHOP: |
202 case BISHOP: |
188 return bishop_chkrules(move) && |
203 chkrules = bishop_chkrules(move) && |
189 !bishop_isblocked(gamestate, move); |
204 !bishop_isblocked(gamestate, move); |
|
205 break; |
190 case QUEEN: |
206 case QUEEN: |
191 return queen_chkrules(move) && |
207 chkrules = queen_chkrules(move) && |
192 !queen_isblocked(gamestate, move); |
208 !queen_isblocked(gamestate, move); |
|
209 break; |
193 case KING: |
210 case KING: |
194 return king_chkrules(gamestate, move) && |
211 chkrules = king_chkrules(gamestate, move) && |
195 !king_isblocked(gamestate, move); |
212 !king_isblocked(gamestate, move); |
|
213 break; |
196 default: |
214 default: |
197 return 0; |
215 return INVALID_MOVE_SYNTAX; |
198 } |
216 } |
199 } |
217 |
200 |
218 return chkrules ? VALID_MOVE_SEMANTICS : RULES_VIOLATED; |
201 _Bool validate_move(GameState *gamestate, Move *move) { |
219 } |
202 // TODO: provide more details via a return code |
220 |
203 |
221 int validate_move(GameState *gamestate, Move *move) { |
204 _Bool result = validate_move_rules(gamestate, move); |
222 |
|
223 int result = validate_move_rules(gamestate, move); |
205 |
224 |
206 /* cancel processing to save resources */ |
225 /* cancel processing to save resources */ |
207 if (!result) { |
226 if (result != VALID_MOVE_SEMANTICS) { |
208 return 0; |
227 return result; |
209 } |
228 } |
210 |
229 |
211 /* find kings for check validation */ |
230 /* find kings for check validation */ |
212 uint8_t piececolor = (move->piece & COLOR_MASK); |
231 uint8_t piececolor = (move->piece & COLOR_MASK); |
213 |
232 |
224 opkingrow = row; |
243 opkingrow = row; |
225 } |
244 } |
226 } |
245 } |
227 } |
246 } |
228 |
247 |
229 /* simulation move for check validation */ |
248 /* simulate move for check validation */ |
230 GameState simulation = *gamestate; |
249 GameState simulation = gamestate_copy_sim(gamestate); |
231 Move simmove = *move; |
250 Move simmove = *move; |
232 apply_move(&simulation, &simmove); |
251 apply_move(&simulation, &simmove); |
233 |
252 |
234 /* don't move into or stay in check position */ |
253 /* don't move into or stay in check position */ |
235 if (is_covered(&simulation, mykingrow, mykingfile, |
254 if (is_covered(&simulation, mykingrow, mykingfile, |
236 opponent_color(piececolor))) { |
255 opponent_color(piececolor))) { |
237 return 0; |
256 |
|
257 gamestate_cleanup(&simulation); |
|
258 if ((move->piece & PIECE_MASK) == KING) { |
|
259 return KING_MOVES_INTO_CHECK; |
|
260 } else { |
|
261 /* last move is always not null in this case */ |
|
262 return gamestate->lastmove->move.check ? |
|
263 KING_IN_CHECK : PIECE_PINNED; |
|
264 } |
238 } |
265 } |
239 |
266 |
240 /* correct check and checkmate flags (move is still valid) */ |
267 /* correct check and checkmate flags (move is still valid) */ |
241 Move threats[16]; |
268 Move threats[16]; |
242 uint8_t threatcount; |
269 uint8_t threatcount; |
373 kingrow = row; |
403 kingrow = row; |
374 } |
404 } |
375 } |
405 } |
376 } |
406 } |
377 |
407 |
378 GameState simulation = *gamestate; |
408 GameState simulation = gamestate_copy_sim(gamestate); |
379 Move simmove = *move; |
409 Move simmove = *move; |
380 apply_move(&simulation, &simmove); |
410 apply_move(&simulation, &simmove); |
381 return is_covered(&simulation, kingrow, kingfile, opponent_color(color)); |
411 _Bool covered = is_covered(&simulation, |
|
412 kingrow, kingfile, opponent_color(color)); |
|
413 gamestate_cleanup(&simulation); |
|
414 |
|
415 return covered; |
382 } |
416 } |
383 |
417 |
384 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, |
418 _Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file, |
385 uint8_t color, Move *threats, uint8_t *threatcount) { |
419 uint8_t color, Move *threats, uint8_t *threatcount) { |
386 |
420 |
402 } |
436 } |
403 } |
437 } |
404 } |
438 } |
405 |
439 |
406 for (uint8_t i = 0 ; i < candidatecount ; i++) { |
440 for (uint8_t i = 0 ; i < candidatecount ; i++) { |
407 GameState simulation = *gamestate; |
441 GameState simulation = gamestate_copy_sim(gamestate); |
408 Move simmove = candidates[i]; |
442 Move simmove = candidates[i]; |
409 apply_move(&simulation, &simmove); |
443 apply_move(&simulation, &simmove); |
410 if (!is_covered(&simulation, kingrow, kingfile, |
444 if (!is_covered(&simulation, kingrow, kingfile, |
411 opponent_color(color))) { |
445 opponent_color(color))) { |
412 result = 1; |
446 result = 1; |
413 if (threats && threatcount) { |
447 if (threats && threatcount) { |
414 threats[(*threatcount)++] = candidates[i]; |
448 threats[(*threatcount)++] = candidates[i]; |
415 } |
449 } |
416 } |
450 } |
|
451 gamestate_cleanup(&simulation); |
417 } |
452 } |
418 |
453 |
419 return result; |
454 return result; |
420 } else { |
455 } else { |
421 return 0; |
456 return 0; |
422 } |
457 } |
423 } |
458 } |
424 #include <ncurses.h> |
459 |
425 static int getlocation(GameState *gamestate, Move *move) { |
460 static int getlocation(GameState *gamestate, Move *move) { |
426 |
461 |
427 uint8_t color = move->piece & COLOR_MASK; |
462 uint8_t color = move->piece & COLOR_MASK; |
428 _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; |
463 _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0; |
429 |
464 |