fixed bishop + added pawn promotion + added move log

Sat, 29 Mar 2014 16:53:58 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 29 Mar 2014 16:53:58 +0100
changeset 18
6008840b859e
parent 17
2aed5418e142
child 19
6a26114297a1

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 ||
--- a/src/rules/rules.h	Sat Mar 29 14:46:33 2014 +0100
+++ b/src/rules/rules.h	Sat Mar 29 16:53:58 2014 +0100
@@ -41,6 +41,7 @@
 #define INVALID_MOVE_SYNTAX 1
 #define INVALID_POSITION    2
 #define AMBIGUOUS_MOVE      3
+#define NEED_PROMOTION      4
 
 #endif	/* RULES_H */
 

mercurial