|
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 } |