Add rudimentary chess engine

This commit is contained in:
2024-12-02 01:40:05 +01:00
parent 319428e1bc
commit 97d2446a90
3 changed files with 206 additions and 47 deletions

View File

@@ -1,56 +1,149 @@
#pragma once
#include <wchar.h>
#include <locale.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <assert.h>
typedef uint64_t bitmap_t;
typedef uint_fast8_t index_t;
typedef int_fast8_t piece_t;
typedef uint64_t bitmap_t;
typedef uint64_t index_t;
typedef uint64_t piece_t;
enum tile_info {
EMPTY = 0b0000,
KING = 0b0001,
QUEEN = 0b0010,
ROOK = 0b0011,
BISHOP = 0b0100,
KNIGHT = 0b0101,
PAWN = 0b0110,
COLOR_BIT = 0b1000,
#define TILE_BITS 4ULL
PIECE_UNKNOWN=-1
enum piece : piece_t {
EMPTY = 0b0000ULL,
W_KING = 0b0001ULL,
W_QUEEN = 0b0010ULL,
W_ROOK = 0b0011ULL,
W_BISHOP = 0b0100ULL,
W_KNIGHT = 0b0101ULL,
W_PAWN = 0b0110ULL,
W_PAWN_ENPASSENTABLE = 0b0111ULL,
B_EMPTY = 0b1000ULL,
B_KING = 0b1001ULL,
B_QUEEN = 0b1010ULL,
B_ROOK = 0b1011ULL,
B_BISHOP = 0b1100ULL,
B_KNIGHT = 0b1101ULL,
B_PAWN = 0b1110ULL,
B_PAWN_ENPASSENTABLE = 0b1111ULL,
BLACK = 0b1000ULL,
#define WHITE 0
};
static const char * const piece_str[] = {
[EMPTY] = "EMPTY",
[W_KING] = "W_KING",
[W_QUEEN] = "W_QUEEN",
[W_ROOK] = "W_ROOK",
[W_BISHOP] = "W_BISHOP",
[W_KNIGHT] = "W_KNIGHT",
[W_PAWN] = "W_PAWN",
[W_PAWN_ENPASSENTABLE] = "W_PAWN (enpassentable)",
[B_EMPTY] = "EMPTY (BLACK flag set)",
[B_KING] = "B_KING",
[B_QUEEN] = "B_QUEEN",
[B_ROOK] = "B_ROOK",
[B_BISHOP] = "B_BISHOP",
[B_KNIGHT] = "B_KNIGHT",
[B_PAWN] = "B_PAWN",
[B_PAWN_ENPASSENTABLE ] = "B_PAWN (enpassentable)",
};
static const wchar_t piece_symbol[] = {
[WHITE | 0] = ' ',
[W_KING] = 0x265A,
[W_QUEEN] = 0x265B,
[W_ROOK] = 0x265C,
[W_BISHOP] = 0x265D,
[W_KNIGHT] = 0x265E,
[W_PAWN] = 0x265F,
[W_PAWN_ENPASSENTABLE] = 0x265F,
[B_KING] = 0x265A,
[B_QUEEN] = 0x265B,
[B_ROOK] = 0x265C,
[B_BISHOP] = 0x265D,
[B_KNIGHT] = 0x265E,
[B_PAWN] = 0x265F,
[B_PAWN_ENPASSENTABLE] = 0x265F,
};
struct board {
static_assert(sizeof(bitmap_t) == 64/8, "bitmap_t must be 64 bits long");
bitmap_t pieces[4]; // 4 bits correspond to 1 tile
};
static_assert(sizeof(((struct board*)0)->pieces) * CHAR_BIT == TILE_BITS*64,
"pieces must contain enough information to hold a 64 chessboard tiles");
static_assert((sizeof(((struct board*)0)->pieces[0]) * CHAR_BIT) % TILE_BITS == 0,
"bitmap_t in bits must be divisible by TILE_BITS");
static inline bitmap_t bit(index_t i)
static inline bitmap_t tile_mask(index_t i)
{
return 0b111UL << 3*i;
return 0b1111ULL << (TILE_BITS*i);
}
static bool is_white(piece_t piece)
static inline bool is_white(piece_t piece)
{
return piece % 2 == 0;
return (piece & BLACK) == 0;
}
static bool is_black(piece_t piece)
static inline bool is_black(piece_t piece)
{
return piece % 2 == 1;
return (piece & BLACK) == BLACK;
}
static piece_t piece_at(struct board* board, index_t tile)
static inline piece_t piece_at(struct board* board, index_t tile)
{
for (size_t i = 0; i < sizeof board->pieces / board->pieces[0]; i++) {
if (bit(tile) & board->pieces[i]) {
return i;
static_assert(sizeof board->pieces[0] * CHAR_BIT == 64, "bad refactor");
return (tile_mask(tile % (64/TILE_BITS)) & board->pieces[tile / (64/TILE_BITS)]) >> 4ULL*tile;
}
static void paint_board(struct board* b)
{
/* https://en.wikipedia.org/wiki/Chess_symbols_in_Unicode
The unicode symbols for the pieces are calculated from adding
0x2653 (#define'd as UNICODE_CHESS_SYMBOL) with the piece value. */
#define BG_RED() printf("\033[48;2;150;150;0m")
#define BG_DARKBLUE() printf("\033[48;2;100;100;150m")
#define BG_LIGHTBLUE() printf("\033[48;2;150;150;200m")
#define FG_BLACK() printf("\033[38;2;0;0;0m")
#define FG_WHITE() printf("\033[38;2;255;255;255m")
for (int i = 7; i >= 0; i--) {
printf("\n %d ", i+1); // number coordinates
for (int j = 0; j < 8; j++) {
piece_t p = piece_at(b, i*8+j);
if ((i + j) % 2) {
BG_DARKBLUE();
} else {
BG_LIGHTBLUE();
}
if (p == EMPTY) {
printf(" ");
continue;
}
if (is_white(p)) {
FG_WHITE();
} else {
FG_BLACK();
}
printf("%lc ", piece_symbol[p]);
}
printf("\033[0m"); // reset text attributes
}
return PIECE_UNKNOWN;
/* horizontal letter coordinates */
printf("\n ");
for (int i = 0; i < 8; i++)
printf(" %c", 'a' + i);
printf("\n");
}

View File

@@ -11,9 +11,11 @@
#include <stdint.h> /* int32_t */
#include <stdio.h> /* printf, scanf */
#include <stdlib.h>
#include <string.h>
#define MAX_DEPTH 4
#define MAX_DEPTH 5
#define CHECKMATE_SCORE 100000
#define SET_SIZE 4096
#define RANK ((index_t)8)
#define COL ((index_t)1)
@@ -171,7 +173,7 @@ static const double piece_position_bonus[PIECE_COUNT][BOARD_SIZE] = {
},
[KING] = {
/* A B C D E F G H */
/* 1 */ 1.1, 1.1, 1.1, 1.0, 1.0, 1.0, 1.15, 1.15,
/* 1 */ 1.1, 1.1, 1.1, 1.0, 1.0, 1.0, 1.3, 1.15,
/* 2 */ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
/* 3 */ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
/* 4 */ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
@@ -214,6 +216,75 @@ enum castle_type {
CASTLE_QUEENSIDE = 2,
};
static bool board_equals(Board* b1, Board* b2)
{
return memcmp(b1, b2, sizeof *b1) == 0;
}
static size_t board_hash(Board* b)
{
size_t n = 5381;
for (int i=0; i<BOARD_SIZE; i++) {
n = n*33+(size_t)b[i];
}
return n;
}
struct boardset_entry {
Board board;
int n;
struct boardset_entry* next;
};
struct boardset {
struct boardset_entry* entries[SET_SIZE];
};
static void boardset_reset(struct boardset* bs)
{
for (size_t i=0; i < sizeof bs->entries / sizeof *(bs->entries); i++) {
struct boardset_entry* next, *e = bs->entries[i];
while (e) {
next = e->next;
free(e->board);
free(e);
e = next;
}
}
}
static struct boardset_entry* boardset_get(struct boardset* bs, Board* b)
{
size_t i = board_hash(b) % SET_SIZE;
struct boardset_entry** bsentry = &(bs->entries[i]);
while (*bsentry != NULL && !board_equals(b, &((*bsentry)->board))) {
*bsentry = (*bsentry)->next;
}
if (*bsentry == NULL) {
*bsentry = calloc(sizeof *bsentry, 1);
if (bsentry == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
}
return *bsentry;
}
int boardset_count(struct boardset* bs, Board* b)
{
return boardset_get(bs, b)->n;
}
void boardset_inc(struct boardset* bs, Board* b)
{
boardset_get(bs, b)->n += 1;
}
static inline bitmap_t bit(index_t i)
{
return 1UL << i;
@@ -902,7 +973,7 @@ static double heuristic(struct game_state* g, int depth)
return 0;
if (checkmate(g))
return g->player * -9999;
return g->player * -10000 * depth;
double score = 0;
for (index_t i=0; i<BOARD_SIZE; i++) {
@@ -942,6 +1013,7 @@ static void print_debug(struct game_state* g)
static void sigint_handler(int signal)
{
(void)signal;
paint_board(&sigint_state_copy, -1, -1);
print_debug(&sigint_state_copy);
dump_game_state(&sigint_state_copy);
@@ -1047,7 +1119,7 @@ int main()
while (true) {
printf("============================\n");
paint_board(&state, from, to);
printf("est. score: %lf", heuristic(&state, 0));
printf("est. score: %lf\n", heuristic(&state, 0));
//print_debug(&state);
//dump_game_state(&state);

View File

@@ -1,20 +1,14 @@
enum chess_piece {
EMPTY = 0,
KING = 1,
QUEEN = 2,
ROOK = 3,
BISHOP = 4,
KNIGHT = 5,
PAWN = 6,
PIECE_COUNT,
};
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
static bool board_equals(
#define SET_SIZE 4096
#define BOARD_SIZE 64
struct board_map {
};
typedef int_fast8_t piece_t;
typedef piece_t Board[BOARD_SIZE];