diff --git a/Cargo.toml b/Cargo.toml index 0071b65..c4213ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zero_sum" -version = "1.2.0" +version = "1.3.0" authors = ["Chris Foster "] license = "GPL-3.0" description = "An analysis engine for zero-sum games with game implementations." @@ -8,18 +8,20 @@ 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]] diff --git a/examples/tic_tac_toe.rs b/examples/tic_tac_toe.rs index 033e047..d76bcab 100644 --- a/examples/tic_tac_toe.rs +++ b/examples/tic_tac_toe.rs @@ -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; @@ -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!("--------------------"); @@ -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::>() + .unwrap() + .principal_variation[0].clone() }; if let Err(error) = board.execute_ply(Some(&ply)) { diff --git a/src/analysis/search/mod.rs b/src/analysis/search/mod.rs index f77f8a0..c7c99b3 100644 --- a/src/analysis/search/mod.rs +++ b/src/analysis/search/mod.rs @@ -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 where S: State + Extrapolatable<::Ply> { /// Generates an analysis of `state`. `interrupt` is optionally provided to interrupt long searches. - fn search(&mut self, state: &S, interrupt: Option>) -> Box; + fn search(&mut self, state: &S, interrupt: Option>) -> Box; } pub use self::pvsearch::{PvSearch, PvSearchAnalysis}; diff --git a/src/analysis/search/pvsearch/mod.rs b/src/analysis/search/pvsearch/mod.rs index da5d884..b8013bd 100644 --- a/src/analysis/search/pvsearch/mod.rs +++ b/src/analysis/search/pvsearch/mod.rs @@ -333,7 +333,7 @@ impl PvSearch where impl Search for PvSearch where S: 'static + State + Extrapolatable<::Ply>, E: 'static + Evaluator { - fn search(&mut self, state: &S, interrupt: Option>) -> Box { + fn search(&mut self, state: &S, interrupt: Option>) -> Box { let mut state = state.clone(); let mut eval = ::Evaluation::null(); let mut principal_variation = Vec::new(); @@ -438,13 +438,13 @@ impl fmt::Display for PvSearchAnalysis where S: State + Extrapolatable<::Ply>, E: Evaluator { 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 { @@ -452,12 +452,12 @@ impl fmt::Display for PvSearchAnalysis where } } 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(()) } } @@ -465,7 +465,7 @@ impl fmt::Display for PvSearchAnalysis where impl Analysis for PvSearchAnalysis where S: 'static + State + Extrapolatable<::Ply>, E: 'static + Evaluator { - fn as_any(&self) -> &Any { + fn as_any(&self) -> &dyn Any { self } } diff --git a/src/analysis/search/pvsearch/ply_generator.rs b/src/analysis/search/pvsearch/ply_generator.rs index 4cbe95e..7d347ce 100644 --- a/src/analysis/search/pvsearch/ply_generator.rs +++ b/src/analysis/search/pvsearch/ply_generator.rs @@ -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; @@ -46,7 +46,7 @@ impl PlyGenerator where P: Ply { pub fn new(state: &X, principal_ply: Option

, history: Arc>) -> PlyGenerator { let mut plies = state.extrapolate(); - RNG.lock().unwrap().shuffle(&mut plies); + plies.shuffle(&mut *RNG.lock().unwrap()); PlyGenerator { principal_ply: principal_ply, diff --git a/src/analysis/search/pvsearch/statistics.rs b/src/analysis/search/pvsearch/statistics.rs index dcfb6d8..b996ca6 100644 --- a/src/analysis/search/pvsearch/statistics.rs +++ b/src/analysis/search/pvsearch/statistics.rs @@ -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 = { @@ -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(()) diff --git a/src/impls/tak/state/evaluator/static_.rs b/src/impls/tak/state/evaluator/static_.rs index edd576d..0b4400f 100644 --- a/src/impls/tak/state/evaluator/static_.rs +++ b/src/impls/tak/state/evaluator/static_.rs @@ -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(); @@ -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, - )); - }); - } } diff --git a/src/impls/tak/state/mod.rs b/src/impls/tak/state/mod.rs index a6fcc49..0e5140b 100644 --- a/src/impls/tak/state/mod.rs +++ b/src/impls/tak/state/mod.rs @@ -388,64 +388,64 @@ impl fmt::Display for State { }) }).collect::>(); - try!(write!(f, "\n Player 1: {:>2} flatstone{}", self.p1_flatstones, + write!(f, "\n Player 1: {:>2} flatstone{}", self.p1_flatstones, if self.p1_flatstones != 1 { "s" } else { "" } - )); + )?; if self.p1_capstones > 0 { - try!(write!(f, ", {} capstone{}", self.p1_capstones, + write!(f, ", {} capstone{}", self.p1_capstones, if self.p1_capstones != 1 { "s" } else { "" } - )); + )?; } - try!(write!(f, "\n Player 2: {:>2} flatstone{}", self.p2_flatstones, + write!(f, "\n Player 2: {:>2} flatstone{}", self.p2_flatstones, if self.p2_flatstones != 1 { "s" } else { "" } - )); + )?; if self.p2_capstones > 0 { - try!(write!(f, ", {} capstone{}\n\n", self.p2_capstones, + write!(f, ", {} capstone{}\n\n", self.p2_capstones, if self.p2_capstones != 1 { "s" } else { "" } - )); + )?; } else { - try!(write!(f, "\n\n")); + write!(f, "\n\n")?; } for row in (0..board_size).rev() { - try!(write!(f, " {} ", row + 1)); + write!(f, " {} ", row + 1)?; for column in 0..board_size { let mut c = String::new(); - try!(write!(c, "[")); + write!(c, "[")?; for (index, piece) in self.board[column][row].iter().rev().enumerate() { if index > 0 { - try!(write!(c, " ")); + write!(c, " ")?; } - try!(write!(c, "{}", match piece.get_color() { + write!(c, "{}", match piece.get_color() { Color::White => "W", Color::Black => "B", - })); + })?; match *piece { - Piece::StandingStone(_) => { try!(write!(c, "S")); }, - Piece::Capstone(_) => { try!(write!(c, "C")); }, + Piece::StandingStone(_) => { write!(c, "S")?; }, + Piece::Capstone(_) => { write!(c, "C")?; }, _ => (), } } - try!(write!(c, "]")); + write!(c, "]")?; - try!(write!(f, "{: fmt::Result { - try!(write!(f, "\n 1 2 3")); + write!(f, "\n 1 2 3")?; for y in 0..3 { - try!(write!(f, "\n{} ", y + 1)); + write!(f, "\n{} ", y + 1)?; for x in 0..3 { - try!(write!(f, "[{}]", match self.0[x + 3 * y] { - Some(mark) => Box::new(mark) as Box, + write!(f, "[{}]", match self.0[x + 3 * y] { + Some(mark) => Box::new(mark) as Box, None => Box::new(" "), - })); + })?; } } diff --git a/src/lib.rs b/src/lib.rs index ce2483b..9ad85a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,18 +71,11 @@ //! The provided tic-tac-toe implementation is very simple and a usage example can //! be found in [examples/tic_tac_toe.rs](https://github.com/cdbfoster/zero_sum/blob/master/examples/tic_tac_toe.rs). -#![feature(test)] - extern crate fnv; -#[cfg(test)] -extern crate test; - -#[cfg(feature = "with_tak")] #[macro_use] extern crate lazy_static; -#[cfg(feature = "with_tak")] extern crate rand; #[cfg(feature = "with_tak_ann")] diff --git a/src/util/jkiss32.rs b/src/util/jkiss32.rs index c5fda97..bb394a7 100644 --- a/src/util/jkiss32.rs +++ b/src/util/jkiss32.rs @@ -16,7 +16,7 @@ // Copyright 2016-2017 Chris Foster // -use rand::{self, Rng}; +use rand::{self, Rng, RngCore}; #[derive(Clone, Copy)] pub struct JKiss32Rng { @@ -40,7 +40,7 @@ impl JKiss32Rng { } } -impl Rng for JKiss32Rng { +impl RngCore for JKiss32Rng { fn next_u32(&mut self) -> u32 { self.y ^= self.y << 5; self.y ^= self.y >> 7; @@ -52,5 +52,16 @@ impl Rng for JKiss32Rng { self.x = self.x.wrapping_add(1411392427); self.x.wrapping_add(self.y).wrapping_add(self.w) } -} + fn next_u64(&mut self) -> u64 { + rand_core::impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + rand_core::impls::fill_bytes_via_next(self, dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + Ok(rand_core::impls::fill_bytes_via_next(self, dest)) + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs index 31ffa77..3fae312 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -16,7 +16,6 @@ // // Copyright 2016-2017 Chris Foster // - pub use self::jkiss32::JKiss32Rng; mod jkiss32;