From 36753f6ecbe526b7401e979642027afdd8f29c4a Mon Sep 17 00:00:00 2001 From: dogeystamp Date: Sun, 17 Nov 2024 10:19:05 -0500 Subject: [PATCH] refactor: random uses a rust 1.83 feature so that the const fn works better --- rust-toolchain.toml | 2 + src/hash.rs | 10 ++-- src/lib.rs | 2 +- src/random.rs | 123 +++++++++++++++++++++++--------------------- 4 files changed, 72 insertions(+), 65 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5afdd59 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "beta-2024-11-17" diff --git a/src/hash.rs b/src/hash.rs index 24d1b5b..fe898ee 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -13,22 +13,22 @@ Copyright © 2024 dogeystamp //! Zobrist hash implementation. -use crate::random::{random_arr_2d_64, random_arr_64}; +use crate::random::Pcg64Random; use crate::{ Board, CastleRights, ColPiece, Color, Square, BOARD_WIDTH, N_COLORS, N_PIECES, N_SQUARES, }; const PIECE_KEYS: [[[u64; N_SQUARES]; N_PIECES]; N_COLORS] = - [random_arr_2d_64(11), random_arr_2d_64(22)]; + [Pcg64Random::new(11).random_arr_2d_64(), Pcg64Random::new(22).random_arr_2d_64()]; // 4 bits in castle perms -> 16 keys -const CASTLE_KEYS: [u64; 16] = random_arr_64(33); +const CASTLE_KEYS: [u64; 16] = Pcg64Random::new(33).random_arr_64(); // ep can be specified by the file -const EP_KEYS: [u64; BOARD_WIDTH] = random_arr_64(44); +const EP_KEYS: [u64; BOARD_WIDTH] = Pcg64Random::new(44).random_arr_64(); // current turn -const COL_KEY: [u64; N_COLORS] = random_arr_64(55); +const COL_KEY: [u64; N_COLORS] = Pcg64Random::new(55).random_arr_64(); /// Zobrist hash state. /// diff --git a/src/lib.rs b/src/lib.rs index 25dcc89..cbdde83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ pub mod eval; pub mod fen; mod hash; pub mod movegen; -mod random; +pub mod random; pub mod search; use crate::fen::{FromFen, ToFen, START_POSITION}; diff --git a/src/random.rs b/src/random.rs index e83fe55..c4e873d 100644 --- a/src/random.rs +++ b/src/random.rs @@ -1,77 +1,82 @@ //! Rust port by dogeystamp of //! the pcg64 dxsm random number generator (https://dotat.at/@/2023-06-21-pcg64-dxsm.html) -struct Pcg64Random { +pub struct Pcg64Random { state: u128, inc: u128, } -/// Generates an array of random numbers. -/// -/// The `rng` parameter only sets the initial state. This function is deterministic and pure. -/// -/// # Returns -/// -/// The array of random numbers, plus the RNG state at the end. -const fn pcg64_dxsm(mut rng: Pcg64Random) -> ([u64; N], Pcg64Random) { - let mut ret = [0; N]; - - const MUL: u64 = 15750249268501108917; - - let mut i = 0; - while i < N { - let state: u128 = rng.state; - rng.state = state.wrapping_mul(MUL as u128).wrapping_add(rng.inc); - let mut hi: u64 = (state >> 64) as u64; - let lo: u64 = (state | 1) as u64; - hi ^= hi >> 32; - hi &= MUL; - hi ^= hi >> 48; - hi = hi.wrapping_mul(lo); - ret[i] = hi; - - i += 1; - } - - (ret, rng) -} - /// Make an RNG state "sane". const fn pcg64_seed(mut rng: Pcg64Random) -> Pcg64Random { // ensure rng.inc is odd rng.inc = (rng.inc << 1) | 1; rng.state += rng.inc; // one iteration of random - let (_, rng) = pcg64_dxsm::<1>(rng); + rng.rand(); rng } -/// Generate array of random numbers, based on a seed. -/// -/// This function is pure and deterministic, and also works at compile-time rather than at runtime. -/// -/// Example (generate 10 random numbers): -/// -///```rust -/// use crate::random::random_arr_64; -/// const ARR: [u64; 10] = random_arr_64(123456); -///``` -pub const fn random_arr_64(seed: u128) -> [u64; N] { - let rng = pcg64_seed(Pcg64Random { - // chosen by fair dice roll - state: 24437033748623976104561743679864923857, - inc: seed, - }); - pcg64_dxsm(rng).0 -} - -/// Generate 2D array of random numbers based on a seed. -pub const fn random_arr_2d_64(seed: u128) -> [[u64; N]; M] { - let mut ret = [[0; N]; M]; - let mut i = 0; - while i < M { - ret[i] = random_arr_64(seed); - i += 1; +impl Pcg64Random { + pub const fn new(seed: u128) -> Self { + pcg64_seed(Pcg64Random { + // chosen by fair dice roll + state: 24437033748623976104561743679864923857, + inc: seed, + }) + } + + /// Returns a single random number. + pub const fn rand(&mut self) -> u64 { + const MUL: u64 = 15750249268501108917; + + let state: u128 = self.state; + self.state = state.wrapping_mul(MUL as u128).wrapping_add(self.inc); + let mut hi: u64 = (state >> 64) as u64; + let lo: u64 = (state | 1) as u64; + hi ^= hi >> 32; + hi &= MUL; + hi ^= hi >> 48; + hi = hi.wrapping_mul(lo); + + hi + } + + /// Generate array of random numbers, based on a seed. + /// + /// # Returns + /// + /// A tuple with the random number array, and the RNG state afterwards so you can reuse it in later + /// calls (otherwise you'll get the same result if you're using the same seed.) + /// + /// # Example + /// + ///```rust + /// use chess_inator::random::Pcg64Random; + /// + /// // generate 3 random numbers + /// const ARR: [u64; 3] = Pcg64Random::new(123456).random_arr_64(); + /// assert_eq!(ARR, [4526545874411451611, 1124465636717751929, 12699417402402334336]) + ///``` + pub const fn random_arr_64(&mut self) -> [u64; N] { + let mut ret = [0; N]; + let mut i = 0; + while i < N { + let num = self.rand(); + ret[i] = num; + i += 1; + } + + ret + } + + /// Generate 2D array of random numbers based on a seed. + pub const fn random_arr_2d_64(&mut self) -> [[u64; N]; M] { + let mut ret = [[0; N]; M]; + let mut i = 0; + while i < M { + ret[i] = self.random_arr_64(); + i += 1; + } + ret } - ret }