26 * POSSIBILITY OF SUCH DAMAGE. |
26 * POSSIBILITY OF SUCH DAMAGE. |
27 * |
27 * |
28 */ |
28 */ |
29 |
29 |
30 #include "game.h" |
30 #include "game.h" |
|
31 #include "network.h" |
31 #include "input.h" |
32 #include "input.h" |
32 #include "rules/rules.h" |
|
33 #include <ncurses.h> |
33 #include <ncurses.h> |
34 #include <string.h> |
34 #include <string.h> |
35 |
35 |
36 static const uint8_t boardx = 10, boardy = 10; |
36 static const uint8_t boardx = 10, boardy = 10; |
37 |
|
38 static uint8_t getpiecechr(uint8_t piece) { |
|
39 switch (piece & PIECE_MASK) { |
|
40 case ROOK: return 'R'; |
|
41 case KNIGHT: return 'N'; |
|
42 case BISHOP: return 'B'; |
|
43 case QUEEN: return 'Q'; |
|
44 case KING: return 'K'; |
|
45 default: return '\0'; |
|
46 } |
|
47 } |
|
48 |
|
49 /** |
|
50 * Maps a character to a piece. |
|
51 * |
|
52 * Does not work for pawns, since they don't have a character. |
|
53 * |
|
54 * @param c one of R,N,B,Q,K |
|
55 * @return numeric value for the specified piece |
|
56 */ |
|
57 static uint8_t getpiece(char c) { |
|
58 switch (c) { |
|
59 case 'R': return ROOK; |
|
60 case 'N': return KNIGHT; |
|
61 case 'B': return BISHOP; |
|
62 case 'Q': return QUEEN; |
|
63 case 'K': return KING; |
|
64 default: return 0; |
|
65 } |
|
66 } |
|
67 |
|
68 /** |
|
69 * Guesses the location of a piece for short algebraic notation. |
|
70 * |
|
71 * @param board the current state of the board |
|
72 * @param move the move date to operate on |
|
73 * @return status code (see rules/rules.h for the codes) |
|
74 */ |
|
75 static int getlocation(Board board, Move *move) { |
|
76 uint8_t piece = move->piece & PIECE_MASK; |
|
77 switch (piece) { |
|
78 case PAWN: return pawn_getlocation(board, move); |
|
79 case ROOK: return rook_getlocation(board, move); |
|
80 case KNIGHT: return knight_getlocation(board, move); |
|
81 case BISHOP: return bishop_getlocation(board, move); |
|
82 case QUEEN: return queen_getlocation(board, move); |
|
83 case KING: return king_getlocation(board, move); |
|
84 default: return INVALID_MOVE_SYNTAX; |
|
85 } |
|
86 } |
|
87 |
|
88 |
37 |
89 static void draw_board(Board board, MoveListRoot *movelist, uint8_t mycolor) { |
38 static void draw_board(Board board, MoveListRoot *movelist, uint8_t mycolor) { |
90 |
39 |
91 for (uint8_t y = 0 ; y < 8 ; y++) { |
40 for (uint8_t y = 0 ; y < 8 ; y++) { |
92 for (uint8_t x = 0 ; x < 8 ; x++) { |
41 for (uint8_t x = 0 ; x < 8 ; x++) { |
156 logelem = logelem->next; |
105 logelem = logelem->next; |
157 } |
106 } |
158 } |
107 } |
159 } |
108 } |
160 |
109 |
161 /** |
|
162 * Applies a move and deletes captured pieces. |
|
163 * |
|
164 * @param board the current board state |
|
165 * @param move the move to apply |
|
166 */ |
|
167 static void apply_move(Board board, Move *move) { |
|
168 uint8_t piece = move->piece & PIECE_MASK; |
|
169 uint8_t color = move->piece & COLOR_MASK; |
|
170 |
|
171 /* en passant capture */ |
|
172 if (move->capture && piece == PAWN && |
|
173 mdst(board, move) == 0) { |
|
174 board[move->fromrow][move->tofile] = 0; |
|
175 } |
|
176 |
|
177 /* remove old en passant threats */ |
|
178 for (uint8_t file = 0 ; file < 8 ; file++) { |
|
179 board[3][file] &= ~ENPASSANT_THREAT; |
|
180 board[4][file] &= ~ENPASSANT_THREAT; |
|
181 } |
|
182 |
|
183 /* add new en passant threat */ |
|
184 if (piece == PAWN && ( |
|
185 (move->fromrow == 1 && move->torow == 3) || |
|
186 (move->fromrow == 6 && move->torow == 4))) { |
|
187 move->piece |= ENPASSANT_THREAT; |
|
188 } |
|
189 |
|
190 /* move (and maybe capture or promote) */ |
|
191 msrc(board, move) = 0; |
|
192 if (move->promotion) { |
|
193 mdst(board, move) = move->promotion; |
|
194 } else { |
|
195 mdst(board, move) = move->piece; |
|
196 } |
|
197 |
|
198 /* castling */ |
|
199 if (piece == KING && |
|
200 move->fromfile == fileidx('e')) { |
|
201 |
|
202 if (move->tofile == fileidx('g')) { |
|
203 board[move->torow][fileidx('h')] = 0; |
|
204 board[move->torow][fileidx('f')] = color|ROOK; |
|
205 } else if (move->tofile == fileidx('c')) { |
|
206 board[move->torow][fileidx('a')] = 0; |
|
207 board[move->torow][fileidx('d')] = color|ROOK; |
|
208 } |
|
209 } |
|
210 } |
|
211 |
|
212 /** |
|
213 * Validates move by applying chess rules. |
|
214 * @param board the current board state |
|
215 * @param move the move to validate |
|
216 * @return TRUE, if the move complies to chess rules, FALSE otherwise |
|
217 */ |
|
218 static _Bool validate_move(Board board, Move *move) { |
|
219 _Bool result; |
|
220 |
|
221 /* validate indices (don't trust opponent) */ |
|
222 if (!chkidx(move)) { |
|
223 return FALSE; |
|
224 } |
|
225 |
|
226 /* does piece exist */ |
|
227 result = msrc(board, move) == move->piece; |
|
228 |
|
229 /* can't capture own pieces */ |
|
230 if ((mdst(board, move) & COLOR_MASK) == (move->piece & COLOR_MASK)) { |
|
231 return FALSE; |
|
232 } |
|
233 |
|
234 /* validate individual rules */ |
|
235 switch (move->piece & PIECE_MASK) { |
|
236 case PAWN: |
|
237 result = result && pawn_chkrules(board, move); |
|
238 result = result && !pawn_isblocked(board, move); |
|
239 break; |
|
240 case ROOK: |
|
241 result = result && rook_chkrules(move); |
|
242 result = result && !rook_isblocked(board, move); |
|
243 break; |
|
244 case KNIGHT: |
|
245 result = result && knight_chkrules(move); |
|
246 result = result && !knight_isblocked(board, move); |
|
247 break; |
|
248 case BISHOP: |
|
249 result = result && bishop_chkrules(move); |
|
250 result = result && !bishop_isblocked(board, move); |
|
251 break; |
|
252 case QUEEN: |
|
253 result = result && queen_chkrules(move); |
|
254 result = result && !queen_isblocked(board, move); |
|
255 break; |
|
256 case KING: |
|
257 result = result && king_chkrules(board, move); |
|
258 result = result && !king_isblocked(board, move); |
|
259 break; |
|
260 default: |
|
261 result = FALSE; |
|
262 } |
|
263 |
|
264 /* is piece pinned */ |
|
265 // TODO: make it so |
|
266 |
|
267 /* correct check and checkmate flags */ |
|
268 // TODO: make it so |
|
269 |
|
270 return result; |
|
271 } |
|
272 |
|
273 /** |
|
274 * Evaluates a move syntactically and stores the move data in the specified |
|
275 * object. |
|
276 * |
|
277 * @param board the current state of the board |
|
278 * @param mycolor the color of the current player |
|
279 * @param mstr the input string to parse |
|
280 * @param move a pointer to object where the move data shall be stored |
|
281 * @return status code (see rules/rules.h for the list of codes) |
|
282 */ |
|
283 static int eval_move(Board board, uint8_t mycolor, char *mstr, Move *move) { |
|
284 memset(move, 0, sizeof(Move)); |
|
285 move->fromfile = POS_UNSPECIFIED; |
|
286 move->fromrow = POS_UNSPECIFIED; |
|
287 |
|
288 size_t len = strlen(mstr); |
|
289 |
|
290 /* evaluate check/checkmate flags */ |
|
291 if (mstr[len-1] == '+') { |
|
292 len--; mstr[len] = '\0'; |
|
293 move->check = TRUE; |
|
294 } else if (mstr[len-1] == '#') { |
|
295 len--; mstr[len] = '\0'; |
|
296 move->checkmate = TRUE; |
|
297 } |
|
298 |
|
299 /* evaluate promotion */ |
|
300 if (len > 3 && mstr[len-2] == '=') { |
|
301 move->promotion = getpiece(mstr[len-1]); |
|
302 if (!move->promotion) { |
|
303 return INVALID_MOVE_SYNTAX; |
|
304 } else { |
|
305 move->promotion |= mycolor; |
|
306 len -= 2; |
|
307 mstr[len] = 0; |
|
308 } |
|
309 } |
|
310 |
|
311 if (len == 2) { |
|
312 /* pawn move (e.g. "e4") */ |
|
313 move->piece = PAWN; |
|
314 move->tofile = fileidx(mstr[0]); |
|
315 move->torow = rowidx(mstr[1]); |
|
316 } else if (len == 3) { |
|
317 if (strcmp(mstr, "O-O") == 0) { |
|
318 /* king side castling */ |
|
319 move->piece = KING; |
|
320 move->fromfile = fileidx('e'); |
|
321 move->tofile = fileidx('g'); |
|
322 move->fromrow = move->torow = mycolor == WHITE ? 0 : 7; |
|
323 } else { |
|
324 /* move (e.g. "Nf3") */ |
|
325 move->piece = getpiece(mstr[0]); |
|
326 move->tofile = fileidx(mstr[1]); |
|
327 move->torow = rowidx(mstr[2]); |
|
328 } |
|
329 |
|
330 } else if (len == 4) { |
|
331 move->piece = getpiece(mstr[0]); |
|
332 if (!move->piece) { |
|
333 move->piece = PAWN; |
|
334 move->fromfile = fileidx(mstr[0]); |
|
335 } |
|
336 if (mstr[1] == 'x') { |
|
337 /* capture (e.g. "Nxf3", "dxe5") */ |
|
338 move->capture = TRUE; |
|
339 } else { |
|
340 /* move (e.g. "Ndf3", "N2c3", "e2e4") */ |
|
341 if (isfile(mstr[1])) { |
|
342 move->fromfile = fileidx(mstr[1]); |
|
343 if (move->piece == PAWN) { |
|
344 move->piece = 0; |
|
345 } |
|
346 } else { |
|
347 move->fromrow = rowidx(mstr[1]); |
|
348 } |
|
349 } |
|
350 move->tofile = fileidx(mstr[2]); |
|
351 move->torow = rowidx(mstr[3]); |
|
352 } else if (len == 5) { |
|
353 if (strcmp(mstr, "O-O-O") == 0) { |
|
354 /* queen side castling "O-O-O" */ |
|
355 move->piece = KING; |
|
356 move->fromfile = fileidx('e'); |
|
357 move->tofile = fileidx('c'); |
|
358 move->fromrow = move->torow = mycolor == WHITE ? 0 : 7; |
|
359 } else { |
|
360 move->piece = getpiece(mstr[0]); |
|
361 if (mstr[2] == 'x') { |
|
362 move->capture = TRUE; |
|
363 if (move->piece) { |
|
364 /* capture (e.g. "Ndxf3") */ |
|
365 move->fromfile = fileidx(mstr[1]); |
|
366 } else { |
|
367 /* long notation capture (e.g. "e5xf6") */ |
|
368 move->piece = PAWN; |
|
369 move->fromfile = fileidx(mstr[0]); |
|
370 move->fromrow = rowidx(mstr[1]); |
|
371 } |
|
372 } else { |
|
373 /* long notation move (e.g. "Nc5a4") */ |
|
374 move->fromfile = fileidx(mstr[1]); |
|
375 move->fromrow = rowidx(mstr[2]); |
|
376 } |
|
377 move->tofile = fileidx(mstr[3]); |
|
378 move->torow = rowidx(mstr[4]); |
|
379 } |
|
380 } else if (len == 6) { |
|
381 /* long notation capture (e.g. "Nc5xf3") */ |
|
382 if (mstr[3] == 'x') { |
|
383 move->capture = TRUE; |
|
384 move->piece = getpiece(mstr[0]); |
|
385 move->fromfile = fileidx(mstr[1]); |
|
386 move->fromrow = rowidx(mstr[2]); |
|
387 move->tofile = fileidx(mstr[4]); |
|
388 move->torow = rowidx(mstr[5]); |
|
389 } |
|
390 } |
|
391 |
|
392 |
|
393 if (move->piece) { |
|
394 if (move->piece == PAWN && move->torow == (mycolor==WHITE?7:0) |
|
395 && !move->promotion) { |
|
396 return NEED_PROMOTION; |
|
397 } |
|
398 |
|
399 move->piece |= mycolor; |
|
400 if (move->fromfile == POS_UNSPECIFIED |
|
401 || move->fromrow == POS_UNSPECIFIED) { |
|
402 return getlocation(board, move); |
|
403 } else { |
|
404 return chkidx(move) ? VALID_MOVE_SYNTAX : INVALID_POSITION; |
|
405 } |
|
406 } else { |
|
407 return INVALID_MOVE_SYNTAX; |
|
408 } |
|
409 } |
|
410 |
110 |
411 static int sendmove(Board board, MoveListRoot *movelist, |
111 static int sendmove(Board board, MoveListRoot *movelist, |
412 uint8_t mycolor, int opponent) { |
112 uint8_t mycolor, int opponent) { |
413 |
113 |
414 const size_t buflen = 8; |
114 const size_t buflen = 8; |