| |
1 /* |
| |
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
| |
3 * |
| |
4 * Copyright 2014 Mike Becker. All rights reserved. |
| |
5 * |
| |
6 * Redistribution and use in source and binary forms, with or without |
| |
7 * modification, are permitted provided that the following conditions are met: |
| |
8 * |
| |
9 * 1. Redistributions of source code must retain the above copyright |
| |
10 * notice, this list of conditions and the following disclaimer. |
| |
11 * |
| |
12 * 2. Redistributions in binary form must reproduce the above copyright |
| |
13 * notice, this list of conditions and the following disclaimer in the |
| |
14 * documentation and/or other materials provided with the distribution. |
| |
15 * |
| |
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| |
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| |
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| |
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| |
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| |
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| |
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| |
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| |
26 * POSSIBILITY OF SUCH DAMAGE. |
| |
27 * |
| |
28 */ |
| |
29 |
| |
30 #include "rules.h" |
| |
31 #include "chess.h" |
| |
32 #include <string.h> |
| |
33 |
| |
34 char getpiecechr(uint8_t piece) { |
| |
35 switch (piece & PIECE_MASK) { |
| |
36 case ROOK: return 'R'; |
| |
37 case KNIGHT: return 'N'; |
| |
38 case BISHOP: return 'B'; |
| |
39 case QUEEN: return 'Q'; |
| |
40 case KING: return 'K'; |
| |
41 default: return '\0'; |
| |
42 } |
| |
43 } |
| |
44 |
| |
45 uint8_t getpiece(char c) { |
| |
46 switch (c) { |
| |
47 case 'R': return ROOK; |
| |
48 case 'N': return KNIGHT; |
| |
49 case 'B': return BISHOP; |
| |
50 case 'Q': return QUEEN; |
| |
51 case 'K': return KING; |
| |
52 default: return 0; |
| |
53 } |
| |
54 } |
| |
55 |
| |
56 /** |
| |
57 * Guesses the location of a piece for short algebraic notation. |
| |
58 * |
| |
59 * @param board the current state of the board |
| |
60 * @param move the move date to operate on |
| |
61 * @return status code (see rules/rules.h for the codes) |
| |
62 */ |
| |
63 static int getlocation(Board board, Move *move) { |
| |
64 uint8_t piece = move->piece & PIECE_MASK; |
| |
65 switch (piece) { |
| |
66 case PAWN: return pawn_getlocation(board, move); |
| |
67 case ROOK: return rook_getlocation(board, move); |
| |
68 case KNIGHT: return knight_getlocation(board, move); |
| |
69 case BISHOP: return bishop_getlocation(board, move); |
| |
70 case QUEEN: return queen_getlocation(board, move); |
| |
71 case KING: return king_getlocation(board, move); |
| |
72 default: return INVALID_MOVE_SYNTAX; |
| |
73 } |
| |
74 } |
| |
75 |
| |
76 |
| |
77 void apply_move(Board board, Move *move) { |
| |
78 uint8_t piece = move->piece & PIECE_MASK; |
| |
79 uint8_t color = move->piece & COLOR_MASK; |
| |
80 |
| |
81 /* en passant capture */ |
| |
82 if (move->capture && piece == PAWN && |
| |
83 mdst(board, move) == 0) { |
| |
84 board[move->fromrow][move->tofile] = 0; |
| |
85 } |
| |
86 |
| |
87 /* remove old en passant threats */ |
| |
88 for (uint8_t file = 0 ; file < 8 ; file++) { |
| |
89 board[3][file] &= ~ENPASSANT_THREAT; |
| |
90 board[4][file] &= ~ENPASSANT_THREAT; |
| |
91 } |
| |
92 |
| |
93 /* add new en passant threat */ |
| |
94 if (piece == PAWN && ( |
| |
95 (move->fromrow == 1 && move->torow == 3) || |
| |
96 (move->fromrow == 6 && move->torow == 4))) { |
| |
97 move->piece |= ENPASSANT_THREAT; |
| |
98 } |
| |
99 |
| |
100 /* move (and maybe capture or promote) */ |
| |
101 msrc(board, move) = 0; |
| |
102 if (move->promotion) { |
| |
103 mdst(board, move) = move->promotion; |
| |
104 } else { |
| |
105 mdst(board, move) = move->piece; |
| |
106 } |
| |
107 |
| |
108 /* castling */ |
| |
109 if (piece == KING && |
| |
110 move->fromfile == fileidx('e')) { |
| |
111 |
| |
112 if (move->tofile == fileidx('g')) { |
| |
113 board[move->torow][fileidx('h')] = 0; |
| |
114 board[move->torow][fileidx('f')] = color|ROOK; |
| |
115 } else if (move->tofile == fileidx('c')) { |
| |
116 board[move->torow][fileidx('a')] = 0; |
| |
117 board[move->torow][fileidx('d')] = color|ROOK; |
| |
118 } |
| |
119 } |
| |
120 } |
| |
121 |
| |
122 _Bool validate_move(Board board, Move *move) { |
| |
123 _Bool result; |
| |
124 |
| |
125 /* validate indices (don't trust opponent) */ |
| |
126 if (!chkidx(move)) { |
| |
127 return 0; |
| |
128 } |
| |
129 |
| |
130 /* does piece exist */ |
| |
131 result = msrc(board, move) == move->piece; |
| |
132 |
| |
133 /* can't capture own pieces */ |
| |
134 if ((mdst(board, move) & COLOR_MASK) == (move->piece & COLOR_MASK)) { |
| |
135 return 0; |
| |
136 } |
| |
137 |
| |
138 /* validate individual rules */ |
| |
139 switch (move->piece & PIECE_MASK) { |
| |
140 case PAWN: |
| |
141 result = result && pawn_chkrules(board, move); |
| |
142 result = result && !pawn_isblocked(board, move); |
| |
143 break; |
| |
144 case ROOK: |
| |
145 result = result && rook_chkrules(move); |
| |
146 result = result && !rook_isblocked(board, move); |
| |
147 break; |
| |
148 case KNIGHT: |
| |
149 result = result && knight_chkrules(move); |
| |
150 result = result && !knight_isblocked(board, move); |
| |
151 break; |
| |
152 case BISHOP: |
| |
153 result = result && bishop_chkrules(move); |
| |
154 result = result && !bishop_isblocked(board, move); |
| |
155 break; |
| |
156 case QUEEN: |
| |
157 result = result && queen_chkrules(move); |
| |
158 result = result && !queen_isblocked(board, move); |
| |
159 break; |
| |
160 case KING: |
| |
161 result = result && king_chkrules(board, move); |
| |
162 result = result && !king_isblocked(board, move); |
| |
163 break; |
| |
164 default: |
| |
165 result = 0; |
| |
166 } |
| |
167 |
| |
168 /* is piece pinned */ |
| |
169 // TODO: make it so |
| |
170 |
| |
171 /* correct check and checkmate flags */ |
| |
172 // TODO: make it so |
| |
173 |
| |
174 return result; |
| |
175 } |
| |
176 |
| |
177 int eval_move(Board board, uint8_t mycolor, char *mstr, Move *move) { |
| |
178 memset(move, 0, sizeof(Move)); |
| |
179 move->fromfile = POS_UNSPECIFIED; |
| |
180 move->fromrow = POS_UNSPECIFIED; |
| |
181 |
| |
182 size_t len = strlen(mstr); |
| |
183 |
| |
184 /* evaluate check/checkmate flags */ |
| |
185 if (mstr[len-1] == '+') { |
| |
186 len--; mstr[len] = '\0'; |
| |
187 move->check = 1; |
| |
188 } else if (mstr[len-1] == '#') { |
| |
189 len--; mstr[len] = '\0'; |
| |
190 move->checkmate = 1; |
| |
191 } |
| |
192 |
| |
193 /* evaluate promotion */ |
| |
194 if (len > 3 && mstr[len-2] == '=') { |
| |
195 move->promotion = getpiece(mstr[len-1]); |
| |
196 if (!move->promotion) { |
| |
197 return INVALID_MOVE_SYNTAX; |
| |
198 } else { |
| |
199 move->promotion |= mycolor; |
| |
200 len -= 2; |
| |
201 mstr[len] = 0; |
| |
202 } |
| |
203 } |
| |
204 |
| |
205 if (len == 2) { |
| |
206 /* pawn move (e.g. "e4") */ |
| |
207 move->piece = PAWN; |
| |
208 move->tofile = fileidx(mstr[0]); |
| |
209 move->torow = rowidx(mstr[1]); |
| |
210 } else if (len == 3) { |
| |
211 if (strcmp(mstr, "O-O") == 0) { |
| |
212 /* king side castling */ |
| |
213 move->piece = KING; |
| |
214 move->fromfile = fileidx('e'); |
| |
215 move->tofile = fileidx('g'); |
| |
216 move->fromrow = move->torow = mycolor == WHITE ? 0 : 7; |
| |
217 } else { |
| |
218 /* move (e.g. "Nf3") */ |
| |
219 move->piece = getpiece(mstr[0]); |
| |
220 move->tofile = fileidx(mstr[1]); |
| |
221 move->torow = rowidx(mstr[2]); |
| |
222 } |
| |
223 |
| |
224 } else if (len == 4) { |
| |
225 move->piece = getpiece(mstr[0]); |
| |
226 if (!move->piece) { |
| |
227 move->piece = PAWN; |
| |
228 move->fromfile = fileidx(mstr[0]); |
| |
229 } |
| |
230 if (mstr[1] == 'x') { |
| |
231 /* capture (e.g. "Nxf3", "dxe5") */ |
| |
232 move->capture = 1; |
| |
233 } else { |
| |
234 /* move (e.g. "Ndf3", "N2c3", "e2e4") */ |
| |
235 if (isfile(mstr[1])) { |
| |
236 move->fromfile = fileidx(mstr[1]); |
| |
237 if (move->piece == PAWN) { |
| |
238 move->piece = 0; |
| |
239 } |
| |
240 } else { |
| |
241 move->fromrow = rowidx(mstr[1]); |
| |
242 } |
| |
243 } |
| |
244 move->tofile = fileidx(mstr[2]); |
| |
245 move->torow = rowidx(mstr[3]); |
| |
246 } else if (len == 5) { |
| |
247 if (strcmp(mstr, "O-O-O") == 0) { |
| |
248 /* queen side castling "O-O-O" */ |
| |
249 move->piece = KING; |
| |
250 move->fromfile = fileidx('e'); |
| |
251 move->tofile = fileidx('c'); |
| |
252 move->fromrow = move->torow = mycolor == WHITE ? 0 : 7; |
| |
253 } else { |
| |
254 move->piece = getpiece(mstr[0]); |
| |
255 if (mstr[2] == 'x') { |
| |
256 move->capture = 1; |
| |
257 if (move->piece) { |
| |
258 /* capture (e.g. "Ndxf3") */ |
| |
259 move->fromfile = fileidx(mstr[1]); |
| |
260 } else { |
| |
261 /* long notation capture (e.g. "e5xf6") */ |
| |
262 move->piece = PAWN; |
| |
263 move->fromfile = fileidx(mstr[0]); |
| |
264 move->fromrow = rowidx(mstr[1]); |
| |
265 } |
| |
266 } else { |
| |
267 /* long notation move (e.g. "Nc5a4") */ |
| |
268 move->fromfile = fileidx(mstr[1]); |
| |
269 move->fromrow = rowidx(mstr[2]); |
| |
270 } |
| |
271 move->tofile = fileidx(mstr[3]); |
| |
272 move->torow = rowidx(mstr[4]); |
| |
273 } |
| |
274 } else if (len == 6) { |
| |
275 /* long notation capture (e.g. "Nc5xf3") */ |
| |
276 if (mstr[3] == 'x') { |
| |
277 move->capture = 1; |
| |
278 move->piece = getpiece(mstr[0]); |
| |
279 move->fromfile = fileidx(mstr[1]); |
| |
280 move->fromrow = rowidx(mstr[2]); |
| |
281 move->tofile = fileidx(mstr[4]); |
| |
282 move->torow = rowidx(mstr[5]); |
| |
283 } |
| |
284 } |
| |
285 |
| |
286 |
| |
287 if (move->piece) { |
| |
288 if (move->piece == PAWN && move->torow == (mycolor==WHITE?7:0) |
| |
289 && !move->promotion) { |
| |
290 return NEED_PROMOTION; |
| |
291 } |
| |
292 |
| |
293 move->piece |= mycolor; |
| |
294 if (move->fromfile == POS_UNSPECIFIED |
| |
295 || move->fromrow == POS_UNSPECIFIED) { |
| |
296 return getlocation(board, move); |
| |
297 } else { |
| |
298 return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION; |
| |
299 } |
| |
300 } else { |
| |
301 return INVALID_MOVE_SYNTAX; |
| |
302 } |
| |
303 } |