Compare commits
4 Commits
5a9e17804c
...
0120c95097
Author | SHA1 | Date | |
---|---|---|---|
0120c95097 | |||
54a4cd07ac | |||
ab7ce6b6a2 | |||
2569e36a5a |
@ -1,21 +1,30 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Runs a fast-chess (https://github.com/Disservin/fastchess) tournament based
|
# Runs a fast-chess (https://github.com/Disservin/fastchess) tournament based
|
||||||
# on two branches of the chess_inator
|
# on two tags of the chess_inator (https://github.com/dogeystamp/chess_inator)
|
||||||
# (https://github.com/dogeystamp/chess_inator) engine.
|
# engine.
|
||||||
#
|
#
|
||||||
# Example usage:
|
# Example usage:
|
||||||
#
|
#
|
||||||
# cd chess_tournaments
|
# cd chess_tournaments
|
||||||
# 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
|
# 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
|
||||||
#
|
#
|
||||||
# Do not use `main` as a branch, or any other branch already checked out in
|
# You need to be in a chess_inator Git repository to run this script. Ensure
|
||||||
# another directory. You need to be in a chess_inator Git repository to run
|
# that the repository you're in is a throw-away worktree. Create one using:
|
||||||
# this script. Ensure that the repository you're in is a throw-away worktree.
|
|
||||||
# Create one using
|
|
||||||
#
|
#
|
||||||
# git worktree add ../chess_tournaments
|
# git worktree add ../chess_tournaments
|
||||||
#
|
#
|
||||||
# inside the original chess_inator repo.
|
# 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:
|
# 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
|
# curl -O https://github.com/official-stockfish/books/raw/refs/heads/master/8moves_v3.pgn.zip
|
||||||
@ -28,43 +37,45 @@
|
|||||||
# errors. The tournament automatically ends when a statistically significant
|
# errors. The tournament automatically ends when a statistically significant
|
||||||
# result is obtained.
|
# 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
|
# 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
|
# 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.
|
# importing the PGN. But Lichess will ignore it, so it's probably fine.
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
BRANCH1="$1"
|
TAG1="$1"
|
||||||
BRANCH2="$2"
|
TAG2="$2"
|
||||||
|
|
||||||
# if this line fails it's because you don't have enough arguments
|
# if this line fails it's because you don't have enough arguments
|
||||||
shift 2
|
shift 2
|
||||||
|
|
||||||
COMM1=$(git rev-parse --short "$BRANCH1")
|
COMM1=$(git rev-parse --short "$TAG1")
|
||||||
COMM2=$(git rev-parse --short "$BRANCH2")
|
COMM2=$(git rev-parse --short "$TAG2")
|
||||||
|
|
||||||
mkdir -p games
|
mkdir -p games
|
||||||
|
|
||||||
PGN=games/"$BRANCH1"__"$BRANCH2".pgn
|
PGN=games/"$TAG1"__"$TAG2".pgn
|
||||||
|
|
||||||
rm -f engine1 engine2
|
rm -f engine1 engine2
|
||||||
if [ -f "$PGN" ]; then
|
if [ -f "$PGN" ]; then
|
||||||
rm -i "$PGN"
|
rm -i "$PGN"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git switch "$BRANCH1"
|
git checkout "$TAG1"
|
||||||
cargo build --release
|
cargo build --release
|
||||||
cp target/release/chess_inator engine1
|
cp target/release/chess_inator engine1
|
||||||
|
|
||||||
git switch "$BRANCH2"
|
git checkout "$TAG2"
|
||||||
cargo build --release
|
cargo build --release
|
||||||
cp target/release/chess_inator engine2
|
cp target/release/chess_inator engine2
|
||||||
|
|
||||||
OUTPUT=$(mktemp)
|
OUTPUT=$(mktemp)
|
||||||
|
|
||||||
fastchess \
|
fastchess \
|
||||||
-engine cmd=engine1 name="c_i $BRANCH1 ($COMM1)" \
|
-engine cmd=engine1 name="c_i $TAG1 ($COMM1)" \
|
||||||
-engine cmd=engine2 name="c_i $BRANCH2 ($COMM2)" \
|
-engine cmd=engine2 name="c_i $TAG2 ($COMM2)" \
|
||||||
-pgnout file="$PGN" \
|
-pgnout file="$PGN" \
|
||||||
timeleft=true \
|
timeleft=true \
|
||||||
$@ \
|
$@ \
|
15
src/eval.rs
15
src/eval.rs
@ -499,6 +499,7 @@ mod tests {
|
|||||||
// set side to move appropriately in the fen
|
// set side to move appropriately in the fen
|
||||||
//
|
//
|
||||||
// otherwise the exchange doesn't work
|
// otherwise the exchange doesn't work
|
||||||
|
use Piece::*;
|
||||||
let test_cases = [
|
let test_cases = [
|
||||||
(
|
(
|
||||||
// fen
|
// fen
|
||||||
@ -506,12 +507,18 @@ mod tests {
|
|||||||
// square where exchange happens
|
// square where exchange happens
|
||||||
"d5",
|
"d5",
|
||||||
// expected (signed) value gain of exchange
|
// expected (signed) value gain of exchange
|
||||||
Piece::Rook.value(),
|
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,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"8/8/4b3/2kq4/2PKP3/8/8/8 w - - 0 1",
|
"r3k2r/1pq2pbp/6p1/p3p3/P5b1/2P3P1/1BN1PPBP/R2QK2R b KQkq - 0 14",
|
||||||
"d5",
|
"e2",
|
||||||
Piece::Queen.value(),
|
0,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1667,25 +1667,23 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_capture_movegen() {
|
fn test_capture_movegen() {
|
||||||
let test_cases = [(
|
let test_cases = [
|
||||||
// fen
|
(
|
||||||
"8/3q4/5N2/8/8/8/8/3K4 w - - 0 1",
|
// fen
|
||||||
// expected moves generated
|
"8/3q4/5N2/8/8/8/8/3K4 w - - 0 1",
|
||||||
"f6d7",
|
// expected moves generated
|
||||||
),
|
"f6d7",
|
||||||
(
|
),
|
||||||
"8/8/8/3pP3/2K5/8/8/8 w - d6 0 1",
|
(
|
||||||
// holy hell
|
"8/8/8/3pP3/2K5/8/8/8 w - d6 0 1",
|
||||||
"e5d6 c4d5",
|
// holy hell
|
||||||
),
|
"e5d6 c4d5",
|
||||||
(
|
),
|
||||||
"8/2q5/3K4/8/8/8/8/8 w - - 0 1",
|
("8/2q5/3K4/8/8/8/8/8 w - - 0 1", "d6c7"),
|
||||||
"d6c7",
|
(
|
||||||
),
|
"2Q5/3r2R1/2B1PN2/8/3K4/8/8/8 w - - 0 1",
|
||||||
(
|
"c6d7 e6d7 c8d7 f6d7 g7d7",
|
||||||
"2Q5/3r2R1/2B1PN2/8/3K4/8/8/8 w - - 0 1",
|
),
|
||||||
"c6d7 e6d7 c8d7 f6d7 g7d7",
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
for (fen, expected) in test_cases {
|
for (fen, expected) in test_cases {
|
||||||
|
@ -23,5 +23,5 @@ pub use crate::search::{
|
|||||||
best_line, best_move, EngineState, SearchConfig, SearchEval, TimeLimits, TranspositionTable,
|
best_line, best_move, EngineState, SearchConfig, SearchEval, TimeLimits, TranspositionTable,
|
||||||
};
|
};
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
Board, ColPiece, Color, Piece, BOARD_HEIGHT, BOARD_WIDTH, N_COLORS, N_PIECES, N_SQUARES, Square,
|
Board, ColPiece, Color, Piece, Square, BOARD_HEIGHT, BOARD_WIDTH, N_COLORS, N_PIECES, N_SQUARES,
|
||||||
};
|
};
|
||||||
|
@ -122,9 +122,8 @@ impl Default for SearchConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SearchConfig {
|
SearchConfig {
|
||||||
alpha_beta_on: true,
|
alpha_beta_on: true,
|
||||||
// try to make this even to be more conservative and avoid horizon problem
|
|
||||||
depth: 10,
|
depth: 10,
|
||||||
qdepth: 3,
|
qdepth: 4,
|
||||||
enable_trans_table: true,
|
enable_trans_table: true,
|
||||||
transposition_size: 24,
|
transposition_size: 24,
|
||||||
}
|
}
|
||||||
@ -209,13 +208,20 @@ 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.depth == 0 {
|
||||||
if mm.quiesce || board.recap_sq.is_none() {
|
if mm.quiesce {
|
||||||
// if we're done with quiescence, static eval.
|
// we hit the limit on quiescence depth
|
||||||
// if there is no capture, skip straight to static eval.
|
return (Vec::new(), SearchEval::Exact(board_eval.unwrap()));
|
||||||
let eval = board.eval() * EvalInt::from(board.turn.sign());
|
|
||||||
return (Vec::new(), SearchEval::Exact(eval));
|
|
||||||
} else {
|
} else {
|
||||||
|
// enter quiescence search
|
||||||
return minmax(
|
return minmax(
|
||||||
board,
|
board,
|
||||||
state,
|
state,
|
||||||
@ -261,11 +267,15 @@ fn minmax(board: &mut Board, state: &mut EngineState, mm: MinmaxState) -> (Vec<M
|
|||||||
mvs.sort_unstable_by_key(|mv| -mv.0);
|
mvs.sort_unstable_by_key(|mv| -mv.0);
|
||||||
|
|
||||||
let mut abs_best = SearchEval::Exact(EVAL_WORST);
|
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_move: Option<Move> = None;
|
||||||
let mut best_continuation: Vec<Move> = Vec::new();
|
let mut best_continuation: Vec<Move> = Vec::new();
|
||||||
|
|
||||||
let n_non_qmoves = mvs.len();
|
|
||||||
|
|
||||||
// determine moves that are allowed in quiescence
|
// determine moves that are allowed in quiescence
|
||||||
if mm.quiesce {
|
if mm.quiesce {
|
||||||
// use static exchange evaluation to prune moves
|
// use static exchange evaluation to prune moves
|
||||||
@ -276,7 +286,11 @@ fn minmax(board: &mut Board, state: &mut EngineState, mm: MinmaxState) -> (Vec<M
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if n_non_qmoves == 0 {
|
if mvs.is_empty() {
|
||||||
|
if mm.quiesce {
|
||||||
|
// use stand pat
|
||||||
|
return (Vec::new(), SearchEval::Exact(board_eval.unwrap()));
|
||||||
|
}
|
||||||
let is_in_check = board.is_check(board.turn);
|
let is_in_check = board.is_check(board.turn);
|
||||||
|
|
||||||
if is_in_check {
|
if is_in_check {
|
||||||
@ -285,10 +299,6 @@ fn minmax(board: &mut Board, state: &mut EngineState, mm: MinmaxState) -> (Vec<M
|
|||||||
// stalemate
|
// stalemate
|
||||||
return (Vec::new(), SearchEval::Exact(0));
|
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 {
|
for (_priority, mv) in mvs {
|
||||||
|
Loading…
Reference in New Issue
Block a user