add functions to list the possible moves for each piece default tip

Thu, 06 Aug 2026 14:45:55 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 06 Aug 2026 14:45:55 +0200
changeset 169
9962f5d98764
parent 168
663676cfef6e

add functions to list the possible moves for each piece

resolves #952

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
--- a/src/chess/bishop.c	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/bishop.c	Thu Aug 06 14:45:55 2026 +0200
@@ -52,3 +52,35 @@
     
     return false;
 }
+
+size_t bishop_moves(GameState *gamestate, Color c, Row r, File f, Move *moves) {
+
+    size_t count = 0;
+    const int directions[4][2] = {
+        { 1,  1},
+        { 1, -1},
+        {-1,  1},
+        {-1, -1}
+    };
+
+    for (size_t i = 0 ; i < 4 ; i++) {
+        int row = r + directions[i][0];
+        int file = f + directions[i][1];
+
+        while (isidx(row) && isidx(file)) {
+            moves[count] = (Move){0};
+            moves[count].piece = mkpiece(BISHOP, c);
+            moves[count].fromrow = r;
+            moves[count].fromfile = f;
+            moves[count].torow = row;
+            moves[count].tofile = file;
+            moves[count].capture = piece_at(gamestate, row, file) != 0;
+            count++;
+
+            row += directions[i][0];
+            file += directions[i][1];
+        }
+    }
+
+    return count;
+}
--- a/src/chess/bishop.h	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/bishop.h	Thu Aug 06 14:45:55 2026 +0200
@@ -39,6 +39,11 @@
 bool bishop_chkrules(const Move *move);
 bool bishop_isblocked(const GameState *gamestate, const Move *move);
 
+#define BISHOP_MOVES_MAX 13
+size_t bishop_moves(GameState *gamestate, Color c, Row r, File f, Move *moves);
+#define bishop_moves_allowed(gamestate, color, row, file, moves) \
+    filter_moves_allowed(gamestate, color, row, file, moves, bishop_moves)
+
 #ifdef	__cplusplus
 }
 #endif
--- a/src/chess/king.c	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/king.c	Thu Aug 06 14:45:55 2026 +0200
@@ -30,8 +30,6 @@
 #include "rules.h"
 #include "king.h"
 
-#include <string.h>
-
 static bool king_castling_chkmoved(
         const GameState *gamestate, Row row, File file) {
 
@@ -89,3 +87,54 @@
     
     return blocked;
 }
+
+size_t king_moves(GameState *gamestate, Color c, Row r, File f, Move *moves) {
+
+    size_t count = 0;
+    Piece king = mkpiece(KING, c);
+
+    for (int dr = -1 ; dr <= 1 ; dr++) {
+        for (int df = -1 ; df <= 1 ; df++) {
+            if (dr == 0 && df == 0) {
+                continue;
+            }
+
+            Row torow = r + dr;
+            File tofile = f + df;
+
+            if (!isidx(torow) || !isidx(tofile)) {
+                continue;
+            }
+
+            moves[count] = (Move){0};
+            moves[count].piece = king;
+            moves[count].fromrow = r;
+            moves[count].fromfile = f;
+            moves[count].torow = torow;
+            moves[count].tofile = tofile;
+            moves[count].capture = piece_at(gamestate, torow, tofile) != 0;
+            count++;
+        }
+    }
+
+    Row homerow = c == WHITE ? 0 : 7;
+    if (r == homerow && f == fileidx('e')) {
+        moves[count] = (Move){0};
+        moves[count].piece = king;
+        moves[count].fromrow = r;
+        moves[count].fromfile = f;
+        moves[count].torow = r;
+        moves[count].tofile = fileidx('c');
+        count++;
+
+        moves[count] = (Move){0};
+        moves[count].piece = king;
+        moves[count].fromrow = r;
+        moves[count].fromfile = f;
+        moves[count].torow = r;
+        moves[count].tofile = fileidx('g');
+        count++;
+    }
+
+    return count;
+}
--- a/src/chess/king.h	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/king.h	Thu Aug 06 14:45:55 2026 +0200
@@ -40,6 +40,11 @@
 bool king_chkrules(const GameState *gamestate, const Move *move);
 bool king_isblocked(const GameState *gamestate, const Move *move);
 
+#define KING_MOVES_MAX 8
+size_t king_moves(GameState *gamestate, Color c, Row r, File f, Move *moves);
+#define king_moves_allowed(gamestate, color, row, file, moves) \
+    filter_moves_allowed(gamestate, color, row, file, moves, king_moves)
+
 #ifdef	__cplusplus
 }
 #endif
--- a/src/chess/knight.c	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/knight.c	Thu Aug 06 14:45:55 2026 +0200
@@ -37,3 +37,36 @@
     
     return (dx == 2 && dy == 1) || (dx == 1 && dy == 2);
 }
+
+size_t knight_moves(GameState *gamestate, Color c, Row r, File f, Move *moves) {
+
+    size_t count = 0;
+    const int offsets[8][2] = {
+        { 2,  1},
+        { 2, -1},
+        {-2,  1},
+        {-2, -1},
+        { 1,  2},
+        { 1, -2},
+        {-1,  2},
+        {-1, -2}
+    };
+
+    for (size_t i = 0 ; i < 8 ; i++) {
+        int row = r + offsets[i][0];
+        int file = f + offsets[i][1];
+
+        if (isidx(row) && isidx(file)) {
+            moves[count] = (Move){0};
+            moves[count].piece = mkpiece(KNIGHT, c);
+            moves[count].fromrow = r;
+            moves[count].fromfile = f;
+            moves[count].torow = row;
+            moves[count].tofile = file;
+            moves[count].capture = piece_at(gamestate, row, file) != 0;
+            count++;
+        }
+    }
+
+    return count;
+}
--- a/src/chess/knight.h	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/knight.h	Thu Aug 06 14:45:55 2026 +0200
@@ -39,6 +39,11 @@
 bool knight_chkrules(const Move *move);
 #define knight_isblocked(gs,m) false
 
+#define KNIGHT_MOVES_MAX 8
+size_t knight_moves(GameState *gamestate, Color c, Row r, File f, Move *moves);
+#define knight_moves_allowed(gamestate, color, row, file, moves) \
+    filter_moves_allowed(gamestate, color, row, file, moves, knight_moves)
+
 #ifdef	__cplusplus
 }
 #endif
--- a/src/chess/pawn.c	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/pawn.c	Thu Aug 06 14:45:55 2026 +0200
@@ -77,3 +77,43 @@
             gamestate->board[(move->fromrow+move->torow)/2][move->tofile];
     }
 }
+
+size_t pawn_moves(GameState *gamestate, Color c, Row r, File f, Move *moves) {
+    size_t count = 0;
+    int rowdelta = c == WHITE ? 1 : -1;
+    int promotionrow = c == WHITE ? 7 : 0;
+    int startrow = c == WHITE ? 1 : 6;
+    const int targets[4][3] = {
+        {r + rowdelta,     f, 0},
+        {r + rowdelta, f - 1, 1},
+        {r + rowdelta, f + 1, 1},
+        {r + rowdelta * 2, f, 0}
+    };
+
+    for (size_t i = 0 ; i < 4 ; i++) {
+        int row = targets[i][0];
+        int file = targets[i][1];
+
+        if (i == 3 && r != startrow) {
+            continue;
+        }
+
+        if (isidx(row) && isidx(file)) {
+            moves[count] = (Move){0};
+            moves[count].piece = mkpiece(PAWN, c);
+            moves[count].fromrow = r;
+            moves[count].fromfile = f;
+            moves[count].torow = row;
+            moves[count].tofile = file;
+            moves[count].capture = targets[i][2];
+
+            if (row == promotionrow) {
+                moves[count].promotion = mkpiece(QUEEN, c);
+            }
+
+            count++;
+        }
+    }
+
+    return count;
+}
--- a/src/chess/pawn.h	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/pawn.h	Thu Aug 06 14:45:55 2026 +0200
@@ -39,6 +39,11 @@
 bool pawn_chkrules(const GameState *gamestate, const Move *move);
 bool pawn_isblocked(const GameState *gamestate, const Move *move);
 
+#define PAWN_MOVES_MAX 4
+size_t pawn_moves(GameState *gamestate, Color c, Row r, File f, Move *moves);
+#define pawn_moves_allowed(gamestate, color, row, file, moves) \
+    filter_moves_allowed(gamestate, color, row, file, moves, pawn_moves)
+
 #ifdef	__cplusplus
 }
 #endif
--- a/src/chess/queen.c	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/queen.c	Thu Aug 06 14:45:55 2026 +0200
@@ -43,3 +43,38 @@
         return bishop_isblocked(gamestate, move);
     }
 }
+
+size_t queen_moves(GameState *gamestate, Color c, Row r, File f, Move *moves) {
+    size_t count = 0;
+    const int directions[8][2] = {
+        { 1,  0},
+        {-1,  0},
+        { 0,  1},
+        { 0, -1},
+        { 1,  1},
+        { 1, -1},
+        {-1,  1},
+        {-1, -1}
+    };
+
+    for (size_t i = 0 ; i < 8 ; i++) {
+        int row = r + directions[i][0];
+        int file = f + directions[i][1];
+
+        while (isidx(row) && isidx(file)) {
+            moves[count] = (Move){0};
+            moves[count].piece = mkpiece(QUEEN, c);
+            moves[count].fromrow = r;
+            moves[count].fromfile = f;
+            moves[count].torow = row;
+            moves[count].tofile = file;
+            moves[count].capture = piece_at(gamestate, row, file) != 0;
+            count++;
+
+            row += directions[i][0];
+            file += directions[i][1];
+        }
+    }
+
+    return count;
+}
--- a/src/chess/queen.h	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/queen.h	Thu Aug 06 14:45:55 2026 +0200
@@ -39,6 +39,11 @@
 bool queen_chkrules(const Move *move);
 bool queen_isblocked(const GameState *gamestate, const Move *move);
 
+#define QUEEN_MOVES_MAX 27
+size_t queen_moves(GameState *gamestate, Color c, Row r, File f, Move *moves);
+#define queen_moves_allowed(gamestate, color, row, file, moves) \
+    filter_moves_allowed(gamestate, color, row, file, moves, queen_moves)
+
 #ifdef	__cplusplus
 }
 #endif
--- a/src/chess/rook.c	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/rook.c	Thu Aug 06 14:45:55 2026 +0200
@@ -58,3 +58,34 @@
     
     return false;
 }
+
+size_t rook_moves(GameState *gamestate, Color c, Row r, File f, Move *moves) {
+    size_t count = 0;
+    const int directions[4][2] = {
+        { 1,  0},
+        {-1,  0},
+        { 0,  1},
+        { 0, -1}
+    };
+
+    for (size_t i = 0 ; i < 4 ; i++) {
+        int row = r + directions[i][0];
+        int file = f + directions[i][1];
+
+        while (isidx(row) && isidx(file)) {
+            moves[count] = (Move){0};
+            moves[count].piece = mkpiece(ROOK, c);
+            moves[count].fromrow = r;
+            moves[count].fromfile = f;
+            moves[count].torow = row;
+            moves[count].tofile = file;
+            moves[count].capture = piece_at(gamestate, row, file) != 0;
+            count++;
+
+            row += directions[i][0];
+            file += directions[i][1];
+        }
+    }
+
+    return count;
+}
--- a/src/chess/rook.h	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/rook.h	Thu Aug 06 14:45:55 2026 +0200
@@ -39,6 +39,11 @@
 bool rook_chkrules(const Move *move);
 bool rook_isblocked(const GameState *gamestate, const Move *move);
 
+#define ROOK_MOVES_MAX 14
+size_t rook_moves(GameState *gamestate, Color c, Row r, File f, Move *moves);
+#define rook_moves_allowed(gamestate, color, row, file, moves) \
+    filter_moves_allowed(gamestate, color, row, file, moves, rook_moves)
+
 #ifdef	__cplusplus
 }
 #endif
--- a/src/chess/rules.c	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/rules.c	Thu Aug 06 14:45:55 2026 +0200
@@ -1135,3 +1135,20 @@
     // TODO: implement threefold repetition detection
     return false;
 }
+
+size_t filter_moves_allowed(GameState *gamestate,
+        Color c, Row r, File f, Move *moves, moves_generator_func func) {
+
+    /* worst case: the queen has the most moves */
+    Move candidates[QUEEN_MOVES_MAX];
+    size_t candidatecount = func(gamestate, c, r, f, candidates);
+    size_t count = 0;
+
+    for (size_t i = 0 ; i < candidatecount ; i++) {
+        if (validate_move(gamestate, &candidates[i]) == VALID_MOVE_SEMANTICS) {
+            moves[count++] = candidates[i];
+        }
+    }
+
+    return count;
+}
\ No newline at end of file
--- a/src/chess/rules.h	Sun Aug 02 15:19:47 2026 +0200
+++ b/src/chess/rules.h	Thu Aug 06 14:45:55 2026 +0200
@@ -287,6 +287,25 @@
     piece_set(gamestate, row, file, 0);
 }
 
+typedef size_t(*moves_generator_func)(GameState *gamestate,
+        Color c, Row r, File f, Move *moves);
+
+/**
+ * Internal function used to filter out illegal moves.
+ *
+ * Use the macros for the specific pieces instead.
+ *
+ * @param gamestate the current gamestate
+ * @param c color of the piece
+ * @param r the row of the piece
+ * @param f the file of the piece
+ * @param moves target array for the list of moves
+ * @param func a function that unconditionally generates the moves
+ * @return the number of moves stored in the @p moves array
+ */
+size_t filter_moves_allowed(GameState *gamestate,
+    Color c, Row r, File f, Move *moves, moves_generator_func func);
+
 /**
  * Determines a list of theoretically possible moves to the specified field.
  *

mercurial