Compare commits

..

No commits in common. "0120c950974d52a8f67762d66ae8a434a2ad755d" and "5a9e17804c45a78a5198523bb0841eea4cd73521" have entirely different histories.

5 changed files with 54 additions and 80 deletions

View File

@ -1,30 +1,21 @@
#!/bin/sh
# Runs a fast-chess (https://github.com/Disservin/fastchess) tournament based
# on two tags of the chess_inator (https://github.com/dogeystamp/chess_inator)
# engine.
# on two branches of the chess_inator
# (https://github.com/dogeystamp/chess_inator) engine.
#
# Example usage:
#
# cd chess_tournaments
# fast-chess-tag.sh quiescence no-quiescence -openings file=8moves_v3.pgn format=pgn order=random -each tc=300+0.1 -rounds 12 -repeat -concurrency 8 -recover -sprt elo0=0 elo1=10 alpha=0.05 beta=0.05
# fast-chess-branch.sh quiescence no-quiescence -openings file=8moves_v3.pgn format=pgn order=random -each tc=300+0.1 -rounds 12 -repeat -concurrency 8 -recover -sprt elo0=0 elo1=10 alpha=0.05 beta=0.05
#
# You need to be in a chess_inator Git repository to run this script. Ensure
# that the repository you're in is a throw-away worktree. Create one using:
# Do not use `main` as a branch, or any other branch already checked out in
# another directory. You need to be in a chess_inator Git repository to run
# this script. Ensure that the repository you're in is a throw-away worktree.
# Create one using
#
# git worktree add ../chess_tournaments
#
# inside the original chess_inator repo.
#
# To create a git tag at the current commit, run
#
# git tag [tag_name]
#
# or to optionally annotate the tag,
#
# git tag -a [tag_name]
#
# which will prompt for a message.
#
# Also, get an opening book from Stockfish's books:
#
# curl -O https://github.com/official-stockfish/books/raw/refs/heads/master/8moves_v3.pgn.zip
@ -37,45 +28,43 @@
# errors. The tournament automatically ends when a statistically significant
# result is obtained.
#
# LOS stands for "likelihood of superiority", LLR "log likelihood ratio".
#
# By default, a PGN file will be exported with the games played, and the
# fast-chess SPRT output will be appended. This comment may interfere with
# importing the PGN. But Lichess will ignore it, so it's probably fine.
set -e
TAG1="$1"
TAG2="$2"
BRANCH1="$1"
BRANCH2="$2"
# if this line fails it's because you don't have enough arguments
shift 2
COMM1=$(git rev-parse --short "$TAG1")
COMM2=$(git rev-parse --short "$TAG2")
COMM1=$(git rev-parse --short "$BRANCH1")
COMM2=$(git rev-parse --short "$BRANCH2")
mkdir -p games
PGN=games/"$TAG1"__"$TAG2".pgn
PGN=games/"$BRANCH1"__"$BRANCH2".pgn
rm -f engine1 engine2
if [ -f "$PGN" ]; then
rm -i "$PGN"
fi
git checkout "$TAG1"
git switch "$BRANCH1"
cargo build --release
cp target/release/chess_inator engine1
git checkout "$TAG2"
git switch "$BRANCH2"
cargo build --release
cp target/release/chess_inator engine2
OUTPUT=$(mktemp)
fastchess \
-engine cmd=engine1 name="c_i $TAG1 ($COMM1)" \
-engine cmd=engine2 name="c_i $TAG2 ($COMM2)" \
-engine cmd=engine1 name="c_i $BRANCH1 ($COMM1)" \
-engine cmd=engine2 name="c_i $BRANCH2 ($COMM2)" \
-pgnout file="$PGN" \
timeleft=true \
$@ \

View File

@ -499,7 +499,6 @@ mod tests {
// set side to move appropriately in the fen
//
// otherwise the exchange doesn't work
use Piece::*;
let test_cases = [
(
// fen
@ -507,18 +506,12 @@ mod tests {
// square where exchange happens
"d5",
// expected (signed) value gain of exchange
Rook.value(),
),
("8/8/4b3/2kq4/2PKP3/8/8/8 w - - 0 1", "d5", Queen.value()),
(
"r3k2r/1pq2pbp/6p1/p2Qpb2/1N6/2P3P1/PB2PPBP/R3K2R w KQkq - 0 14",
"e5",
0,
Piece::Rook.value(),
),
(
"r3k2r/1pq2pbp/6p1/p3p3/P5b1/2P3P1/1BN1PPBP/R2QK2R b KQkq - 0 14",
"e2",
0,
"8/8/4b3/2kq4/2PKP3/8/8/8 w - - 0 1",
"d5",
Piece::Queen.value(),
),
];

View File

@ -1667,23 +1667,25 @@ mod tests {
#[test]
fn test_capture_movegen() {
let test_cases = [
(
// fen
"8/3q4/5N2/8/8/8/8/3K4 w - - 0 1",
// expected moves generated
"f6d7",
),
(
"8/8/8/3pP3/2K5/8/8/8 w - d6 0 1",
// holy hell
"e5d6 c4d5",
),
("8/2q5/3K4/8/8/8/8/8 w - - 0 1", "d6c7"),
(
"2Q5/3r2R1/2B1PN2/8/3K4/8/8/8 w - - 0 1",
"c6d7 e6d7 c8d7 f6d7 g7d7",
),
let test_cases = [(
// fen
"8/3q4/5N2/8/8/8/8/3K4 w - - 0 1",
// expected moves generated
"f6d7",
),
(
"8/8/8/3pP3/2K5/8/8/8 w - d6 0 1",
// holy hell
"e5d6 c4d5",
),
(
"8/2q5/3K4/8/8/8/8/8 w - - 0 1",
"d6c7",
),
(
"2Q5/3r2R1/2B1PN2/8/3K4/8/8/8 w - - 0 1",
"c6d7 e6d7 c8d7 f6d7 g7d7",
),
];
for (fen, expected) in test_cases {

View File

@ -23,5 +23,5 @@ pub use crate::search::{
best_line, best_move, EngineState, SearchConfig, SearchEval, TimeLimits, TranspositionTable,
};
pub use crate::{
Board, ColPiece, Color, Piece, Square, BOARD_HEIGHT, BOARD_WIDTH, N_COLORS, N_PIECES, N_SQUARES,
Board, ColPiece, Color, Piece, BOARD_HEIGHT, BOARD_WIDTH, N_COLORS, N_PIECES, N_SQUARES, Square,
};

View File

@ -122,8 +122,9 @@ impl Default for SearchConfig {
fn default() -> Self {
SearchConfig {
alpha_beta_on: true,
// try to make this even to be more conservative and avoid horizon problem
depth: 10,
qdepth: 4,
qdepth: 3,
enable_trans_table: true,
transposition_size: 24,
}
@ -208,20 +209,13 @@ fn minmax(board: &mut Board, state: &mut EngineState, mm: MinmaxState) -> (Vec<M
}
}
// quiescence stand-pat score (only calculated if needed).
// this is where static eval goes.
let mut board_eval: Option<EvalInt> = None;
if mm.quiesce {
board_eval = Some(board.eval() * EvalInt::from(board.turn.sign()));
}
if mm.depth == 0 {
if mm.quiesce {
// we hit the limit on quiescence depth
return (Vec::new(), SearchEval::Exact(board_eval.unwrap()));
if mm.quiesce || board.recap_sq.is_none() {
// if we're done with quiescence, static eval.
// if there is no capture, skip straight to static eval.
let eval = board.eval() * EvalInt::from(board.turn.sign());
return (Vec::new(), SearchEval::Exact(eval));
} else {
// enter quiescence search
return minmax(
board,
state,
@ -267,15 +261,11 @@ fn minmax(board: &mut Board, state: &mut EngineState, mm: MinmaxState) -> (Vec<M
mvs.sort_unstable_by_key(|mv| -mv.0);
let mut abs_best = SearchEval::Exact(EVAL_WORST);
if mm.quiesce {
// stand pat
abs_best = SearchEval::Exact(board_eval.unwrap());
}
let mut best_move: Option<Move> = None;
let mut best_continuation: Vec<Move> = Vec::new();
let n_non_qmoves = mvs.len();
// determine moves that are allowed in quiescence
if mm.quiesce {
// use static exchange evaluation to prune moves
@ -286,11 +276,7 @@ fn minmax(board: &mut Board, state: &mut EngineState, mm: MinmaxState) -> (Vec<M
});
}
if mvs.is_empty() {
if mm.quiesce {
// use stand pat
return (Vec::new(), SearchEval::Exact(board_eval.unwrap()));
}
if n_non_qmoves == 0 {
let is_in_check = board.is_check(board.turn);
if is_in_check {
@ -299,6 +285,10 @@ fn minmax(board: &mut Board, state: &mut EngineState, mm: MinmaxState) -> (Vec<M
// stalemate
return (Vec::new(), SearchEval::Exact(0));
}
} else if mvs.is_empty() {
// pruned all the moves due to quiescence
let eval = board.eval() * EvalInt::from(board.turn.sign());
return (Vec::new(), SearchEval::Exact(eval));
}
for (_priority, mv) in mvs {