52 elem = elem->next; |
52 elem = elem->next; |
53 free(cur); |
53 free(cur); |
54 }; |
54 }; |
55 } |
55 } |
56 |
56 |
|
57 /* MUST be called IMMEDIATLY after applying a move to work correctly */ |
|
58 static void format_move(GameState *gamestate, Move *move) { |
|
59 char *string = move->string; |
|
60 |
|
61 /* at least 8 characters should be available, wipe them out */ |
|
62 memset(string, 0, 8); |
|
63 |
|
64 /* special formats for castling */ |
|
65 if ((move->piece&PIECE_MASK) == KING && |
|
66 abs(move->tofile-move->fromfile) == 2) { |
|
67 if (move->tofile==fileidx('c')) { |
|
68 memcpy(string, "O-O-O", 5); |
|
69 } else { |
|
70 memcpy(string, "O-O", 3); |
|
71 } |
|
72 } |
|
73 |
|
74 /* start by notating the piece character */ |
|
75 string[0] = getpiecechr(move->piece); |
|
76 int idx = string[0] ? 1 : 0; |
|
77 |
|
78 /* find out how many source information we do need */ |
|
79 uint8_t piece = move->piece & PIECE_MASK; |
|
80 if (piece == PAWN) { |
|
81 if (move->capture) { |
|
82 string[idx++] = filechr(move->fromfile); |
|
83 } |
|
84 } else if (piece != KING) { |
|
85 Move threats[16]; |
|
86 uint8_t threatcount; |
|
87 get_real_threats(gamestate, move->torow, move->tofile, |
|
88 move->piece&COLOR_MASK, threats, &threatcount); |
|
89 if (threatcount > 1) { |
|
90 int ambrows = 0, ambfiles = 0; |
|
91 for (uint8_t i = 0 ; i < threatcount ; i++) { |
|
92 if (threats[i].fromrow == move->fromrow) { |
|
93 ambrows++; |
|
94 } |
|
95 if (threats[i].fromfile == move->fromfile) { |
|
96 ambfiles++; |
|
97 } |
|
98 } |
|
99 /* ambiguous row, name file */ |
|
100 if (ambrows > 1) { |
|
101 string[idx++] = filechr(move->fromfile); |
|
102 } |
|
103 /* ambiguous file, name row */ |
|
104 if (ambfiles > 1) { |
|
105 string[idx++] = filechr(move->fromrow); |
|
106 } |
|
107 } |
|
108 } |
|
109 |
|
110 /* capturing? */ |
|
111 if (move->capture) { |
|
112 string[idx++] = 'x'; |
|
113 } |
|
114 |
|
115 /* destination */ |
|
116 string[idx++] = filechr(move->tofile); |
|
117 string[idx++] = rowchr(move->torow); |
|
118 |
|
119 /* promotion? */ |
|
120 if (move->promotion) { |
|
121 string[idx++] = '='; |
|
122 string[idx++] = getpiecechr(move->promotion); |
|
123 } |
|
124 |
|
125 /* check? */ |
|
126 if (move->check) { |
|
127 /* works only, if this function is called when applying the move */ |
|
128 string[idx++] = gamestate->checkmate?'#':'+'; |
|
129 } |
|
130 } |
|
131 |
57 static void addmove(GameState* gamestate, Move *move) { |
132 static void addmove(GameState* gamestate, Move *move) { |
58 MoveList *elem = malloc(sizeof(MoveList)); |
133 MoveList *elem = malloc(sizeof(MoveList)); |
59 elem->next = NULL; |
134 elem->next = NULL; |
60 elem->move = *move; |
135 elem->move = *move; |
61 |
136 |
107 case 'K': return KING; |
182 case 'K': return KING; |
108 default: return 0; |
183 default: return 0; |
109 } |
184 } |
110 } |
185 } |
111 |
186 |
112 void apply_move(GameState *gamestate, Move *move) { |
187 static void apply_move_impl(GameState *gamestate, Move *move, _Bool simulate) { |
113 uint8_t piece = move->piece & PIECE_MASK; |
188 uint8_t piece = move->piece & PIECE_MASK; |
114 uint8_t color = move->piece & COLOR_MASK; |
189 uint8_t color = move->piece & COLOR_MASK; |
115 |
190 |
116 /* en passant capture */ |
191 /* en passant capture */ |
117 if (move->capture && piece == PAWN && |
192 if (move->capture && piece == PAWN && |
139 } else { |
214 } else { |
140 mdst(gamestate->board, move) = move->piece; |
215 mdst(gamestate->board, move) = move->piece; |
141 } |
216 } |
142 |
217 |
143 /* castling */ |
218 /* castling */ |
144 if (piece == KING && |
219 if (piece == KING && move->fromfile == fileidx('e')) { |
145 move->fromfile == fileidx('e')) { |
|
146 |
220 |
147 if (move->tofile == fileidx('g')) { |
221 if (move->tofile == fileidx('g')) { |
148 gamestate->board[move->torow][fileidx('h')] = 0; |
222 gamestate->board[move->torow][fileidx('h')] = 0; |
149 gamestate->board[move->torow][fileidx('f')] = color|ROOK; |
223 gamestate->board[move->torow][fileidx('f')] = color|ROOK; |
150 } else if (move->tofile == fileidx('c')) { |
224 } else if (move->tofile == fileidx('c')) { |
151 gamestate->board[move->torow][fileidx('a')] = 0; |
225 gamestate->board[move->torow][fileidx('a')] = 0; |
152 gamestate->board[move->torow][fileidx('d')] = color|ROOK; |
226 gamestate->board[move->torow][fileidx('d')] = color|ROOK; |
153 } |
227 } |
154 } |
228 } |
155 |
229 |
|
230 if (!simulate) { |
|
231 if (!move->string[0]) { |
|
232 format_move(gamestate, move); |
|
233 } |
|
234 } |
|
235 /* add move, even in simulation (checkmate test needs it) */ |
156 addmove(gamestate, move); |
236 addmove(gamestate, move); |
|
237 } |
|
238 |
|
239 void apply_move(GameState *gamestate, Move *move) { |
|
240 apply_move_impl(gamestate, move, 0); |
157 } |
241 } |
158 |
242 |
159 static int validate_move_rules(GameState *gamestate, Move *move) { |
243 static int validate_move_rules(GameState *gamestate, Move *move) { |
160 /* validate indices (don't trust opponent) */ |
244 /* validate indices (don't trust opponent) */ |
161 if (!chkidx(move)) { |
245 if (!chkidx(move)) { |
246 } |
330 } |
247 |
331 |
248 /* simulate move for check validation */ |
332 /* simulate move for check validation */ |
249 GameState simulation = gamestate_copy_sim(gamestate); |
333 GameState simulation = gamestate_copy_sim(gamestate); |
250 Move simmove = *move; |
334 Move simmove = *move; |
251 apply_move(&simulation, &simmove); |
335 apply_move_impl(&simulation, &simmove, 1); |
252 |
336 |
253 /* don't move into or stay in check position */ |
337 /* don't move into or stay in check position */ |
254 if (is_covered(&simulation, mykingrow, mykingfile, |
338 if (is_covered(&simulation, mykingrow, mykingfile, |
255 opponent_color(piececolor))) { |
339 opponent_color(piececolor))) { |
256 |
340 |
465 Move threats[16], *threat = NULL; |
548 Move threats[16], *threat = NULL; |
466 uint8_t threatcount; |
549 uint8_t threatcount; |
467 |
550 |
468 if (get_threats(gamestate, move->torow, move->tofile, color, |
551 if (get_threats(gamestate, move->torow, move->tofile, color, |
469 threats, &threatcount)) { |
552 threats, &threatcount)) { |
|
553 |
|
554 int reason = INVALID_POSITION; |
470 |
555 |
471 // find threats for the specified position |
556 // find threats for the specified position |
472 for (uint8_t i = 0 ; i < threatcount ; i++) { |
557 for (uint8_t i = 0 ; i < threatcount ; i++) { |
473 if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) |
558 if ((threats[i].piece & (PIECE_MASK | COLOR_MASK)) |
474 == move->piece && |
559 == move->piece && |
478 move->fromfile == threats[i].fromfile)) { |
563 move->fromfile == threats[i].fromfile)) { |
479 |
564 |
480 if (threat) { |
565 if (threat) { |
481 return AMBIGUOUS_MOVE; |
566 return AMBIGUOUS_MOVE; |
482 } else { |
567 } else { |
483 threat = &(threats[i]); |
568 // found threat is no real threat |
|
569 if (is_pinned(gamestate, &(threats[i]))) { |
|
570 reason = incheck?KING_IN_CHECK:PIECE_PINNED; |
|
571 } else { |
|
572 threat = &(threats[i]); |
|
573 } |
484 } |
574 } |
485 } |
575 } |
486 } |
576 } |
487 |
577 |
488 // can't threaten specified position |
578 // can't threaten specified position |
489 if (!threat) { |
579 if (!threat) { |
490 return INVALID_POSITION; |
580 return reason; |
491 } |
581 } |
492 |
582 |
493 // found threat is no real threat |
583 memcpy(move, threat, sizeof(Move)); |
494 if (is_pinned(gamestate, threat)) { |
584 return VALID_MOVE_SYNTAX; |
495 return incheck?KING_IN_CHECK:PIECE_PINNED; |
|
496 } else { |
|
497 memcpy(move, threat, sizeof(Move)); |
|
498 return VALID_MOVE_SYNTAX; |
|
499 } |
|
500 } else { |
585 } else { |
501 return INVALID_POSITION; |
586 return INVALID_POSITION; |
502 } |
587 } |
503 } |
588 } |
504 |
589 |