touchups
This commit is contained in:
parent
657bb967a0
commit
d03cdde5ad
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
@ -69,6 +75,12 @@ dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
@ -152,7 +164,9 @@ dependencies = [
|
||||
name = "othello"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"either",
|
||||
"lazy_static",
|
||||
"num",
|
||||
"rand",
|
||||
"rayon",
|
||||
|
||||
@ -8,7 +8,9 @@ edition = "2021"
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
arrayvec = "0.7.6"
|
||||
either = "1.13.0"
|
||||
lazy_static = "1.5.0"
|
||||
num = "0.4"
|
||||
rand = "0.9"
|
||||
rayon = "1.10"
|
||||
|
||||
81
src/board.rs
81
src/board.rs
@ -2,10 +2,51 @@ use crate::{
|
||||
misc::{diag_raw, split_from},
|
||||
piece::Piece,
|
||||
};
|
||||
use std::{cmp::Ordering, fmt};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use lazy_static::lazy_static;
|
||||
use std::{cmp::Ordering, collections::HashMap, fmt};
|
||||
pub const BOARD_SIZE: usize = 8;
|
||||
|
||||
/// A chain of positions across the board
|
||||
type Chain = ArrayVec<(usize, usize), BOARD_SIZE>;
|
||||
|
||||
/// A collection of chains (up vert, down vert, left horiz, right horiz, diagonals....)
|
||||
type ChainCollection = ArrayVec<Chain, 8>;
|
||||
|
||||
lazy_static! {
|
||||
/// Precompute all possible chains for each position on the board
|
||||
pub static ref ADJ_LOOKUP: HashMap<(usize, usize), ChainCollection> = {
|
||||
let mut output = HashMap::new();
|
||||
for (i, j) in Board::all_positions() {
|
||||
let (i_chain, j_chain) = (
|
||||
split_from(0..=BOARD_SIZE - 1, i),
|
||||
split_from(0..=BOARD_SIZE - 1, j),
|
||||
);
|
||||
|
||||
let mut chains: ChainCollection = ArrayVec::new_const();
|
||||
|
||||
chains.extend(
|
||||
i_chain
|
||||
.clone()
|
||||
.map(|range| range.map(move |i| (i, j)))
|
||||
.map(Iterator::collect),
|
||||
);
|
||||
chains.extend(
|
||||
j_chain
|
||||
.clone()
|
||||
.map(|range| range.map(move |j| (i, j)))
|
||||
.map(Iterator::collect),
|
||||
);
|
||||
|
||||
// handle diagonals
|
||||
chains.extend(diag_raw(i_chain, j_chain).map(Iterator::collect));
|
||||
output.insert((i, j), chains);
|
||||
}
|
||||
|
||||
output
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Board {
|
||||
board: [[Option<Piece>; BOARD_SIZE]; BOARD_SIZE],
|
||||
@ -68,10 +109,12 @@ impl Board {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn all_positions() -> impl Iterator<Item = (usize, usize)> {
|
||||
(0..BOARD_SIZE).flat_map(|i| (0..BOARD_SIZE).map(move |j| (i, j)))
|
||||
}
|
||||
|
||||
pub fn possible_moves(&self, color: Piece) -> impl Iterator<Item = (usize, usize)> + use<'_> {
|
||||
(0..BOARD_SIZE)
|
||||
.flat_map(|i| (0..BOARD_SIZE).map(move |j| (i, j)))
|
||||
.filter(move |(i, j)| self.would_prop(*i, *j, color))
|
||||
Self::all_positions().filter(move |(i, j)| self.would_prop(*i, *j, color))
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to a place on the [`Board`]
|
||||
@ -138,29 +181,7 @@ impl Board {
|
||||
// NOTE! got it down to 24.86% (61.1% decrease) with allocator optimizations
|
||||
// IDEAS: early-exit from each chain so we don't have to call `diag` (which allocs a lot and uses a lot of cycles)
|
||||
fn propegate_from_dry(&self, i: usize, j: usize, starting_color: Piece) -> Vec<(usize, usize)> {
|
||||
// Create all chains from the piece being propegated from in `i` and `j` coordinates
|
||||
let (i_chain, j_chain) = (
|
||||
split_from(0, BOARD_SIZE - 1, i),
|
||||
split_from(0, BOARD_SIZE - 1, j),
|
||||
);
|
||||
|
||||
let mut chains: Vec<Vec<(usize, usize)>> = Vec::with_capacity(8);
|
||||
|
||||
chains.extend(
|
||||
i_chain
|
||||
.clone()
|
||||
.map(|range| range.map(move |i| (i, j)))
|
||||
.map(Iterator::collect),
|
||||
);
|
||||
chains.extend(
|
||||
j_chain
|
||||
.clone()
|
||||
.map(|range| range.map(move |j| (i, j)))
|
||||
.map(Iterator::collect),
|
||||
);
|
||||
|
||||
// handle diagonals
|
||||
chains.extend(diag_raw(i_chain, j_chain).map(Iterator::collect));
|
||||
let chains = ADJ_LOOKUP.get(&(i, j)).unwrap();
|
||||
|
||||
// Longest chain is (BOARD_SIZE - 2) as there needs to be the two pieces containing it
|
||||
let mut fill: Vec<(usize, usize)> = Vec::with_capacity((BOARD_SIZE - 2) * chains.len());
|
||||
@ -176,9 +197,7 @@ impl Board {
|
||||
// get history of this chain
|
||||
if let Some(history) = chain.get(..chain_length) {
|
||||
// fill all opposite colors with this color
|
||||
for &(i_o, j_o) in history {
|
||||
fill.push((i_o, j_o));
|
||||
}
|
||||
fill.extend(history);
|
||||
}
|
||||
|
||||
// either the other pieces were replaced, or this was an invalid chain,
|
||||
|
||||
@ -11,8 +11,8 @@ mod piece;
|
||||
fn main() {
|
||||
let player1 = complexagent::ComplexAgent::new(Piece::Black);
|
||||
// let player2 = complexagent::ComplexAgent::new(Piece::White);
|
||||
let player2 = agent::ManualAgent::new(Piece::White);
|
||||
// let player2 = agent::RandomAgent::new(Piece::White);
|
||||
// let player2 = agent::ManualAgent::new(Piece::White);
|
||||
let player2 = agent::RandomAgent::new(Piece::White);
|
||||
let mut game = Game::new(Box::new(player1), Box::new(player2));
|
||||
game.game_loop();
|
||||
}
|
||||
|
||||
55
src/misc.rs
55
src/misc.rs
@ -1,48 +1,31 @@
|
||||
use either::Either;
|
||||
use std::{iter::Rev, ops::RangeInclusive};
|
||||
|
||||
pub fn split_from<T>(min: T, max: T, x: T) -> [impl Iterator<Item = T> + Clone; 2]
|
||||
pub fn split_from<T>(range: RangeInclusive<T>, x: T) -> [impl Iterator<Item = T> + Clone; 2]
|
||||
where
|
||||
T: num::Integer + Copy,
|
||||
RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
|
||||
Rev<RangeInclusive<T>>: Iterator<Item = T>,
|
||||
{
|
||||
let in_range = (x >= min) && (x <= max);
|
||||
let in_range = range.contains(&x);
|
||||
let (start, end) = (*range.start(), *range.end());
|
||||
|
||||
// RangeInclusive (1..=0), has 0 elements
|
||||
let base = Either::Right(T::one()..=T::zero());
|
||||
[
|
||||
if in_range && x > min + T::one() {
|
||||
Either::Left((min..=(x - T::one())).rev())
|
||||
if in_range && x > start + T::one() {
|
||||
Either::Left((start..=(x - T::one())).rev())
|
||||
} else {
|
||||
base.clone()
|
||||
},
|
||||
if in_range && x + T::one() < max {
|
||||
Either::Right((x + T::one())..=max)
|
||||
if in_range && x + T::one() < end {
|
||||
Either::Right((x + T::one())..=end)
|
||||
} else {
|
||||
base
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
pub fn diag_test_helper<T>(
|
||||
i: T,
|
||||
j: T,
|
||||
min_i: T,
|
||||
min_j: T,
|
||||
max_i: T,
|
||||
max_j: T,
|
||||
) -> [impl Iterator<Item = (T, T)> + Clone; 4]
|
||||
where
|
||||
T: num::Integer + Copy,
|
||||
RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
|
||||
Rev<RangeInclusive<T>>: Iterator<Item = T>,
|
||||
{
|
||||
let i_chains = split_from(min_i, max_i, i);
|
||||
let j_chains = split_from(min_j, max_j, j);
|
||||
diag_raw(i_chains, j_chains)
|
||||
}
|
||||
|
||||
pub fn diag_raw<T>(
|
||||
i_chains: [impl Iterator<Item = T> + Clone; 2],
|
||||
|
||||
@ -60,26 +43,40 @@ where
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
pub fn diag_test_helper<T>(
|
||||
i: T,
|
||||
j: T,
|
||||
range_i: RangeInclusive<T>,
|
||||
range_j: RangeInclusive<T>,
|
||||
) -> [impl Iterator<Item = (T, T)> + Clone; 4]
|
||||
where
|
||||
T: num::Integer + Copy,
|
||||
RangeInclusive<T>: Iterator<Item = T> + DoubleEndedIterator,
|
||||
Rev<RangeInclusive<T>>: Iterator<Item = T>,
|
||||
{
|
||||
diag_raw(split_from(range_i, i), split_from(range_j, j))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_test() {
|
||||
assert_eq!(
|
||||
split_from(0, 6, 2).map(Iterator::collect::<Vec<usize>>),
|
||||
split_from(0..=6, 2).map(Iterator::collect::<Vec<usize>>),
|
||||
[vec![1, 0], vec![3, 4, 5, 6]]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
split_from(0, 6, 0).map(Iterator::collect::<Vec<usize>>),
|
||||
split_from(0..=6, 0).map(Iterator::collect::<Vec<usize>>),
|
||||
[vec![], vec![1, 2, 3, 4, 5, 6]]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
split_from(0, 6, 6).map(Iterator::collect::<Vec<usize>>),
|
||||
split_from(0..=6, 6).map(Iterator::collect::<Vec<usize>>),
|
||||
[vec![5, 4, 3, 2, 1, 0], vec![]]
|
||||
);
|
||||
|
||||
// test out-of-bounds and also generics
|
||||
assert_eq!(
|
||||
split_from::<i16>(-1i16, 4i16, 10i16).map(Iterator::collect::<Vec<i16>>),
|
||||
split_from::<i16>(-1i16..=4i16, 10i16).map(Iterator::collect::<Vec<i16>>),
|
||||
[const { Vec::new() }; 2]
|
||||
);
|
||||
}
|
||||
@ -87,7 +84,7 @@ mod test {
|
||||
#[test]
|
||||
fn diag_test() {
|
||||
assert_eq!(
|
||||
diag_test_helper(2, 3, 0, 0, 7, 7).map(Iterator::collect::<Vec<(usize, usize)>>),
|
||||
diag_test_helper(2, 3, 0..=7, 0..=7).map(Iterator::collect::<Vec<(usize, usize)>>),
|
||||
[
|
||||
vec![(1, 2), (0, 1)],
|
||||
vec![(3, 4), (4, 5), (5, 6), (6, 7)],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user