From 8381959c85574624a97ffd60c94bce9eb2eb3cb9 Mon Sep 17 00:00:00 2001 From: Shotaro Tanaka Date: Sat, 13 Aug 2022 21:49:54 +0900 Subject: [PATCH] Refactor mate (#118) * misc * separate selector from traverser * resolver is subtrait of VCTHelper * move field to analysis --- src/analysis.rs | 1 + src/{mate/vct => analysis}/field.rs | 0 src/lib.rs | 1 + src/mate/vct.rs | 2 +- src/mate/vct/resolver.rs | 6 +- src/mate/vct/selector.rs | 74 ++++++ src/mate/vct/solver/dfpns.rs | 53 ++--- src/mate/vct/solver/dfs.rs | 53 ++--- src/mate/vct/solver/pns.rs | 54 ++--- src/mate/vct/state.rs | 12 +- src/mate/vct/traverser.rs | 72 +----- src/mate/vct_lazy.rs | 1 - src/mate/vct_lazy/field.rs | 354 ---------------------------- src/mate/vct_lazy/state.rs | 2 +- 14 files changed, 157 insertions(+), 528 deletions(-) create mode 100644 src/analysis.rs rename src/{mate/vct => analysis}/field.rs (100%) create mode 100644 src/mate/vct/selector.rs delete mode 100644 src/mate/vct_lazy/field.rs diff --git a/src/analysis.rs b/src/analysis.rs new file mode 100644 index 0000000..5b8f2df --- /dev/null +++ b/src/analysis.rs @@ -0,0 +1 @@ +pub mod field; diff --git a/src/mate/vct/field.rs b/src/analysis/field.rs similarity index 100% rename from src/mate/vct/field.rs rename to src/analysis/field.rs diff --git a/src/lib.rs b/src/lib.rs index e546d96..83f60f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +pub mod analysis; pub mod board; pub mod mate; pub mod wasm; diff --git a/src/mate/vct.rs b/src/mate/vct.rs index 1043fac..9c53884 100644 --- a/src/mate/vct.rs +++ b/src/mate/vct.rs @@ -1,9 +1,9 @@ -mod field; mod generator; mod helper; mod proof; mod resolver; mod searcher; +mod selector; mod solver; mod state; mod traverser; diff --git a/src/mate/vct/resolver.rs b/src/mate/vct/resolver.rs index 49e64fa..c0bead6 100644 --- a/src/mate/vct/resolver.rs +++ b/src/mate/vct/resolver.rs @@ -1,13 +1,11 @@ +use super::helper::VCFHelper; use super::state::VCTState; use crate::mate::game::*; use crate::mate::mate::Mate; use crate::mate::state::State; use crate::mate::vct::proof::*; -pub trait Resolver: ProofTree { - fn solve_attacker_vcf(&mut self, state: &VCTState) -> Option; - fn solve_attacker_threat(&mut self, state: &VCTState) -> Option; - +pub trait Resolver: ProofTree + VCFHelper { fn resolve(&mut self, state: &mut VCTState) -> Option { self.resolve_attacks(state) } diff --git a/src/mate/vct/selector.rs b/src/mate/vct/selector.rs new file mode 100644 index 0000000..79d7aa3 --- /dev/null +++ b/src/mate/vct/selector.rs @@ -0,0 +1,74 @@ +use crate::board::Point; +use crate::mate::vct::proof::*; +use crate::mate::vct::state::VCTState; + +pub struct Selection { + pub best: Option, + pub current: Node, + pub next1: Node, + pub next2: Node, +} + +pub trait Selector: ProofTree { + fn select_attack(&mut self, state: &mut VCTState, attacks: &[Point]) -> Selection { + let limit = state.limit; + let mut best: Option = Some(attacks[0]); + let mut current = Node::zero_dn(limit); + let mut next1 = Node::zero_dn(limit); + let mut next2 = Node::zero_dn(limit); + let init = Node::unit_dn(attacks.len() as u32, limit); // trick + for &attack in attacks { + let maybe_child = self.attacker_table().lookup_next(state, Some(attack)); + let child = maybe_child.unwrap_or(init); + current = current.min_pn_sum_dn(child); + if child.pn < next1.pn { + best.replace(attack); + next2 = next1; + next1 = child; + } else if child.pn < next2.pn { + next2 = child; + } + if current.pn == 0 { + current.dn = INF; + break; + } + } + Selection { + best: best, + current: current, + next1: next1, + next2: next2, + } + } + + fn select_defence(&mut self, state: &mut VCTState, defences: &[Point]) -> Selection { + let limit = state.limit; + let mut best: Option = Some(defences[0]); + let mut current = Node::zero_pn(limit - 1); + let mut next1 = Node::zero_pn(limit - 1); + let mut next2 = Node::zero_pn(limit - 1); + let init = Node::unit_pn(defences.len() as u32, limit - 1); // trick + for &defence in defences { + let maybe_child = self.defender_table().lookup_next(state, Some(defence)); + let child = maybe_child.unwrap_or(init); + current = current.min_dn_sum_pn(child); + if child.dn < next1.dn { + best.replace(defence); + next2 = next1; + next1 = child; + } else if child.dn < next2.dn { + next2 = child; + } + if current.dn == 0 { + current.pn = INF; + break; + } + } + Selection { + best: best, + current: current, + next1: next1, + next2: next2, + } + } +} diff --git a/src/mate/vct/solver/dfpns.rs b/src/mate/vct/solver/dfpns.rs index 01806a7..0f12912 100644 --- a/src/mate/vct/solver/dfpns.rs +++ b/src/mate/vct/solver/dfpns.rs @@ -1,13 +1,12 @@ use crate::board::Point; -use crate::mate::mate::Mate; use crate::mate::vcf; -use crate::mate::vct::generator::*; +use crate::mate::vct::generator::Generator; use crate::mate::vct::helper::VCFHelper; use crate::mate::vct::proof::*; use crate::mate::vct::resolver::Resolver; use crate::mate::vct::searcher::Searcher; +use crate::mate::vct::selector::*; use crate::mate::vct::solver::VCTSolver; -use crate::mate::vct::state::VCTState; use crate::mate::vct::traverser::*; use lru::LruCache; @@ -51,13 +50,21 @@ impl Generator for DFPNSVCTSolver { } } -impl ProofTree for DFPNSVCTSolver { - fn attacker_table(&mut self) -> &mut Table { - &mut self.attacker_table +impl VCFHelper for DFPNSVCTSolver { + fn attacker_vcf_depth(&self) -> u8 { + self.attacker_vcf_depth } - fn defender_table(&mut self) -> &mut Table { - &mut self.defender_table + fn defender_vcf_depth(&self) -> u8 { + self.defender_vcf_depth + } + + fn attacker_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { + &mut self.attacker_vcf_solver + } + + fn defender_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { + &mut self.defender_vcf_solver } } @@ -73,30 +80,16 @@ impl Traverser for DFPNSVCTSolver { impl DFPNSTraverser for DFPNSVCTSolver {} -impl Resolver for DFPNSVCTSolver { - fn solve_attacker_vcf(&mut self, state: &VCTState) -> Option { - VCFHelper::solve_attacker_vcf(self, state) - } +impl Selector for DFPNSVCTSolver {} - fn solve_attacker_threat(&mut self, state: &VCTState) -> Option { - VCFHelper::solve_attacker_threat(self, state) - } -} - -impl VCFHelper for DFPNSVCTSolver { - fn attacker_vcf_depth(&self) -> u8 { - self.attacker_vcf_depth - } - - fn defender_vcf_depth(&self) -> u8 { - self.defender_vcf_depth - } - - fn attacker_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { - &mut self.attacker_vcf_solver +impl ProofTree for DFPNSVCTSolver { + fn attacker_table(&mut self) -> &mut Table { + &mut self.attacker_table } - fn defender_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { - &mut self.defender_vcf_solver + fn defender_table(&mut self) -> &mut Table { + &mut self.defender_table } } + +impl Resolver for DFPNSVCTSolver {} diff --git a/src/mate/vct/solver/dfs.rs b/src/mate/vct/solver/dfs.rs index 7411bf2..1f8258c 100644 --- a/src/mate/vct/solver/dfs.rs +++ b/src/mate/vct/solver/dfs.rs @@ -1,13 +1,12 @@ use crate::board::Point; -use crate::mate::mate::Mate; use crate::mate::vcf; -use crate::mate::vct::generator::*; +use crate::mate::vct::generator::Generator; use crate::mate::vct::helper::VCFHelper; use crate::mate::vct::proof::*; use crate::mate::vct::resolver::Resolver; use crate::mate::vct::searcher::Searcher; +use crate::mate::vct::selector::*; use crate::mate::vct::solver::VCTSolver; -use crate::mate::vct::state::VCTState; use crate::mate::vct::traverser::*; use lru::LruCache; @@ -51,13 +50,21 @@ impl Generator for DFSVCTSolver { } } -impl ProofTree for DFSVCTSolver { - fn attacker_table(&mut self) -> &mut Table { - &mut self.attacker_table +impl VCFHelper for DFSVCTSolver { + fn attacker_vcf_depth(&self) -> u8 { + self.attacker_vcf_depth } - fn defender_table(&mut self) -> &mut Table { - &mut self.defender_table + fn defender_vcf_depth(&self) -> u8 { + self.defender_vcf_depth + } + + fn attacker_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { + &mut self.attacker_vcf_solver + } + + fn defender_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { + &mut self.defender_vcf_solver } } @@ -73,30 +80,16 @@ impl Traverser for DFSVCTSolver { impl DFSTraverser for DFSVCTSolver {} -impl Resolver for DFSVCTSolver { - fn solve_attacker_vcf(&mut self, state: &VCTState) -> Option { - VCFHelper::solve_attacker_vcf(self, state) - } +impl Selector for DFSVCTSolver {} - fn solve_attacker_threat(&mut self, state: &VCTState) -> Option { - VCFHelper::solve_attacker_threat(self, state) - } -} - -impl VCFHelper for DFSVCTSolver { - fn attacker_vcf_depth(&self) -> u8 { - self.attacker_vcf_depth - } - - fn defender_vcf_depth(&self) -> u8 { - self.defender_vcf_depth - } - - fn attacker_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { - &mut self.attacker_vcf_solver +impl ProofTree for DFSVCTSolver { + fn attacker_table(&mut self) -> &mut Table { + &mut self.attacker_table } - fn defender_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { - &mut self.defender_vcf_solver + fn defender_table(&mut self) -> &mut Table { + &mut self.defender_table } } + +impl Resolver for DFSVCTSolver {} diff --git a/src/mate/vct/solver/pns.rs b/src/mate/vct/solver/pns.rs index 2559f2c..f2150ea 100644 --- a/src/mate/vct/solver/pns.rs +++ b/src/mate/vct/solver/pns.rs @@ -1,13 +1,12 @@ use crate::board::Point; -use crate::mate::mate::Mate; use crate::mate::vcf; -use crate::mate::vct::generator::*; +use crate::mate::vct::generator::Generator; use crate::mate::vct::helper::VCFHelper; use crate::mate::vct::proof::*; use crate::mate::vct::resolver::Resolver; use crate::mate::vct::searcher::Searcher; +use crate::mate::vct::selector::*; use crate::mate::vct::solver::VCTSolver; -use crate::mate::vct::state::VCTState; use crate::mate::vct::traverser::*; use lru::LruCache; @@ -41,6 +40,8 @@ impl VCTSolver for PNSVCTSolver {} impl Searcher for PNSVCTSolver {} +impl Selector for PNSVCTSolver {} + impl Generator for PNSVCTSolver { fn attacks_cache(&mut self) -> &mut LruCache, Node>> { &mut self.attacks_cache @@ -51,16 +52,23 @@ impl Generator for PNSVCTSolver { } } -impl ProofTree for PNSVCTSolver { - fn attacker_table(&mut self) -> &mut Table { - &mut self.attacker_table +impl VCFHelper for PNSVCTSolver { + fn attacker_vcf_depth(&self) -> u8 { + self.attacker_vcf_depth } - fn defender_table(&mut self) -> &mut Table { - &mut self.defender_table + fn defender_vcf_depth(&self) -> u8 { + self.defender_vcf_depth + } + + fn attacker_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { + &mut self.attacker_vcf_solver } -} + fn defender_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { + &mut self.defender_vcf_solver + } +} impl Traverser for PNSVCTSolver { fn next_threshold_attack(&self, selection: &Selection, threshold: Node) -> Node { PNSTraverser::next_threshold_attack(self, selection, threshold) @@ -73,30 +81,14 @@ impl Traverser for PNSVCTSolver { impl PNSTraverser for PNSVCTSolver {} -impl Resolver for PNSVCTSolver { - fn solve_attacker_vcf(&mut self, state: &VCTState) -> Option { - VCFHelper::solve_attacker_vcf(self, state) +impl ProofTree for PNSVCTSolver { + fn attacker_table(&mut self) -> &mut Table { + &mut self.attacker_table } - fn solve_attacker_threat(&mut self, state: &VCTState) -> Option { - VCFHelper::solve_attacker_threat(self, state) + fn defender_table(&mut self) -> &mut Table { + &mut self.defender_table } } -impl VCFHelper for PNSVCTSolver { - fn attacker_vcf_depth(&self) -> u8 { - self.attacker_vcf_depth - } - - fn defender_vcf_depth(&self) -> u8 { - self.defender_vcf_depth - } - - fn attacker_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { - &mut self.attacker_vcf_solver - } - - fn defender_vcf_solver(&mut self) -> &mut vcf::IDDFSSolver { - &mut self.defender_vcf_solver - } -} +impl Resolver for PNSVCTSolver {} diff --git a/src/mate/vct/state.rs b/src/mate/vct/state.rs index 976fbe8..29535c2 100644 --- a/src/mate/vct/state.rs +++ b/src/mate/vct/state.rs @@ -1,10 +1,10 @@ -use super::field::*; +use crate::analysis::field::PotentialField; use crate::board::StructureKind::*; use crate::board::*; use crate::mate::game::*; use crate::mate::mate::Mate; use crate::mate::state::State; -use crate::mate::vcf; +use crate::mate::vcf::VCFState; pub struct VCTState { game: Game, @@ -29,13 +29,13 @@ impl VCTState { Self::new(game, limit, field) } - pub fn vcf_state(&self, max_limit: u8) -> vcf::VCFState { + pub fn vcf_state(&self, max_limit: u8) -> VCFState { let game = self.game.clone(); let limit = self.limit.min(max_limit); - vcf::VCFState::new(game, limit) + VCFState::new(game, limit) } - pub fn threat_state(&self, max_limit: u8) -> vcf::VCFState { + pub fn threat_state(&self, max_limit: u8) -> VCFState { let mut game = self.game.clone(); game.play(None); let limit = if self.attacking() { @@ -44,7 +44,7 @@ impl VCTState { self.limit } .min(max_limit); - vcf::VCFState::new(game, limit) + VCFState::new(game, limit) } pub fn is_forbidden_move(&self, p: Point) -> bool { diff --git a/src/mate/vct/traverser.rs b/src/mate/vct/traverser.rs index 670771c..18191da 100644 --- a/src/mate/vct/traverser.rs +++ b/src/mate/vct/traverser.rs @@ -6,19 +6,13 @@ pub use dfpns::DFPNSTraverser; pub use dfs::DFSTraverser; pub use pns::PNSTraverser; +use super::selector::*; use crate::board::Point; use crate::mate::state::State; use crate::mate::vct::proof::*; use crate::mate::vct::state::VCTState; -pub struct Selection { - pub best: Option, - pub current: Node, - pub next1: Node, - pub next2: Node, -} - -pub trait Traverser: ProofTree { +pub trait Traverser: Selector { fn next_threshold_attack(&self, selection: &Selection, threshold: Node) -> Node; fn next_threshold_defence(&self, selection: &Selection, threshold: Node) -> Node; @@ -71,66 +65,4 @@ pub trait Traverser: ProofTree { fn backoff(&self, current: Node, threshold: Node) -> bool { current.pn >= threshold.pn || current.dn >= threshold.dn } - - fn select_attack(&mut self, state: &mut VCTState, attacks: &[Point]) -> Selection { - let limit = state.limit; - let mut best: Option = Some(attacks[0]); - let mut current = Node::zero_dn(limit); - let mut next1 = Node::zero_dn(limit); - let mut next2 = Node::zero_dn(limit); - let init = Node::unit_dn(attacks.len() as u32, limit); // trick - for &attack in attacks { - let maybe_child = self.attacker_table().lookup_next(state, Some(attack)); - let child = maybe_child.unwrap_or(init); - current = current.min_pn_sum_dn(child); - if child.pn < next1.pn { - best.replace(attack); - next2 = next1; - next1 = child; - } else if child.pn < next2.pn { - next2 = child; - } - if current.pn == 0 { - current.dn = INF; - break; - } - } - Selection { - best: best, - current: current, - next1: next1, - next2: next2, - } - } - - fn select_defence(&mut self, state: &mut VCTState, defences: &[Point]) -> Selection { - let limit = state.limit; - let mut best: Option = Some(defences[0]); - let mut current = Node::zero_pn(limit - 1); - let mut next1 = Node::zero_pn(limit - 1); - let mut next2 = Node::zero_pn(limit - 1); - let init = Node::unit_pn(defences.len() as u32, limit - 1); // trick - for &defence in defences { - let maybe_child = self.defender_table().lookup_next(state, Some(defence)); - let child = maybe_child.unwrap_or(init); - current = current.min_dn_sum_pn(child); - if child.dn < next1.dn { - best.replace(defence); - next2 = next1; - next1 = child; - } else if child.dn < next2.dn { - next2 = child; - } - if current.dn == 0 { - current.pn = INF; - break; - } - } - Selection { - best: best, - current: current, - next1: next1, - next2: next2, - } - } } diff --git a/src/mate/vct_lazy.rs b/src/mate/vct_lazy.rs index b0416bc..477228a 100644 --- a/src/mate/vct_lazy.rs +++ b/src/mate/vct_lazy.rs @@ -5,7 +5,6 @@ The idea was inspired by following paper. 長井歩. "難解な必至問題を解くアルゴリズムとその実装." ゲームプログラミングワークショップ 2011 論文集 2011.6 (2011): 1-8. */ -mod field; mod generator; mod helper; mod proof; diff --git a/src/mate/vct_lazy/field.rs b/src/mate/vct_lazy/field.rs deleted file mode 100644 index b72f480..0000000 --- a/src/mate/vct_lazy/field.rs +++ /dev/null @@ -1,354 +0,0 @@ -use crate::board::Direction::*; -use crate::board::Player::*; -use crate::board::*; - -#[derive(Default, Clone)] -pub struct Potential { - v: u8, - h: u8, - a: u8, - d: u8, -} - -impl Potential { - fn set(&mut self, d: Direction, o: u8) { - match d { - Vertical => self.v = o, - Horizontal => self.h = o, - Ascending => self.a = o, - Descending => self.d = o, - } - } - - fn sum(&self) -> u8 { - // TODO: faster computation - self.v + self.h + self.a + self.d - } -} - -type Potentials = [[Potential; RANGE as usize]; RANGE as usize]; - -#[derive(Clone)] -pub struct PotentialField { - potentials: Potentials, - player: Player, - min: u8, -} - -impl PotentialField { - pub fn new(player: Player, min: u8) -> Self { - Self { - potentials: Potentials::default(), - player: player, - min: min, - } - } - - pub fn init(player: Player, min: u8, board: &Board) -> Self { - let mut result = Self::new(player, min); - let os = board.potentials(player, min, player.is_black()); - for (idx, o) in os { - result.set(idx, o); - } - result - } - - pub fn update_along(&mut self, p: Point, board: &Board) { - self.reset_along(p); - let os = board.potentials_along(p, self.player, self.min, self.player.is_black()); - for (idx, o) in os { - self.set(idx, o); - } - } - - pub fn get(&self, p: Point) -> u8 { - self.sum(p) - } - - pub fn collect(&self, min: u8) -> Vec<(Point, u8)> { - (0..RANGE) - .flat_map(|x| { - (0..RANGE).map(move |y| { - let p = Point(x, y); - (p, self.sum(p)) - }) - }) - .filter(|&(_, o)| o >= min) - .collect() - } - - #[allow(dead_code)] - pub fn overlay(&self, board: &Board) -> String { - (0..RANGE) - .rev() - .map(|y| { - (0..RANGE) - .map(|x| { - let p = Point(x, y); - match board.stone(p) { - Some(Black) => " o".to_string(), - Some(White) => " x".to_string(), - None => match self.sum(p) { - 0 => " .".to_string(), - po => format!("{: >2}", po), - }, - } - }) - .collect::() - }) - .collect::>() - .join("\n") - } - - fn reset_along(&mut self, p: Point) { - let indices = [ - p.to_index(Vertical), - p.to_index(Horizontal), - p.to_index(Ascending), - p.to_index(Descending), - ]; - let neighbor_indices = indices.iter().flat_map(|idx| { - (0..RANGE).flat_map(move |j| { - if j <= idx.maxj() { - Some(Index::new(idx.d, idx.i, j)) - } else { - None - } - }) - }); - for idx in neighbor_indices { - self.set(idx, 0); - } - } - - fn sum(&self, p: Point) -> u8 { - self.potentials[p.0 as usize][p.1 as usize].sum() - } - - fn set(&mut self, i: Index, o: u8) { - let p = i.to_point(); - self.potentials[p.0 as usize][p.1 as usize].set(i.d, o) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_overlay() -> Result<(), String> { - let board = " - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . o . . o . . . . . - . . . . . . o x x . . . . . . - . . . . . . . o . . . . . . . - . . . . . . . . x . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - " - .parse::()?; - - let field = PotentialField::init(Black, 3, &board); - let result = field.overlay(&board); - let expected = trim_lines_string( - " - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . 3 . . . . . . . . - . . . 3 . . 6 . . . . . . . . - . . . . 3 . 9 . . . . . . . . - . . . . . 6 o 6 6 o 3 . . . . - . . . . . . o x x . . . . . . - . . . . . . 9 o . . . . . . . - . . . . . . 6 . x . . . . . . - . . . . . . 3 . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - ", - ); - assert_eq!(result, expected); - - let field = PotentialField::init(Black, 2, &board); - let result = field.overlay(&board); - let expected = trim_lines_string( - " - . . . . . . . . . . . . . . . - . . 2 . . 2 . . . 2 2 . . 2 . - . . . 2 . . 7 . . 8 2 . 2 . . - . . . 3 2 . 6 6 610 . 2 . . . - . . . . 3 2 9 814 8 2 . . . . - . . 2 4 4 6 o14 6 o 3 4 4 2 . - . . 2 2 210 o x x 8 8 . . . . - . . . 2101417 o 812 4 8 . . . - . . . 4 6 . 8 2 x 4 . . 4 . . - . . 2 4 . 2 3 2 . 2 . . . 2 . - . . 2 . 2 . . 2 . . . . . . . - . . . 2 . . . 2 . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - ", - ); - assert_eq!(result, expected); - - let field = PotentialField::init(White, 3, &board); - let result = field.overlay(&board); - let expected = trim_lines_string( - " - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . 3 . . . . . . - . . . . . . o . 6 o . . . . . - . . . . . . o x x 3 3 3 . . . - . . . . . . . o 9 . . . . . . - . . . . . . . . x . . . . . . - . . . . . . . . 6 . . . . . . - . . . . . . . . 3 . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - ", - ); - assert_eq!(result, expected); - - let field = PotentialField::init(White, 2, &board); - let result = field.overlay(&board); - let expected = trim_lines_string( - " - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . 2 . . 2 2 . . 2 . . . - . . . . . 4 . 2 4 . 4 . . . . - . . . . . . 6 2 3 6 . . 2 . . - . . . . . . o1014 o . 4 . . . - . . . . . . o x x 3 9 3 2 . . - . . . . . . 8 o1116 . . . . . - . . . . 210 6 8 x1012 4 2 . . - . . . . 4 . . 8 6 2 2 4 . . . - . . . 2 . . 6 . 3 . 2 2 2 . . - . . . . . 4 . . 4 . . 2 . . . - . . . . 2 . . . 2 . . . 2 . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - ", - ); - assert_eq!(result, expected); - - Ok(()) - } - - #[test] - fn test_collect() -> Result<(), String> { - let mut board = " - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . o . x o . . . . . - . . . . . . o x x . . . . . . - . . . . . . . o o . . . . . . - . . . . . . . . x . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - " - .parse::()?; - - let mut field = PotentialField::init(White, 3, &board); - let result = field.overlay(&board); - let expected = trim_lines_string( - " - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . 3 . . 3 . . . - . . . . . . . . 3 . 6 . . . . - . . . . . . . . 3 9 . . . . . - . . . . . . o . x o . . . . . - . . . . . . o x x 3 3 3 . . . - . . . . . . 9 o o . . . . . . - . . . . . 6 . . x . . . . . . - . . . . 3 . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - ", - ); - assert_eq!(result, expected); - - let p = Point(5, 6); - board.put_mut(White, p); - field.update_along(p, &board); - let result = field.overlay(&board); - let expected = trim_lines_string( - " - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . 3 . . 3 . . . - . . . . . . . . 3 . 6 . . . . - . . . . . . . . 3 4 . . . . . - . . . . . . o . x o . . . . . - . . . . . . o x x 3 3 3 . . . - . . . . . . 8 o o . . . . . . - . . . . 3 x 6 6 x 3 . . . . . - . . . . 4 . . . . . . . . . . - . . . 3 . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - . . . . . . . . . . . . . . . - ", - ); - assert_eq!(result, expected); - - let result = field.collect(3); - let expected = [ - (Point(3, 4), 3), - (Point(4, 5), 4), - (Point(4, 6), 3), - (Point(6, 6), 6), - (Point(6, 7), 8), - (Point(7, 6), 6), - (Point(8, 10), 3), - (Point(8, 11), 3), - (Point(8, 12), 3), - (Point(9, 6), 3), - (Point(9, 8), 3), - (Point(9, 10), 4), - (Point(10, 8), 3), - (Point(10, 11), 6), - (Point(11, 8), 3), - (Point(11, 12), 3), - ]; - assert_eq!(result, expected); - - Ok(()) - } - - fn trim_lines_string(s: &str) -> String { - s.trim() - .split("\n") - .map(|ls| " ".to_string() + ls.trim()) - .collect::>() - .join("\n") - } -} diff --git a/src/mate/vct_lazy/state.rs b/src/mate/vct_lazy/state.rs index 6562d40..e73dc65 100644 --- a/src/mate/vct_lazy/state.rs +++ b/src/mate/vct_lazy/state.rs @@ -1,4 +1,4 @@ -use super::field::*; +use crate::analysis::field::PotentialField; use crate::board::StructureKind::*; use crate::board::*; use crate::mate::game::*;