From 7ec8a66a4e71079895b4a600c974f31eb99d0dd1 Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Wed, 26 Feb 2025 23:45:58 -0500 Subject: [PATCH] switch coordinates to global 'Coord' type --- src/agent.rs | 10 ++++----- src/complexagent.rs | 4 ++-- src/logic/board_value.rs | 2 +- src/logic/future_moves.rs | 10 ++++----- src/logic/move.rs | 6 ++--- src/repr/bitboard.rs | 15 ++++++++----- src/repr/board.rs | 47 +++++++++++++++++++++++---------------- src/repr/chains.rs | 13 ++++++----- src/repr/misc.rs | 4 ++-- src/repr/mod.rs | 2 +- 10 files changed, 63 insertions(+), 50 deletions(-) diff --git a/src/agent.rs b/src/agent.rs index 29a8f82..a0a8a06 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -1,4 +1,4 @@ -use crate::repr::{Board, Piece}; +use crate::repr::{Board, Coord, Piece}; use rand::prelude::*; use std::io; use std::io::prelude::*; @@ -6,7 +6,7 @@ use std::io::prelude::*; #[allow(dead_code)] pub trait Agent { /// Returns the move of an [`Agent`] - fn next_move(&mut self, board: &Board) -> Option<(usize, usize)>; + fn next_move(&mut self, board: &Board) -> Option<(Coord, Coord)>; /// Returns the name of the [`Agent`] fn name(&self) -> &'static str; /// Returns the color the [`Agent`] is playing @@ -27,7 +27,7 @@ impl ManualAgent { #[allow(dead_code)] impl Agent for ManualAgent { - fn next_move(&mut self, board: &Board) -> Option<(usize, usize)> { + fn next_move(&mut self, board: &Board) -> Option<(Coord, Coord)> { let stdin = io::stdin(); let mut input = String::new(); println!("Your turn! ('Skip' to skip)"); @@ -44,7 +44,7 @@ impl Agent for ManualAgent { .map(str::parse) .map(Result::ok) .collect::>>() - .and_then(|x| -> Option<[usize; 2]> { x.try_into().ok() }) + .and_then(|x| -> Option<[Coord; 2]> { x.try_into().ok() }) .map(|x| (x[0], x[1])); if let Some(got) = got { @@ -73,7 +73,7 @@ pub struct RandomAgent { #[allow(dead_code)] impl Agent for RandomAgent { - fn next_move(&mut self, board: &Board) -> Option<(usize, usize)> { + fn next_move(&mut self, board: &Board) -> Option<(Coord, Coord)> { board.possible_moves(self.color).choose(&mut rand::rng()) } diff --git a/src/complexagent.rs b/src/complexagent.rs index 4774107..2759aa4 100644 --- a/src/complexagent.rs +++ b/src/complexagent.rs @@ -1,7 +1,7 @@ use crate::{ agent::Agent, logic::{FutureMoveConfig, FutureMoves}, - repr::{Board, Piece}, + repr::{Board, Coord, Piece}, }; pub struct ComplexAgent { @@ -27,7 +27,7 @@ impl ComplexAgent { } impl Agent for ComplexAgent { - fn next_move(&mut self, board: &Board) -> Option<(usize, usize)> { + fn next_move(&mut self, board: &Board) -> Option<(Coord, Coord)> { self.future_moves.update_from_board(board); println!("# of moves stored: {}", self.future_moves.arena_len()); diff --git a/src/logic/board_value.rs b/src/logic/board_value.rs index 0e60169..61b82e7 100644 --- a/src/logic/board_value.rs +++ b/src/logic/board_value.rs @@ -39,7 +39,7 @@ impl BoardValueMap { ]; for (i, j) in Board::all_positions() { - map.set(i, j, POSITION_VALUES[i][j]) + map.set(i, j, POSITION_VALUES[i as usize][j as usize]) } Self(map) diff --git a/src/logic/future_moves.rs b/src/logic/future_moves.rs index 2102f67..e1ad42c 100644 --- a/src/logic/future_moves.rs +++ b/src/logic/future_moves.rs @@ -1,6 +1,6 @@ use crate::{ logic::r#move::Move, - repr::{Board, Piece, Winner}, + repr::{Board, Coord, Piece, Winner}, }; use indicatif::{ProgressIterator, ProgressStyle}; use std::{collections::HashMap, hash::BuildHasherDefault}; @@ -216,7 +216,7 @@ impl FutureMoves { } /// Return the best move which is a child of `self.current_root` - pub fn best_move(&self) -> Option<(usize, usize)> { + pub fn best_move(&self) -> Option<(Coord, Coord)> { self.current_root .and_then(|x| { self.arena[x] @@ -267,7 +267,7 @@ impl FutureMoves { /// Update the root based on the coordinate of the move /// Returns a boolean, `true` if the operation was successful, false if not #[must_use = "You must check if the root was properly set"] - pub fn update_root_coord(&mut self, i: usize, j: usize) -> bool { + pub fn update_root_coord(&mut self, i: Coord, j: Coord) -> bool { // check to make sure current_root is some so we dont // have to do that in the iterator if self.current_root.is_none() { @@ -493,7 +493,7 @@ mod tests { // dummy (2) futm.arena.push(Move::new( - Some((1234, 1234)), + Some((123, 123)), Board::new(), Piece::White, Piece::Black, @@ -517,7 +517,7 @@ mod tests { assert_ne!( futm.arena[2].coord, - Some((1234, 1234)), + Some((123, 123)), "dummy value still exists" ); } diff --git a/src/logic/move.rs b/src/logic/move.rs index 006cd56..27c9f2d 100644 --- a/src/logic/move.rs +++ b/src/logic/move.rs @@ -1,11 +1,11 @@ use super::board_value::BoardValueMap; -use crate::repr::{Board, Piece, Winner}; +use crate::repr::{Board, Coord, Piece, Winner}; use lazy_static::lazy_static; #[derive(Clone, Debug)] pub struct Move { /// Coordinates (i, j) of the move (if it exists) - pub coord: Option<(usize, usize)>, + pub coord: Option<(Coord, Coord)>, /// [`Board`] state after move is made pub board: Board, @@ -41,7 +41,7 @@ lazy_static! { impl Move { pub fn new( - coord: Option<(usize, usize)>, + coord: Option<(Coord, Coord)>, board: Board, color: Piece, agent_color: Piece, diff --git a/src/repr/bitboard.rs b/src/repr/bitboard.rs index 08c1a00..4a922f1 100644 --- a/src/repr/bitboard.rs +++ b/src/repr/bitboard.rs @@ -1,4 +1,7 @@ -use super::{board::Board, misc::get_index}; +use super::{ + board::{Board, Coord}, + misc::get_index, +}; use const_fn::const_fn; use static_assertions::const_assert; @@ -24,7 +27,7 @@ pub type BitBoardInner = u64; pub struct BitBoard(BitBoardInner); // BitBoard should be big enough to fit all points on the board -const_assert!(std::mem::size_of::() * 8 >= Board::BOARD_AREA); +const_assert!(std::mem::size_of::() * 8 >= Board::BOARD_AREA as usize); impl BitBoard { #[cfg(feature = "bitvec")] @@ -40,13 +43,13 @@ impl BitBoard { } #[const_fn(cfg(not(feature = "bitvec")))] - pub const fn get(&self, row: usize, col: usize) -> bool { - self.get_by_index(get_index(row, col)) + pub const fn get(&self, row: Coord, col: Coord) -> bool { + self.get_by_index(get_index(row, col) as usize) } #[const_fn(cfg(not(feature = "bitvec")))] - pub const fn set(&mut self, row: usize, col: usize, value: bool) { - self.set_by_index(get_index(row, col), value); + pub const fn set(&mut self, row: Coord, col: Coord, value: bool) { + self.set_by_index(get_index(row, col) as usize, value); } #[cfg(not(feature = "bitvec"))] diff --git a/src/repr/board.rs b/src/repr/board.rs index f5917c5..e224170 100644 --- a/src/repr/board.rs +++ b/src/repr/board.rs @@ -5,8 +5,15 @@ use super::{ }; use const_fn::const_fn; use lazy_static::lazy_static; +use static_assertions::const_assert; use std::{cmp::Ordering, fmt}; +// PERF! having `Coord` be of type `u8` (instead of usize) increases +// performance by about 1-2% overall +pub type Coord = u8; + +const_assert!(Board::BOARD_SIZE_RAW <= Coord::MAX as usize); + #[derive(PartialEq, Eq, Copy, Clone, Debug)] pub enum Winner { Player(Piece), @@ -32,7 +39,7 @@ pub struct Board { impl fmt::Display for Board { #[allow(clippy::repeat_once)] // clippy gets mad about when PADDING == 1 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let horiz_sep_line = "-".repeat(Self::BOARD_SIZE * 2 + 1); + let horiz_sep_line = "-".repeat((Self::BOARD_SIZE * 2 + 1) as usize); // basically calculates the # of digits BOARD_SIZE needs const PADDING: usize = (Board::BOARD_SIZE - 1).ilog10() as usize + 1; @@ -82,10 +89,11 @@ impl fmt::Debug for Board { } impl Board { - pub const BOARD_SIZE: usize = 8; + const BOARD_SIZE_RAW: usize = 8; + pub const BOARD_SIZE: Coord = Self::BOARD_SIZE_RAW as Coord; /// Area of the board - pub const BOARD_AREA: usize = Self::BOARD_SIZE.pow(2); + pub const BOARD_AREA: Coord = Self::BOARD_SIZE.pow(2); /// Create a new empty board #[allow(clippy::new_without_default)] @@ -119,16 +127,17 @@ impl Board { } /// Provides an iterator of all possible positions on the board - pub fn all_positions() -> impl Iterator { - (0..Self::BOARD_SIZE).flat_map(|i| (0..Self::BOARD_SIZE).map(move |j| (i, j))) + pub fn all_positions() -> impl Iterator { + (0..Self::BOARD_SIZE) + .flat_map(|i| (0..Self::BOARD_SIZE).map(move |j| (i as Coord, j as Coord))) } /// Returns an iterator of all possible moves a `color` can make - pub fn possible_moves(&self, color: Piece) -> impl Iterator + use<'_> { + pub fn possible_moves(&self, color: Piece) -> impl Iterator + use<'_> { Self::all_positions().filter(move |&(i, j)| self.would_prop(i, j, color)) } - pub fn sides() -> impl Iterator { + pub fn sides() -> impl Iterator { (0..Self::BOARD_SIZE) .map(|i| (i, Self::BOARD_SIZE - 1)) .chain((0..Self::BOARD_SIZE).map(|i| (i, 0))) @@ -136,7 +145,7 @@ impl Board { .chain((0..Self::BOARD_SIZE).map(|j| (0, j))) } - pub fn corners() -> impl Iterator { + pub fn corners() -> impl Iterator { [0, Self::BOARD_SIZE - 1] .into_iter() .flat_map(|i| [0, Self::BOARD_SIZE - 1].into_iter().map(move |j| (i, j))) @@ -159,13 +168,13 @@ impl Board { } #[const_fn(cfg(not(feature = "bitvec")))] - pub const fn get_piece(&self, i: usize, j: usize, color: Piece) -> bool { + pub const fn get_piece(&self, i: Coord, j: Coord, color: Piece) -> bool { self.board(color).get(i, j) } /// Returns the color of a place on the [`Board`] at a position #[const_fn(cfg(not(feature = "bitvec")))] - pub const fn get(&self, i: usize, j: usize) -> Option { + pub const fn get(&self, i: Coord, j: Coord) -> Option { if self.get_piece(i, j, Piece::White) { Some(Piece::White) } else if self.get_piece(i, j, Piece::Black) { @@ -177,20 +186,20 @@ impl Board { /// Place a piece without checking for propegation of validity #[const_fn(cfg(not(feature = "bitvec")))] - const fn place_unchecked(&mut self, i: usize, j: usize, piece: Piece) { + const fn place_unchecked(&mut self, i: Coord, j: Coord, piece: Piece) { self.board_mut(piece).set(i, j, true); self.board_mut(piece.flip()).set(i, j, false); } #[const_fn(cfg(not(feature = "bitvec")))] - const fn delete(&mut self, i: usize, j: usize) { + const fn delete(&mut self, i: Coord, j: Coord) { self.board_mut(Piece::White).set(i, j, false); self.board_mut(Piece::Black).set(i, j, false); } /// Return a modified [`Board`] with the piece placed at a position /// Returns None if the move was invalid - pub fn what_if(&self, i: usize, j: usize, piece: Piece) -> Result { + pub fn what_if(&self, i: Coord, j: Coord, piece: Piece) -> Result { // extract check here to avoid copy if self.get(i, j).is_some() { return Err("position is occupied"); @@ -201,11 +210,11 @@ impl Board { } /// Returns a bool which represents whether or not a move would propegate and be valid - pub fn would_prop(&self, i: usize, j: usize, piece: Piece) -> bool { + pub fn would_prop(&self, i: Coord, j: Coord, piece: Piece) -> bool { self.get(i, j).is_none() && self.propegate_from_dry(i, j, piece).next().is_some() } - pub fn place(&mut self, i: usize, j: usize, piece: Piece) -> Result<(), &'static str> { + pub fn place(&mut self, i: Coord, j: Coord, piece: Piece) -> Result<(), &'static str> { if self.get(i, j).is_some() { return Err("position is occupied"); } @@ -220,7 +229,7 @@ impl Board { } /// Propegate the board and captures starting from a specific position - fn propegate_from(&mut self, i: usize, j: usize) -> usize { + fn propegate_from(&mut self, i: Coord, j: Coord) -> usize { let Some(starting_color) = self.get(i, j) else { return 0; }; @@ -247,10 +256,10 @@ impl Board { /// [`Board::place`] or [`Board::place_and_prop_unchecked`] fn propegate_from_dry( &self, - i: usize, - j: usize, + i: Coord, + j: Coord, starting_color: Piece, - ) -> impl Iterator + use<'_> { + ) -> impl Iterator + use<'_> { ADJ_LOOKUP .get(i, j) .iter() diff --git a/src/repr/chains.rs b/src/repr/chains.rs index ed67200..0e32ef1 100644 --- a/src/repr/chains.rs +++ b/src/repr/chains.rs @@ -1,4 +1,5 @@ use super::{ + board::Coord, misc::{diag_raw, get_index, split_from}, Board, }; @@ -6,7 +7,7 @@ use arrayvec::ArrayVec; use std::collections::HashSet; /// A chain of positions across the board -type Chain = ArrayVec<(usize, usize), { Board::BOARD_SIZE - 1 }>; +type Chain = ArrayVec<(Coord, Coord), { (Board::BOARD_SIZE - 1) as usize }>; /// A collection of chains (up vert, down vert, left horiz, right horiz, diagonals....) pub type ChainCollection = ArrayVec; @@ -14,7 +15,7 @@ pub type ChainCollection = ArrayVec; /// Map of all points on the board against some type T /// Used to index like so: example[i][j] /// with each coordinate -pub struct PosMap(ArrayVec); +pub struct PosMap(ArrayVec); impl PosMap { #[allow(clippy::new_without_default)] @@ -24,7 +25,7 @@ impl PosMap { )) } - pub fn get(&self, row: usize, col: usize) -> &T { + pub fn get(&self, row: Coord, col: Coord) -> &T { let index = get_index(row, col); debug_assert!( @@ -33,10 +34,10 @@ impl PosMap { index ); - unsafe { self.0.get_unchecked(index) } + unsafe { self.0.get_unchecked(index as usize) } } - pub fn set(&mut self, row: usize, col: usize, value: T) { + pub fn set(&mut self, row: Coord, col: Coord, value: T) { let index = get_index(row, col); debug_assert!( Board::BOARD_AREA + 1 >= index, @@ -44,7 +45,7 @@ impl PosMap { index ); - self.0[index] = value; + self.0[index as usize] = value; } } diff --git a/src/repr/misc.rs b/src/repr/misc.rs index f453578..f036527 100644 --- a/src/repr/misc.rs +++ b/src/repr/misc.rs @@ -1,4 +1,4 @@ -use super::Board; +use super::{board::Coord, Board}; use either::Either; use std::{iter::Rev, ops::RangeInclusive}; @@ -40,7 +40,7 @@ where } /// Convert a row and column to an index in the board -pub const fn get_index(row: usize, col: usize) -> usize { +pub const fn get_index(row: Coord, col: Coord) -> Coord { row * Board::BOARD_SIZE + col } diff --git a/src/repr/mod.rs b/src/repr/mod.rs index 377a812..dbebbdd 100644 --- a/src/repr/mod.rs +++ b/src/repr/mod.rs @@ -4,6 +4,6 @@ mod chains; mod misc; mod piece; -pub use board::{Board, Winner}; +pub use board::{Board, Coord, Winner}; pub use chains::PosMap; pub use piece::Piece;