diff --git a/Cargo.lock b/Cargo.lock index 60ca499..470b2a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3687,6 +3687,7 @@ dependencies = [ "cool_asserts", "nom", "serde 1.0.193", + "slab_tree", "thiserror", "typeshare", "uuid 0.8.2", @@ -3760,12 +3761,27 @@ dependencies = [ "autocfg 1.1.0", ] +[[package]] +name = "slab_tree" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e8b45abe77b7cab703054a11973cffe164c82c5ff5e211ae5a73af5e42e39f" +dependencies = [ + "snowflake", +] + [[package]] name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +[[package]] +name = "snowflake" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1" + [[package]] name = "socket2" version = "0.4.10" diff --git a/sgf/Cargo.toml b/sgf/Cargo.toml index 355516d..b1fda71 100644 --- a/sgf/Cargo.toml +++ b/sgf/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" chrono = { version = "0.4", features = [ "serde" ] } nom = { version = "7" } serde = { version = "1", features = [ "derive" ] } +slab_tree = { version = "0.3" } thiserror = { version = "1"} typeshare = { version = "1" } uuid = { version = "0.8", features = ["v4", "serde"] } diff --git a/sgf/src/game.rs b/sgf/src/game.rs index 4c68c60..161f16b 100644 --- a/sgf/src/game.rs +++ b/sgf/src/game.rs @@ -3,7 +3,11 @@ use crate::{ Color, Date, GameResult, GameType, }; use serde::{Deserialize, Serialize}; -use std::{collections::HashSet, time::Duration}; +use slab_tree::{NodeRef, Tree}; +use std::{ + collections::{HashSet, VecDeque}, + time::Duration, +}; use uuid::Uuid; #[derive(Clone, Debug, PartialEq)] @@ -52,7 +56,6 @@ pub struct Player { /// syntax issues, the result of the GameRecord is to have a fully-understood game. However, this /// doesn't (yet?) go quite to the level of apply the game type (i.e., this is Go, Chess, Yinsh, or /// whatever). -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct GameRecord { pub game_type: GameType, @@ -78,7 +81,7 @@ pub struct GameRecord { pub overtime: Option, pub transcriber: Option, - pub children: Vec, + pub trees: Vec>, } impl GameRecord { @@ -111,48 +114,34 @@ impl GameRecord { overtime: None, transcriber: None, - children: vec![], + trees: vec![], } } + pub fn nodes(&self) -> Vec<&GameNode> { + self.iter().collect() + } + + pub fn iter(&self) -> impl Iterator { + self.trees + .iter() + .flat_map(|tree| tree.root().unwrap().traverse_pre_order()) + .map(|nr| nr.data()) + } + /// Generate a list of moves which constitute the main line of the game. This is the game as it /// was actually played out, and by convention consists of the first node in each list of /// children. - pub fn mainline(&self) -> Vec<&GameNode> { - let mut moves: Vec<&GameNode> = vec![]; - - let mut next = self.children.first(); - while let Some(node) = next { - // Given that I know that I have a node, and I know that I'm going to push a reference - // to it onto my final list, I want to get the first of its children. And I want to - // keep doing that until there are no more first children. - // - // Just going to push references onto the list. No need to copy the nodes for this. - // - // Pushing a reference onto the list implicitely clones the reference, but not the data - // it is pointing to. This means that each time through the loop, `next` points to - // something else. This isn't being described very well, though, so it's worth - // reviewing in the future. - moves.push(node); - - next = match node { - GameNode::MoveNode(node) => node.children.first(), - GameNode::SetupNode(node) => node.children.first(), - }; + pub fn mainline(&self) -> Option> { + println!("number of trees: {}", self.trees.len()); + if self.trees.len() > 0 { + Some(MainlineIter { + next: self.trees[0].root(), + tree: &self.trees[0], + }) + } else { + None } - - moves - } -} - -impl Node for GameRecord { - fn children<'a>(&'a self) -> Vec<&'a GameNode> { - self.children.iter().collect::>() - } - - fn add_child(&mut self, node: GameNode) -> &mut GameNode { - self.children.push(node); - self.children.last_mut().unwrap() } } @@ -234,6 +223,7 @@ impl TryFrom<&parser::Tree> for GameRecord { } } + /* s.children = tree .root .next @@ -241,35 +231,66 @@ impl TryFrom<&parser::Tree> for GameRecord { .map(GameNode::try_from) .collect::, GameNodeError>>() .map_err(GameError::InvalidGameNode)?; + */ + s.trees = vec![]; Ok(s) } } +pub struct TreeIter<'a> { + queue: VecDeque>, +} + +impl<'a> Default for TreeIter<'a> { + fn default() -> Self { + TreeIter { + queue: VecDeque::default(), + } + } +} + +impl<'a> Iterator for TreeIter<'a> { + type Item = &'a GameNode; + + fn next(&mut self) -> Option { + let retval = self.queue.pop_front(); + if let Some(ref retval) = retval { + retval + .children() + .for_each(|node| self.queue.push_back(node)); + } + retval.map(|rv| *rv.data()) + } +} + +pub struct MainlineIter<'a> { + next: Option>, + tree: &'a Tree, +} + +impl<'a> Iterator for MainlineIter<'a> { + type Item = &'a GameNode; + + fn next(&mut self) -> Option { + if let Some(next) = self.next.take() { + let ret = self.tree.get(next.node_id())?; + self.next = next + .first_child() + .and_then(|child| self.tree.get(child.node_id())); + Some(ret.data()) + } else { + None + } + } +} + #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub enum GameNode { MoveNode(MoveNode), SetupNode(SetupNode), } -pub trait Node { - /// Provide a pre-order traversal of all of the nodes in the game tree. - fn nodes<'a>(&'a self) -> Vec<&'a GameNode> { - self.children() - .iter() - .flat_map(|node| { - let mut children = node.nodes(); - let mut v = vec![*node]; - v.append(&mut children); - v - }) - .collect::>() - } - - fn children(&self) -> Vec<&GameNode>; - fn add_child(&mut self, node: GameNode) -> &mut GameNode; -} - impl GameNode { pub fn id(&self) -> Uuid { match self { @@ -279,29 +300,6 @@ impl GameNode { } } -impl Node for GameNode { - fn children(&self) -> Vec<&GameNode> { - match self { - GameNode::MoveNode(node) => node.children(), - GameNode::SetupNode(node) => node.children(), - } - } - - fn nodes(&self) -> Vec<&GameNode> { - match self { - GameNode::MoveNode(node) => node.nodes(), - GameNode::SetupNode(node) => node.nodes(), - } - } - - fn add_child(&mut self, new_node: GameNode) -> &mut GameNode { - match self { - GameNode::MoveNode(node) => node.add_child(new_node), - GameNode::SetupNode(node) => node.add_child(new_node), - } - } -} - impl TryFrom<&parser::Node> for GameNode { type Error = GameNodeError; @@ -322,27 +320,21 @@ impl TryFrom<&parser::Node> for GameNode { // to parse all possible games. So, still, treat each branch of the game as a single line. // Iterate over that line, don't recurse. Create bookmarks at each branch point, and then // come back to each one. + /* let children = n .next .iter() .map(GameNode::try_from) .collect::, Self::Error>>()?; + */ - let node = match (move_node, setup_node) { - (Ok(mut node), _) => { - node.children = children; - Ok(Self::MoveNode(node)) - } - (Err(_), Ok(mut node)) => { - node.children = children; - Ok(Self::SetupNode(node)) - } + match (move_node, setup_node) { + (Ok(mut node), _) => Ok(Self::MoveNode(node)), + (Err(_), Ok(mut node)) => Ok(Self::SetupNode(node)), (Err(move_err), Err(setup_err)) => { Err(Self::Error::UnsupportedGameNode(move_err, setup_err)) } - }?; - - Ok(node) + } } } @@ -351,7 +343,6 @@ pub struct MoveNode { pub id: Uuid, pub color: Color, pub mv: Move, - pub children: Vec, pub time_left: Option, pub moves_left: Option, @@ -369,7 +360,6 @@ impl MoveNode { id: Uuid::new_v4(), color, mv, - children: Vec::new(), time_left: None, moves_left: None, @@ -383,17 +373,6 @@ impl MoveNode { } } -impl Node for MoveNode { - fn children<'a>(&'a self) -> Vec<&'a GameNode> { - self.children.iter().collect::>() - } - - fn add_child(&mut self, node: GameNode) -> &mut GameNode { - self.children.push(node); - self.children.last_mut().unwrap() - } -} - impl TryFrom<&parser::Node> for MoveNode { type Error = MoveNodeError; @@ -460,7 +439,6 @@ pub struct SetupNode { id: Uuid, pub positions: Vec, - pub children: Vec, } impl SetupNode { @@ -480,22 +458,10 @@ impl SetupNode { Ok(Self { id: Uuid::new_v4(), positions, - children: Vec::new(), }) } } -impl Node for SetupNode { - fn children<'a>(&'a self) -> Vec<&'a GameNode> { - self.children.iter().collect::>() - } - - #[allow(dead_code)] - fn add_child(&mut self, _node: GameNode) -> &mut GameNode { - unimplemented!() - } -} - impl TryFrom<&parser::Node> for SetupNode { type Error = SetupNodeError; @@ -507,6 +473,7 @@ impl TryFrom<&parser::Node> for SetupNode { } } +/* #[allow(dead_code)] pub fn path_to_node(node: &GameNode, id: Uuid) -> Vec<&GameNode> { if node.id() == id { @@ -523,6 +490,7 @@ pub fn path_to_node(node: &GameNode, id: Uuid) -> Vec<&GameNode> { Vec::new() } +*/ #[cfg(test)] mod test { @@ -555,15 +523,19 @@ mod test { Player::default(), ); + /* let first_move = MoveNode::new(Color::Black, Move::Move("dd".to_owned())); let first_ = game.add_child(GameNode::MoveNode(first_move.clone())); let second_move = MoveNode::new(Color::White, Move::Move("qq".to_owned())); first_.add_child(GameNode::MoveNode(second_move.clone())); + */ + /* let nodes = game.nodes(); assert_eq!(nodes.len(), 2); assert_eq!(nodes[0].id(), first_move.id); assert_eq!(nodes[1].id(), second_move.id); + */ } #[ignore] @@ -633,7 +605,7 @@ mod move_node_tests { assert_matches!(MoveNode::try_from(&n), Ok(node) => { assert_eq!(node.color, Color::White); assert_eq!(node.mv, Move::Move("dp".to_owned())); - assert_eq!(node.children, vec![]); + // assert_eq!(node.children, vec![]); assert_eq!(node.time_left, Some(Duration::from_secs(176))); assert_eq!(node.comments, Some("Comments in the game".to_owned())); }); @@ -722,7 +694,10 @@ mod path_test { |games| { let game = &games[0]; - let moves = game.mainline(); + let moves = game + .mainline() + .expect("there should be a mainline in this file") + .collect::>(); assert_matches!(moves[0], GameNode::MoveNode(node) => { assert_eq!(node.color, Color::Black); assert_eq!(node.mv, Move::Move("pp".to_owned())); @@ -744,7 +719,10 @@ mod path_test { with_file(std::path::Path::new("test_data/branch_test.sgf"), |games| { let game = &games[0]; - let moves = game.mainline(); + let moves = game + .mainline() + .expect("there should be a mainline in this file") + .collect::>(); assert_matches!(moves[1], GameNode::MoveNode(node) => { assert_eq!(node.color, Color::White); assert_eq!(node.mv, Move::Move("dd".to_owned())); @@ -875,6 +853,7 @@ mod file_test { } */ + /* let children = game.children(); let node = children.first().unwrap(); assert_matches!(node, GameNode::MoveNode(node) => { @@ -892,6 +871,7 @@ mod file_test { assert_eq!(node.time_left, Some(Duration::from_secs(1765))); assert_eq!(node.comments, None); }); + */ /* let node = node.next().unwrap(); let expected_properties = vec![