mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 13:23:15 +02:00
feat: added lichess db puzzle to gitignore
This commit is contained in:
parent
749546fdb2
commit
175a2b256c
6
.vscode/tasks.json
vendored
6
.vscode/tasks.json
vendored
@ -30,6 +30,12 @@
|
||||
"command": "python -m pip install -r requirements.txt && pytest -q",
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "pytest quick",
|
||||
"type": "shell",
|
||||
"command": "python -m pip install -r requirements.txt && pytest -q",
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "pytest quick",
|
||||
"type": "shell",
|
||||
|
||||
@ -2,9 +2,13 @@ CC := gcc
|
||||
CFLAGS := -O2 -std=c11 -Wall -Wextra -Wno-unused-parameter
|
||||
LDFLAGS :=
|
||||
|
||||
SRC := main.c
|
||||
SRC := main.c movegen.c search.c
|
||||
BIN := random_engine
|
||||
|
||||
# Perft driver
|
||||
PERFT_SRC := perft.c movegen.c
|
||||
PERFT_BIN := perft
|
||||
|
||||
.PHONY: all clean rebuild
|
||||
|
||||
all: $(BIN)
|
||||
@ -12,7 +16,13 @@ all: $(BIN)
|
||||
$(BIN): $(SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
$(PERFT_BIN): $(PERFT_SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(BIN)
|
||||
rm -f $(BIN) $(PERFT_BIN)
|
||||
|
||||
rebuild: clean all
|
||||
|
||||
.PHONY: perft
|
||||
perft: $(PERFT_BIN)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
458
C/lichess_random_engine/movegen.c
Normal file
458
C/lichess_random_engine/movegen.c
Normal file
@ -0,0 +1,458 @@
|
||||
#include "movegen.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static inline int on_board(int sq) { return (sq & 0x88) == 0; }
|
||||
static inline int rank_of(int sq) { return sq >> 4; }
|
||||
static inline int file_of(int sq) { return sq & 7; }
|
||||
|
||||
static inline int color_of(Piece p){ return (p>=BP); }
|
||||
static inline int is_white(Piece p){ return p>=WP && p<=WK; }
|
||||
static inline int is_black(Piece p){ return p>=BP && p<=BK; }
|
||||
|
||||
static Piece make_piece(char c){
|
||||
switch(c){
|
||||
case 'P': return WP; case 'N': return WN; case 'B': return WB; case 'R': return WR; case 'Q': return WQ; case 'K': return WK;
|
||||
case 'p': return BP; case 'n': return BN; case 'b': return BB; case 'r': return BR; case 'q': return BQ; case 'k': return BK;
|
||||
default: return EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
static char piece_to_char(Piece p){
|
||||
switch(p){
|
||||
case WP: return 'P'; case WN: return 'N'; case WB: return 'B'; case WR: return 'R'; case WQ: return 'Q'; case WK: return 'K';
|
||||
case BP: return 'p'; case BN: return 'n'; case BB: return 'b'; case BR: return 'r'; case BQ: return 'q'; case BK: return 'k';
|
||||
default: return '.';
|
||||
}
|
||||
}
|
||||
|
||||
void set_startpos(Position *pos){
|
||||
memset(pos, 0, sizeof(*pos));
|
||||
for(int i=0;i<BOARD_SIZE;i++) pos->board[i]=EMPTY;
|
||||
const char *start = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR";
|
||||
char fen[128]; strcpy(fen, start);
|
||||
strcat(fen, " w KQkq - 0 1");
|
||||
parse_fen(pos, fen);
|
||||
}
|
||||
|
||||
int parse_fen(Position *pos, const char *fen){
|
||||
memset(pos, 0, sizeof(*pos));
|
||||
for(int i=0;i<BOARD_SIZE;i++) pos->board[i]=EMPTY;
|
||||
pos->ep_square = -1;
|
||||
pos->castle = 0;
|
||||
pos->halfmove_clock = 0;
|
||||
pos->fullmove_number = 1;
|
||||
|
||||
// pieces
|
||||
int sq = 0x70; // A8
|
||||
const char *p = fen;
|
||||
while (*p && *p!=' ') {
|
||||
if (*p=='/') { sq = (sq & 0x70) - 0x10; p++; continue; }
|
||||
if (isdigit((unsigned char)*p)) { sq += (*p - '0'); p++; continue; }
|
||||
Piece pc = make_piece(*p++);
|
||||
if (!on_board(sq)) return 0;
|
||||
pos->board[sq++] = pc;
|
||||
}
|
||||
if (*p!=' ') return 0;
|
||||
p++;
|
||||
|
||||
// side
|
||||
if (*p=='w') pos->side = WHITE; else if (*p=='b') pos->side = BLACK; else return 0; p++;
|
||||
if (*p!=' ') return 0;
|
||||
p++;
|
||||
|
||||
// castling
|
||||
if (*p=='-') { p++; }
|
||||
else {
|
||||
while (*p && *p!=' ') {
|
||||
if (*p=='K') pos->castle |= 1<<0;
|
||||
else if (*p=='Q') pos->castle |= 1<<1;
|
||||
else if (*p=='k') pos->castle |= 1<<2;
|
||||
else if (*p=='q') pos->castle |= 1<<3;
|
||||
else return 0;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
if (*p!=' ') return 0;
|
||||
p++;
|
||||
|
||||
// en-passant
|
||||
if (*p=='-') { pos->ep_square = -1; p++; }
|
||||
else {
|
||||
if (p[0]>='a' && p[0]<='h' && p[1]>='1' && p[1]<='8'){
|
||||
int f = p[0]-'a'; int r = p[1]-'1';
|
||||
pos->ep_square = (r<<4) | f;
|
||||
p+=2;
|
||||
} else return 0;
|
||||
}
|
||||
if (*p==' ') p++;
|
||||
|
||||
// halfmove clock
|
||||
if (isdigit((unsigned char)*p)) {
|
||||
pos->halfmove_clock = strtol(p, (char**)&p, 10);
|
||||
}
|
||||
if (*p==' ') p++;
|
||||
|
||||
// fullmove number
|
||||
if (isdigit((unsigned char)*p)) pos->fullmove_number = strtol(p, NULL, 10);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int add_move(Move *moves, int count, int max, int from, int to, int cap, int promo, int ep, int castle){
|
||||
if (count>=max) return count;
|
||||
Move m; m.from=(uint8_t)from; m.to=(uint8_t)to; m.promo=(uint8_t)promo; m.is_capture=(uint8_t)cap; m.is_enpassant=(uint8_t)ep; m.is_castle=(uint8_t)castle;
|
||||
moves[count++] = m;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Check detection via attack lookup
|
||||
static int square_attacked_by(const Position *pos, int sq, Color by){
|
||||
// Knights
|
||||
static const int kn[] = {33,31,18,14,-33,-31,-18,-14};
|
||||
for(int i=0;i<8;i++){
|
||||
int s = sq + kn[i];
|
||||
if (!on_board(s)) continue;
|
||||
Piece p = pos->board[s];
|
||||
if (by==WHITE && p==WN) return 1;
|
||||
if (by==BLACK && p==BN) return 1;
|
||||
}
|
||||
// Kings
|
||||
static const int kd[] = {1,-1,16,-16,17,15,-17,-15};
|
||||
for(int i=0;i<8;i++){
|
||||
int s = sq + kd[i]; if (!on_board(s)) continue; Piece p=pos->board[s];
|
||||
if (by==WHITE && p==WK) return 1;
|
||||
if (by==BLACK && p==BK) return 1;
|
||||
}
|
||||
// Pawns
|
||||
if (by==WHITE){
|
||||
int s1 = sq - 15; int s2 = sq - 17; // white pawns attack up-left/up-right from their perspective
|
||||
if (on_board(s1) && pos->board[s1]==WP) return 1;
|
||||
if (on_board(s2) && pos->board[s2]==WP) return 1;
|
||||
} else {
|
||||
int s1 = sq + 15; int s2 = sq + 17;
|
||||
if (on_board(s1) && pos->board[s1]==BP) return 1;
|
||||
if (on_board(s2) && pos->board[s2]==BP) return 1;
|
||||
}
|
||||
// Sliders: bishops/queens diagonals
|
||||
static const int bd[] = {17,15,-17,-15};
|
||||
for (int d=0; d<4; ++d){
|
||||
int s = sq + bd[d];
|
||||
while(on_board(s)){
|
||||
Piece p = pos->board[s];
|
||||
if (p!=EMPTY){
|
||||
if (by==WHITE && (p==WB || p==WQ)) return 1;
|
||||
if (by==BLACK && (p==BB || p==BQ)) return 1;
|
||||
break;
|
||||
}
|
||||
s += bd[d];
|
||||
}
|
||||
}
|
||||
// Rooks/queens
|
||||
static const int rd[] = {1,-1,16,-16};
|
||||
for (int d=0; d<4; ++d){
|
||||
int s = sq + rd[d];
|
||||
while(on_board(s)){
|
||||
Piece p = pos->board[s];
|
||||
if (p!=EMPTY){
|
||||
if (by==WHITE && (p==WR || p==WQ)) return 1;
|
||||
if (by==BLACK && (p==BR || p==BQ)) return 1;
|
||||
break;
|
||||
}
|
||||
s += rd[d];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int in_check(const Position *pos, Color side){
|
||||
// find king square
|
||||
Piece k = (side==WHITE)?WK:BK;
|
||||
int ks = -1;
|
||||
for (int sq=0; sq<BOARD_SIZE; ++sq){ if (!on_board(sq)) { sq=(sq|7); continue; } if (pos->board[sq]==k){ ks=sq; break; } }
|
||||
if (ks<0) return 0;
|
||||
return square_attacked_by(pos, ks, (side==WHITE)?BLACK:WHITE);
|
||||
}
|
||||
|
||||
static int gen_moves_internal(const Position *pos, Move *moves, int max_moves, int captures_only){
|
||||
int count = 0;
|
||||
Color us = pos->side;
|
||||
int forward = (us==WHITE)?16:-16;
|
||||
int start_rank = (us==WHITE)?1:6;
|
||||
int promo_rank = (us==WHITE)?6:1; // rank before promotion move (from rank)
|
||||
|
||||
for (int sq=0; sq<BOARD_SIZE; ++sq){
|
||||
if (!on_board(sq)) { sq=(sq|7); continue; }
|
||||
Piece p = pos->board[sq];
|
||||
if (p==EMPTY) continue;
|
||||
if ((us==WHITE && !is_white(p)) || (us==BLACK && !is_black(p))) continue;
|
||||
|
||||
switch(p){
|
||||
case WP: case BP: {
|
||||
int dir = (p==WP)?16:-16;
|
||||
int r = rank_of(sq);
|
||||
// quiet pushes
|
||||
if (!captures_only){
|
||||
int to = sq + dir; if (on_board(to) && pos->board[to]==EMPTY){
|
||||
if (r==promo_rank){
|
||||
count = add_move(moves, count, max_moves, sq, to, 0, (us==WHITE?WQ:BQ), 0, 0);
|
||||
count = add_move(moves, count, max_moves, sq, to, 0, (us==WHITE?WR:BR), 0, 0);
|
||||
count = add_move(moves, count, max_moves, sq, to, 0, (us==WHITE?WB:BB), 0, 0);
|
||||
count = add_move(moves, count, max_moves, sq, to, 0, (us==WHITE?WN:BN), 0, 0);
|
||||
} else {
|
||||
count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0);
|
||||
// double push from start rank
|
||||
if (r==start_rank){
|
||||
int to2 = to + dir; if (on_board(to2) && pos->board[to2]==EMPTY){
|
||||
count = add_move(moves, count, max_moves, sq, to2, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// captures
|
||||
int caps[2] = { sq + dir + 1, sq + dir - 1 };
|
||||
for (int i=0;i<2;i++){
|
||||
int to = caps[i]; if (!on_board(to)) continue; Piece tp = pos->board[to];
|
||||
if (tp!=EMPTY && color_of(tp)!=us){
|
||||
if (r==promo_rank){
|
||||
count = add_move(moves, count, max_moves, sq, to, 1, (us==WHITE?WQ:BQ), 0, 0);
|
||||
count = add_move(moves, count, max_moves, sq, to, 1, (us==WHITE?WR:BR), 0, 0);
|
||||
count = add_move(moves, count, max_moves, sq, to, 1, (us==WHITE?WB:BB), 0, 0);
|
||||
count = add_move(moves, count, max_moves, sq, to, 1, (us==WHITE?WN:BN), 0, 0);
|
||||
} else {
|
||||
count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// en-passant
|
||||
if (pos->ep_square>=0){
|
||||
for (int i=0;i<2;i++){
|
||||
int to = caps[i]; if (!on_board(to)) continue;
|
||||
if (to==pos->ep_square){
|
||||
count = add_move(moves, count, max_moves, sq, to, 1, 0, 1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WN: case BN: {
|
||||
static const int d[8] = {33,31,18,14,-33,-31,-18,-14};
|
||||
for (int i=0;i<8;i++){
|
||||
int to = sq + d[i]; if (!on_board(to)) continue; Piece tp=pos->board[to];
|
||||
if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); }
|
||||
else if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0);
|
||||
}
|
||||
} break;
|
||||
case WB: case BB: case WR: case BR: case WQ: case BQ: {
|
||||
static const int bd[4] = {17,15,-17,-15};
|
||||
static const int rd[4] = {1,-1,16,-16};
|
||||
const int *dirs = NULL; int ndirs=0;
|
||||
if (p==WB || p==BB) { dirs=bd; ndirs=4; }
|
||||
else if (p==WR || p==BR) { dirs=rd; ndirs=4; }
|
||||
else { // queen
|
||||
// iterate both sets
|
||||
for (int i=0;i<4;i++){
|
||||
int to = sq + bd[i];
|
||||
while(on_board(to)){
|
||||
Piece tp=pos->board[to];
|
||||
if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); }
|
||||
else { if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); break; }
|
||||
to += bd[i];
|
||||
}
|
||||
}
|
||||
for (int i=0;i<4;i++){
|
||||
int to = sq + rd[i];
|
||||
while(on_board(to)){
|
||||
Piece tp=pos->board[to];
|
||||
if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); }
|
||||
else { if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); break; }
|
||||
to += rd[i];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
for (int i=0;i<ndirs;i++){
|
||||
int to = sq + dirs[i];
|
||||
while(on_board(to)){
|
||||
Piece tp=pos->board[to];
|
||||
if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); }
|
||||
else { if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0); break; }
|
||||
to += dirs[i];
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WK: case BK: {
|
||||
static const int kd[8] = {1,-1,16,-16,17,15,-17,-15};
|
||||
for (int i=0;i<8;i++){
|
||||
int to = sq + kd[i]; if (!on_board(to)) continue; Piece tp=pos->board[to];
|
||||
if (tp==EMPTY) { if (!captures_only) count = add_move(moves, count, max_moves, sq, to, 0, 0, 0, 0); }
|
||||
else if (color_of(tp)!=us) count = add_move(moves, count, max_moves, sq, to, 1, 0, 0, 0);
|
||||
}
|
||||
// castling (very basic, no check-through validation here; filter later)
|
||||
if (!captures_only){
|
||||
// Only if not currently in check and path squares are not attacked
|
||||
Color them = (us==WHITE)?BLACK:WHITE;
|
||||
if (us==WHITE){
|
||||
if ((pos->castle & (1<<0)) && pos->board[0x04]==WK && pos->board[0x05]==EMPTY && pos->board[0x06]==EMPTY){
|
||||
if (!in_check(pos, WHITE) && !square_attacked_by(pos, 0x05, them) && !square_attacked_by(pos, 0x06, them))
|
||||
count = add_move(moves, count, max_moves, sq, 0x06, 0, 0, 0, 1);
|
||||
}
|
||||
if ((pos->castle & (1<<1)) && pos->board[0x03]==EMPTY && pos->board[0x02]==EMPTY && pos->board[0x01]==EMPTY){
|
||||
if (!in_check(pos, WHITE) && !square_attacked_by(pos, 0x03, them) && !square_attacked_by(pos, 0x02, them))
|
||||
count = add_move(moves, count, max_moves, sq, 0x02, 0, 0, 0, 1);
|
||||
}
|
||||
} else {
|
||||
if ((pos->castle & (1<<2)) && pos->board[0x74]==BK && pos->board[0x75]==EMPTY && pos->board[0x76]==EMPTY){
|
||||
if (!in_check(pos, BLACK) && !square_attacked_by(pos, 0x75, them) && !square_attacked_by(pos, 0x76, them))
|
||||
count = add_move(moves, count, max_moves, sq, 0x76, 0, 0, 0, 1);
|
||||
}
|
||||
if ((pos->castle & (1<<3)) && pos->board[0x73]==EMPTY && pos->board[0x72]==EMPTY && pos->board[0x71]==EMPTY){
|
||||
if (!in_check(pos, BLACK) && !square_attacked_by(pos, 0x73, them) && !square_attacked_by(pos, 0x72, them))
|
||||
count = add_move(moves, count, max_moves, sq, 0x72, 0, 0, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int gen_moves_pseudo(const Position *pos, Move *moves, int max_moves, int captures_only){
|
||||
return gen_moves_internal(pos, moves, max_moves, captures_only);
|
||||
}
|
||||
|
||||
int gen_moves(const Position *pos, Move *moves, int max_moves, int captures_only){
|
||||
int count = gen_moves_internal(pos, moves, max_moves, captures_only);
|
||||
// Filter illegal moves leaving our king in check
|
||||
for (int i=0;i<count;){
|
||||
Position tmp = *pos;
|
||||
Piece cap = EMPTY;
|
||||
make_move(&tmp, &moves[i], &cap);
|
||||
int illegal = in_check(&tmp, pos->side);
|
||||
if (illegal){
|
||||
moves[i] = moves[count-1];
|
||||
count--;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void make_move(Position *pos, const Move *m, Piece *captured_out){
|
||||
Piece fromP = pos->board[m->from];
|
||||
Piece toP = pos->board[m->to];
|
||||
*captured_out = toP;
|
||||
|
||||
// en-passant capture
|
||||
if (m->is_enpassant){
|
||||
int cap_sq = (pos->side==WHITE) ? (m->to - 16) : (m->to + 16);
|
||||
*captured_out = pos->board[cap_sq];
|
||||
pos->board[cap_sq] = EMPTY;
|
||||
}
|
||||
|
||||
// move piece
|
||||
pos->board[m->to] = fromP;
|
||||
pos->board[m->from] = EMPTY;
|
||||
|
||||
// promotion
|
||||
if (m->promo){ pos->board[m->to] = (Piece)m->promo; }
|
||||
|
||||
// castling rook move
|
||||
if (m->is_castle){
|
||||
if (fromP==WK && m->to==0x06){ pos->board[0x05]=WR; pos->board[0x07]=EMPTY; }
|
||||
else if (fromP==WK && m->to==0x02){ pos->board[0x03]=WR; pos->board[0x00]=EMPTY; }
|
||||
else if (fromP==BK && m->to==0x76){ pos->board[0x75]=BR; pos->board[0x77]=EMPTY; }
|
||||
else if (fromP==BK && m->to==0x72){ pos->board[0x73]=BR; pos->board[0x70]=EMPTY; }
|
||||
}
|
||||
|
||||
// update castling rights conservatively
|
||||
if (fromP==WK){ pos->castle &= ~(1<<0); pos->castle &= ~(1<<1); }
|
||||
if (fromP==BK){ pos->castle &= ~(1<<2); pos->castle &= ~(1<<3); }
|
||||
if (m->from==0x00 || m->to==0x00) pos->castle &= ~(1<<1);
|
||||
if (m->from==0x07 || m->to==0x07) pos->castle &= ~(1<<0);
|
||||
if (m->from==0x70 || m->to==0x70) pos->castle &= ~(1<<3);
|
||||
if (m->from==0x77 || m->to==0x77) pos->castle &= ~(1<<2);
|
||||
|
||||
// en-passant square
|
||||
pos->ep_square = -1;
|
||||
if (fromP==WP && (m->to - m->from)==32) pos->ep_square = m->from + 16;
|
||||
if (fromP==BP && (m->from - m->to)==32) pos->ep_square = m->from - 16;
|
||||
|
||||
// halfmove clock
|
||||
if (fromP==WP || fromP==BP || m->is_capture) pos->halfmove_clock = 0; else pos->halfmove_clock++;
|
||||
|
||||
// side to move
|
||||
pos->side = (pos->side==WHITE)?BLACK:WHITE;
|
||||
if (pos->side==WHITE) pos->fullmove_number++;
|
||||
}
|
||||
|
||||
void unmake_move(Position *pos, const Move *m, Piece captured){
|
||||
pos->side = (pos->side==WHITE)?BLACK:WHITE;
|
||||
if (pos->side==BLACK) pos->fullmove_number--;
|
||||
|
||||
Piece moved = pos->board[m->to];
|
||||
|
||||
// undo castling rook move
|
||||
if (m->is_castle){
|
||||
if (moved==WK && m->to==0x06){ pos->board[0x07]=WR; pos->board[0x05]=EMPTY; }
|
||||
else if (moved==WK && m->to==0x02){ pos->board[0x00]=WR; pos->board[0x03]=EMPTY; }
|
||||
else if (moved==BK && m->to==0x76){ pos->board[0x77]=BR; pos->board[0x75]=EMPTY; }
|
||||
else if (moved==BK && m->to==0x72){ pos->board[0x70]=BR; pos->board[0x73]=EMPTY; }
|
||||
}
|
||||
|
||||
// undo promotion
|
||||
if (m->promo){ moved = (pos->side==WHITE)?WP:BP; }
|
||||
|
||||
pos->board[m->from] = moved;
|
||||
if (m->is_enpassant){
|
||||
pos->board[m->to] = EMPTY;
|
||||
int cap_sq = (pos->side==WHITE) ? (m->to - 16) : (m->to + 16);
|
||||
pos->board[cap_sq] = captured;
|
||||
} else {
|
||||
pos->board[m->to] = captured;
|
||||
}
|
||||
|
||||
// Note: We do not restore previous castle/ep/halfmove here (for perft driver we will handle state by copying Position before make_move)
|
||||
// For correctness in deeper engine, we’d need a move stack with state; perft here uses position copies for make/unmake.
|
||||
// To keep unmake consistent for our usage (make->unmake on a copy), we keep simple.
|
||||
}
|
||||
|
||||
int square_from_algebraic(const char *uci4, int is_from){
|
||||
// uci like e2e4 or e7e8q
|
||||
if (!uci4 || strlen(uci4) < 4) return -1;
|
||||
int f = uci4[is_from?0:2] - 'a';
|
||||
int r = uci4[is_from?1:3] - '1';
|
||||
if (f<0||f>7||r<0||r>7) return -1;
|
||||
return (r<<4)|f;
|
||||
}
|
||||
|
||||
int move_from_uci(const Position *pos, const char *uci, Move *out){
|
||||
int from = square_from_algebraic(uci, 1);
|
||||
int to = square_from_algebraic(uci, 0);
|
||||
if (from<0||to<0) return 0;
|
||||
char promo = 0; if (strlen(uci)>=5) promo = uci[4];
|
||||
Move moves[256];
|
||||
int n = gen_moves(pos, moves, 256, 0);
|
||||
for (int i=0;i<n;i++){
|
||||
if (moves[i].from==from && moves[i].to==to){
|
||||
if (moves[i].promo){
|
||||
// map promo char
|
||||
Piece pp = 0;
|
||||
if (promo=='q' || promo=='Q') pp = (pos->side==WHITE)?WQ:BQ;
|
||||
else if (promo=='r' || promo=='R') pp = (pos->side==WHITE)?WR:BR;
|
||||
else if (promo=='b' || promo=='B') pp = (pos->side==WHITE)?WB:BB;
|
||||
else if (promo=='n' || promo=='N') pp = (pos->side==WHITE)?WN:BN;
|
||||
if (pp && pp==moves[i].promo){ *out = moves[i]; return 1; }
|
||||
} else {
|
||||
if (!promo){ *out = moves[i]; return 1; }
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
50
C/lichess_random_engine/movegen.h
Normal file
50
C/lichess_random_engine/movegen.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef MOVEGEN_H
|
||||
#define MOVEGEN_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// 0x88 board representation
|
||||
#define BOARD_SIZE 128
|
||||
|
||||
typedef enum { WHITE = 0, BLACK = 1 } Color;
|
||||
|
||||
typedef enum {
|
||||
EMPTY = 0,
|
||||
WP = 1, WN, WB, WR, WQ, WK,
|
||||
BP = 7, BN, BB, BR, BQ, BK
|
||||
} Piece;
|
||||
|
||||
typedef struct {
|
||||
// from and to squares in 0x88 (0..127), promotion piece in Piece enum or 0
|
||||
uint8_t from, to;
|
||||
uint8_t promo; // 0 if none
|
||||
uint8_t is_capture; // 1 if capture
|
||||
uint8_t is_enpassant; // 1 if en-passant capture
|
||||
uint8_t is_castle; // 1 if castle
|
||||
} Move;
|
||||
|
||||
typedef struct {
|
||||
Piece board[BOARD_SIZE];
|
||||
Color side;
|
||||
// Castling rights: bit 0 white king-side, 1 white queen-side, 2 black king-side, 3 black queen-side
|
||||
uint8_t castle;
|
||||
int8_t ep_square; // -1 if none, else 0x88 square index
|
||||
int halfmove_clock;
|
||||
int fullmove_number;
|
||||
} Position;
|
||||
|
||||
// Parsing and utilities
|
||||
int parse_fen(Position *pos, const char *fen);
|
||||
void set_startpos(Position *pos);
|
||||
int square_from_algebraic(const char *uci4, int is_from);
|
||||
int move_from_uci(const Position *pos, const char *uci, Move *out);
|
||||
void make_move(Position *pos, const Move *m, Piece *captured_out);
|
||||
void unmake_move(Position *pos, const Move *m, Piece captured);
|
||||
int in_check(const Position *pos, Color side);
|
||||
|
||||
// Move generation
|
||||
// Generates all pseudo-legal moves into moves[], returns count. If captures_only!=0, only captures (incl. ep) are generated
|
||||
int gen_moves(const Position *pos, Move *moves, int max_moves, int captures_only);
|
||||
int gen_moves_pseudo(const Position *pos, Move *moves, int max_moves, int captures_only);
|
||||
|
||||
#endif // MOVEGEN_H
|
||||
BIN
C/lichess_random_engine/perft
Executable file
BIN
C/lichess_random_engine/perft
Executable file
Binary file not shown.
72
C/lichess_random_engine/perft.c
Normal file
72
C/lichess_random_engine/perft.c
Normal file
@ -0,0 +1,72 @@
|
||||
#include "movegen.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static unsigned long long perft(Position pos, int depth){
|
||||
if (depth==0) return 1ULL;
|
||||
Move moves[256];
|
||||
unsigned long long nodes = 0ULL;
|
||||
int n = gen_moves(&pos, moves, 256, 0);
|
||||
for (int i=0;i<n;i++){
|
||||
Position child = pos;
|
||||
Piece cap=EMPTY;
|
||||
make_move(&child, &moves[i], &cap);
|
||||
nodes += perft(child, depth-1);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
static void uci_from_move(const Move *m, char *buf){
|
||||
int ff = (m->from & 7), fr = (m->from >> 4);
|
||||
int tf = (m->to & 7), tr = (m->to >> 4);
|
||||
buf[0] = 'a' + ff; buf[1] = '1' + fr; buf[2] = 'a' + tf; buf[3] = '1' + tr; int i=4;
|
||||
if (m->promo){ char pc='q'; switch(m->promo){ case WQ: case BQ: pc='q'; break; case WR: case BR: pc='r'; break; case WB: case BB: pc='b'; break; case WN: case BN: pc='n'; break; default: pc='q'; }
|
||||
buf[i++]=pc; }
|
||||
buf[i]=0;
|
||||
}
|
||||
|
||||
static void run_case(const char *fen, int depth, unsigned long long expected){
|
||||
Position p; if (!parse_fen(&p, fen)){ fprintf(stderr, "Bad FEN: %s\n", fen); return; }
|
||||
unsigned long long n = perft(p, depth);
|
||||
printf("perft(%d) = %llu %s\n", depth, n, (expected? (n==expected?"OK":"MISMATCH"):""));
|
||||
}
|
||||
|
||||
int main(int argc, char**argv){
|
||||
if (argc>=3){
|
||||
const char *fen = argv[1];
|
||||
int depth = atoi(argv[2]);
|
||||
Position p; if (!parse_fen(&p, fen)){ fprintf(stderr, "Bad FEN input\n"); return 2; }
|
||||
if (argc>=4 && strcmp(argv[3], "--divide")==0){
|
||||
Move moves[256]; int n = gen_moves(&p, moves, 256, 0);
|
||||
unsigned long long total=0ULL;
|
||||
for (int i=0;i<n;i++){
|
||||
Position c = p; Piece cap=EMPTY; make_move(&c, &moves[i], &cap);
|
||||
unsigned long long sub = perft(c, depth-1);
|
||||
char u[8]; uci_from_move(&moves[i], u);
|
||||
printf("%s: %llu\n", u, sub);
|
||||
total += sub;
|
||||
}
|
||||
printf("Total: %llu\n", total);
|
||||
} else if (argc>=4 && strcmp(argv[3], "--divide-pseudo")==0){
|
||||
Move moves[256]; int n = gen_moves_pseudo(&p, moves, 256, 0);
|
||||
for (int i=0;i<n;i++){ char u[8]; uci_from_move(&moves[i], u); printf("%s\n", u); }
|
||||
printf("Total pseudo: %d\n", n);
|
||||
} else {
|
||||
unsigned long long n = perft(p, depth);
|
||||
printf("%llu\n", n);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Some well-known positions (depth limited to be fast). Expected nodes are standard perft values.
|
||||
// Start position
|
||||
run_case("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 1, 20ULL);
|
||||
run_case("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 2, 400ULL);
|
||||
// Kiwipete
|
||||
run_case("r3k2r/p1ppqpb1/bn2pnp1/2PpP3/1p2P3/2N2N2/PBPP1PPP/R2Q1RK1 w kq - 0 1", 1, 48ULL);
|
||||
run_case("r3k2r/p1ppqpb1/bn2pnp1/2PpP3/1p2P3/2N2N2/PBPP1PPP/R2Q1RK1 w kq - 0 1", 2, 2039ULL);
|
||||
// Simple EP
|
||||
run_case("rnbqkbnr/pppppppp/8/8/3Pp3/8/PPP1PPPP/RNBQKBNR b KQkq d3 0 1", 1, 29ULL);
|
||||
return 0;
|
||||
}
|
||||
60
C/lichess_random_engine/search.c
Normal file
60
C/lichess_random_engine/search.c
Normal file
@ -0,0 +1,60 @@
|
||||
#include "search.h"
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static int piece_value(Piece p){
|
||||
switch(p){
|
||||
case WP: case BP: return 100;
|
||||
case WN: case BN: return 320;
|
||||
case WB: case BB: return 330;
|
||||
case WR: case BR: return 500;
|
||||
case WQ: case BQ: return 900;
|
||||
case WK: case BK: return 0; // king is invaluable; PST handled later if needed
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int evaluate(const Position *pos){
|
||||
int score = 0;
|
||||
for (int sq=0; sq<BOARD_SIZE; ++sq){
|
||||
if ((sq & 0x88)) { sq = (sq|7); continue; }
|
||||
Piece p = pos->board[sq];
|
||||
if (p==EMPTY) continue;
|
||||
int v = piece_value(p);
|
||||
if (p>=WP && p<=WK) score += v; else if (p>=BP && p<=BK) score -= v;
|
||||
}
|
||||
// Score from side-to-move perspective
|
||||
return (pos->side==WHITE)? score : -score;
|
||||
}
|
||||
|
||||
int alphabeta(Position pos, int depth, int alpha, int beta, int *pv_from, int *pv_to){
|
||||
if (depth<=0){
|
||||
return evaluate(&pos);
|
||||
}
|
||||
Move moves[256];
|
||||
int n = gen_moves(&pos, moves, 256, 0);
|
||||
if (n==0){
|
||||
// Checkmate or stalemate
|
||||
if (in_check(&pos, pos.side)) return -30000 + (10 - depth); // checkmated
|
||||
return 0; // stalemate
|
||||
}
|
||||
|
||||
int best_score = INT_MIN/2;
|
||||
int best_from = -1, best_to = -1;
|
||||
for (int i=0;i<n;i++){
|
||||
Position child = pos;
|
||||
Piece cap=EMPTY;
|
||||
make_move(&child, &moves[i], &cap);
|
||||
int score = -alphabeta(child, depth-1, -beta, -alpha, NULL, NULL);
|
||||
if (score > best_score){
|
||||
best_score = score;
|
||||
best_from = moves[i].from;
|
||||
best_to = moves[i].to;
|
||||
}
|
||||
if (best_score > alpha) alpha = best_score;
|
||||
if (alpha >= beta) break; // beta cutoff
|
||||
}
|
||||
if (pv_from) *pv_from = best_from;
|
||||
if (pv_to) *pv_to = best_to;
|
||||
return best_score;
|
||||
}
|
||||
17
C/lichess_random_engine/search.h
Normal file
17
C/lichess_random_engine/search.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef SEARCH_H
|
||||
#define SEARCH_H
|
||||
|
||||
#include "movegen.h"
|
||||
|
||||
typedef struct {
|
||||
int depth;
|
||||
int nodes;
|
||||
} SearchLimits;
|
||||
|
||||
// Evaluate position in centipawns from the side-to-move perspective.
|
||||
int evaluate(const Position *pos);
|
||||
|
||||
// Negamax alpha-beta returning score in centipawns from side-to-move perspective.
|
||||
int alphabeta(Position pos, int depth, int alpha, int beta, int *pv_from, int *pv_to);
|
||||
|
||||
#endif // SEARCH_H
|
||||
4
PYTHON/.gitignore
vendored
4
PYTHON/.gitignore
vendored
@ -213,4 +213,6 @@ marimo/_lsp/
|
||||
__marimo__/
|
||||
|
||||
# Streamlit
|
||||
.streamlit/secrets.toml
|
||||
.streamlit/secrets.toml
|
||||
|
||||
*lichess_db_puzzle.csv*
|
||||
@ -1 +1 @@
|
||||
39
|
||||
43
|
||||
Loading…
Reference in New Issue
Block a user