M README.md => README.md +2 -0
@@ 385,6 385,8 @@ chessboard displaying code in [PolyGlot](http://hgm.nubati.net/book_format.html)
## ?
+- Add 50-moves detection and 3-position repetition detection to the play
+ subcommand.
- upgrade `once_cell` crate from `1.18` to `1.19`.
- upgrade `rayon` crate from `1.7` to `1.8`.
- upgrade `zstd` crate from `0.12` to `0.13`.
M src/random.rs => src/random.rs +46 -10
@@ 6,7 6,7 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
-use std::sync::Arc;
+use std::{collections::HashMap, sync::Arc};
use rand::{seq::SliceRandom, SeedableRng};
use rand_xorshift::XorShiftRng;
@@ 35,24 35,41 @@ pub enum MoveSelection {
/// # Returns
///
/// Returns a `shakmaty::Outcome` indicating the outcome of the random game, such as a win, draw, or loss.
-pub fn play_random_game(position: &mut Chess, initial_move: &Move) -> shakmaty::Outcome {
+pub fn play_random_game(position: &mut Chess, initial_move: &Move) -> Outcome {
+ // Set HashMap for 3-position detection.
+ let mut rep = HashMap::<u64, u8>::new();
+ rep.insert(zobrist_hash(position), 1);
+
// Play the initial move.
position.play_unchecked(initial_move);
// Play the random game
let mut rng = XorShiftRng::from_entropy();
- while !position.is_game_over() {
- let legal_moves: Vec<Move> = position.legal_moves().as_slice().to_vec();
+ loop {
+ match position.outcome() {
+ Some(outcome) => return outcome,
+ None => {
+ // Step 1: Check 50-move rule.
+ if position.halfmoves() >= 50 {
+ return Outcome::Draw;
+ }
- if legal_moves.is_empty() {
- break;
+ // Step 2: Repetition detection.
+ let key = zobrist_hash(position);
+ let val = rep.entry(key).and_modify(|v| *v += 1).or_insert(1);
+ if *val >= 3 {
+ return Outcome::Draw;
+ }
+
+ // Fall through, the game is not over.
+ }
}
+ // Step 1: Play the random move.
+ let legal_moves: Vec<Move> = position.legal_moves().as_slice().to_vec();
let random_move = legal_moves.choose(&mut rng).unwrap();
position.play_unchecked(random_move);
}
-
- position.outcome().expect("game outcome")
}
/// Plays a random game using two `PolyGlotBook` instances, switching between them on each move.
@@ 82,6 99,10 @@ pub fn play_random_book_game(
let mut book2_miss = false;
let mut rng = XorShiftRng::from_entropy();
+ // Set HashMap for 3-position detection.
+ let mut rep = HashMap::<u64, u8>::new();
+ rep.insert(zobrist_hash(position), 1);
+
loop {
let book = if is_book1_turn { &book1 } else { &book2 };
let move_ = if (is_book1_turn && !book1_miss) || (!is_book1_turn && !book2_miss) {
@@ 160,8 181,23 @@ pub fn play_random_book_game(
pos.play_unchecked(&move_);
- if let Some(outcome) = pos.outcome() {
- return Ok(outcome);
+ match position.outcome() {
+ Some(outcome) => return Ok(outcome),
+ None => {
+ // Step 1: Check 50-move rule.
+ if position.halfmoves() >= 50 {
+ return Ok(Outcome::Draw);
+ }
+
+ // Step 2: Repetition detection.
+ let key = zobrist_hash(position);
+ let val = rep.entry(key).and_modify(|v| *v += 1).or_insert(1);
+ if *val >= 3 {
+ return Ok(Outcome::Draw);
+ }
+
+ // Fall through, the game is not over.
+ }
}
is_book1_turn = !is_book1_turn;
M src/system.rs => src/system.rs +0 -1
@@ 55,7 55,6 @@ impl fmt::Display for EditTempfileError {
tr!("Editor error during edit: {}", format!("{err:?}"))
)
}
-
}
}
}