chess

Terminal based Chess trainer using Anki
git clone git://git.dimitrijedobrota.com/chess.git
Log | Files | Refs

board.c (8520B)


      1 #include <ctype.h>
      2 #include <stddef.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 
      7 #include <cii/assert.h>
      8 #include <cii/except.h>
      9 #include <cii/mem.h>
     10 
     11 #include "board.h"
     12 
     13 #define T Board_T
     14 #define G Grave_T
     15 
     16 #define MAX_MOVE 10
     17 #define MAX_PLAY 1024
     18 
     19 const Except_T BOARDE_MOVE = {"Board: Invalid move"};
     20 
     21 const char  def_board[8][8] = {"rnbqkbnr", "pppppppp", "        ", "        ",
     22                                "        ", "        ", "PPPPPPPP", "RNBQKBNR"};
     23 static char piece_lookup[] = {'K', 'Q', 'R', 'B', 'N', 'P',
     24                               'k', 'q', 'r', 'b', 'n', 'p'};
     25 static int  piece_value[] = {100, 8, 5, 3, 3, 1, 100, 8, 5, 3, 3, 1};
     26 struct G {
     27   char arr[20];
     28   int  size;
     29   int  value;
     30 };
     31 
     32 struct T {
     33   int   turn;
     34   char *board;
     35   G     white;
     36   G     black;
     37 };
     38 
     39 typedef struct {
     40   int x[8];
     41   int y[8];
     42   int size;
     43   int limited;
     44 } move_offset_t;
     45 
     46 move_offset_t piece_offset[] = {
     47     { {0, 0, 1, -1, -1, -1, 1, 1},  {-1, 1, 0, 0, -1, 1, 1, -1}, 8, 1},
     48     { {0, 0, 1, -1, -1, -1, 1, 1},  {-1, 1, 0, 0, -1, 1, 1, -1}, 8, 0},
     49     {   {0, 0, 1, -1, 0, 0, 0, 0},    {-1, 1, 0, 0, 0, 0, 0, 0}, 4, 0},
     50     {  {-1, -1, 1, 1, 0, 0, 0, 0},   {-1, 1, 1, -1, 0, 0, 0, 0}, 4, 0},
     51     {{-2, -1, 1, 2, 2, 1, -1, -2}, {1, 2, 2, 1, -1, -2, -2, -1}, 8, 1}
     52 };
     53 
     54 void __grave_copy(G self, G new) {
     55   new->size = self->size;
     56   new->value = self->value;
     57   for (int i = 0; i < self->size; i++)
     58     new->arr[i] = self->arr[i];
     59 }
     60 
     61 void __board_send_grave(T self, char piece, char color) {
     62   G g = (color == 'w') ? self->white : self->black;
     63 
     64   g->arr[g->size++] = piece;
     65   g->value += piece_value[piece_get_index(piece)];
     66 }
     67 
     68 char __board_get_at(T self, char file, int rank) {
     69   if (file > 'h' || file < 'a' || rank < 1 || rank > 8)
     70     return '!';
     71   return self->board[(8 - rank) * 8 + file - 'a'];
     72 }
     73 
     74 void __board_set_at(T self, char file, int rank, char to) {
     75   self->board[(8 - rank) * 8 + file - 'a'] = to;
     76 }
     77 
     78 typedef int (*__board_find_next_f)(T, char, char *, int *);
     79 
     80 int __board_find_next_rank(T self, char piece, char *file, int *rank) {
     81   for (; *file <= 'h'; (*file)++)
     82     if (__board_get_at(self, *file, *rank) == piece)
     83       return 1;
     84   return 0;
     85 }
     86 
     87 int __board_find_next_file(T self, char piece, char *file, int *rank) {
     88   for (; *rank <= 8; (*rank)++)
     89     if (__board_get_at(self, *file, *rank) == piece)
     90       return 1;
     91   return 0;
     92 }
     93 
     94 int __board_find_next(T self, char piece, char *file, int *rank) {
     95   for (; *file <= 'h'; (*file)++) {
     96     for (; *rank <= 8; (*rank)++)
     97       if (__board_get_at(self, *file, *rank) == piece)
     98         return 1;
     99     *rank = 1;
    100   }
    101   return 0;
    102 }
    103 
    104 void __board_copy(T self, T new) {
    105   __grave_copy(self->white, new->white);
    106   __grave_copy(self->black, new->black);
    107   new->turn = self->turn;
    108   for (int i = 0; i < 8; i++)
    109     for (int j = 0; j < 8; j++)
    110       new->board[i * 8 + j] = self->board[i * 8 + j];
    111 }
    112 
    113 int __board_can_move(T self, char piece, char f_s, int r_s, char f_e, int r_e,
    114                      int take) {
    115 
    116   char piece_e = __board_get_at(self, f_e, r_e);
    117   if (take && !(piece_e != ' ' || (piece == 'P')))
    118     return 0;
    119 
    120   if (toupper(piece) == 'P') {
    121     int mul = (piece == 'P') ? 1 : -1;
    122     int step = ((piece == 'P' && r_s == 2) || r_s == 7) ? 2 : 1;
    123     if (!take) {
    124       if (f_s != f_e)
    125         return 0;
    126       for (int i = 1; i <= step; i++) {
    127         char piece_c = __board_get_at(self, f_s, r_s + i * mul);
    128         if (piece_c != ' ')
    129           return 0;
    130         if (r_s + i * mul == r_e)
    131           return 1;
    132       }
    133     } else {
    134       return (r_s + mul == r_e && (f_s + 1 == f_e || f_s - 1 == f_e));
    135     }
    136   } else {
    137     piece = toupper(piece);
    138     move_offset_t *move = piece_offset + piece_get_index(piece);
    139 
    140     for (int i = 0; i < move->size; i++) {
    141       int rank = r_s, file = f_s;
    142       do {
    143         file += move->y[i];
    144         rank += move->x[i];
    145         if (rank == r_e && file == f_e)
    146           return 1;
    147       } while (!move->limited && __board_get_at(self, file, rank) == ' ');
    148     }
    149   }
    150   return 0;
    151 }
    152 
    153 G grave_new(void) {
    154   G p;
    155   NEW(p);
    156   p->size = 0;
    157   p->value = 0;
    158   return p;
    159 }
    160 
    161 char Grave_atIndex(G self, int index) {
    162   assert(self);
    163   return self->arr[index];
    164 }
    165 
    166 int Grave_size(G self) {
    167   assert(self);
    168   return self->size;
    169 }
    170 
    171 G Board_grave(T self, char player) {
    172   assert(self);
    173   printf("%c\n", player);
    174   return (player == 'b') ? self->white : self->black;
    175 }
    176 
    177 T Board_new(void) {
    178   T board;
    179   NEW(board);
    180   board->white = grave_new();
    181   board->black = grave_new();
    182   board->board = ALLOC(64 * sizeof(char) + 1);
    183   for (int i = 0; i < 8; i++)
    184     for (int j = 0; j < 8; j++)
    185       board->board[i * 8 + j] = def_board[i][j];
    186 
    187   board->turn = 0;
    188   return board;
    189 }
    190 
    191 T Board_from_FEN(char *fen) {
    192   T board = Board_new();
    193 
    194   int j = 0;
    195   do {
    196     if (isalpha(*fen))
    197       board->board[j++] = *fen;
    198     else if (isdigit(*fen))
    199       for (int i = 0; i < *fen - '0'; i++)
    200         board->board[j++] = ' ';
    201   } while (*fen++ != '\0' && *fen != ' ');
    202   return board;
    203 }
    204 
    205 void Board_free(T *self) {
    206   assert(self);
    207   FREE((*self)->board);
    208   FREE(*self);
    209 }
    210 
    211 T Board_play(T self, char *m, int white) {
    212   assert(self);
    213   assert(m);
    214 
    215   char mover;
    216   mover = (white) ? 'w' : 'b';
    217 
    218   T new = Board_new();
    219   __board_copy(self, new);
    220 
    221   int (*conv)(int) = (mover == 'w') ? toupper : tolower;
    222   int castle_rank = (mover == 'w') ? 1 : 8;
    223 
    224   if (strcmp(m, "O-O") == 0) {
    225     __board_set_at(new, 'e', castle_rank, ' ');
    226     __board_set_at(new, 'f', castle_rank, conv('R'));
    227     __board_set_at(new, 'g', castle_rank, conv('K'));
    228     __board_set_at(new, 'h', castle_rank, ' ');
    229     return new;
    230   } else if (strcmp(m, "O-O-O") == 0) {
    231     __board_set_at(new, 'a', castle_rank, ' ');
    232     __board_set_at(new, 'c', castle_rank, conv('K'));
    233     __board_set_at(new, 'd', castle_rank, conv('R'));
    234     __board_set_at(new, 'e', castle_rank, ' ');
    235     return new;
    236   } else {
    237     // Decoding move MESS Start
    238     int l = strlen(m);
    239     while (m[l - 1] == '+' || m[l - 1] == '#' || m[l - 1] == '!' ||
    240            m[l - 1] == '?')
    241       l--;
    242 
    243     char promoted = 0;
    244     if (m[l - 2] == '=') {
    245       promoted = conv(m[l - 1]);
    246       l -= 2;
    247     }
    248 
    249     char file = m[l - 2];
    250     int  rank = m[l - 1] - '0';
    251 
    252     int  take = 0;
    253     char select = '\0', moved;
    254     if (l == 4 && m[1] == 'x')
    255       take = 1;
    256     else if (l >= 4) {
    257       select = m[1];
    258       take = m[2] == 'x';
    259     }
    260 
    261     if (isupper(m[0]))
    262       moved = conv(m[0]);
    263     else {
    264       moved = conv('P');
    265       select = m[0];
    266     }
    267     // MESS End
    268 
    269     char file_s = (isalpha(select)) ? select : 'a';
    270     int  rank_s = (isdigit(select)) ? select - '0' : 1;
    271 
    272     __board_find_next_f find_f;
    273     if (select)
    274       find_f =
    275           (isalpha(select)) ? __board_find_next_file : __board_find_next_rank;
    276     else
    277       find_f = __board_find_next;
    278 
    279     while (find_f(self, moved, &file_s, &rank_s)) {
    280       if (__board_can_move(self, moved, file_s, rank_s, file, rank, take)) {
    281         if (take)
    282           __board_send_grave(new, __board_get_at(self, file, rank), mover);
    283         __board_set_at(new, file_s, rank_s, ' ');
    284         __board_set_at(new, file, rank, promoted ? promoted : moved);
    285         return new;
    286       }
    287       rank_s++;
    288     }
    289   }
    290 
    291   RAISE(BOARDE_MOVE);
    292   return NULL;
    293 }
    294 
    295 int piece_get_index(char l) {
    296   for (size_t k = 0; k < sizeof(piece_lookup); k++)
    297     if (piece_lookup[k] == l)
    298       return k;
    299 
    300   return -1;
    301 }
    302 
    303 char Board_atIndex(T self, int i, int j) {
    304   assert(i >= 0 && i < 9 && j >= 0 && j < 9);
    305   return self->board[8 * i + j];
    306 }
    307 
    308 /* GAME */
    309 
    310 game_T game_new(size_t moves_num) {
    311   game_T game = ALLOC(sizeof(*game) + moves_num * sizeof(struct Board_T));
    312   game->moves_num = moves_num;
    313   game->display_current = 0;
    314   game->buffer_crnt = 0;
    315   game->pass = 0;
    316   game->fail = 0;
    317   memset(game->buffer, '\0', BUFF_SIZE);
    318   game->buffer[BUFF_SIZE] = '\0';
    319 
    320   return game;
    321 }
    322 
    323 /* MOVE */
    324 
    325 char **Move_list(char *pgn, size_t *size) {
    326   char  *crnt;
    327   size_t moves_size = 1, cnt = 0;
    328 
    329   char **moves = malloc(MAX_PLAY * sizeof(*moves));
    330   char  *buffer = malloc(1000 * sizeof(char));
    331   strcpy(buffer, pgn);
    332 
    333   moves[0] = "Starting move, empty board!";
    334   for (crnt = strtok(buffer, " "); crnt;
    335        crnt = strtok(NULL, " "), cnt = (cnt + 1) % 3) {
    336     switch (cnt) {
    337     case 0:
    338       continue;
    339     case 1:
    340     case 2:
    341       if (strcmp(crnt, "*") != 0) {
    342         moves[moves_size] = malloc(MAX_MOVE * sizeof(char));
    343         strcpy(moves[moves_size], crnt);
    344         moves_size++;
    345       }
    346     }
    347   }
    348 
    349   if (size)
    350     *size = moves_size - 1;
    351 
    352   return moves;
    353 }
    354 
    355 #undef G