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 |
--- a/src/game.c Sat Mar 29 14:46:33 2014 +0100 +++ b/src/game.c Sat Mar 29 16:53:58 2014 +0100 @@ -35,20 +35,68 @@ static const uint8_t boardx = 10, boardy = 10; -static void draw_board(Board board, uint8_t mycolor) { +static uint8_t getpiecechr(uint8_t piece) { + switch (piece & PIECE_MASK) { + case ROOK: return 'R'; + case KNIGHT: return 'N'; + case BISHOP: return 'B'; + case QUEEN: return 'Q'; + case KING: return 'K'; + default: return '\0'; + } +} + +/** + * Maps a character to a piece. + * + * Does not work for pawns, since they don't have a character. + * + * @param c one of R,N,B,Q,K + * @return numeric value for the specified piece + */ +static uint8_t getpiece(char c) { + switch (c) { + case 'R': return ROOK; + case 'N': return KNIGHT; + case 'B': return BISHOP; + case 'Q': return QUEEN; + case 'K': return KING; + default: return 0; + } +} + +/** + * Guesses the location of a piece for short algebraic notation. + * + * @param board the current state of the board + * @param move the move date to operate on + * @return status code (see rules/rules.h for the codes) + */ +static int getlocation(Board board, Move *move) { + uint8_t piece = move->piece & PIECE_MASK; + switch (piece) { + case PAWN: return pawn_getlocation(board, move); + case ROOK: return rook_getlocation(board, move); + case KNIGHT: return knight_getlocation(board, move); + case BISHOP: return bishop_getlocation(board, move); + case QUEEN: return queen_getlocation(board, move); + case KING: return king_getlocation(board, move); + default: return INVALID_MOVE_SYNTAX; + } +} + + +static void draw_board(Board board, MoveListRoot *movelist, uint8_t mycolor) { for (uint8_t y = 0 ; y < 8 ; y++) { for (uint8_t x = 0 ; x < 8 ; x++) { uint8_t col = board[y][x] & COLOR_MASK; uint8_t piece = board[y][x] & PIECE_MASK; - char piecec = ' '; - switch (piece) { - case PAWN: piecec = 'P'; break; - case ROOK: piecec = 'R'; break; - case KNIGHT: piecec = 'N'; break; - case BISHOP: piecec = 'B'; break; - case QUEEN: piecec = 'Q'; break; - case KING: piecec = 'K'; break; + char piecec; + if (piece) { + piecec = piece == PAWN ? 'P' : getpiecechr(piece); + } else { + piecec = ' '; } attrset((col == WHITE ? A_BOLD : A_DIM) | @@ -69,6 +117,45 @@ mvaddch(boardy+1, x, 'a'+i); mvaddch(y, boardx-2, '1'+i); } + + /* move log */ + // TODO: introduce window to avoid bugs with a long move log + uint8_t logy = 0; + const uint8_t logx = boardx + 30; + int logi = 1; + MoveList *logelem = movelist->first; + + while (logelem) { + logi++; + if (logi % 2 == 0) { + if ((logi - 2) % 4 == 0) { + logy++; + wmove(tchess_window, logy, logx); + } + printw("%d. ", logi / 2); + } + + if (logelem) { + Move move = logelem->move; + char logstr[] = { + getpiecechr(move.piece), + filechr(move.fromfile), rowchr(move.fromrow), + move.capture ? 'x':'\0', + filechr(move.tofile), rowchr(move.torow), + move.check ? '+' : (move.checkmate ? '#' : + (move.promotion ? '=' : '\0')), + move.promotion ? getpiecechr(move.promotion) : '\0', + ' ' + }; + for (int stri = 0 ; stri < sizeof(logstr) ; stri++) { + if (logstr[stri]) { + addch(logstr[stri]); + } + } + + logelem = logelem->next; + } + } } /** @@ -100,9 +187,13 @@ move->piece |= ENPASSANT_THREAT; } - /* move (and maybe capture) */ + /* move (and maybe capture or promote) */ msrc(board, move) = 0; - mdst(board, move) = move->piece; + if (move->promotion) { + mdst(board, move) = move->promotion; + } else { + mdst(board, move) = move->piece; + } /* castling */ if (piece == KING && @@ -180,45 +271,6 @@ } /** - * Maps a character to a piece. - * - * Does not work for pawns, since they don't have a character. - * - * @param c one of R,N,B,Q,K - * @return numeric value for the specified piece - */ -static uint8_t getpiece(char c) { - switch (c) { - case 'R': return ROOK; - case 'N': return KNIGHT; - case 'B': return BISHOP; - case 'Q': return QUEEN; - case 'K': return KING; - default: return 0; - } -} - -/** - * Guesses the location of a piece for short algebraic notation. - * - * @param board the current state of the board - * @param move the move date to operate on - * @return status code (see rules/rules.h for the codes) - */ -static int getlocation(Board board, Move *move) { - uint8_t piece = move->piece & PIECE_MASK; - switch (piece) { - case PAWN: return pawn_getlocation(board, move); - case ROOK: return rook_getlocation(board, move); - case KNIGHT: return knight_getlocation(board, move); - case BISHOP: return bishop_getlocation(board, move); - case QUEEN: return queen_getlocation(board, move); - case KING: return king_getlocation(board, move); - default: return INVALID_MOVE_SYNTAX; - } -} - -/** * Evaluates a move syntactically and stores the move data in the specified * object. * @@ -232,7 +284,7 @@ memset(move, 0, sizeof(Move)); move->fromfile = POS_UNSPECIFIED; move->fromrow = POS_UNSPECIFIED; - // TODO: promotion + size_t len = strlen(mstr); /* evaluate check/checkmate flags */ @@ -244,6 +296,18 @@ move->checkmate = TRUE; } + /* evaluate promotion */ + if (len > 3 && mstr[len-2] == '=') { + move->promotion = getpiece(mstr[len-1]); + if (!move->promotion) { + return INVALID_MOVE_SYNTAX; + } else { + move->promotion |= mycolor; + len -= 2; + mstr[len] = 0; + } + } + if (len == 2) { /* pawn move (e.g. "e4") */ move->piece = PAWN; @@ -327,6 +391,11 @@ if (move->piece) { + if (move->piece == PAWN && move->torow == (mycolor==WHITE?7:0) + && !move->promotion) { + return NEED_PROMOTION; + } + move->piece |= mycolor; if (move->fromfile == POS_UNSPECIFIED || move->fromrow == POS_UNSPECIFIED) { @@ -339,14 +408,17 @@ } } -static int sendmove(Board board, uint8_t mycolor, int opponent) { +static int sendmove(Board board, MoveListRoot *movelist, + uint8_t mycolor, int opponent) { + const size_t buflen = 8; char movestr[buflen]; _Bool remisrejected = FALSE; uint8_t code; + int inputy = getmaxy(tchess_window) - 6; while (1) { - move(boardy+3, 0); + move(inputy, 0); if (remisrejected) { printw( "Use chess notation to enter your move.\n" @@ -386,46 +458,48 @@ Move move; int eval_result = eval_move(board, mycolor, movestr, &move); switch (eval_result) { - case VALID_MOVE_SYNTAX: - net_send_code(opponent, NETCODE_MOVE); - net_send_data(opponent, &move, sizeof(Move)); - code = net_recieve_code(opponent); - move.check = code == NETCODE_CHECK; - move.checkmate = code == NETCODE_CHECKMATE; - // TODO: record move - if (code == NETCODE_DECLINE) { - printw("Invalid move."); + case VALID_MOVE_SYNTAX: + net_send_code(opponent, NETCODE_MOVE); + net_send_data(opponent, &move, sizeof(Move)); + code = net_recieve_code(opponent); + move.check = code == NETCODE_CHECK; + move.checkmate = code == NETCODE_CHECKMATE; + addmove(movelist, &move); + if (code == NETCODE_DECLINE) { + printw("Invalid move."); + } else { + apply_move(board, &move); + if (move.checkmate) { + printw("Checkmate!"); + clrtoeol(); + return 1; } else { - apply_move(board, &move); - if (move.checkmate) { - printw("Checkmate!"); - clrtoeol(); - return 1; - } else { - return 0; - } + return 0; } - break; - case AMBIGUOUS_MOVE: - printw("Ambiguous move - " - "please specify the piece to move."); - break; - case INVALID_POSITION: - printw("Cannot find the piece that shall be moved."); - break; - default: - printw("Can't interpret move - " - "please use algebraic notation."); + } + break; + case AMBIGUOUS_MOVE: + printw("Ambiguous move - please specify the piece to move."); + break; + case INVALID_POSITION: + printw("Cannot find the piece that shall be moved."); + break; + case NEED_PROMOTION: + printw("You need to promote the pawn (append \"=Q\" e.g.)!"); + break; + default: + printw("Can't interpret move - please use algebraic notation."); } clrtoeol(); } } } -static int recvmove(Board board, int opponent) { +static int recvmove(Board board, MoveListRoot *movelist, int opponent) { + int inputy = getmaxy(tchess_window) - 6; while (1) { - move(boardy+3, 0); + move(inputy, 0); printw("Awaiting opponent move..."); clrtoeol(); refresh(); @@ -454,7 +528,7 @@ net_recieve_data(opponent, &move, sizeof(Move)); if (validate_move(board, &move)) { apply_move(board, &move); - // TODO: record move + addmove(movelist, &move); if (move.check) { net_send_code(opponent, NETCODE_CHECK); } else if (move.checkmate) { @@ -470,6 +544,30 @@ } } +void freemovelist(MoveListRoot* list) { + MoveList *elem; + elem = list->first; + while (elem) { + MoveList *cur = elem; + elem = elem->next; + free(cur); + }; + free(list); +} + +void addmove(MoveListRoot* list, Move *move) { + MoveList *elem = malloc(sizeof(MoveList)); + elem->next = NULL; + elem->move = *move; + + if (list->last) { + list->last->next = elem; + list->last = elem; + } else { + list->first = list->last = elem; + } +} + void game_start(Settings *settings, int opponent) { _Bool myturn = is_server(settings) == (settings->gameinfo.servercolor == WHITE); @@ -477,6 +575,8 @@ _Bool running; + MoveListRoot* movelist = calloc(1, sizeof(MoveListRoot)); + Board board = { {WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK}, {WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN}, @@ -490,16 +590,18 @@ do { clear(); - draw_board(board, mycolor); + draw_board(board, movelist, mycolor); if (myturn) { - running = !sendmove(board, mycolor, opponent); + running = !sendmove(board, movelist, mycolor, opponent); } else { - running = !recvmove(board, opponent); + running = !recvmove(board, movelist, opponent); flushinp(); // flush any input the user hacked in while waiting } myturn ^= TRUE; } while (running); + freemovelist(movelist); + mvaddstr(getmaxy(tchess_window)-1, 0, "Game has ended. Press any key to leave..."); getch();
--- a/src/game.h Sat Mar 29 14:46:33 2014 +0100 +++ b/src/game.h Sat Mar 29 16:53:58 2014 +0100 @@ -72,11 +72,25 @@ uint8_t fromrow; uint8_t tofile; uint8_t torow; + uint8_t promotion; _Bool check; _Bool checkmate; _Bool capture; } Move; +typedef struct MoveList MoveList; +typedef struct MoveListRoot MoveListRoot; + +struct MoveListRoot { + MoveList* first; + MoveList* last; +}; + +struct MoveList { + Move move; + MoveList* next; +}; + #define POS_UNSPECIFIED UINT8_MAX #define mdst(b,m) b[(m)->torow][(m)->tofile] #define msrc(b,m) b[(m)->fromrow][(m)->fromfile] @@ -89,6 +103,9 @@ #define rowidx(row) (row-'1') #define fileidx(file) (file-'a') +#define rowchr(row) (row+'1') +#define filechr(file) (file+'a') + #define chkidx(move) (isidx((move)->fromfile) && isidx((move)->fromrow) && \ isidx((move)->tofile) && isidx((move)->torow)) @@ -98,6 +115,9 @@ void game_start(Settings *settings, int opponent); +void freemovelist(MoveListRoot* list); +void addmove(MoveListRoot *movelist, Move *move); + #ifdef __cplusplus } #endif
--- a/src/network.h Sat Mar 29 14:46:33 2014 +0100 +++ b/src/network.h Sat Mar 29 16:53:58 2014 +0100 @@ -46,7 +46,7 @@ #define NETCODE_CHECK 0x23 #define NETCODE_CHECKMATE 0x24 -#define NETCODE_VERSION 4 +#define NETCODE_VERSION 5 typedef struct { int fd; /* -1, if we are the client */
--- a/src/rules/bishop.c Sat Mar 29 14:46:33 2014 +0100 +++ b/src/rules/bishop.c Sat Mar 29 16:53:58 2014 +0100 @@ -109,7 +109,7 @@ move->fromfile = file; } file = move->tofile - d; - if (isfile(file) && board[row][file] == move->piece) { + if (isidx(file) && board[row][file] == move->piece) { if (amb) { return AMBIGUOUS_MOVE; }
--- a/src/rules/pawn.c Sat Mar 29 14:46:33 2014 +0100 +++ b/src/rules/pawn.c Sat Mar 29 16:53:58 2014 +0100 @@ -32,6 +32,22 @@ _Bool pawn_chkrules(Board board, Move *move) { int8_t d = ((move->piece & COLOR_MASK) == WHITE ? -1 : 1); + + if (move->torow == (d < 0 ? 7 : 0)) { + if (move->promotion) { + uint8_t promopiece = move->promotion & PIECE_MASK; + if (!promopiece || promopiece == PAWN || promopiece == KING) { + return FALSE; + } + } else { + return FALSE; + } + } else { + if (move->promotion) { + return FALSE; + } + } + if (move->capture) { if (move->fromrow == move->torow + d && ( move->fromfile == move->tofile + 1 ||