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 |
37 |
38 static void draw_board(Board board, uint8_t mycolor) { |
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 |
|
89 static void draw_board(Board board, MoveListRoot *movelist, uint8_t mycolor) { |
39 |
90 |
40 for (uint8_t y = 0 ; y < 8 ; y++) { |
91 for (uint8_t y = 0 ; y < 8 ; y++) { |
41 for (uint8_t x = 0 ; x < 8 ; x++) { |
92 for (uint8_t x = 0 ; x < 8 ; x++) { |
42 uint8_t col = board[y][x] & COLOR_MASK; |
93 uint8_t col = board[y][x] & COLOR_MASK; |
43 uint8_t piece = board[y][x] & PIECE_MASK; |
94 uint8_t piece = board[y][x] & PIECE_MASK; |
44 char piecec = ' '; |
95 char piecec; |
45 switch (piece) { |
96 if (piece) { |
46 case PAWN: piecec = 'P'; break; |
97 piecec = piece == PAWN ? 'P' : getpiecechr(piece); |
47 case ROOK: piecec = 'R'; break; |
98 } else { |
48 case KNIGHT: piecec = 'N'; break; |
99 piecec = ' '; |
49 case BISHOP: piecec = 'B'; break; |
|
50 case QUEEN: piecec = 'Q'; break; |
|
51 case KING: piecec = 'K'; break; |
|
52 } |
100 } |
53 |
101 |
54 attrset((col == WHITE ? A_BOLD : A_DIM) | |
102 attrset((col == WHITE ? A_BOLD : A_DIM) | |
55 COLOR_PAIR((y&1)==(x&1) ? COL_WB : COL_BW)); |
103 COLOR_PAIR((y&1)==(x&1) ? COL_WB : COL_BW)); |
56 |
104 |
67 int x = mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3; |
115 int x = mycolor == WHITE ? boardx+i*3+1 : boardx+22-i*3; |
68 int y = mycolor == WHITE ? boardy-i : boardy-7+i; |
116 int y = mycolor == WHITE ? boardy-i : boardy-7+i; |
69 mvaddch(boardy+1, x, 'a'+i); |
117 mvaddch(boardy+1, x, 'a'+i); |
70 mvaddch(y, boardx-2, '1'+i); |
118 mvaddch(y, boardx-2, '1'+i); |
71 } |
119 } |
|
120 |
|
121 /* move log */ |
|
122 // TODO: introduce window to avoid bugs with a long move log |
|
123 uint8_t logy = 0; |
|
124 const uint8_t logx = boardx + 30; |
|
125 int logi = 1; |
|
126 MoveList *logelem = movelist->first; |
|
127 |
|
128 while (logelem) { |
|
129 logi++; |
|
130 if (logi % 2 == 0) { |
|
131 if ((logi - 2) % 4 == 0) { |
|
132 logy++; |
|
133 wmove(tchess_window, logy, logx); |
|
134 } |
|
135 printw("%d. ", logi / 2); |
|
136 } |
|
137 |
|
138 if (logelem) { |
|
139 Move move = logelem->move; |
|
140 char logstr[] = { |
|
141 getpiecechr(move.piece), |
|
142 filechr(move.fromfile), rowchr(move.fromrow), |
|
143 move.capture ? 'x':'\0', |
|
144 filechr(move.tofile), rowchr(move.torow), |
|
145 move.check ? '+' : (move.checkmate ? '#' : |
|
146 (move.promotion ? '=' : '\0')), |
|
147 move.promotion ? getpiecechr(move.promotion) : '\0', |
|
148 ' ' |
|
149 }; |
|
150 for (int stri = 0 ; stri < sizeof(logstr) ; stri++) { |
|
151 if (logstr[stri]) { |
|
152 addch(logstr[stri]); |
|
153 } |
|
154 } |
|
155 |
|
156 logelem = logelem->next; |
|
157 } |
|
158 } |
72 } |
159 } |
73 |
160 |
74 /** |
161 /** |
75 * Applies a move and deletes captured pieces. |
162 * Applies a move and deletes captured pieces. |
76 * |
163 * |
98 (move->fromrow == 1 && move->torow == 3) || |
185 (move->fromrow == 1 && move->torow == 3) || |
99 (move->fromrow == 6 && move->torow == 4))) { |
186 (move->fromrow == 6 && move->torow == 4))) { |
100 move->piece |= ENPASSANT_THREAT; |
187 move->piece |= ENPASSANT_THREAT; |
101 } |
188 } |
102 |
189 |
103 /* move (and maybe capture) */ |
190 /* move (and maybe capture or promote) */ |
104 msrc(board, move) = 0; |
191 msrc(board, move) = 0; |
105 mdst(board, move) = move->piece; |
192 if (move->promotion) { |
|
193 mdst(board, move) = move->promotion; |
|
194 } else { |
|
195 mdst(board, move) = move->piece; |
|
196 } |
106 |
197 |
107 /* castling */ |
198 /* castling */ |
108 if (piece == KING && |
199 if (piece == KING && |
109 move->fromfile == fileidx('e')) { |
200 move->fromfile == fileidx('e')) { |
110 |
201 |
178 |
269 |
179 return result; |
270 return result; |
180 } |
271 } |
181 |
272 |
182 /** |
273 /** |
183 * Maps a character to a piece. |
|
184 * |
|
185 * Does not work for pawns, since they don't have a character. |
|
186 * |
|
187 * @param c one of R,N,B,Q,K |
|
188 * @return numeric value for the specified piece |
|
189 */ |
|
190 static uint8_t getpiece(char c) { |
|
191 switch (c) { |
|
192 case 'R': return ROOK; |
|
193 case 'N': return KNIGHT; |
|
194 case 'B': return BISHOP; |
|
195 case 'Q': return QUEEN; |
|
196 case 'K': return KING; |
|
197 default: return 0; |
|
198 } |
|
199 } |
|
200 |
|
201 /** |
|
202 * Guesses the location of a piece for short algebraic notation. |
|
203 * |
|
204 * @param board the current state of the board |
|
205 * @param move the move date to operate on |
|
206 * @return status code (see rules/rules.h for the codes) |
|
207 */ |
|
208 static int getlocation(Board board, Move *move) { |
|
209 uint8_t piece = move->piece & PIECE_MASK; |
|
210 switch (piece) { |
|
211 case PAWN: return pawn_getlocation(board, move); |
|
212 case ROOK: return rook_getlocation(board, move); |
|
213 case KNIGHT: return knight_getlocation(board, move); |
|
214 case BISHOP: return bishop_getlocation(board, move); |
|
215 case QUEEN: return queen_getlocation(board, move); |
|
216 case KING: return king_getlocation(board, move); |
|
217 default: return INVALID_MOVE_SYNTAX; |
|
218 } |
|
219 } |
|
220 |
|
221 /** |
|
222 * Evaluates a move syntactically and stores the move data in the specified |
274 * Evaluates a move syntactically and stores the move data in the specified |
223 * object. |
275 * object. |
224 * |
276 * |
225 * @param board the current state of the board |
277 * @param board the current state of the board |
226 * @param mycolor the color of the current player |
278 * @param mycolor the color of the current player |
230 */ |
282 */ |
231 static int eval_move(Board board, uint8_t mycolor, char *mstr, Move *move) { |
283 static int eval_move(Board board, uint8_t mycolor, char *mstr, Move *move) { |
232 memset(move, 0, sizeof(Move)); |
284 memset(move, 0, sizeof(Move)); |
233 move->fromfile = POS_UNSPECIFIED; |
285 move->fromfile = POS_UNSPECIFIED; |
234 move->fromrow = POS_UNSPECIFIED; |
286 move->fromrow = POS_UNSPECIFIED; |
235 // TODO: promotion |
287 |
236 size_t len = strlen(mstr); |
288 size_t len = strlen(mstr); |
237 |
289 |
238 /* evaluate check/checkmate flags */ |
290 /* evaluate check/checkmate flags */ |
239 if (mstr[len-1] == '+') { |
291 if (mstr[len-1] == '+') { |
240 len--; mstr[len] = '\0'; |
292 len--; mstr[len] = '\0'; |
241 move->check = TRUE; |
293 move->check = TRUE; |
242 } else if (mstr[len-1] == '#') { |
294 } else if (mstr[len-1] == '#') { |
243 len--; mstr[len] = '\0'; |
295 len--; mstr[len] = '\0'; |
244 move->checkmate = TRUE; |
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 } |
245 } |
309 } |
246 |
310 |
247 if (len == 2) { |
311 if (len == 2) { |
248 /* pawn move (e.g. "e4") */ |
312 /* pawn move (e.g. "e4") */ |
249 move->piece = PAWN; |
313 move->piece = PAWN; |
337 } else { |
406 } else { |
338 return INVALID_MOVE_SYNTAX; |
407 return INVALID_MOVE_SYNTAX; |
339 } |
408 } |
340 } |
409 } |
341 |
410 |
342 static int sendmove(Board board, uint8_t mycolor, int opponent) { |
411 static int sendmove(Board board, MoveListRoot *movelist, |
|
412 uint8_t mycolor, int opponent) { |
|
413 |
343 const size_t buflen = 8; |
414 const size_t buflen = 8; |
344 char movestr[buflen]; |
415 char movestr[buflen]; |
345 _Bool remisrejected = FALSE; |
416 _Bool remisrejected = FALSE; |
346 uint8_t code; |
417 uint8_t code; |
347 |
418 |
|
419 int inputy = getmaxy(tchess_window) - 6; |
348 while (1) { |
420 while (1) { |
349 move(boardy+3, 0); |
421 move(inputy, 0); |
350 if (remisrejected) { |
422 if (remisrejected) { |
351 printw( |
423 printw( |
352 "Use chess notation to enter your move.\n" |
424 "Use chess notation to enter your move.\n" |
353 "Remis offer rejected - type 'surr' to surrender. \n\n" |
425 "Remis offer rejected - type 'surr' to surrender. \n\n" |
354 "Type your move: "); |
426 "Type your move: "); |
384 } |
456 } |
385 } else { |
457 } else { |
386 Move move; |
458 Move move; |
387 int eval_result = eval_move(board, mycolor, movestr, &move); |
459 int eval_result = eval_move(board, mycolor, movestr, &move); |
388 switch (eval_result) { |
460 switch (eval_result) { |
389 case VALID_MOVE_SYNTAX: |
461 case VALID_MOVE_SYNTAX: |
390 net_send_code(opponent, NETCODE_MOVE); |
462 net_send_code(opponent, NETCODE_MOVE); |
391 net_send_data(opponent, &move, sizeof(Move)); |
463 net_send_data(opponent, &move, sizeof(Move)); |
392 code = net_recieve_code(opponent); |
464 code = net_recieve_code(opponent); |
393 move.check = code == NETCODE_CHECK; |
465 move.check = code == NETCODE_CHECK; |
394 move.checkmate = code == NETCODE_CHECKMATE; |
466 move.checkmate = code == NETCODE_CHECKMATE; |
395 // TODO: record move |
467 addmove(movelist, &move); |
396 if (code == NETCODE_DECLINE) { |
468 if (code == NETCODE_DECLINE) { |
397 printw("Invalid move."); |
469 printw("Invalid move."); |
|
470 } else { |
|
471 apply_move(board, &move); |
|
472 if (move.checkmate) { |
|
473 printw("Checkmate!"); |
|
474 clrtoeol(); |
|
475 return 1; |
398 } else { |
476 } else { |
399 apply_move(board, &move); |
477 return 0; |
400 if (move.checkmate) { |
|
401 printw("Checkmate!"); |
|
402 clrtoeol(); |
|
403 return 1; |
|
404 } else { |
|
405 return 0; |
|
406 } |
|
407 } |
478 } |
408 break; |
479 } |
409 case AMBIGUOUS_MOVE: |
480 break; |
410 printw("Ambiguous move - " |
481 case AMBIGUOUS_MOVE: |
411 "please specify the piece to move."); |
482 printw("Ambiguous move - please specify the piece to move."); |
412 break; |
483 break; |
413 case INVALID_POSITION: |
484 case INVALID_POSITION: |
414 printw("Cannot find the piece that shall be moved."); |
485 printw("Cannot find the piece that shall be moved."); |
415 break; |
486 break; |
416 default: |
487 case NEED_PROMOTION: |
417 printw("Can't interpret move - " |
488 printw("You need to promote the pawn (append \"=Q\" e.g.)!"); |
418 "please use algebraic notation."); |
489 break; |
|
490 default: |
|
491 printw("Can't interpret move - please use algebraic notation."); |
419 } |
492 } |
420 clrtoeol(); |
493 clrtoeol(); |
421 } |
494 } |
422 } |
495 } |
423 } |
496 } |
424 |
497 |
425 static int recvmove(Board board, int opponent) { |
498 static int recvmove(Board board, MoveListRoot *movelist, int opponent) { |
426 |
499 |
|
500 int inputy = getmaxy(tchess_window) - 6; |
427 while (1) { |
501 while (1) { |
428 move(boardy+3, 0); |
502 move(inputy, 0); |
429 printw("Awaiting opponent move..."); |
503 printw("Awaiting opponent move..."); |
430 clrtoeol(); |
504 clrtoeol(); |
431 refresh(); |
505 refresh(); |
432 |
506 |
433 // TODO: nonblocking |
507 // TODO: nonblocking |
452 break; |
526 break; |
453 case NETCODE_MOVE: |
527 case NETCODE_MOVE: |
454 net_recieve_data(opponent, &move, sizeof(Move)); |
528 net_recieve_data(opponent, &move, sizeof(Move)); |
455 if (validate_move(board, &move)) { |
529 if (validate_move(board, &move)) { |
456 apply_move(board, &move); |
530 apply_move(board, &move); |
457 // TODO: record move |
531 addmove(movelist, &move); |
458 if (move.check) { |
532 if (move.check) { |
459 net_send_code(opponent, NETCODE_CHECK); |
533 net_send_code(opponent, NETCODE_CHECK); |
460 } else if (move.checkmate) { |
534 } else if (move.checkmate) { |
461 net_send_code(opponent, NETCODE_CHECKMATE); |
535 net_send_code(opponent, NETCODE_CHECKMATE); |
462 } else { |
536 } else { |
468 } |
542 } |
469 } |
543 } |
470 } |
544 } |
471 } |
545 } |
472 |
546 |
|
547 void freemovelist(MoveListRoot* list) { |
|
548 MoveList *elem; |
|
549 elem = list->first; |
|
550 while (elem) { |
|
551 MoveList *cur = elem; |
|
552 elem = elem->next; |
|
553 free(cur); |
|
554 }; |
|
555 free(list); |
|
556 } |
|
557 |
|
558 void addmove(MoveListRoot* list, Move *move) { |
|
559 MoveList *elem = malloc(sizeof(MoveList)); |
|
560 elem->next = NULL; |
|
561 elem->move = *move; |
|
562 |
|
563 if (list->last) { |
|
564 list->last->next = elem; |
|
565 list->last = elem; |
|
566 } else { |
|
567 list->first = list->last = elem; |
|
568 } |
|
569 } |
|
570 |
473 void game_start(Settings *settings, int opponent) { |
571 void game_start(Settings *settings, int opponent) { |
474 _Bool myturn = is_server(settings) == |
572 _Bool myturn = is_server(settings) == |
475 (settings->gameinfo.servercolor == WHITE); |
573 (settings->gameinfo.servercolor == WHITE); |
476 uint8_t mycolor = myturn ? WHITE:BLACK; |
574 uint8_t mycolor = myturn ? WHITE:BLACK; |
477 |
575 |
478 _Bool running; |
576 _Bool running; |
|
577 |
|
578 MoveListRoot* movelist = calloc(1, sizeof(MoveListRoot)); |
479 |
579 |
480 Board board = { |
580 Board board = { |
481 {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, |
581 {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, |
482 {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, |
582 {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, |
483 {0, 0, 0, 0, 0, 0, 0, 0}, |
583 {0, 0, 0, 0, 0, 0, 0, 0}, |
488 {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} |
588 {BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK} |
489 }; |
589 }; |
490 |
590 |
491 do { |
591 do { |
492 clear(); |
592 clear(); |
493 draw_board(board, mycolor); |
593 draw_board(board, movelist, mycolor); |
494 if (myturn) { |
594 if (myturn) { |
495 running = !sendmove(board, mycolor, opponent); |
595 running = !sendmove(board, movelist, mycolor, opponent); |
496 } else { |
596 } else { |
497 running = !recvmove(board, opponent); |
597 running = !recvmove(board, movelist, opponent); |
498 flushinp(); // flush any input the user hacked in while waiting |
598 flushinp(); // flush any input the user hacked in while waiting |
499 } |
599 } |
500 myturn ^= TRUE; |
600 myturn ^= TRUE; |
501 } while (running); |
601 } while (running); |
|
602 |
|
603 freemovelist(movelist); |
502 |
604 |
503 mvaddstr(getmaxy(tchess_window)-1, 0, |
605 mvaddstr(getmaxy(tchess_window)-1, 0, |
504 "Game has ended. Press any key to leave..."); |
606 "Game has ended. Press any key to leave..."); |
505 getch(); |
607 getch(); |
506 } |
608 } |