Skip to content
Open
10 changes: 6 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
[package]
name = "zero_sum"
version = "1.2.0"
version = "1.3.0"
authors = ["Chris Foster <cdbfoster@gmail.com>"]
license = "GPL-3.0"
description = "An analysis engine for zero-sum games with game implementations."
repository = "https://github.com/cdbfoster/zero_sum"
readme = "README.md"
documentation = "https://cdbfoster.github.io/doc/zero_sum"
keywords = ["zero-sum", "game", "chess", "tak", "tic-tac-toe"]
edition = "2015"

[features]
with_all = ["with_tak", "with_tak_ann", "with_tic_tac_toe"]
with_tak = ["lazy_static", "rand"]
with_tak = []
with_tak_ann = ["with_tak", "blas", "rusqlite"]
with_tic_tac_toe = []

[dependencies]
blas = { version = "0.15.3", optional = true }
fnv = "1.0"
lazy_static = { version = "0.2", optional = true }
rand = { version = "0.3", optional = true }
lazy_static = "1"
rand = "0.8"
rand_core = "0.6"
rusqlite = { version = "0.10", optional = true }

[[bin]]
Expand Down
10 changes: 7 additions & 3 deletions examples/tic_tac_toe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extern crate zero_sum;

use std::io::{self, Write};

use zero_sum::analysis::search::Search;
use zero_sum::analysis::search::{Search, PvSearchAnalysis};
use zero_sum::impls::tic_tac_toe::*;
use zero_sum::State;

Expand All @@ -31,7 +31,7 @@ fn main() {
loop {
let mut board = Board::new();
let evaluator = Evaluator;
let mut ai = zero_sum::analysis::search::pvsearch::PvSearch::new(evaluator);
let mut ai = zero_sum::analysis::search::PvSearch::new(evaluator);

println!("--------------------");

Expand Down Expand Up @@ -84,7 +84,11 @@ fn main() {
} else {
println!("Computer's turn:");

ai.search(&board, None).principal_variation[0].clone()
ai.search(&board, None)
.as_any()
.downcast_ref::<PvSearchAnalysis<Board, Evaluator>>()
.unwrap()
.principal_variation[0].clone()
};

if let Err(error) = board.execute_ply(Some(&ply)) {
Expand Down
4 changes: 2 additions & 2 deletions src/analysis/search/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ use state::State;
/// # }
/// ```
pub trait Analysis: Display {
fn as_any(&self) -> &Any;
fn as_any(&self) -> &dyn Any;
}

/// Provides search capabilities.
pub trait Search<S> where
S: State + Extrapolatable<<S as State>::Ply> {
/// Generates an analysis of `state`. `interrupt` is optionally provided to interrupt long searches.
fn search(&mut self, state: &S, interrupt: Option<Receiver<()>>) -> Box<Analysis>;
fn search(&mut self, state: &S, interrupt: Option<Receiver<()>>) -> Box<dyn Analysis>;
}

pub use self::pvsearch::{PvSearch, PvSearchAnalysis};
Expand Down
18 changes: 9 additions & 9 deletions src/analysis/search/pvsearch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ impl<S, E> PvSearch<S, E> where
impl<S, E> Search<S> for PvSearch<S, E> where
S: 'static + State + Extrapolatable<<S as State>::Ply>,
E: 'static + Evaluator<State = S> {
fn search(&mut self, state: &S, interrupt: Option<Receiver<()>>) -> Box<Analysis> {
fn search(&mut self, state: &S, interrupt: Option<Receiver<()>>) -> Box<dyn Analysis> {
let mut state = state.clone();
let mut eval = <E as Evaluator>::Evaluation::null();
let mut principal_variation = Vec::new();
Expand Down Expand Up @@ -438,34 +438,34 @@ impl<S, E> fmt::Display for PvSearchAnalysis<S, E> where
S: State + Extrapolatable<<S as State>::Ply>,
E: Evaluator<State = S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "State: {}\n", self.state));
write!(f, "State: {}\n", self.state)?;
let mut result = self.state.clone();
if result.execute_plies(&self.principal_variation).is_ok() {
try!(write!(f, "Resultant State: {}\n", result));
write!(f, "Resultant State: {}\n", result)?;
// XXX Make Resolution require Display and print the resolution if any
}
try!(write!(f, "Evaluation: {}{}", self.evaluation, if self.evaluation.is_end() {
write!(f, "Evaluation: {}{}", self.evaluation, if self.evaluation.is_end() {
if self.evaluation.is_win() {
" (Win)\n"
} else {
" (Lose)\n"
}
} else {
"\n"
}));
try!(write!(f, "Principal Variation:"));
})?;
write!(f, "Principal Variation:")?;
for ply in &self.principal_variation {
try!(write!(f, "\n {}", ply));
write!(f, "\n {}", ply)?;
}
try!(write!(f, "\nStatistics:\n{}", self.statistics));
write!(f, "\nStatistics:\n{}", self.statistics)?;
Ok(())
}
}

impl<S, E> Analysis for PvSearchAnalysis<S, E> where
S: 'static + State + Extrapolatable<<S as State>::Ply>,
E: 'static + Evaluator<State = S> {
fn as_any(&self) -> &Any {
fn as_any(&self) -> &dyn Any {
self
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/analysis/search/pvsearch/ply_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use std::marker::PhantomData;
use std::sync::{Arc, Mutex};

use rand::Rng;
use rand::seq::SliceRandom;

use analysis::Extrapolatable;
use analysis::search::pvsearch::history::History;
Expand All @@ -46,7 +46,7 @@ impl<X, P> PlyGenerator<X, P> where
P: Ply {
pub fn new(state: &X, principal_ply: Option<P>, history: Arc<Mutex<History>>) -> PlyGenerator<X, P> {
let mut plies = state.extrapolate();
RNG.lock().unwrap().shuffle(&mut plies);
plies.shuffle(&mut *RNG.lock().unwrap());

PlyGenerator {
principal_ply: principal_ply,
Expand Down
22 changes: 11 additions & 11 deletions src/analysis/search/pvsearch/statistics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,17 @@ impl fmt::Display for Statistics {
};

for (i, max_depth) in data[..data.len() - 1].iter().enumerate() {
try!(write!(f, "\n {0:1$}", format!("Max Depth {}:", i + 1), title_width + 2));
write!(f, "\n {0:1$}", format!("Max Depth {}:", i + 1), title_width + 2)?;
for j in 0..max_depth.len() {
try!(write!(f, " {0:>1$}", j + 1, column_widths[j]));
write!(f, " {0:>1$}", j + 1, column_widths[j])?;
}
for (j, title) in titles.iter().enumerate() {
try!(write!(f, "\n {0:1$}", title, title_width));
write!(f, "\n {0:1$}", title, title_width)?;
for (k, depth) in max_depth.iter().enumerate() {
try!(write!(f, " {0:>1$}", depth[j], column_widths[k]));
write!(f, " {0:>1$}", depth[j], column_widths[k])?;
}
}
try!(write!(f, "\n"));
write!(f, "\n")?;
}

let final_totals = {
Expand All @@ -181,17 +181,17 @@ impl fmt::Display for Statistics {
);

let totals_strings = data.last().unwrap();
try!(write!(f, "\n {0:1$}", "Totals:", title_width + 2));
write!(f, "\n {0:1$}", "Totals:", title_width + 2)?;
for j in 0..totals_strings.len() {
try!(write!(f, " {0:>1$}", j + 1, column_widths[j]));
write!(f, " {0:>1$}", j + 1, column_widths[j])?;
}
try!(write!(f, " {0:>1$}", "Total", final_totals_width));
write!(f, " {0:>1$}", "Total", final_totals_width)?;
for (j, title) in titles.iter().enumerate() {
try!(write!(f, "\n {0:1$}", title, title_width));
write!(f, "\n {0:1$}", title, title_width)?;
for (k, depth) in totals_strings.iter().enumerate() {
try!(write!(f, " {0:>1$}", depth[j], column_widths[k]));
write!(f, " {0:>1$}", depth[j], column_widths[k])?;
}
try!(write!(f, " {0:>1$}", final_totals[j], final_totals_width));
write!(f, " {0:>1$}", final_totals[j], final_totals_width)?;
}

Ok(())
Expand Down
145 changes: 0 additions & 145 deletions src/impls/tak/state/evaluator/static_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,20 +395,8 @@ fn evaluate_influence(

#[cfg(test)]
mod test {
use std::cmp;
use test::{self, Bencher};

use analysis::Evaluator;
use impls::tak::*;
use super::{
END_GAME_FLATSTONE_THRESHOLD,
evaluate_influence,
evaluate_road_groups,
evaluate_stacked_flatstones,
evaluate_threats,
evaluate_top_pieces,
WEIGHT,
};

lazy_static! {
static ref STATE: State = State::from_tps("[TPS \"21,22221C,1,12212S,x/2121,2S,2,1S,2/x2,2,2,x/1,2111112C,2,x,21/x,1,21,x2 1 32\"]").unwrap();
Expand Down Expand Up @@ -488,137 +476,4 @@ mod test {
println!("{}\n{}", transformed_evaluation, transformed);
assert!(transformed_evaluation == original_evaluation);
}

#[bench]
fn bench_evaluate(b: &mut Bencher) {
let evaluator = evaluator::StaticEvaluator;

b.iter(|| {
evaluator.evaluate(test::black_box(&STATE))
});
}

#[bench]
fn bench_evaluate_top_pieces(b: &mut Bencher) {
let mut p1_eval = 0;
let mut p2_eval = 0;

let m = &STATE.metadata;

let p1_standing_stones = m.p1_pieces & m.standing_stones;
let p2_standing_stones = m.p2_pieces & m.standing_stones;

let p1_capstones = m.p1_pieces & m.capstones;
let p2_capstones = m.p2_pieces & m.capstones;

let (p1_flatstone_weight, p2_flatstone_weight) = {
let flatstone_threshold = END_GAME_FLATSTONE_THRESHOLD[m.board_size];

let p1_position = cmp::min(STATE.p1_flatstones as i32, flatstone_threshold);
let p2_position = cmp::min(STATE.p2_flatstones as i32, flatstone_threshold);

(
WEIGHT.flatstone.0 * p1_position / flatstone_threshold +
WEIGHT.flatstone.1 * (flatstone_threshold - p1_position) / flatstone_threshold,
WEIGHT.flatstone.0 * p2_position / flatstone_threshold +
WEIGHT.flatstone.1 * (flatstone_threshold - p2_position) / flatstone_threshold,
)
};

b.iter(|| {
p1_eval += test::black_box(evaluate_top_pieces(m.p1_flatstone_count as i32, p1_flatstone_weight, p1_standing_stones, p1_capstones));
p2_eval += test::black_box(evaluate_top_pieces(m.p2_flatstone_count as i32, p2_flatstone_weight, p2_standing_stones, p2_capstones));
});
}

#[bench]
fn bench_evaluate_stacked_flatstones(b: &mut Bencher) {
let mut p1_eval = 0;
let mut p2_eval = 0;

let m = &STATE.metadata;

let p1_flatstones = m.p1_pieces & !m.standing_stones & !m.capstones;
let p2_flatstones = m.p2_pieces & !m.standing_stones & !m.capstones;

let p1_standing_stones = m.p1_pieces & m.standing_stones;
let p2_standing_stones = m.p2_pieces & m.standing_stones;

let p1_capstones = m.p1_pieces & m.capstones;
let p2_capstones = m.p2_pieces & m.capstones;

b.iter(|| {
let stacked_flatstones_eval = test::black_box(evaluate_stacked_flatstones(
m,
p1_flatstones,
p2_flatstones,
p1_standing_stones,
p2_standing_stones,
p1_capstones,
p2_capstones,
));
p1_eval += stacked_flatstones_eval.0;
p2_eval += stacked_flatstones_eval.1;
});
}

#[bench]
fn bench_evaluate_road_groups(b: &mut Bencher) {
let mut p1_eval = 0;
let mut p2_eval = 0;

let m = &STATE.metadata;

b.iter(|| {
p1_eval += test::black_box(evaluate_road_groups(m, &m.p1_road_groups));
p2_eval += test::black_box(evaluate_road_groups(m, &m.p2_road_groups));
});
}

#[bench]
fn bench_evaluate_threats(b: &mut Bencher) {
let mut p1_eval = 0;
let mut p2_eval = 0;

let m = &STATE.metadata;

let total_pieces = m.p1_pieces | m.p2_pieces;

b.iter(|| {
p1_eval += test::black_box(evaluate_threats(m, total_pieces, &m.p1_road_groups));
p2_eval += test::black_box(evaluate_threats(m, total_pieces, &m.p2_road_groups));
});
}

#[bench]
fn bench_evaluate_influence(b: &mut Bencher) {
let mut p1_eval = 0;
let mut p2_eval = 0;

let m = &STATE.metadata;

let total_pieces = m.p1_pieces | m.p2_pieces;

let p1_flatstones = m.p1_pieces & !m.standing_stones & !m.capstones;
let p2_flatstones = m.p2_pieces & !m.standing_stones & !m.capstones;

b.iter(|| {
p1_eval += test::black_box(evaluate_influence(
m,
total_pieces,
m.p1_pieces,
&m.p1_flatstones,
p1_flatstones,
m.p2_pieces,
));
p2_eval += test::black_box(evaluate_influence(
m,
total_pieces,
m.p2_pieces,
&m.p2_flatstones,
p2_flatstones,
m.p1_pieces,
));
});
}
}
Loading