refactoring of getlocation mechanism for better short algebraic notation support (does now respect pinned pieces) + fixed a bug where a pawn could advance through a piece (e.g. e2e4 could jump over a piece on e3)

Wed, 28 May 2014 15:47:57 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 28 May 2014 15:47:57 +0200
changeset 47
d726e4b46c33
parent 46
4dcfb4c58b6d
child 48
0cedda2544da

refactoring of getlocation mechanism for better short algebraic notation support (does now respect pinned pieces) + fixed a bug where a pawn could advance through a piece (e.g. e2e4 could jump over a piece on e3)

src/chess/bishop.c file | annotate | diff | comparison | revisions
src/chess/bishop.h file | annotate | diff | comparison | revisions
src/chess/king.c file | annotate | diff | comparison | revisions
src/chess/king.h file | annotate | diff | comparison | revisions
src/chess/knight.c file | annotate | diff | comparison | revisions
src/chess/knight.h file | annotate | diff | comparison | revisions
src/chess/pawn.c file | annotate | diff | comparison | revisions
src/chess/pawn.h file | annotate | diff | comparison | revisions
src/chess/queen.c file | annotate | diff | comparison | revisions
src/chess/queen.h file | annotate | diff | comparison | revisions
src/chess/rook.c file | annotate | diff | comparison | revisions
src/chess/rook.h file | annotate | diff | comparison | revisions
src/chess/rules.c file | annotate | diff | comparison | revisions
src/chess/rules.h file | annotate | diff | comparison | revisions
src/game.c file | annotate | diff | comparison | revisions
src/main.c file | annotate | diff | comparison | revisions
src/terminal-chess.h file | annotate | diff | comparison | revisions
--- a/src/chess/bishop.c	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/bishop.c	Wed May 28 15:47:57 2014 +0200
@@ -52,77 +52,3 @@
     
     return 0;
 }
-
-static int bishop_getloc_fixedfile(GameState *gamestate, Move *move) {
-    uint8_t d = abs(move->fromfile - move->tofile);
-    if (gamestate->board[move->torow - d][move->fromfile] == move->piece) {
-        move->fromrow = move->torow - d;
-    }
-    if (gamestate->board[move->torow + d][move->fromfile] == move->piece) {
-        if (move->fromrow == POS_UNSPECIFIED) {
-            move->fromrow = move->torow + d;
-        } else {
-            return AMBIGUOUS_MOVE; /* rare situation after promotion */
-        }
-    }
-    return move->fromrow == POS_UNSPECIFIED ?
-        INVALID_POSITION : VALID_MOVE_SYNTAX;
-}
-
-static int bishop_getloc_fixedrow(GameState *gamestate, Move *move) {
-    uint8_t d = abs(move->fromrow - move->torow);
-    if (gamestate->board[move->fromrow][move->tofile - d] == move->piece) {
-        move->fromfile = move->tofile - d;
-    }
-    if (gamestate->board[move->fromrow][move->tofile + d] == move->piece) {
-        if (move->fromfile == POS_UNSPECIFIED) {
-            move->fromfile = move->tofile + d;
-        } else {
-            return AMBIGUOUS_MOVE; /* rare situation after promotion */
-        }
-    }
-    return move->fromfile == POS_UNSPECIFIED ?
-        INVALID_POSITION : VALID_MOVE_SYNTAX;
-}
-
-int bishop_getlocation(GameState *gamestate, Move *move) {
-    
-    if (move->fromfile != POS_UNSPECIFIED) {
-        return bishop_getloc_fixedfile(gamestate, move);
-    }
-    
-    if (move->fromrow != POS_UNSPECIFIED) {
-        return bishop_getloc_fixedrow(gamestate, move);
-    }
-    
-    _Bool amb = 0;
-    for (int d = -7 ; d < 8  ; d++) {
-        uint8_t row = move->torow + d;
-        if (isidx(row)) {
-            uint8_t file = move->tofile + d;
-            if (isidx(file) && gamestate->board[row][file] == move->piece) {
-                if (amb) {
-                    return AMBIGUOUS_MOVE;
-                }
-                amb = 1;
-                move->fromrow = row;
-                move->fromfile = file;
-            }
-            file = move->tofile - d;
-            if (isidx(file) && gamestate->board[row][file] == move->piece) {
-                if (amb) {
-                    return AMBIGUOUS_MOVE;
-                }
-                amb = 1;
-                move->fromrow = row;
-                move->fromfile = file;
-            }
-        }
-    }
-    
-    if (move->fromrow == POS_UNSPECIFIED || move->fromfile == POS_UNSPECIFIED) {
-        return INVALID_POSITION;
-    } else {
-        return VALID_MOVE_SYNTAX;
-    }
-}
--- a/src/chess/bishop.h	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/bishop.h	Wed May 28 15:47:57 2014 +0200
@@ -38,7 +38,6 @@
 
 _Bool bishop_chkrules(Move *move);
 _Bool bishop_isblocked(GameState *gamestate, Move *move);
-int bishop_getlocation(GameState *gamestate, Move *move);
 
 #ifdef	__cplusplus
 }
--- a/src/chess/king.c	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/king.c	Wed May 28 15:47:57 2014 +0200
@@ -69,9 +69,11 @@
 _Bool king_isblocked(GameState *gamestate, Move *move) {
     
     uint8_t opponent_color = opponent_color(move->piece&COLOR_MASK);
-    _Bool blocked = is_covered(gamestate, move->torow, move->tofile,
-        opponent_color);
     
+    // being in check does not "block" the king, so don't test it here
+    _Bool blocked = 0;
+    
+    // just test, if castling move is blocked
     if (abs(move->tofile - move->fromfile) == 2) {
         if (move->tofile == fileidx('c')) {
             blocked |= gamestate->board[move->torow][fileidx('b')];
@@ -84,33 +86,3 @@
     
     return blocked;
 }
-
-int king_getlocation(GameState *gamestate, Move *move) {
-    
-    uint8_t file, row;
-    
-    for (int f = -1 ; f <= 1 ; f++) {
-        for (int r = -1 ; r <= 1 ; r++) {
-            if (f == 0 && r == 0) {
-                continue;
-            }
-            file = move->tofile + f;
-            row = move->torow + r;
-            if (isidx(file) && isidx(row)) {
-                if (gamestate->board[row][file] == move->piece) {
-                    if ((move->fromfile != POS_UNSPECIFIED
-                        && move->fromfile != file) ||
-                        (move->fromrow != POS_UNSPECIFIED
-                        && move->fromrow != row)) {
-                        return INVALID_POSITION;
-                    }
-                    move->fromfile = file;
-                    move->fromrow = row;
-                    return VALID_MOVE_SYNTAX;
-                }
-            }
-        }
-    }
-    
-    return INVALID_POSITION;
-}
--- a/src/chess/king.h	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/king.h	Wed May 28 15:47:57 2014 +0200
@@ -39,7 +39,6 @@
 
 _Bool king_chkrules(GameState *gamestate, Move *move);
 _Bool king_isblocked(GameState *gamestate, Move *move);
-int king_getlocation(GameState *gamestate, Move *move);
 
 #ifdef	__cplusplus
 }
--- a/src/chess/knight.c	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/knight.c	Wed May 28 15:47:57 2014 +0200
@@ -37,98 +37,3 @@
     
     return (dx == 2 && dy == 1) || (dx == 1 && dy == 2);
 }
-
-static int knight_getloc_fixedrow(GameState *gamestate, Move *move) {
-    int d = 3 - abs(move->fromrow - move->torow);
-    
-    if (d == 1 || d == 2) {
-        if (move->tofile < 6 &&
-            gamestate->board[move->fromrow][move->tofile + d] == move->piece) {
-            if (move->fromfile == POS_UNSPECIFIED) {
-                move->fromfile = move->tofile + d;
-                return VALID_MOVE_SYNTAX;
-            } else {
-                return AMBIGUOUS_MOVE;
-            }
-        }
-        if (move->tofile > 1 &&
-            gamestate->board[move->fromrow][move->tofile - d] == move->piece) {
-            if (move->fromfile == POS_UNSPECIFIED) {
-                move->fromfile = move->tofile - d;
-                return VALID_MOVE_SYNTAX;
-            } else {
-                return AMBIGUOUS_MOVE;
-            }
-        }
-    }
-    
-    return INVALID_POSITION;
-}
-
-static int knight_getloc_fixedfile(GameState *gamestate, Move *move) {
-    int d = 3 - abs(move->fromfile - move->tofile);
-    
-    if (d == 1 || d == 2) {
-        if (move->torow < 6 &&
-            gamestate->board[move->torow + d][move->fromfile] == move->piece) {
-            if (move->fromrow == POS_UNSPECIFIED) {
-                move->fromrow = move->torow + d;
-                return VALID_MOVE_SYNTAX;
-            } else {
-                return AMBIGUOUS_MOVE;
-            }
-        }
-        if (move->torow > 1 &&
-            gamestate->board[move->torow - d][move->fromfile] == move->piece) {
-            if (move->fromrow == POS_UNSPECIFIED) {
-                move->fromrow = move->torow - d;
-                return VALID_MOVE_SYNTAX;
-            } else {
-                return AMBIGUOUS_MOVE;
-            }
-        }
-    }
-    
-    return INVALID_POSITION;
-}
-
-int knight_getlocation(GameState *gamestate, Move *move) {
-    
-    if (move->fromfile != POS_UNSPECIFIED) {
-        return knight_getloc_fixedfile(gamestate, move);
-    }
-    
-    if (move->fromrow != POS_UNSPECIFIED) {
-        return knight_getloc_fixedrow(gamestate, move);
-    }
-    
-    for (int x = -2 ; x <= 2 ; x++) {
-        if (x == 0) {
-            continue;
-        }
-        for (int y = -2 ; y <= 2 ; y++) {
-            if (y == 0 || y == x) {
-                continue;
-            }
-            uint8_t cx = move->tofile + x;
-            uint8_t cy = move->torow + y;
-
-            if (isidx(cx) && isidx(cy)
-                && gamestate->board[cy][cx] == move->piece) {
-                if (move->fromfile == POS_UNSPECIFIED
-                    && move->fromrow == POS_UNSPECIFIED) {
-                    move->fromfile = cx;
-                    move->fromrow = cy;
-                } else {
-                    return AMBIGUOUS_MOVE;
-                }
-            }
-        }
-    }
-    
-    if (move->fromfile == POS_UNSPECIFIED || move->fromrow == POS_UNSPECIFIED) {
-        return INVALID_POSITION;
-    } else {
-        return VALID_MOVE_SYNTAX;
-    }
-}
--- a/src/chess/knight.h	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/knight.h	Wed May 28 15:47:57 2014 +0200
@@ -38,7 +38,6 @@
 
 _Bool knight_chkrules(Move *move);
 #define knight_isblocked(gs,m) 0
-int knight_getlocation(GameState *gamestate, Move *move);
 
 #ifdef	__cplusplus
 }
--- a/src/chess/pawn.c	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/pawn.c	Wed May 28 15:47:57 2014 +0200
@@ -71,28 +71,10 @@
 }
 
 _Bool pawn_isblocked(GameState *gamestate, Move *move) {
-    return mdst(gamestate->board, move) && !move->capture;
-}
-
-int pawn_getlocation(GameState *gamestate, Move *move) {
-    int8_t d = ((move->piece & COLOR_MASK) == WHITE ? -1 : 1);
-    
-    if (move->fromfile == POS_UNSPECIFIED) {
-        move->fromfile = move->tofile;
-    }
-    move->fromrow = move->torow + d;
-    if (move->fromrow > 6) {
-        return INVALID_POSITION;
+    if (move->torow == move->fromrow + 1 || move->torow == move->fromrow - 1) {
+        return mdst(gamestate->board, move) && !move->capture;
     } else {
-        /* advanced first move */
-        if (move->fromrow == (d < 0 ? 2 : 5) &&
-            msrc(gamestate->board,move) != move->piece) {
-
-            move->fromrow += d;
-            if (move->fromrow > 6) {
-                return INVALID_POSITION;
-            }
-        }
+        return mdst(gamestate->board, move) ||
+            gamestate->board[(move->fromrow+move->torow)/2][move->tofile];
     }
-    return VALID_MOVE_SYNTAX;
 }
--- a/src/chess/pawn.h	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/pawn.h	Wed May 28 15:47:57 2014 +0200
@@ -38,7 +38,6 @@
 
 _Bool pawn_chkrules(GameState *gamestate, Move *move);
 _Bool pawn_isblocked(GameState *gamestate, Move *move);
-int pawn_getlocation(GameState *gamestate, Move *move);
 
 #ifdef	__cplusplus
 }
--- a/src/chess/queen.c	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/queen.c	Wed May 28 15:47:57 2014 +0200
@@ -43,28 +43,3 @@
         return bishop_isblocked(gamestate, move);
     }
 }
-
-int queen_getlocation(GameState *gamestate, Move *move) {
-    
-    Move moveasrook = *move;
-    int rookaspect = rook_getlocation(gamestate, &moveasrook);
-    
-    Move moveasbishop = *move;
-    int bishopaspect = bishop_getlocation(gamestate, &moveasbishop);
-    
-    if (rookaspect == VALID_MOVE_SYNTAX && bishopaspect == VALID_MOVE_SYNTAX) {
-        return AMBIGUOUS_MOVE;
-    }
-    
-    if (rookaspect == VALID_MOVE_SYNTAX) {
-        *move = moveasrook;
-        return VALID_MOVE_SYNTAX;
-    }
-    
-    if (bishopaspect == VALID_MOVE_SYNTAX) {
-        *move = moveasbishop;
-        return VALID_MOVE_SYNTAX;
-    }
-    
-    return INVALID_POSITION;
-}
--- a/src/chess/queen.h	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/queen.h	Wed May 28 15:47:57 2014 +0200
@@ -38,7 +38,6 @@
 
 _Bool queen_chkrules(Move *move);
 _Bool queen_isblocked(GameState *gamestate, Move *move);
-int queen_getlocation(GameState *gamestate, Move *move);
 
 #ifdef	__cplusplus
 }
--- a/src/chess/rook.c	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/rook.c	Wed May 28 15:47:57 2014 +0200
@@ -58,95 +58,3 @@
     
     return 0;
 }
-
-static int rook_getloc_fixedrow(GameState *gamestate, Move *move) {
-    uint8_t file = POS_UNSPECIFIED;
-    for (uint8_t f = 0 ; f < 8 ; f++) {
-        if (gamestate->board[move->fromrow][f] == move->piece) {
-            if (file == POS_UNSPECIFIED) {
-                file = f;
-            } else {
-                return AMBIGUOUS_MOVE;
-            }
-        }
-    }
-    if (file == POS_UNSPECIFIED) {
-        return INVALID_POSITION;
-    } else {
-        move->fromfile = file;
-        return VALID_MOVE_SYNTAX;
-    }
-}
-
-static int rook_getloc_fixedfile(GameState *gamestate, Move *move) {
-    uint8_t row = POS_UNSPECIFIED;
-    for (uint8_t r = 0 ; r < 8 ; r++) {
-        if (gamestate->board[r][move->fromfile] == move->piece) {
-            if (row == POS_UNSPECIFIED) {
-                row = r;
-            } else {
-                return AMBIGUOUS_MOVE;
-            }
-        }   
-    }
-    if (row == POS_UNSPECIFIED) {
-        return INVALID_POSITION;
-    } else {
-        move->fromrow = row;
-        return VALID_MOVE_SYNTAX;
-    }
-}
-
-int rook_getlocation(GameState *gamestate, Move *move) {
-    
-    if (move->fromfile != POS_UNSPECIFIED) {
-        if (move->fromfile == move->tofile) {
-            return rook_getloc_fixedfile(gamestate, move);
-        } else {
-            if (gamestate->board[move->torow][move->fromfile] == move->piece) {
-                move->fromrow = move->torow;
-                return VALID_MOVE_SYNTAX;
-            } else {
-                return INVALID_POSITION;
-            }
-        }
-    }
-    
-    if (move->fromrow != POS_UNSPECIFIED) {
-        if (move->fromrow == move->torow) {
-            return rook_getloc_fixedrow(gamestate, move);
-        } else {
-            if (gamestate->board[move->fromrow][move->tofile] == move->piece) {
-                move->fromfile = move->tofile;
-                return VALID_MOVE_SYNTAX;
-            } else {
-                return INVALID_POSITION;
-            }
-        }
-    }
-    
-    Move chkrowmove = *move, chkfilemove = *move;
-    
-    chkrowmove.fromrow = move->torow;
-    int chkrow = rook_getloc_fixedrow(gamestate, &chkrowmove);
-    
-    chkfilemove.fromfile = move->tofile;
-    int chkfile = rook_getloc_fixedfile(gamestate, &chkfilemove);
-    
-    if ((chkrow == VALID_MOVE_SYNTAX && chkfile == VALID_MOVE_SYNTAX) ||
-        chkrow == AMBIGUOUS_MOVE || chkfile == AMBIGUOUS_MOVE) {
-        return AMBIGUOUS_MOVE;
-    }
-    
-    if (chkrow == VALID_MOVE_SYNTAX) {
-        *move = chkrowmove;
-        return VALID_MOVE_SYNTAX;
-    }
-    
-    if (chkfile == VALID_MOVE_SYNTAX) {
-        *move = chkfilemove;
-        return VALID_MOVE_SYNTAX;
-    }
-    
-    return INVALID_POSITION;
-}
--- a/src/chess/rook.h	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/rook.h	Wed May 28 15:47:57 2014 +0200
@@ -38,7 +38,6 @@
 
 _Bool rook_chkrules(Move *move);
 _Bool rook_isblocked(GameState *gamestate, Move *move);
-int rook_getlocation(GameState *gamestate, Move *move);
 
 #ifdef	__cplusplus
 }
--- a/src/chess/rules.c	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/rules.c	Wed May 28 15:47:57 2014 +0200
@@ -98,19 +98,6 @@
     }
 }
 
-static int getlocation(GameState *gamestate, Move *move) {   
-    uint8_t piece = move->piece & PIECE_MASK;
-    switch (piece) {
-        case PAWN: return pawn_getlocation(gamestate, move);
-        case ROOK: return rook_getlocation(gamestate, move);
-        case KNIGHT: return knight_getlocation(gamestate, move);
-        case BISHOP: return bishop_getlocation(gamestate, move);
-        case QUEEN: return queen_getlocation(gamestate, move);
-        case KING: return king_getlocation(gamestate, move);
-        default: return INVALID_MOVE_SYNTAX;
-    }
-}
-
 void apply_move(GameState *gamestate, Move *move) {
     uint8_t piece = move->piece & PIECE_MASK;
     uint8_t color = move->piece & COLOR_MASK;
@@ -170,13 +157,20 @@
     }
     
     /* does piece exist */
-    if (msrc(gamestate->board, move) != move->piece) {
+    if ((msrc(gamestate->board, move)&(PIECE_MASK|COLOR_MASK))
+           != (move->piece&(PIECE_MASK|COLOR_MASK))) {
         return 0;
     }
     
     /* can't capture own pieces */
     if ((mdst(gamestate->board, move) & COLOR_MASK)
-        == (move->piece & COLOR_MASK)) {
+            == (move->piece & COLOR_MASK)) {
+        return 0;
+    }
+    
+    /* must capture, if and only if destination is occupied */
+    if ((mdst(gamestate->board, move) == 0 && move->capture) ||
+            (mdst(gamestate->board, move) != 0 && !move->capture)) {
         return 0;
     }
     
@@ -205,6 +199,7 @@
 }
 
 _Bool validate_move(GameState *gamestate, Move *move) {
+    // TODO: provide more details via a return code
     
     _Bool result = validate_move_rules(gamestate, move);
     
@@ -232,9 +227,9 @@
     }
     
     /* simulation move for check validation */
-    GameState simulation;
-    memcpy(&simulation, gamestate, sizeof(GameState));
-    apply_move(&simulation, move);
+    GameState simulation = *gamestate;
+    Move simmove = *move;
+    apply_move(&simulation, &simmove);
     
     /* don't move into or stay in check position */
     if (is_covered(&simulation, mykingrow, mykingfile,
@@ -323,6 +318,155 @@
     return 1;
 }
 
+_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
+        uint8_t color, Move *threats, uint8_t *threatcount) {
+    Move candidates[32];
+    int candidatecount = 0;
+    for (uint8_t r = 0 ; r < 8 ; r++) {
+        for (uint8_t f = 0 ; f < 8 ; f++) {
+            if ((gamestate->board[r][f] & COLOR_MASK) == color) {
+                // non-capturing move
+                memset(&(candidates[candidatecount]), 0, sizeof(Move));
+                candidates[candidatecount].piece = gamestate->board[r][f];
+                candidates[candidatecount].fromrow = r;
+                candidates[candidatecount].fromfile = f;
+                candidates[candidatecount].torow = row;
+                candidates[candidatecount].tofile = file;
+                candidatecount++;
+
+                // capturing move
+                memcpy(&(candidates[candidatecount]),
+                    &(candidates[candidatecount-1]), sizeof(Move));
+                candidates[candidatecount].capture = 1;
+                candidatecount++;
+            }
+        }
+    }
+
+    if (threatcount) {
+        *threatcount = 0;
+    }
+    
+    
+    _Bool result = 0;
+    
+    for (int i = 0 ; i < candidatecount ; i++) {
+        if (validate_move_rules(gamestate, &(candidates[i]))) {
+            result = 1;
+            if (threats && threatcount) {
+                threats[(*threatcount)++] = candidates[i];
+            }
+        }
+    }
+    
+    return result;
+}
+
+_Bool is_pinned(GameState *gamestate, Move *move) {
+    uint8_t color = move->piece & COLOR_MASK;
+
+    uint8_t kingfile = 0, kingrow = 0;
+    for (uint8_t row = 0 ; row < 8 ; row++) {
+        for (uint8_t file = 0 ; file < 8 ; file++) {
+            if (gamestate->board[row][file] == (color|KING)) {
+                kingfile = file;
+                kingrow = row;
+            }
+        }
+    }
+
+    GameState simulation = *gamestate;
+    Move simmove = *move;
+    apply_move(&simulation, &simmove);
+    return is_covered(&simulation, kingrow, kingfile, opponent_color(color));
+}
+
+_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
+        uint8_t color, Move *threats, uint8_t *threatcount) {
+    
+    if (threatcount) {
+        *threatcount = 0;
+    }
+
+    Move candidates[16];
+    uint8_t candidatecount;
+    if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
+        
+        _Bool result = 0;
+        uint8_t kingfile = 0, kingrow = 0;
+        for (uint8_t row = 0 ; row < 8 ; row++) {
+            for (uint8_t file = 0 ; file < 8 ; file++) {
+                if (gamestate->board[row][file] == (color|KING)) {
+                    kingfile = file;
+                    kingrow = row;
+                }
+            }
+        }
+
+        for (uint8_t i = 0 ; i < candidatecount ; i++) {
+            GameState simulation = *gamestate;
+            Move simmove = candidates[i];
+            apply_move(&simulation, &simmove);
+            if (!is_covered(&simulation, kingrow, kingfile,
+                    opponent_color(color))) {
+                result = 1;
+                if (threats && threatcount) {
+                    threats[(*threatcount)++] = candidates[i];
+                }
+            }
+        }
+        
+        return result;
+    } else {
+        return 0;
+    }
+}
+#include <ncurses.h>
+static int getlocation(GameState *gamestate, Move *move) {   
+
+    uint8_t color = move->piece & COLOR_MASK;
+    _Bool incheck = gamestate->lastmove?gamestate->lastmove->move.check:0;
+    
+    Move threats[16], *threat = NULL;
+    uint8_t threatcount;
+    
+    if (get_threats(gamestate, move->torow, move->tofile, color,
+            threats, &threatcount)) {
+        
+        // find threats for the specified position
+        for (uint8_t i = 0 ; i < threatcount ; i++) {
+            if ((threats[i].piece & (PIECE_MASK | COLOR_MASK))
+                    == move->piece &&
+                    (move->fromrow == POS_UNSPECIFIED ||
+                    move->fromrow == threats[i].fromrow) &&
+                    (move->fromfile == POS_UNSPECIFIED ||
+                    move->fromfile == threats[i].fromfile)) {
+
+                if (threat) {
+                    return AMBIGUOUS_MOVE;
+                } else {
+                    threat = &(threats[i]);
+                }
+            }
+        }
+        
+        // can't threaten specified position
+        if (!threat) {
+            return INVALID_POSITION;
+        }
+
+        // found threat is no real threat
+        if (is_pinned(gamestate, threat)) {
+            return incheck?KING_IN_CHECK:PIECE_PINNED;
+        } else {
+            memcpy(move, threat, sizeof(Move));
+            return VALID_MOVE_SYNTAX;
+        }
+    } else {
+        return INVALID_POSITION;
+    }
+}
+
 int eval_move(GameState *gamestate, char *mstr, Move *move) {
     memset(move, 0, sizeof(Move));
     move->fromfile = POS_UNSPECIFIED;
@@ -452,83 +596,6 @@
     }
 }
 
-_Bool get_threats(GameState *gamestate, uint8_t row, uint8_t file,
-        uint8_t color, Move *threats, uint8_t *threatcount) {
-    Move candidates[16];
-    int candidatecount = 0;
-    for (uint8_t r = 0 ; r < 8 ; r++) {
-        for (uint8_t f = 0 ; f < 8 ; f++) {
-            if ((gamestate->board[r][f] & COLOR_MASK) == color) {
-                memset(&(candidates[candidatecount]), 0, sizeof(Move));
-                candidates[candidatecount].piece = gamestate->board[r][f];
-                candidates[candidatecount].fromrow = r;
-                candidates[candidatecount].fromfile = f;
-                candidates[candidatecount].torow = row;
-                candidates[candidatecount].tofile = file;
-                candidatecount++;
-            }
-        }
-    }
-
-    if (threatcount) {
-        *threatcount = 0;
-    }
-    
-    
-    _Bool result = 0;
-    
-    for (int i = 0 ; i < candidatecount ; i++) {
-        if (validate_move_rules(gamestate, &(candidates[i]))) {
-            result = 1;
-            if (threats && threatcount) {
-                threats[(*threatcount)++] = candidates[i];
-            }
-        }
-    }
-    
-    return result;
-}
-
-_Bool get_real_threats(GameState *gamestate, uint8_t row, uint8_t file,
-        uint8_t color, Move *threats, uint8_t *threatcount) {
-
-    Move candidates[16];
-    uint8_t candidatecount;
-    if (get_threats(gamestate, row, file, color, candidates, &candidatecount)) {
-        
-        if (threatcount) {
-            *threatcount = 0;
-        }
-        _Bool result = 0;
-        uint8_t kingfile = 0, kingrow = 0;
-        for (uint8_t row = 0 ; row < 8 ; row++) {
-            for (uint8_t file = 0 ; file < 8 ; file++) {
-                if ((gamestate->board[row][file] & COLOR_MASK) == color) {
-                    kingfile = file;
-                    kingrow = row;
-                }
-            }
-        }
-        
-        for (uint8_t i = 0 ; i < candidatecount ; i++) {
-            GameState simulation;
-            memcpy(&simulation, gamestate, sizeof(GameState));
-            apply_move(&simulation, &(candidates[i]));
-            if (!is_covered(&simulation, kingrow, kingfile,
-                    opponent_color(color))) {
-                result = 1;
-                if (threats && threatcount) {
-                    threats[(*threatcount)++] = candidates[i];
-                }
-            }
-        }
-        
-        return result;
-    } else {
-        return 0;
-    }
-}
-
 _Bool is_protected(GameState *gamestate, uint8_t row, uint8_t file,
         uint8_t color) {
     
--- a/src/chess/rules.h	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/chess/rules.h	Wed May 28 15:47:57 2014 +0200
@@ -38,6 +38,8 @@
 #define INVALID_POSITION    2
 #define AMBIGUOUS_MOVE      3
 #define NEED_PROMOTION      4
+#define PIECE_PINNED        5
+#define KING_IN_CHECK       6
 
 
 #define PIECE_MASK       0x0F
@@ -167,9 +169,10 @@
  * @param row row of the field to check
  * @param file file of the field to check
  * @param color the color of the piece that should threaten the field
- * @param threats the array where to store the threats (should be able to the
- * rare maximum of 16 elements)
- * @param threatcount a pointer to an uint8_t where to store the amount of threats
+ * @param threats the array where to store the threats (should be able to hold
+ * the rare maximum of 16 elements)
+ * @param threatcount a pointer to an uint8_t where the count of threats is
+ * stored
  * @return TRUE, if any piece of the specified color threatens the specified
  * field (i.e. could capture an opponent piece)
  */
@@ -187,9 +190,10 @@
  * @param row row of the field to check
  * @param file file of the field to check
  * @param color the color of the piece that should threaten the field
- * @param threats the array where to store the threats (should be able to the
- * rare maximum of 16 elements)
- * @param threatcount a pointer to an uint8_t where to store the amount of threats
+ * @param threats the array where to store the threats (should be able to hold
+ * the rare maximum of 16 elements)
+ * @param threatcount a pointer to an uint8_t where the count of threats is
+ * stored
  * @return TRUE, if any piece of the specified color threatens the specified
  * field (i.e. could capture an opponent piece)
  */
@@ -242,6 +246,22 @@
         uint8_t color);
 
 /**
+ * Checks, if the specified move cannot be performed, because the piece is
+ * either pinned or cannot remove the check.
+ * 
+ * Note: in chess a piece is pinned, when it can't be moved because the move
+ * would result in a check position. But this function <u>also</u> returns true,
+ * if the king is already in check position and the specified move does not
+ * protect the king.
+ * 
+ * @param gamestate the current game state
+ * @param move the move to check
+ * @return TRUE, if the move cannot be performed because the king would be in
+ * check after the move
+ */
+_Bool is_pinned(GameState *gamestate, Move *move);
+
+/**
  * Evaluates a move syntactically and stores the move data in the specified
  * object.
  * 
--- a/src/game.c	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/game.c	Wed May 28 15:47:57 2014 +0200
@@ -167,8 +167,17 @@
     case NEED_PROMOTION:
         printw("You need to promote the pawn (append \"=Q\" e.g.)!");
         break;
+    case KING_IN_CHECK:
+        printw("Your king is in check!");
+        break;
+    case PIECE_PINNED:
+        printw("This piece is pinned!");
+        break;
+    case INVALID_MOVE_SYNTAX:
+        printw("Can't interpret move - please use algebraic notation.");
+        break;
     default:
-        printw("Can't interpret move - please use algebraic notation.");
+        printw("Unknown move parser error.");
     }
 }
 
--- a/src/main.c	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/main.c	Wed May 28 15:47:57 2014 +0200
@@ -41,8 +41,11 @@
     uint8_t timeunit = 60;
     size_t len;
     
-    for (int opt ; (opt = getopt(argc, argv, "a:bhp:rst:")) != -1 ;) {
+    for (int opt ; (opt = getopt(argc, argv, "a:bc:hp:rst:")) != -1 ;) {
         switch (opt) {
+        case 'c':
+            settings->continuepgn = optarg;
+            break;
         case 'b':
             settings->gameinfo.servercolor = BLACK;
             break;
@@ -108,6 +111,7 @@
     memset(&settings, 0, sizeof(Settings));
     settings.gameinfo.servercolor = WHITE;
     settings.port = "27015";
+    settings.continuepgn = NULL;
     return settings;
 }
 
@@ -147,21 +151,26 @@
     
     if (settings.printhelp) {
         printf(
-            "Usage: terminal-chess [OPTION]... [HOST]\n"
-            "Starts/joins a network chess game\n"
-            "\nGeneral options\n"
-            "  -h            This help page\n"
-            "  -p            TCP port to use (default: 27015)\n"
-            "\nServer options\n"
-            "  -a <time>     Specifies the time to add after each move\n"
-            "  -b            Server plays black pieces (default: white)\n"
-            "  -r            Distribute color randomly\n"
-            "  -s            Single machine mode\n"
-            "  -t <time>     Specifies time limit (default: no limit)\n"
-            "\nNotes\n"
-            "The time unit for -a is seconds and for -t minutes by default. To "
-            "specify\nseconds for the -t option, use the s suffix.\n"
-            "Example: -t 150s\n"
+"Usage: terminal-chess [OPTION]... [HOST]\n"
+"Starts/joins a network chess game\n"
+"\nGeneral options\n"
+"  -h            This help page\n"
+"  -p            TCP port to use (default: 27015)\n"
+"\nServer options\n"
+"  -a <time>     Specifies the time to add after each move\n"
+"  -b            Server plays black pieces (default: white)\n"
+// TODO: implement and activate feature
+//"  -c <PGN file> Continue the specified game\n"
+"  -r            Distribute color randomly\n"
+"  -s            Single machine mode\n"
+// TODO: implement and activate feature
+//"  -S <PGN file> Compute and print statistics for the specified game\n"
+"  -t <time>     Specifies time limit (default: no limit)\n"
+"\nNotes\n"
+"The time unit for -a is seconds and for -t minutes by default. To "
+"specify\nseconds for the -t option, use the s suffix.\n"
+"Example: -t 150s\n\n"
+"Use '-' for PGN files to read PGN data from standard input\n"
         );
         return EXIT_SUCCESS;
     }    
--- a/src/terminal-chess.h	Thu Apr 17 12:16:14 2014 +0200
+++ b/src/terminal-chess.h	Wed May 28 15:47:57 2014 +0200
@@ -44,6 +44,7 @@
     GameInfo gameinfo;
     char* port;
     char* serverhost; /* NULL, if we are about to start a server */
+    char* continuepgn;
     _Bool printhelp;
     _Bool singlemachine;
 } Settings;

mercurial