~alip/jja

db2933d976b78ee70c7a0ef13032cb5847df0ab9 — Ali Polatel 11 months ago a9efe86
play: add 50-move and repetition detection
3 files changed, 48 insertions(+), 11 deletions(-)

M README.md
M src/random.rs
M src/system.rs
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:?}"))
                )
            }

        }
    }
}