Sat, 29 Mar 2014 16:53:58 +0100
fixed bishop + added pawn promotion + added move log
src/game.c | file | annotate | diff | comparison | revisions | |
src/game.h | file | annotate | diff | comparison | revisions | |
src/network.h | file | annotate | diff | comparison | revisions | |
src/rules/bishop.c | file | annotate | diff | comparison | revisions | |
src/rules/pawn.c | file | annotate | diff | comparison | revisions | |
src/rules/rules.h | file | annotate | diff | comparison | revisions |
1.1 --- a/src/game.c Sat Mar 29 14:46:33 2014 +0100 1.2 +++ b/src/game.c Sat Mar 29 16:53:58 2014 +0100 1.3 @@ -35,20 +35,68 @@ 1.4 1.5 static const uint8_t boardx = 10, boardy = 10; 1.6 1.7 -static void draw_board(Board board, uint8_t mycolor) { 1.8 +static uint8_t getpiecechr(uint8_t piece) { 1.9 + switch (piece & PIECE_MASK) { 1.10 + case ROOK: return 'R'; 1.11 + case KNIGHT: return 'N'; 1.12 + case BISHOP: return 'B'; 1.13 + case QUEEN: return 'Q'; 1.14 + case KING: return 'K'; 1.15 + default: return '\0'; 1.16 + } 1.17 +} 1.18 + 1.19 +/** 1.20 + * Maps a character to a piece. 1.21 + * 1.22 + * Does not work for pawns, since they don't have a character. 1.23 + * 1.24 + * @param c one of R,N,B,Q,K 1.25 + * @return numeric value for the specified piece 1.26 + */ 1.27 +static uint8_t getpiece(char c) { 1.28 + switch (c) { 1.29 + case 'R': return ROOK; 1.30 + case 'N': return KNIGHT; 1.31 + case 'B': return BISHOP; 1.32 + case 'Q': return QUEEN; 1.33 + case 'K': return KING; 1.34 + default: return 0; 1.35 + } 1.36 +} 1.37 + 1.38 +/** 1.39 + * Guesses the location of a piece for short algebraic notation. 1.40 + * 1.41 + * @param board the current state of the board 1.42 + * @param move the move date to operate on 1.43 + * @return status code (see rules/rules.h for the codes) 1.44 + */ 1.45 +static int getlocation(Board board, Move *move) { 1.46 + uint8_t piece = move->piece & PIECE_MASK; 1.47 + switch (piece) { 1.48 + case PAWN: return pawn_getlocation(board, move); 1.49 + case ROOK: return rook_getlocation(board, move); 1.50 + case KNIGHT: return knight_getlocation(board, move); 1.51 + case BISHOP: return bishop_getlocation(board, move); 1.52 + case QUEEN: return queen_getlocation(board, move); 1.53 + case KING: return king_getlocation(board, move); 1.54 + default: return INVALID_MOVE_SYNTAX; 1.55 + } 1.56 +} 1.57 + 1.58 + 1.59 +static void draw_board(Board board, MoveListRoot *movelist, uint8_t mycolor) { 1.60 1.61 for (uint8_t y = 0 ; y < 8 ; y++) { 1.62 for (uint8_t x = 0 ; x < 8 ; x++) { 1.63 uint8_t col = board[y][x] & COLOR_MASK; 1.64 uint8_t piece = board[y][x] & PIECE_MASK; 1.65 - char piecec = ' '; 1.66 - switch (piece) { 1.67 - case PAWN: piecec = 'P'; break; 1.68 - case ROOK: piecec = 'R'; break; 1.69 - case KNIGHT: piecec = 'N'; break; 1.70 - case BISHOP: piecec = 'B'; break; 1.71 - case QUEEN: piecec = 'Q'; break; 1.72 - case KING: piecec = 'K'; break; 1.73 + char piecec; 1.74 + if (piece) { 1.75 + piecec = piece == PAWN ? 'P' : getpiecechr(piece); 1.76 + } else { 1.77 + piecec = ' '; 1.78 } 1.79 1.80 attrset((col == WHITE ? A_BOLD : A_DIM) | 1.81 @@ -69,6 +117,45 @@ 1.82 mvaddch(boardy+1, x, 'a'+i); 1.83 mvaddch(y, boardx-2, '1'+i); 1.84 } 1.85 + 1.86 + /* move log */ 1.87 + // TODO: introduce window to avoid bugs with a long move log 1.88 + uint8_t logy = 0; 1.89 + const uint8_t logx = boardx + 30; 1.90 + int logi = 1; 1.91 + MoveList *logelem = movelist->first; 1.92 + 1.93 + while (logelem) { 1.94 + logi++; 1.95 + if (logi % 2 == 0) { 1.96 + if ((logi - 2) % 4 == 0) { 1.97 + logy++; 1.98 + wmove(tchess_window, logy, logx); 1.99 + } 1.100 + printw("%d. ", logi / 2); 1.101 + } 1.102 + 1.103 + if (logelem) { 1.104 + Move move = logelem->move; 1.105 + char logstr[] = { 1.106 + getpiecechr(move.piece), 1.107 + filechr(move.fromfile), rowchr(move.fromrow), 1.108 + move.capture ? 'x':'\0', 1.109 + filechr(move.tofile), rowchr(move.torow), 1.110 + move.check ? '+' : (move.checkmate ? '#' : 1.111 + (move.promotion ? '=' : '\0')), 1.112 + move.promotion ? getpiecechr(move.promotion) : '\0', 1.113 + ' ' 1.114 + }; 1.115 + for (int stri = 0 ; stri < sizeof(logstr) ; stri++) { 1.116 + if (logstr[stri]) { 1.117 + addch(logstr[stri]); 1.118 + } 1.119 + } 1.120 + 1.121 + logelem = logelem->next; 1.122 + } 1.123 + } 1.124 } 1.125 1.126 /** 1.127 @@ -100,9 +187,13 @@ 1.128 move->piece |= ENPASSANT_THREAT; 1.129 } 1.130 1.131 - /* move (and maybe capture) */ 1.132 + /* move (and maybe capture or promote) */ 1.133 msrc(board, move) = 0; 1.134 - mdst(board, move) = move->piece; 1.135 + if (move->promotion) { 1.136 + mdst(board, move) = move->promotion; 1.137 + } else { 1.138 + mdst(board, move) = move->piece; 1.139 + } 1.140 1.141 /* castling */ 1.142 if (piece == KING && 1.143 @@ -180,45 +271,6 @@ 1.144 } 1.145 1.146 /** 1.147 - * Maps a character to a piece. 1.148 - * 1.149 - * Does not work for pawns, since they don't have a character. 1.150 - * 1.151 - * @param c one of R,N,B,Q,K 1.152 - * @return numeric value for the specified piece 1.153 - */ 1.154 -static uint8_t getpiece(char c) { 1.155 - switch (c) { 1.156 - case 'R': return ROOK; 1.157 - case 'N': return KNIGHT; 1.158 - case 'B': return BISHOP; 1.159 - case 'Q': return QUEEN; 1.160 - case 'K': return KING; 1.161 - default: return 0; 1.162 - } 1.163 -} 1.164 - 1.165 -/** 1.166 - * Guesses the location of a piece for short algebraic notation. 1.167 - * 1.168 - * @param board the current state of the board 1.169 - * @param move the move date to operate on 1.170 - * @return status code (see rules/rules.h for the codes) 1.171 - */ 1.172 -static int getlocation(Board board, Move *move) { 1.173 - uint8_t piece = move->piece & PIECE_MASK; 1.174 - switch (piece) { 1.175 - case PAWN: return pawn_getlocation(board, move); 1.176 - case ROOK: return rook_getlocation(board, move); 1.177 - case KNIGHT: return knight_getlocation(board, move); 1.178 - case BISHOP: return bishop_getlocation(board, move); 1.179 - case QUEEN: return queen_getlocation(board, move); 1.180 - case KING: return king_getlocation(board, move); 1.181 - default: return INVALID_MOVE_SYNTAX; 1.182 - } 1.183 -} 1.184 - 1.185 -/** 1.186 * Evaluates a move syntactically and stores the move data in the specified 1.187 * object. 1.188 * 1.189 @@ -232,7 +284,7 @@ 1.190 memset(move, 0, sizeof(Move)); 1.191 move->fromfile = POS_UNSPECIFIED; 1.192 move->fromrow = POS_UNSPECIFIED; 1.193 - // TODO: promotion 1.194 + 1.195 size_t len = strlen(mstr); 1.196 1.197 /* evaluate check/checkmate flags */ 1.198 @@ -244,6 +296,18 @@ 1.199 move->checkmate = TRUE; 1.200 } 1.201 1.202 + /* evaluate promotion */ 1.203 + if (len > 3 && mstr[len-2] == '=') { 1.204 + move->promotion = getpiece(mstr[len-1]); 1.205 + if (!move->promotion) { 1.206 + return INVALID_MOVE_SYNTAX; 1.207 + } else { 1.208 + move->promotion |= mycolor; 1.209 + len -= 2; 1.210 + mstr[len] = 0; 1.211 + } 1.212 + } 1.213 + 1.214 if (len == 2) { 1.215 /* pawn move (e.g. "e4") */ 1.216 move->piece = PAWN; 1.217 @@ -327,6 +391,11 @@ 1.218 1.219 1.220 if (move->piece) { 1.221 + if (move->piece == PAWN && move->torow == (mycolor==WHITE?7:0) 1.222 + && !move->promotion) { 1.223 + return NEED_PROMOTION; 1.224 + } 1.225 + 1.226 move->piece |= mycolor; 1.227 if (move->fromfile == POS_UNSPECIFIED 1.228 || move->fromrow == POS_UNSPECIFIED) { 1.229 @@ -339,14 +408,17 @@ 1.230 } 1.231 } 1.232 1.233 -static int sendmove(Board board, uint8_t mycolor, int opponent) { 1.234 +static int sendmove(Board board, MoveListRoot *movelist, 1.235 + uint8_t mycolor, int opponent) { 1.236 + 1.237 const size_t buflen = 8; 1.238 char movestr[buflen]; 1.239 _Bool remisrejected = FALSE; 1.240 uint8_t code; 1.241 1.242 + int inputy = getmaxy(tchess_window) - 6; 1.243 while (1) { 1.244 - move(boardy+3, 0); 1.245 + move(inputy, 0); 1.246 if (remisrejected) { 1.247 printw( 1.248 "Use chess notation to enter your move.\n" 1.249 @@ -386,46 +458,48 @@ 1.250 Move move; 1.251 int eval_result = eval_move(board, mycolor, movestr, &move); 1.252 switch (eval_result) { 1.253 - case VALID_MOVE_SYNTAX: 1.254 - net_send_code(opponent, NETCODE_MOVE); 1.255 - net_send_data(opponent, &move, sizeof(Move)); 1.256 - code = net_recieve_code(opponent); 1.257 - move.check = code == NETCODE_CHECK; 1.258 - move.checkmate = code == NETCODE_CHECKMATE; 1.259 - // TODO: record move 1.260 - if (code == NETCODE_DECLINE) { 1.261 - printw("Invalid move."); 1.262 + case VALID_MOVE_SYNTAX: 1.263 + net_send_code(opponent, NETCODE_MOVE); 1.264 + net_send_data(opponent, &move, sizeof(Move)); 1.265 + code = net_recieve_code(opponent); 1.266 + move.check = code == NETCODE_CHECK; 1.267 + move.checkmate = code == NETCODE_CHECKMATE; 1.268 + addmove(movelist, &move); 1.269 + if (code == NETCODE_DECLINE) { 1.270 + printw("Invalid move."); 1.271 + } else { 1.272 + apply_move(board, &move); 1.273 + if (move.checkmate) { 1.274 + printw("Checkmate!"); 1.275 + clrtoeol(); 1.276 + return 1; 1.277 } else { 1.278 - apply_move(board, &move); 1.279 - if (move.checkmate) { 1.280 - printw("Checkmate!"); 1.281 - clrtoeol(); 1.282 - return 1; 1.283 - } else { 1.284 - return 0; 1.285 - } 1.286 + return 0; 1.287 } 1.288 - break; 1.289 - case AMBIGUOUS_MOVE: 1.290 - printw("Ambiguous move - " 1.291 - "please specify the piece to move."); 1.292 - break; 1.293 - case INVALID_POSITION: 1.294 - printw("Cannot find the piece that shall be moved."); 1.295 - break; 1.296 - default: 1.297 - printw("Can't interpret move - " 1.298 - "please use algebraic notation."); 1.299 + } 1.300 + break; 1.301 + case AMBIGUOUS_MOVE: 1.302 + printw("Ambiguous move - please specify the piece to move."); 1.303 + break; 1.304 + case INVALID_POSITION: 1.305 + printw("Cannot find the piece that shall be moved."); 1.306 + break; 1.307 + case NEED_PROMOTION: 1.308 + printw("You need to promote the pawn (append \"=Q\" e.g.)!"); 1.309 + break; 1.310 + default: 1.311 + printw("Can't interpret move - please use algebraic notation."); 1.312 } 1.313 clrtoeol(); 1.314 } 1.315 } 1.316 } 1.317 1.318 -static int recvmove(Board board, int opponent) { 1.319 +static int recvmove(Board board, MoveListRoot *movelist, int opponent) { 1.320 1.321 + int inputy = getmaxy(tchess_window) - 6; 1.322 while (1) { 1.323 - move(boardy+3, 0); 1.324 + move(inputy, 0); 1.325 printw("Awaiting opponent move..."); 1.326 clrtoeol(); 1.327 refresh(); 1.328 @@ -454,7 +528,7 @@ 1.329 net_recieve_data(opponent, &move, sizeof(Move)); 1.330 if (validate_move(board, &move)) { 1.331 apply_move(board, &move); 1.332 - // TODO: record move 1.333 + addmove(movelist, &move); 1.334 if (move.check) { 1.335 net_send_code(opponent, NETCODE_CHECK); 1.336 } else if (move.checkmate) { 1.337 @@ -470,6 +544,30 @@ 1.338 } 1.339 } 1.340 1.341 +void freemovelist(MoveListRoot* list) { 1.342 + MoveList *elem; 1.343 + elem = list->first; 1.344 + while (elem) { 1.345 + MoveList *cur = elem; 1.346 + elem = elem->next; 1.347 + free(cur); 1.348 + }; 1.349 + free(list); 1.350 +} 1.351 + 1.352 +void addmove(MoveListRoot* list, Move *move) { 1.353 + MoveList *elem = malloc(sizeof(MoveList)); 1.354 + elem->next = NULL; 1.355 + elem->move = *move; 1.356 + 1.357 + if (list->last) { 1.358 + list->last->next = elem; 1.359 + list->last = elem; 1.360 + } else { 1.361 + list->first = list->last = elem; 1.362 + } 1.363 +} 1.364 + 1.365 void game_start(Settings *settings, int opponent) { 1.366 _Bool myturn = is_server(settings) == 1.367 (settings->gameinfo.servercolor == WHITE); 1.368 @@ -477,6 +575,8 @@ 1.369 1.370 _Bool running; 1.371 1.372 + MoveListRoot* movelist = calloc(1, sizeof(MoveListRoot)); 1.373 + 1.374 Board board = { 1.375 {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, 1.376 {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, 1.377 @@ -490,16 +590,18 @@ 1.378 1.379 do { 1.380 clear(); 1.381 - draw_board(board, mycolor); 1.382 + draw_board(board, movelist, mycolor); 1.383 if (myturn) { 1.384 - running = !sendmove(board, mycolor, opponent); 1.385 + running = !sendmove(board, movelist, mycolor, opponent); 1.386 } else { 1.387 - running = !recvmove(board, opponent); 1.388 + running = !recvmove(board, movelist, opponent); 1.389 flushinp(); // flush any input the user hacked in while waiting 1.390 } 1.391 myturn ^= TRUE; 1.392 } while (running); 1.393 1.394 + freemovelist(movelist); 1.395 + 1.396 mvaddstr(getmaxy(tchess_window)-1, 0, 1.397 "Game has ended. Press any key to leave..."); 1.398 getch();
2.1 --- a/src/game.h Sat Mar 29 14:46:33 2014 +0100 2.2 +++ b/src/game.h Sat Mar 29 16:53:58 2014 +0100 2.3 @@ -72,11 +72,25 @@ 2.4 uint8_t fromrow; 2.5 uint8_t tofile; 2.6 uint8_t torow; 2.7 + uint8_t promotion; 2.8 _Bool check; 2.9 _Bool checkmate; 2.10 _Bool capture; 2.11 } Move; 2.12 2.13 +typedef struct MoveList MoveList; 2.14 +typedef struct MoveListRoot MoveListRoot; 2.15 + 2.16 +struct MoveListRoot { 2.17 + MoveList* first; 2.18 + MoveList* last; 2.19 +}; 2.20 + 2.21 +struct MoveList { 2.22 + Move move; 2.23 + MoveList* next; 2.24 +}; 2.25 + 2.26 #define POS_UNSPECIFIED UINT8_MAX 2.27 #define mdst(b,m) b[(m)->torow][(m)->tofile] 2.28 #define msrc(b,m) b[(m)->fromrow][(m)->fromfile] 2.29 @@ -89,6 +103,9 @@ 2.30 #define rowidx(row) (row-'1') 2.31 #define fileidx(file) (file-'a') 2.32 2.33 +#define rowchr(row) (row+'1') 2.34 +#define filechr(file) (file+'a') 2.35 + 2.36 #define chkidx(move) (isidx((move)->fromfile) && isidx((move)->fromrow) && \ 2.37 isidx((move)->tofile) && isidx((move)->torow)) 2.38 2.39 @@ -98,6 +115,9 @@ 2.40 2.41 void game_start(Settings *settings, int opponent); 2.42 2.43 +void freemovelist(MoveListRoot* list); 2.44 +void addmove(MoveListRoot *movelist, Move *move); 2.45 + 2.46 #ifdef __cplusplus 2.47 } 2.48 #endif
3.1 --- a/src/network.h Sat Mar 29 14:46:33 2014 +0100 3.2 +++ b/src/network.h Sat Mar 29 16:53:58 2014 +0100 3.3 @@ -46,7 +46,7 @@ 3.4 #define NETCODE_CHECK 0x23 3.5 #define NETCODE_CHECKMATE 0x24 3.6 3.7 -#define NETCODE_VERSION 4 3.8 +#define NETCODE_VERSION 5 3.9 3.10 typedef struct { 3.11 int fd; /* -1, if we are the client */
4.1 --- a/src/rules/bishop.c Sat Mar 29 14:46:33 2014 +0100 4.2 +++ b/src/rules/bishop.c Sat Mar 29 16:53:58 2014 +0100 4.3 @@ -109,7 +109,7 @@ 4.4 move->fromfile = file; 4.5 } 4.6 file = move->tofile - d; 4.7 - if (isfile(file) && board[row][file] == move->piece) { 4.8 + if (isidx(file) && board[row][file] == move->piece) { 4.9 if (amb) { 4.10 return AMBIGUOUS_MOVE; 4.11 }
5.1 --- a/src/rules/pawn.c Sat Mar 29 14:46:33 2014 +0100 5.2 +++ b/src/rules/pawn.c Sat Mar 29 16:53:58 2014 +0100 5.3 @@ -32,6 +32,22 @@ 5.4 5.5 _Bool pawn_chkrules(Board board, Move *move) { 5.6 int8_t d = ((move->piece & COLOR_MASK) == WHITE ? -1 : 1); 5.7 + 5.8 + if (move->torow == (d < 0 ? 7 : 0)) { 5.9 + if (move->promotion) { 5.10 + uint8_t promopiece = move->promotion & PIECE_MASK; 5.11 + if (!promopiece || promopiece == PAWN || promopiece == KING) { 5.12 + return FALSE; 5.13 + } 5.14 + } else { 5.15 + return FALSE; 5.16 + } 5.17 + } else { 5.18 + if (move->promotion) { 5.19 + return FALSE; 5.20 + } 5.21 + } 5.22 + 5.23 if (move->capture) { 5.24 if (move->fromrow == move->torow + d && ( 5.25 move->fromfile == move->tofile + 1 ||
6.1 --- a/src/rules/rules.h Sat Mar 29 14:46:33 2014 +0100 6.2 +++ b/src/rules/rules.h Sat Mar 29 16:53:58 2014 +0100 6.3 @@ -41,6 +41,7 @@ 6.4 #define INVALID_MOVE_SYNTAX 1 6.5 #define INVALID_POSITION 2 6.6 #define AMBIGUOUS_MOVE 3 6.7 +#define NEED_PROMOTION 4 6.8 6.9 #endif /* RULES_H */ 6.10