From 093e1f7f8afa311a749319725b35d2fce25016d5 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Tue, 9 Apr 2024 22:55:33 -0400 Subject: [PATCH 01/11] Start writing functions for finding a node within the tree --- tree/src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tree/src/lib.rs b/tree/src/lib.rs index 4644876..337a97f 100644 --- a/tree/src/lib.rs +++ b/tree/src/lib.rs @@ -9,6 +9,10 @@ use std::{ rc::Rc, }; +// I need to take what I learned about linked lists and about the other Tree data structure, and +// apply it here with arena allocation. +// +// Also, smarter node allocation and pointer handling in order to avoid clones. #[derive(Clone, Debug, Default)] pub enum Tree { #[default] @@ -55,6 +59,16 @@ impl Tree { None } + // Do a depth-first-search in order to get the path to a node. Start with a naive recursive + // implementation, then switch to a stack-based implementation in order to avoid exceeding the + // stack. + pub fn path_to(&self, f: F) -> Vec> + where + F: FnOnce(&T) -> bool + Copy, + { + unimplemented!() + } + /// Convert each node of a tree from type T to type U pub fn map(&self, op: F) -> Tree where @@ -146,6 +160,13 @@ impl Node { } } +impl PartialEq for Node { + fn eq(&self, other: &Node) -> bool { + self.0.borrow().value == other.0.borrow().value + && self.0.borrow().children == other.0.borrow().children + } +} + #[cfg(test)] mod tests { use super::*; @@ -184,4 +205,31 @@ mod tests { assert!(tree2.find_bfs(|val| *val == "16").is_some()); assert!(tree2.find_bfs(|val| *val == "17").is_some()); } + + #[test] + fn path_to_on_empty_tree_returns_empty() { + let tree: Tree<&str> = Tree::default(); + + assert_eq!(tree.path_to(|val| *val == "i"), vec![]); + } + + // A + // B G H + // C I + // D E F + #[test] + fn it_can_find_a_path_to_a_node() { + let (tree, a) = Tree::new("A"); + let b = a.add_child_value("B"); + let c = b.add_child_value("C"); + let _d = c.add_child_value("D"); + let _e = c.add_child_value("D"); + let _f = c.add_child_value("D"); + let _g = a.add_child_value("G"); + let h = a.add_child_value("H"); + let i = a.add_child_value("I"); + + assert_eq!(tree.path_to(|val| *val == "z"), vec![]); + assert_eq!(tree.path_to(|val| *val == "i"), vec![a, h, i]); + } } -- 2.44.1 From c50bd652f19fe8a64d394f430c15805087c6427c Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Fri, 19 Apr 2024 08:03:40 -0400 Subject: [PATCH 02/11] Switch from my custom Tree setup to a slab tree in GameRecord --- Cargo.lock | 16 ++++ sgf/Cargo.toml | 1 + sgf/src/game.rs | 220 ++++++++++++++++++++++-------------------------- 3 files changed, 117 insertions(+), 120 deletions(-) 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![ -- 2.44.1 From c913e9da3725d82bc9514e8d623bf03804cf775e Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Fri, 19 Apr 2024 09:22:42 -0400 Subject: [PATCH 03/11] Convert the recursive parse tree to a slab GameTree --- sgf/src/game.rs | 97 +++++++++++++++++++++++++++++------- sgf/src/lib.rs | 2 +- sgf/test_data/multi-tree.sgf | 1 + 3 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 sgf/test_data/multi-tree.sgf diff --git a/sgf/src/game.rs b/sgf/src/game.rs index 161f16b..cca9488 100644 --- a/sgf/src/game.rs +++ b/sgf/src/game.rs @@ -3,7 +3,7 @@ use crate::{ Color, Date, GameResult, GameType, }; use serde::{Deserialize, Serialize}; -use slab_tree::{NodeRef, Tree}; +use slab_tree::{NodeId, NodeMut, NodeRef, Tree}; use std::{ collections::{HashSet, VecDeque}, time::Duration, @@ -36,7 +36,7 @@ pub enum SetupNodeError { #[derive(Clone, Debug, PartialEq)] pub enum GameNodeError { - UnsupportedGameNode(MoveNodeError, SetupNodeError), + UnsupportedGameNode(MoveNodeError, SetupNodeError, parser::Node), ConflictingProperty, ConflictingPosition, } @@ -145,10 +145,10 @@ impl GameRecord { } } -impl TryFrom<&parser::Tree> for GameRecord { +impl TryFrom for GameRecord { type Error = GameError; - fn try_from(tree: &parser::Tree) -> Result { + fn try_from(tree: parser::Tree) -> Result { let mut ty = None; let mut size = None; let mut black_player = Player { @@ -232,12 +232,46 @@ impl TryFrom<&parser::Tree> for GameRecord { .collect::, GameNodeError>>() .map_err(GameError::InvalidGameNode)?; */ - s.trees = vec![]; + + s.trees = tree.root.next.into_iter() + .map(recursive_tree_to_slab_tree) + .collect::>, GameError>>()?; Ok(s) } } +fn recursive_tree_to_slab_tree(node: parser::Node) -> Result, GameError> { + let mut slab = Tree::new(); + let mut nodes: VecDeque<(NodeId, parser::Node)> = VecDeque::new(); + + let root_id = + slab.set_root(GameNode::try_from(node.clone()).map_err(GameError::InvalidGameNode)?); + nodes.push_back((root_id, node)); + + // I need to keep track of the current parent, and I need to keep on digging deeper into the + // tree. Given that I have the root, I can then easily find out all of the children. + // + // So, maybe I take the list of children. Assign each one of them to a place in the slab tree. + // Then push the child *and* its ID into a dequeue. So long as the dequeue is not empty, I want + // to pop a node and its ID from the dequeue. The retrieve the NodeMut for it and work on the + // node's children. + while let Some((node_id, node)) = nodes.pop_front() { + let mut game_node: NodeMut = slab + .get_mut(node_id) + .expect("invalid node_id when retrieving nodes from the game"); + // I have a node that is in the tree. Now run across all of its children, adding each one + // to the tree and pushing them into the deque along with their IDs. + for child in node.next { + let slab_child = game_node + .append(GameNode::try_from(child.clone()).map_err(GameError::InvalidGameNode)?); + nodes.push_back((slab_child.node_id(), child)); + } + } + + Ok(slab) +} + pub struct TreeIter<'a> { queue: VecDeque>, } @@ -300,10 +334,10 @@ impl GameNode { } } -impl TryFrom<&parser::Node> for GameNode { +impl TryFrom for GameNode { type Error = GameNodeError; - fn try_from(n: &parser::Node) -> Result { + fn try_from(n: parser::Node) -> Result { // I originally wrote this recursively. However, on an ordinary game of a couple hundred // moves, that meant that I was recursing 500 functions, and that exceeded the stack limit. // So, instead, I need to unroll everything to non-recursive form. @@ -312,8 +346,8 @@ impl TryFrom<&parser::Node> for GameNode { // only use the MoveNode::try_from and SetupNode::try_from if those functions don't // recurse. Instead, I'm going to process just that node, then return to here and process // the children. - let move_node = MoveNode::try_from(n); - let setup_node = SetupNode::try_from(n); + let move_node = MoveNode::try_from(n.clone()); + let setup_node = SetupNode::try_from(n.clone()); // I'm much too tired when writing this. I'm still recursing, but I did cut the number of // recursions in half. This helps, but it still doesn't guarantee that I'm going to be able @@ -332,7 +366,7 @@ impl TryFrom<&parser::Node> for GameNode { (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)) + Err(Self::Error::UnsupportedGameNode(move_err, setup_err, n)) } } } @@ -373,10 +407,10 @@ impl MoveNode { } } -impl TryFrom<&parser::Node> for MoveNode { +impl TryFrom for MoveNode { type Error = MoveNodeError; - fn try_from(n: &parser::Node) -> Result { + fn try_from(n: parser::Node) -> Result { let s = match n.mv() { Some((color, mv)) => { let mut s = Self::new(color, mv); @@ -462,10 +496,10 @@ impl SetupNode { } } -impl TryFrom<&parser::Node> for SetupNode { +impl TryFrom for SetupNode { type Error = SetupNodeError; - fn try_from(n: &parser::Node) -> Result { + fn try_from(n: parser::Node) -> Result { match n.setup() { Some(elements) => Self::new(elements), None => Err(Self::Error::NotASetupNode), @@ -560,7 +594,7 @@ mod test { ], next: vec![], }; - assert_matches!(GameNode::try_from(&n), Ok(GameNode::MoveNode(_))); + assert_matches!(GameNode::try_from(n), Ok(GameNode::MoveNode(_))); } } @@ -602,7 +636,7 @@ mod move_node_tests { ], next: vec![], }; - assert_matches!(MoveNode::try_from(&n), Ok(node) => { + 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![]); @@ -625,7 +659,7 @@ mod move_node_tests { next: vec![], }; assert_matches!( - MoveNode::try_from(&n), + MoveNode::try_from(n), Err(MoveNodeError::IncompatibleProperty(_)) ); } @@ -675,7 +709,7 @@ mod path_test { let (_, games) = parse_collection::>(text).unwrap(); let games = games .into_iter() - .map(|game| GameRecord::try_from(&game).expect("game to parse")) + .map(|game| GameRecord::try_from(game).expect("game to parse")) .collect::>(); f(games); } @@ -769,7 +803,7 @@ mod file_test { let (_, games) = parse_collection::>(text).unwrap(); let games = games .into_iter() - .map(|game| GameRecord::try_from(&game).expect("game to parse")) + .map(|game| GameRecord::try_from(game).expect("game to parse")) .collect::>(); f(games); } @@ -891,4 +925,29 @@ mod file_test { }, ); } + + #[test] + fn it_can_load_a_file_with_multiple_roots() { + with_file(std::path::Path::new("test_data/multi-tree.sgf"), |games| { + assert_eq!(games.len(), 1); + let game = &games[0]; + assert_eq!(game.game_type, GameType::Go); + assert_eq!( + game.board_size, + Size { + width: 19, + height: 19 + } + ); + assert_eq!(game.trees.len(), 2); + assert_matches!(game.trees[0].root().unwrap().data(), GameNode::MoveNode(node) => { + assert_eq!(node.color, Color::Black); + assert_eq!(node.mv, Move::Move("pd".to_owned())); + }); + assert_matches!(game.trees[1].root().unwrap().data(), GameNode::MoveNode(node) => { + assert_eq!(node.color, Color::Black); + assert_eq!(node.mv, Move::Move("pc".to_owned())); + }); + }); + } } diff --git a/sgf/src/lib.rs b/sgf/src/lib.rs index 6849499..1685bb3 100644 --- a/sgf/src/lib.rs +++ b/sgf/src/lib.rs @@ -73,7 +73,7 @@ pub fn parse_sgf(input: &str) -> Result> let (_, games) = parse_collection::>(input)?; let games = games .into_iter() - .map(|game| GameRecord::try_from(&game)) + .map(|game| GameRecord::try_from(game)) .collect::>>(); Ok(games) diff --git a/sgf/test_data/multi-tree.sgf b/sgf/test_data/multi-tree.sgf new file mode 100644 index 0000000..3fb4dd2 --- /dev/null +++ b/sgf/test_data/multi-tree.sgf @@ -0,0 +1 @@ +(;GM[1]FF[4]CA[UTF-8]AP[Sabaki:0.52.2]KM[7.5]SZ[19]DT[2024-04-19](;B[pd](;W[qc];B[qd];W[pc];B[oc];W[ob];B[nc];W[nb];B[mc];W[rd];B[re];W[rc];B[qf])(;W[qf];B[nc];W[rd];B[qc];W[pi]))(;B[pc];W[qe];B[oe];W[pg];B[ld];W[qj])) \ No newline at end of file -- 2.44.1 From d7f5269e152a80593de2813977e1652cf692ef47 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Thu, 25 Apr 2024 11:04:59 -0400 Subject: [PATCH 04/11] Begin implementing traits for a game record --- sgf/src/game.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/sgf/src/game.rs b/sgf/src/game.rs index cca9488..181956d 100644 --- a/sgf/src/game.rs +++ b/sgf/src/game.rs @@ -5,8 +5,7 @@ use crate::{ use serde::{Deserialize, Serialize}; use slab_tree::{NodeId, NodeMut, NodeRef, Tree}; use std::{ - collections::{HashSet, VecDeque}, - time::Duration, + collections::{HashMap, HashSet, VecDeque}, fmt::Debug, ops::Deref, time::Duration }; use uuid::Uuid; @@ -56,6 +55,7 @@ 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)] pub struct GameRecord { pub game_type: GameType, @@ -81,7 +81,7 @@ pub struct GameRecord { pub overtime: Option, pub transcriber: Option, - pub trees: Vec>, + pub trees: Vec, } impl GameRecord { @@ -235,13 +235,13 @@ impl TryFrom for GameRecord { s.trees = tree.root.next.into_iter() .map(recursive_tree_to_slab_tree) - .collect::>, GameError>>()?; + .collect::, GameError>>()?; Ok(s) } } -fn recursive_tree_to_slab_tree(node: parser::Node) -> Result, GameError> { +fn recursive_tree_to_slab_tree(node: parser::Node) -> Result { let mut slab = Tree::new(); let mut nodes: VecDeque<(NodeId, parser::Node)> = VecDeque::new(); @@ -269,7 +269,7 @@ fn recursive_tree_to_slab_tree(node: parser::Node) -> Result, Gam } } - Ok(slab) + Ok(GameTree(slab)) } pub struct TreeIter<'a> { @@ -298,6 +298,50 @@ impl<'a> Iterator for TreeIter<'a> { } } +#[derive(PartialEq)] +struct GameTree(Tree); + +impl Clone for GameTree { + fn clone(&self) -> Self { + match self.0.root() { + None => Self(Tree::new()), + Some(source_root_node) => { + let mut dest = Tree::new(); + let dest_root_id = dest.set_root(source_root_node.data().clone()); + + // In order to add a node to the new tree, I need to know the ID of the parent in + // the source tree and the ID of the parent in the destination tree. So I want a + // lookup table that maps source IDs to destination IDs. But is that sufficient? + // Perhaps I can just keep a mapping from a source noderef to a destination ID. + // I don't think I can keep more than one mutable destination node. + + let mut mapping: HashMap = HashMap::new(); + mapping.insert(source_root_node.node_id(), dest_root_id); + + for source_node in source_root_node.traverse_level_order() { + } + + Self(dest) + } + } + } +} + +impl Debug for GameTree { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + self.write_formatted(f) + } +} + +impl Deref for GameTree { + type Target = Tree; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + + pub struct MainlineIter<'a> { next: Option>, tree: &'a Tree, @@ -950,4 +994,14 @@ mod file_test { }); }); } + + #[test] + fn it_can_copy_a_game_record() { + with_file(std::path::Path::new("test_data/multi-tree.sgf"), |games| { + let dest = games.clone(); + + assert_eq!(games.len(), dest.len()); + assert_eq!(games[0], dest[0]); + }); + } } -- 2.44.1 From 0534143d6b2cb8cdc15ae0b6a764211ba3811ba7 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Fri, 26 Apr 2024 09:05:38 -0400 Subject: [PATCH 05/11] Finish implementing GameTree.Clone and PartialEq --- sgf/src/game.rs | 80 +++++++++++++++++++++++++++++++----------------- sgf/src/lib.rs | 3 +- sgf/src/types.rs | 1 + 3 files changed, 55 insertions(+), 29 deletions(-) diff --git a/sgf/src/game.rs b/sgf/src/game.rs index 181956d..9f748da 100644 --- a/sgf/src/game.rs +++ b/sgf/src/game.rs @@ -5,7 +5,10 @@ use crate::{ use serde::{Deserialize, Serialize}; use slab_tree::{NodeId, NodeMut, NodeRef, Tree}; use std::{ - collections::{HashMap, HashSet, VecDeque}, fmt::Debug, ops::Deref, time::Duration + collections::{HashMap, HashSet, VecDeque}, + fmt::Debug, + ops::Deref, + time::Duration, }; use uuid::Uuid; @@ -134,7 +137,7 @@ impl GameRecord { /// children. pub fn mainline(&self) -> Option> { println!("number of trees: {}", self.trees.len()); - if self.trees.len() > 0 { + if !self.trees.is_empty(){ Some(MainlineIter { next: self.trees[0].root(), tree: &self.trees[0], @@ -233,7 +236,10 @@ impl TryFrom for GameRecord { .map_err(GameError::InvalidGameNode)?; */ - s.trees = tree.root.next.into_iter() + s.trees = tree + .root + .next + .into_iter() .map(recursive_tree_to_slab_tree) .collect::, GameError>>()?; @@ -272,10 +278,12 @@ fn recursive_tree_to_slab_tree(node: parser::Node) -> Result { queue: VecDeque>, } +/* impl<'a> Default for TreeIter<'a> { fn default() -> Self { TreeIter { @@ -283,6 +291,7 @@ impl<'a> Default for TreeIter<'a> { } } } +*/ impl<'a> Iterator for TreeIter<'a> { type Item = &'a GameNode; @@ -298,8 +307,7 @@ impl<'a> Iterator for TreeIter<'a> { } } -#[derive(PartialEq)] -struct GameTree(Tree); +pub struct GameTree(Tree); impl Clone for GameTree { fn clone(&self) -> Self { @@ -319,6 +327,19 @@ impl Clone for GameTree { mapping.insert(source_root_node.node_id(), dest_root_id); for source_node in source_root_node.traverse_level_order() { + match source_node.parent() { + None => {} + Some(parent) => { + let source_node_parent_id = parent.node_id(); + let target_node_parent_id = mapping.get(&source_node_parent_id).expect("node should have been added to the source to dest mapping when being cloned"); + + let mut parent = dest.get_mut(*target_node_parent_id).expect( + "destination parent node to exist before reaching potential children", + ); + let dest_id = parent.append(source_node.data().clone()).node_id(); + mapping.insert(source_node.node_id(), dest_id); + } + } } Self(dest) @@ -341,6 +362,30 @@ impl Deref for GameTree { } } +impl PartialEq for GameTree { + fn eq(&self, other: &Self) -> bool { + // Get pre-order iterators over both trees, zip them, and ensure that the data contents are + // the same between them + let left_root = self.root(); + let right_root = other.root(); + + match (left_root, right_root) { + (Some(left_root), Some(right_root)) => { + for (left_node, right_node) in std::iter::zip( + left_root.traverse_pre_order(), + right_root.traverse_pre_order(), + ) { + if left_node.data() != right_node.data() { + return false; + } + } + } + (None, None) => return true, + _ => return false, + } + true + } +} pub struct MainlineIter<'a> { next: Option>, @@ -382,33 +427,12 @@ impl TryFrom for GameNode { type Error = GameNodeError; fn try_from(n: parser::Node) -> Result { - // I originally wrote this recursively. However, on an ordinary game of a couple hundred - // moves, that meant that I was recursing 500 functions, and that exceeded the stack limit. - // So, instead, I need to unroll everything to non-recursive form. - // - // So, I can treat each branch of the tree as a single line. Iterate over that line. I can - // only use the MoveNode::try_from and SetupNode::try_from if those functions don't - // recurse. Instead, I'm going to process just that node, then return to here and process - // the children. let move_node = MoveNode::try_from(n.clone()); let setup_node = SetupNode::try_from(n.clone()); - // I'm much too tired when writing this. I'm still recursing, but I did cut the number of - // recursions in half. This helps, but it still doesn't guarantee that I'm going to be able - // 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>>()?; - */ - match (move_node, setup_node) { - (Ok(mut node), _) => Ok(Self::MoveNode(node)), - (Err(_), Ok(mut node)) => Ok(Self::SetupNode(node)), + (Ok(node), _) => Ok(Self::MoveNode(node)), + (Err(_), Ok(node)) => Ok(Self::SetupNode(node)), (Err(move_err), Err(setup_err)) => { Err(Self::Error::UnsupportedGameNode(move_err, setup_err, n)) } diff --git a/sgf/src/lib.rs b/sgf/src/lib.rs index 1685bb3..d06b8fc 100644 --- a/sgf/src/lib.rs +++ b/sgf/src/lib.rs @@ -22,6 +22,7 @@ pub enum Error { InvalidSgf(VerboseNomError), } +#[allow(dead_code)] #[derive(Debug)] pub struct VerboseNomError(nom::error::VerboseError); @@ -73,7 +74,7 @@ pub fn parse_sgf(input: &str) -> Result> let (_, games) = parse_collection::>(input)?; let games = games .into_iter() - .map(|game| GameRecord::try_from(game)) + .map(GameRecord::try_from) .collect::>>(); Ok(games) diff --git a/sgf/src/types.rs b/sgf/src/types.rs index 3cf3d13..723164d 100644 --- a/sgf/src/types.rs +++ b/sgf/src/types.rs @@ -56,6 +56,7 @@ pub enum Error { InvalidSgf(VerboseNomError), } +#[allow(dead_code)] #[derive(Debug)] pub struct VerboseNomError(nom::error::VerboseError); -- 2.44.1 From bc5042c0045c8545acb6eabea34fbf405ee9789c Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 28 Apr 2024 13:48:52 -0400 Subject: [PATCH 06/11] Start propogating the slab_tree up to OTG --- Cargo.lock | 1 + otg/core/Cargo.toml | 1 + otg/core/src/api.rs | 2 +- otg/core/src/database.rs | 3 +- otg/core/src/lib.rs | 2 +- otg/core/src/library.rs | 2 +- otg/core/src/types.rs | 250 +++++++++++++++++++++++++-------------- sgf/src/game.rs | 16 ++- sgf/src/lib.rs | 2 +- 9 files changed, 180 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 470b2a9..93efbef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2708,6 +2708,7 @@ dependencies = [ "serde 1.0.193", "serde_json", "sgf", + "slab_tree", "thiserror", "uuid 0.8.2", ] diff --git a/otg/core/Cargo.toml b/otg/core/Cargo.toml index dc8954d..a6b7215 100644 --- a/otg/core/Cargo.toml +++ b/otg/core/Cargo.toml @@ -14,6 +14,7 @@ sgf = { path = "../../sgf" } grid = { version = "0.9" } serde_json = { version = "1" } serde = { version = "1", features = [ "derive" ] } +slab_tree = { version = "0.3" } thiserror = { version = "1" } uuid = { version = "0.8", features = ["v4", "serde"] } diff --git a/otg/core/src/api.rs b/otg/core/src/api.rs index 0f5d914..153984b 100644 --- a/otg/core/src/api.rs +++ b/otg/core/src/api.rs @@ -81,7 +81,7 @@ impl From for Player { } */ -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug)] pub enum CoreResponse { Library(library::LibraryResponse), Settings(settings::SettingsResponse), diff --git a/otg/core/src/database.rs b/otg/core/src/database.rs index 035cd61..e144b57 100644 --- a/otg/core/src/database.rs +++ b/otg/core/src/database.rs @@ -42,7 +42,8 @@ impl Database { .unwrap(); match parse_sgf(&buffer) { Ok(sgfs) => { - let mut sgfs = sgfs.into_iter().flatten().collect::>(); + let mut sgfs = + sgfs.into_iter().flatten().collect::>(); games.append(&mut sgfs); } Err(err) => println!("Error parsing {:?}: {:?}", entry.path(), err), diff --git a/otg/core/src/lib.rs b/otg/core/src/lib.rs index 3168a04..5fde824 100644 --- a/otg/core/src/lib.rs +++ b/otg/core/src/lib.rs @@ -29,5 +29,5 @@ pub mod library; pub mod settings; mod types; -pub use types::{BoardError, Color, Config, ConfigOption, LibraryPath, Player, Rank, Size, Tree}; +pub use types::{BoardError, Color, Config, ConfigOption, LibraryPath, Player, Rank, Size}; diff --git a/otg/core/src/library.rs b/otg/core/src/library.rs index a18d454..135bb47 100644 --- a/otg/core/src/library.rs +++ b/otg/core/src/library.rs @@ -23,7 +23,7 @@ pub enum LibraryRequest { ListGames } -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug)] pub enum LibraryResponse { Games(Vec) } diff --git a/otg/core/src/types.rs b/otg/core/src/types.rs index 09a0a0f..12153eb 100644 --- a/otg/core/src/types.rs +++ b/otg/core/src/types.rs @@ -2,10 +2,9 @@ use crate::goban::{Coordinate, Goban}; use config::define_config; use config_derive::ConfigOption; use serde::{Deserialize, Serialize}; -use sgf::GameNode; +use sgf::GameTree; use std::{cell::RefCell, collections::VecDeque, fmt, path::PathBuf, time::Duration}; use thiserror::Error; -use uuid::Uuid; define_config! { LibraryPath(LibraryPath), @@ -229,6 +228,7 @@ impl GameState { } } +/* // To properly generate a tree, I need to know how deep to go. Then I can backtrace. Each node // needs to have a depth. Given a tree, the depth of the node is just the distance from the root. // This seems obvious, but I had to write it to discover how important that fact was. @@ -238,18 +238,22 @@ impl GameState { pub struct Tree { nodes: Vec>, } +*/ + +struct DepthTree(slab_tree::Tree); #[derive(Debug)] -pub struct Node { +pub struct SizeNode { pub id: usize, - node: T, + node_id: slab_tree::NodeId, parent: Option, depth: usize, width: RefCell>, children: Vec, } -impl Tree { +impl DepthTree { + /* fn new(root: T) -> Self { Tree { nodes: vec![Node { @@ -286,12 +290,16 @@ impl Tree { parent.children.push(next_idx); next_idx } + */ pub fn max_depth(&self) -> usize { + unimplemented!() + /* self.nodes.iter().fold( 0, |max, node| if node.depth > max { node.depth } else { max }, ) + */ } // Since I know the width of a node, now I want to figure out its placement in the larger @@ -310,6 +318,8 @@ impl Tree { // // When drawing nodes, I don't know how to persist the level of indent. pub fn position(&self, idx: usize) -> (usize, usize) { + unimplemented!() + /* let node = &self.nodes[idx]; match node.parent { Some(parent_idx) => { @@ -326,8 +336,10 @@ impl Tree { // Root nodes won't have a parent, so just put them in the first column None => (0, 0), } + */ } + /* // Given a node, do a postorder traversal to figure out the width of the node based on all of // its children. This is equivalent to the widest of all of its children at all depths. // @@ -359,8 +371,16 @@ impl Tree { queue.push_back(&self.nodes[0]); BFSIter { tree: self, queue } } + */ } +impl<'a> From<&'a GameTree> for DepthTree { + fn from(root: &'a GameTree) -> Self { + unimplemented!() + } +} + +/* impl<'a> From<&'a GameNode> for Tree { fn from(root: &'a GameNode) -> Self { fn add_subtree(tree: &mut Tree, parent_idx: usize, node: &GameNode) { @@ -390,7 +410,9 @@ impl<'a> From<&'a GameNode> for Tree { tree } } +*/ +/* pub struct BFSIter<'a, T> { tree: &'a Tree, queue: VecDeque<&'a Node>, @@ -410,12 +432,13 @@ impl<'a, T> Iterator for BFSIter<'a, T> { retval } } +*/ #[cfg(test)] mod test { use super::*; - use cool_asserts::assert_matches; - use sgf::{Move, MoveNode}; + // use sgf::{GameRecord, GameTree, GameType, Move, MoveNode}; + use sgf::{GameNode, GameTree, Move, MoveNode}; #[test] fn current_player_changes_after_move() { @@ -474,69 +497,91 @@ mod test { // B G H // C I // D E F + fn branching_tree() -> GameTree { + let mut game_tree = GameTree::default(); + let node_a = game_tree.set_root(GameNode::MoveNode(MoveNode::new( + sgf::Color::Black, + Move::Move("dp".to_owned()), + ))); + + let node_b = game_tree + .get_mut(node_a) + .unwrap() + .append(GameNode::MoveNode(MoveNode::new( + sgf::Color::Black, + Move::Move("dp".to_owned()), + ))).node_id(); + + let node_c = game_tree + .get_mut(node_b) + .unwrap() + .append(GameNode::MoveNode(MoveNode::new( + sgf::Color::Black, + Move::Move("dp".to_owned()), + ))).node_id(); + + let node_d = game_tree + .get_mut(node_c) + .unwrap() + .append(GameNode::MoveNode(MoveNode::new( + sgf::Color::Black, + Move::Move("dp".to_owned()), + ))).node_id(); + + let node_e = game_tree + .get_mut(node_c) + .unwrap() + .append(GameNode::MoveNode(MoveNode::new( + sgf::Color::Black, + Move::Move("dp".to_owned()), + ))).node_id(); + + let node_f = game_tree + .get_mut(node_c) + .unwrap() + .append(GameNode::MoveNode(MoveNode::new( + sgf::Color::Black, + Move::Move("dp".to_owned()), + ))).node_id(); + + let node_g = game_tree + .get_mut(node_a) + .unwrap() + .append(GameNode::MoveNode(MoveNode::new( + sgf::Color::Black, + Move::Move("dp".to_owned()), + ))).node_id(); + + let node_h = game_tree + .get_mut(node_a) + .unwrap() + .append(GameNode::MoveNode(MoveNode::new( + sgf::Color::Black, + Move::Move("dp".to_owned()), + ))).node_id(); + + let _ = game_tree + .get_mut(node_h) + .unwrap() + .append(GameNode::MoveNode(MoveNode::new( + sgf::Color::Black, + Move::Move("dp".to_owned()), + ))); + + game_tree + } + #[test] fn it_can_calculate_depth_from_game_tree() { - let mut node_a = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_b = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_c = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_d = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_e = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_f = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_g = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_h = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_i = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - - node_c.children.push(GameNode::MoveNode(node_d)); - node_c.children.push(GameNode::MoveNode(node_e)); - node_c.children.push(GameNode::MoveNode(node_f)); - - node_b.children.push(GameNode::MoveNode(node_c)); - - node_h.children.push(GameNode::MoveNode(node_i)); - - node_a.children.push(GameNode::MoveNode(node_b)); - node_a.children.push(GameNode::MoveNode(node_g)); - node_a.children.push(GameNode::MoveNode(node_h)); - - let game_tree = GameNode::MoveNode(node_a); - - let tree = Tree::from(&game_tree); - + let game_tree = branching_tree(); + let tree = DepthTree::from(&game_tree); assert_eq!(tree.max_depth(), 3); } - // A - // B G H - // C I - // D E F #[test] fn it_calculates_horizontal_position_of_nodes() { - let mut node_a = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_b = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_c = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_d = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_e = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_f = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_g = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_h = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_i = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - - node_c.children.push(GameNode::MoveNode(node_d)); - node_c.children.push(GameNode::MoveNode(node_e)); - node_c.children.push(GameNode::MoveNode(node_f)); - - node_b.children.push(GameNode::MoveNode(node_c)); - - node_h.children.push(GameNode::MoveNode(node_i)); - - node_a.children.push(GameNode::MoveNode(node_b)); - node_a.children.push(GameNode::MoveNode(node_g)); - node_a.children.push(GameNode::MoveNode(node_h)); - - let game_tree = GameNode::MoveNode(node_a); - - let tree = Tree::from(&game_tree); - + let game_tree = branching_tree(); + let tree = DepthTree::from(&game_tree); assert_eq!(tree.position(2), (2, 0)); assert_eq!(tree.position(1), (1, 0)); assert_eq!(tree.position(0), (0, 0)); @@ -546,44 +591,65 @@ mod test { assert_eq!(tree.position(7), (1, 4)); } + #[ignore] #[test] fn breadth_first_iter() { - let mut node_a = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_b = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_c = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_d = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_e = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_f = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_g = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let mut node_h = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - let node_i = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); + /* + let mut node_a = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); + let mut node_b = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); + let mut node_c = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); + let node_d = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); + let node_e = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); + let node_f = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); + let node_g = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); + let mut node_h = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); + let node_i = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); - node_c.children.push(GameNode::MoveNode(node_d.clone())); - node_c.children.push(GameNode::MoveNode(node_e.clone())); - node_c.children.push(GameNode::MoveNode(node_f.clone())); + let game = GameRecord::new( + GameType::Go, + Size { + width: 19, + height: 19, + }, + Player { + name: Some("Black".to_owned()), + rank: None, + team: None, + }, + Player { + name: Some("White".to_owned()), + rank: None, + team: None, + }, + ); - node_b.children.push(GameNode::MoveNode(node_c.clone())); + node_c.children.push(GameNode::MoveNode(node_d.clone())); + node_c.children.push(GameNode::MoveNode(node_e.clone())); + node_c.children.push(GameNode::MoveNode(node_f.clone())); - node_h.children.push(GameNode::MoveNode(node_i.clone())); + node_b.children.push(GameNode::MoveNode(node_c.clone())); - node_a.children.push(GameNode::MoveNode(node_b.clone())); - node_a.children.push(GameNode::MoveNode(node_g.clone())); - node_a.children.push(GameNode::MoveNode(node_h.clone())); + node_h.children.push(GameNode::MoveNode(node_i.clone())); - let game_tree = GameNode::MoveNode(node_a.clone()); + node_a.children.push(GameNode::MoveNode(node_b.clone())); + node_a.children.push(GameNode::MoveNode(node_g.clone())); + node_a.children.push(GameNode::MoveNode(node_h.clone())); - let tree = Tree::from(&game_tree); + let game_tree = GameNode::MoveNode(node_a.clone()); - let mut iter = tree.bfs_iter(); + let tree = Tree::from(&game_tree); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_a.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_b.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_g.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_h.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_c.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_i.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_d.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_e.id)); - assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_f.id)); + let mut iter = tree.bfs_iter(); + + assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_a.id)); + assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_b.id)); + assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_g.id)); + assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_h.id)); + assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_c.id)); + assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_i.id)); + assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_d.id)); + assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_e.id)); + assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_f.id)); + */ } } diff --git a/sgf/src/game.rs b/sgf/src/game.rs index 9f748da..5d64737 100644 --- a/sgf/src/game.rs +++ b/sgf/src/game.rs @@ -7,7 +7,7 @@ use slab_tree::{NodeId, NodeMut, NodeRef, Tree}; use std::{ collections::{HashMap, HashSet, VecDeque}, fmt::Debug, - ops::Deref, + ops::{Deref, DerefMut}, time::Duration, }; use uuid::Uuid; @@ -137,7 +137,7 @@ impl GameRecord { /// children. pub fn mainline(&self) -> Option> { println!("number of trees: {}", self.trees.len()); - if !self.trees.is_empty(){ + if !self.trees.is_empty() { Some(MainlineIter { next: self.trees[0].root(), tree: &self.trees[0], @@ -309,6 +309,12 @@ impl<'a> Iterator for TreeIter<'a> { pub struct GameTree(Tree); +impl Default for GameTree { + fn default() -> Self { + Self(Tree::new()) + } +} + impl Clone for GameTree { fn clone(&self) -> Self { match self.0.root() { @@ -362,6 +368,12 @@ impl Deref for GameTree { } } +impl DerefMut for GameTree { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + impl PartialEq for GameTree { fn eq(&self, other: &Self) -> bool { // Get pre-order iterators over both trees, zip them, and ensure that the data contents are diff --git a/sgf/src/lib.rs b/sgf/src/lib.rs index d06b8fc..9fc15ca 100644 --- a/sgf/src/lib.rs +++ b/sgf/src/lib.rs @@ -1,7 +1,7 @@ mod date; mod game; -pub use game::{GameNode, GameRecord, MoveNode, Player}; +pub use game::{GameNode, GameRecord, GameTree, MoveNode, Player}; mod parser; pub use parser::{parse_collection, Move}; -- 2.44.1 From b1374229f3eebc506f79212cb433100d6e8a13b1 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Mon, 29 Apr 2024 22:24:01 -0400 Subject: [PATCH 07/11] Calculate out the depth and width of each node --- otg/core/src/library.rs | 8 +- otg/core/src/types.rs | 210 +++++++++++++++++++++++++++++++++------- 2 files changed, 179 insertions(+), 39 deletions(-) diff --git a/otg/core/src/library.rs b/otg/core/src/library.rs index 135bb47..4fe5241 100644 --- a/otg/core/src/library.rs +++ b/otg/core/src/library.rs @@ -14,18 +14,18 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with On the Grid. If not, see . */ -use crate::{Core}; +use crate::Core; use serde::{Deserialize, Serialize}; use sgf::GameRecord; #[derive(Clone, Debug, Serialize, Deserialize)] pub enum LibraryRequest { - ListGames + ListGames, } #[derive(Clone, Debug)] pub enum LibraryResponse { - Games(Vec) + Games(Vec), } async fn handle_list_games(model: &Core) -> LibraryResponse { @@ -39,10 +39,8 @@ async fn handle_list_games(model: &Core) -> LibraryResponse { } } - pub async fn handle(model: &Core, request: LibraryRequest) -> LibraryResponse { match request { LibraryRequest::ListGames => handle_list_games(model).await, } } - diff --git a/otg/core/src/types.rs b/otg/core/src/types.rs index 12153eb..fb1c364 100644 --- a/otg/core/src/types.rs +++ b/otg/core/src/types.rs @@ -3,7 +3,9 @@ use config::define_config; use config_derive::ConfigOption; use serde::{Deserialize, Serialize}; use sgf::GameTree; -use std::{cell::RefCell, collections::VecDeque, fmt, path::PathBuf, time::Duration}; +use std::{ + cell::RefCell, collections::{HashMap, VecDeque}, fmt, ops::Deref, path::PathBuf, time::Duration +}; use thiserror::Error; define_config! { @@ -242,19 +244,39 @@ pub struct Tree { struct DepthTree(slab_tree::Tree); +impl Deref for DepthTree { + type Target = slab_tree::Tree; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + #[derive(Debug)] pub struct SizeNode { - pub id: usize, node_id: slab_tree::NodeId, - parent: Option, + parent: Option, depth: usize, - width: RefCell>, - children: Vec, + width: usize, +} + +impl SizeNode { + pub fn position(&self) -> (usize, usize) { + (self.depth, self.width) + } } impl DepthTree { - /* - fn new(root: T) -> Self { + // My previous work to convert from a node tree to this tree-with-width dependend on the node tree + // being a recursive data structure. Now I need to find a way to convert a slab tree to this width + // tree. + // + // It all feels like a lot of custom weirdness. I shouldn't need a bunch of custom data structures, + // so I want to eliminate the "Tree" above and keep using the slab tree. I think I should be able + // to build these Node objects without needing a custom data structure. + fn new() -> Self { + Self(slab_tree::Tree::new()) + /* Tree { nodes: vec![Node { id: 0, @@ -265,8 +287,10 @@ impl DepthTree { children: vec![], }], } + */ } + /* pub fn node(&self, idx: usize) -> &T { &self.nodes[idx].node } @@ -293,13 +317,18 @@ impl DepthTree { */ pub fn max_depth(&self) -> usize { - unimplemented!() - /* - self.nodes.iter().fold( - 0, - |max, node| if node.depth > max { node.depth } else { max }, - ) - */ + self.0 + .root() + .unwrap() + .traverse_pre_order() + .fold(0, |max, node| { + println!("node depth: {}", node.data().depth); + if node.data().depth > max { + node.data().depth + } else { + max + } + }) } // Since I know the width of a node, now I want to figure out its placement in the larger @@ -317,8 +346,8 @@ impl DepthTree { // amounts to the position of the parent node. // // When drawing nodes, I don't know how to persist the level of indent. - pub fn position(&self, idx: usize) -> (usize, usize) { - unimplemented!() + + // unimplemented!() /* let node = &self.nodes[idx]; match node.parent { @@ -337,7 +366,6 @@ impl DepthTree { None => (0, 0), } */ - } /* // Given a node, do a postorder traversal to figure out the width of the node based on all of @@ -375,8 +403,88 @@ impl DepthTree { } impl<'a> From<&'a GameTree> for DepthTree { - fn from(root: &'a GameTree) -> Self { - unimplemented!() + fn from(tree: &'a GameTree) -> Self { + // Like in the conversion from SGF to GameTree, I need to traverse the entire tree one node + // at a time, keeping track of node ids as we go. I'm going to go with a depth-first + // traversal. When generating each node, I think I want to generate all of the details of + // the node as we go. + let source_root_node = tree.root(); + match source_root_node { + Some(source_root_node) => { + // Do the real work + // The id_map indexes from the source tree to the destination tree. Reverse + // indexing is accomplished by looking at the node_id in a node in the destination + // tree. + let mut id_map: HashMap = HashMap::new(); + let mut tree = slab_tree::Tree::new(); + + let mut iter = source_root_node.traverse_pre_order(); + let _ = iter.next().unwrap(); // we already know that the first element to be + // returned is the root node, and that the root node + // already exists. Otherwise we wouldn't even be in + // this branch. + + let dest_root_id = tree.set_root(SizeNode { + node_id: source_root_node.node_id(), + parent: None, + depth: 0, + width: 0, + }); + + id_map.insert(source_root_node.node_id(), dest_root_id); + + for source_node in iter { + let dest_parent_id = id_map + .get(&source_node.parent().unwrap().node_id()) + .unwrap(); + + let mut dest_parent = tree.get_mut(*dest_parent_id).unwrap(); + + let new_depth_node = SizeNode { + node_id: source_node.node_id(), + parent: Some(*dest_parent_id), + depth: 1 + dest_parent.data().depth, + width: dest_parent.data().width, + }; + + let new_node_id = dest_parent.append(new_depth_node).node_id(); + + match tree + .get(new_node_id) + .unwrap() + .prev_sibling() + .map(|node| node.data().width) + { + None => {} + Some(previous_width) => { + let mut new_node = tree.get_mut(new_node_id).unwrap(); + new_node.data().width = previous_width + 1; + } + } + + /* + let new_node = tree.get_mut(*dest_parent_id).unwrap().append(new_depth_node); + let previous_node = new_node.prev_sibling(); + + match previous_node { + None => {} + } + */ + + /* + match dest_noderef.prev_sibling() { + None => {} + Some(mut node) => { dest_noderef.data().width = node.data().width + 1 } + } + */ + + id_map.insert(source_node.node_id(), new_node_id); + } + + Self(tree) + } + None => Self::new(), + } } } @@ -510,7 +618,8 @@ mod test { .append(GameNode::MoveNode(MoveNode::new( sgf::Color::Black, Move::Move("dp".to_owned()), - ))).node_id(); + ))) + .node_id(); let node_c = game_tree .get_mut(node_b) @@ -518,7 +627,8 @@ mod test { .append(GameNode::MoveNode(MoveNode::new( sgf::Color::Black, Move::Move("dp".to_owned()), - ))).node_id(); + ))) + .node_id(); let node_d = game_tree .get_mut(node_c) @@ -526,7 +636,8 @@ mod test { .append(GameNode::MoveNode(MoveNode::new( sgf::Color::Black, Move::Move("dp".to_owned()), - ))).node_id(); + ))) + .node_id(); let node_e = game_tree .get_mut(node_c) @@ -534,7 +645,8 @@ mod test { .append(GameNode::MoveNode(MoveNode::new( sgf::Color::Black, Move::Move("dp".to_owned()), - ))).node_id(); + ))) + .node_id(); let node_f = game_tree .get_mut(node_c) @@ -542,7 +654,8 @@ mod test { .append(GameNode::MoveNode(MoveNode::new( sgf::Color::Black, Move::Move("dp".to_owned()), - ))).node_id(); + ))) + .node_id(); let node_g = game_tree .get_mut(node_a) @@ -550,7 +663,8 @@ mod test { .append(GameNode::MoveNode(MoveNode::new( sgf::Color::Black, Move::Move("dp".to_owned()), - ))).node_id(); + ))) + .node_id(); let node_h = game_tree .get_mut(node_a) @@ -558,7 +672,8 @@ mod test { .append(GameNode::MoveNode(MoveNode::new( sgf::Color::Black, Move::Move("dp".to_owned()), - ))).node_id(); + ))) + .node_id(); let _ = game_tree .get_mut(node_h) @@ -566,7 +681,8 @@ mod test { .append(GameNode::MoveNode(MoveNode::new( sgf::Color::Black, Move::Move("dp".to_owned()), - ))); + ))) + .node_id(); game_tree } @@ -575,6 +691,10 @@ mod test { fn it_can_calculate_depth_from_game_tree() { let game_tree = branching_tree(); let tree = DepthTree::from(&game_tree); + assert_eq!( + game_tree.root().unwrap().traverse_pre_order().count(), + tree.0.root().unwrap().traverse_pre_order().count() + ); assert_eq!(tree.max_depth(), 3); } @@ -582,13 +702,35 @@ mod test { fn it_calculates_horizontal_position_of_nodes() { let game_tree = branching_tree(); let tree = DepthTree::from(&game_tree); - assert_eq!(tree.position(2), (2, 0)); - assert_eq!(tree.position(1), (1, 0)); - assert_eq!(tree.position(0), (0, 0)); - assert_eq!(tree.position(4), (3, 1)); - assert_eq!(tree.position(5), (3, 2)); - assert_eq!(tree.position(6), (1, 3)); - assert_eq!(tree.position(7), (1, 4)); + + let node_a = tree.root().unwrap(); + assert_eq!(node_a.data().position(), (0, 0)); + + let node_b = node_a.first_child().unwrap(); + assert_eq!(node_b.data().position(), (1, 0)); + let node_g = node_b.next_sibling().unwrap(); + assert_eq!(node_g.data().position(), (1, 1)); + let node_h = node_g.next_sibling().unwrap(); + assert_eq!(node_h.data().position(), (1, 2)); + + let node_c = node_b.first_child().unwrap(); + assert_eq!(node_c.data().position(), (2, 0)); + + let node_d = node_c.first_child().unwrap(); + assert_eq!(node_d.data().position(), (3, 0)); + + let node_i = node_h.first_child().unwrap(); + assert_eq!(node_i.data().position(), (2, 2)); + + /* + assert_eq!(tree.position(test_tree.node_c), (2, 0)); + assert_eq!(tree.position(test_tree.node_b), (1, 0)); + assert_eq!(tree.position(test_tree.node_a), (0, 0)); + assert_eq!(tree.position(test_tree.node_d), (3, 1)); + assert_eq!(tree.position(test_tree.node_e), (3, 2)); + assert_eq!(tree.position(test_tree.node_f), (1, 3)); + assert_eq!(tree.position(test_tree.node_g), (1, 4)); + */ } #[ignore] -- 2.44.1 From 5441a3c4417687aad3451a1848372697c60ae94d Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Tue, 30 Apr 2024 20:31:17 -0400 Subject: [PATCH 08/11] Adapt the app to the new slab tree --- otg/core/src/lib.rs | 5 +++-- otg/core/src/types.rs | 25 +++++++++++-------------- otg/gtk/src/components/review_tree.rs | 12 +++++++----- otg/gtk/src/views/game_review.rs | 7 ++++--- sgf/src/game.rs | 1 - 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/otg/core/src/lib.rs b/otg/core/src/lib.rs index 5fde824..5964ce9 100644 --- a/otg/core/src/lib.rs +++ b/otg/core/src/lib.rs @@ -29,5 +29,6 @@ pub mod library; pub mod settings; mod types; -pub use types::{BoardError, Color, Config, ConfigOption, LibraryPath, Player, Rank, Size}; - +pub use types::{ + BoardError, Color, Config, ConfigOption, DepthTree, LibraryPath, Player, Rank, Size, +}; diff --git a/otg/core/src/types.rs b/otg/core/src/types.rs index fb1c364..c48cc7f 100644 --- a/otg/core/src/types.rs +++ b/otg/core/src/types.rs @@ -242,7 +242,7 @@ pub struct Tree { } */ -struct DepthTree(slab_tree::Tree); +pub struct DepthTree(slab_tree::Tree); impl Deref for DepthTree { type Target = slab_tree::Tree; @@ -393,13 +393,13 @@ impl DepthTree { width } + */ - pub fn bfs_iter(&self) -> BFSIter { + pub fn bfs_iter(&self) -> BFSIter<'_, SizeNode> { let mut queue = VecDeque::new(); - queue.push_back(&self.nodes[0]); + queue.push_back(self.0.root().unwrap()); BFSIter { tree: self, queue } } - */ } impl<'a> From<&'a GameTree> for DepthTree { @@ -520,27 +520,24 @@ impl<'a> From<&'a GameNode> for Tree { } */ -/* pub struct BFSIter<'a, T> { - tree: &'a Tree, - queue: VecDeque<&'a Node>, + tree: &'a DepthTree, + queue: VecDeque>, } impl<'a, T> Iterator for BFSIter<'a, T> { - type Item = &'a Node; + type Item = &'a T; fn next(&mut self) -> Option { let retval = self.queue.pop_front(); - if let Some(retval) = retval { + if let Some(ref retval) = retval { retval - .children - .iter() - .for_each(|idx| self.queue.push_back(&self.tree.nodes[*idx])); + .children() + .for_each(|noderef| self.queue.push_back(noderef)); } - retval + retval.map(|retval| retval.data()) } } -*/ #[cfg(test)] mod test { diff --git a/otg/gtk/src/components/review_tree.rs b/otg/gtk/src/components/review_tree.rs index f759a34..c2297ff 100644 --- a/otg/gtk/src/components/review_tree.rs +++ b/otg/gtk/src/components/review_tree.rs @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with On use cairo::Context; use glib::Object; use gtk::{prelude::*, subclass::prelude::*}; -use otg_core::Tree; +use otg_core::DepthTree; use sgf::GameRecord; use std::{cell::RefCell, rc::Rc}; use uuid::Uuid; @@ -28,7 +28,7 @@ const HEIGHT: i32 = 800; #[derive(Default)] pub struct ReviewTreePrivate { record: Rc>>, - tree: Rc>>>, + tree: Rc>>, } #[glib::object_subclass] @@ -50,7 +50,9 @@ impl ReviewTree { pub fn new(record: GameRecord) -> Self { let s: Self = Object::new(); - *s.imp().tree.borrow_mut() = Some(Tree::from(&record.children[0])); + // TODO: there can be more than one tree, especially in instructional files. Either unify + // them into a single tree in the GameTree, or draw all of them here. + *s.imp().tree.borrow_mut() = Some(DepthTree::from(&record.trees[0])); *s.imp().record.borrow_mut() = Some(record); s.set_width_request(WIDTH); @@ -67,7 +69,7 @@ impl ReviewTree { } pub fn redraw(&self, ctx: &Context, _width: i32, _height: i32) { - let tree: &Option> = &self.imp().tree.borrow(); + let tree: &Option = &self.imp().tree.borrow(); match tree { Some(ref tree) => { for node in tree.bfs_iter() { @@ -76,7 +78,7 @@ impl ReviewTree { // the parent? do I need to just make it more intrinsically a part of the position // code? ctx.set_source_rgb(0.7, 0.7, 0.7); - let (row, column) = tree.position(node.id); + let (row, column) = node.position(); let y = (row as f64) * 20. + 10.; let x = (column as f64) * 20. + 10.; ctx.arc(x, y, 5., 0., 2. * std::f64::consts::PI); diff --git a/otg/gtk/src/views/game_review.rs b/otg/gtk/src/views/game_review.rs index 721c5d9..101c53c 100644 --- a/otg/gtk/src/views/game_review.rs +++ b/otg/gtk/src/views/game_review.rs @@ -55,9 +55,10 @@ impl GameReview { // It's actually really bad to be just throwing away errors. Panics make everyone unhappy. // This is not a fatal error, so I'll replace this `unwrap` call with something that // renders the board and notifies the user of a problem that cannot be resolved. - let board_repr = otg_core::Goban::default() - .apply_moves(record.mainline()) - .unwrap(); + let board_repr = match record.mainline() { + Some(iter) => otg_core::Goban::default().apply_moves(iter).unwrap(), + None => otg_core::Goban::default(), + }; let board = Goban::new(board_repr, resources); /* diff --git a/sgf/src/game.rs b/sgf/src/game.rs index 5d64737..ae11f54 100644 --- a/sgf/src/game.rs +++ b/sgf/src/game.rs @@ -136,7 +136,6 @@ impl GameRecord { /// was actually played out, and by convention consists of the first node in each list of /// children. pub fn mainline(&self) -> Option> { - println!("number of trees: {}", self.trees.len()); if !self.trees.is_empty() { Some(MainlineIter { next: self.trees[0].root(), -- 2.44.1 From 8b7add37c154b5657067123f3cd3376e50408d3a Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Tue, 30 Apr 2024 21:59:51 -0400 Subject: [PATCH 09/11] Switch from slab_tree to nary_tree --- Cargo.lock | 23 ++++++++++++----------- otg/core/Cargo.toml | 2 +- otg/core/src/types.rs | 16 ++++++++-------- sgf/Cargo.toml | 2 +- sgf/src/game.rs | 12 +++++++++++- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93efbef..4118f22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2503,6 +2503,16 @@ dependencies = [ "version_check 0.9.4", ] +[[package]] +name = "nary_tree" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb86edb8951cb3852cbb33ef558650e9f18c9d2e7fd79a6849c984a3825719c7" +dependencies = [ + "slab", + "snowflake", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -2705,10 +2715,10 @@ dependencies = [ "config-derive", "cool_asserts", "grid", + "nary_tree", "serde 1.0.193", "serde_json", "sgf", - "slab_tree", "thiserror", "uuid 0.8.2", ] @@ -3686,9 +3696,9 @@ version = "0.1.0" dependencies = [ "chrono", "cool_asserts", + "nary_tree", "nom", "serde 1.0.193", - "slab_tree", "thiserror", "typeshare", "uuid 0.8.2", @@ -3762,15 +3772,6 @@ 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" diff --git a/otg/core/Cargo.toml b/otg/core/Cargo.toml index a6b7215..42af9aa 100644 --- a/otg/core/Cargo.toml +++ b/otg/core/Cargo.toml @@ -14,7 +14,7 @@ sgf = { path = "../../sgf" } grid = { version = "0.9" } serde_json = { version = "1" } serde = { version = "1", features = [ "derive" ] } -slab_tree = { version = "0.3" } +nary_tree = { version = "0.4" } thiserror = { version = "1" } uuid = { version = "0.8", features = ["v4", "serde"] } diff --git a/otg/core/src/types.rs b/otg/core/src/types.rs index c48cc7f..7ce672b 100644 --- a/otg/core/src/types.rs +++ b/otg/core/src/types.rs @@ -242,10 +242,10 @@ pub struct Tree { } */ -pub struct DepthTree(slab_tree::Tree); +pub struct DepthTree(nary_tree::Tree); impl Deref for DepthTree { - type Target = slab_tree::Tree; + type Target = nary_tree::Tree; fn deref(&self) -> &Self::Target { &self.0 @@ -254,8 +254,8 @@ impl Deref for DepthTree { #[derive(Debug)] pub struct SizeNode { - node_id: slab_tree::NodeId, - parent: Option, + node_id: nary_tree::NodeId, + parent: Option, depth: usize, width: usize, } @@ -275,7 +275,7 @@ impl DepthTree { // so I want to eliminate the "Tree" above and keep using the slab tree. I think I should be able // to build these Node objects without needing a custom data structure. fn new() -> Self { - Self(slab_tree::Tree::new()) + Self(nary_tree::Tree::new()) /* Tree { nodes: vec![Node { @@ -415,8 +415,8 @@ impl<'a> From<&'a GameTree> for DepthTree { // The id_map indexes from the source tree to the destination tree. Reverse // indexing is accomplished by looking at the node_id in a node in the destination // tree. - let mut id_map: HashMap = HashMap::new(); - let mut tree = slab_tree::Tree::new(); + let mut id_map: HashMap = HashMap::new(); + let mut tree = nary_tree::Tree::new(); let mut iter = source_root_node.traverse_pre_order(); let _ = iter.next().unwrap(); // we already know that the first element to be @@ -522,7 +522,7 @@ impl<'a> From<&'a GameNode> for Tree { pub struct BFSIter<'a, T> { tree: &'a DepthTree, - queue: VecDeque>, + queue: VecDeque>, } impl<'a, T> Iterator for BFSIter<'a, T> { diff --git a/sgf/Cargo.toml b/sgf/Cargo.toml index b1fda71..0b83c35 100644 --- a/sgf/Cargo.toml +++ b/sgf/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" chrono = { version = "0.4", features = [ "serde" ] } nom = { version = "7" } serde = { version = "1", features = [ "derive" ] } -slab_tree = { version = "0.3" } +nary_tree = { version = "0.4" } 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 ae11f54..d21812a 100644 --- a/sgf/src/game.rs +++ b/sgf/src/game.rs @@ -3,9 +3,10 @@ use crate::{ Color, Date, GameResult, GameType, }; use serde::{Deserialize, Serialize}; -use slab_tree::{NodeId, NodeMut, NodeRef, Tree}; +use nary_tree::{NodeId, NodeMut, NodeRef, Tree}; use std::{ collections::{HashMap, HashSet, VecDeque}, + fmt, fmt::Debug, ops::{Deref, DerefMut}, time::Duration, @@ -425,6 +426,15 @@ pub enum GameNode { SetupNode(SetupNode), } +impl fmt::Display for GameNode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + GameNode::MoveNode(_) => write!(f, "MoveNode"), + GameNode::SetupNode(_) => write!(f, "SetupNode"), + } + } +} + impl GameNode { pub fn id(&self) -> Uuid { match self { -- 2.44.1 From 278ec27b4e36fd9b8b97029c3048bd3258a21930 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Tue, 30 Apr 2024 22:05:15 -0400 Subject: [PATCH 10/11] Linting --- otg/core/src/types.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/otg/core/src/types.rs b/otg/core/src/types.rs index 7ce672b..c89b55c 100644 --- a/otg/core/src/types.rs +++ b/otg/core/src/types.rs @@ -4,7 +4,7 @@ use config_derive::ConfigOption; use serde::{Deserialize, Serialize}; use sgf::GameTree; use std::{ - cell::RefCell, collections::{HashMap, VecDeque}, fmt, ops::Deref, path::PathBuf, time::Duration + collections::{HashMap, VecDeque}, fmt, ops::Deref, path::PathBuf, time::Duration }; use thiserror::Error; @@ -254,9 +254,15 @@ impl Deref for DepthTree { #[derive(Debug)] pub struct SizeNode { - node_id: nary_tree::NodeId, - parent: Option, + /// Use this to map back to the node in the original game tree. This way we know how to + /// correspond from a node in the review tree back to there. + #[allow(dead_code)] + game_node_id: nary_tree::NodeId, + + /// How deep into the tree is this node? depth: usize, + + /// How far from the leftmost margin is this node? width: usize, } @@ -398,7 +404,7 @@ impl DepthTree { pub fn bfs_iter(&self) -> BFSIter<'_, SizeNode> { let mut queue = VecDeque::new(); queue.push_back(self.0.root().unwrap()); - BFSIter { tree: self, queue } + BFSIter { queue } } } @@ -425,8 +431,7 @@ impl<'a> From<&'a GameTree> for DepthTree { // this branch. let dest_root_id = tree.set_root(SizeNode { - node_id: source_root_node.node_id(), - parent: None, + game_node_id: source_root_node.node_id(), depth: 0, width: 0, }); @@ -441,8 +446,7 @@ impl<'a> From<&'a GameTree> for DepthTree { let mut dest_parent = tree.get_mut(*dest_parent_id).unwrap(); let new_depth_node = SizeNode { - node_id: source_node.node_id(), - parent: Some(*dest_parent_id), + game_node_id: source_node.node_id(), depth: 1 + dest_parent.data().depth, width: dest_parent.data().width, }; @@ -521,7 +525,6 @@ impl<'a> From<&'a GameNode> for Tree { */ pub struct BFSIter<'a, T> { - tree: &'a DepthTree, queue: VecDeque>, } -- 2.44.1 From 20b02fbd90d3e7c735c1e75a82cb9e0c80a0f2e2 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Tue, 30 Apr 2024 22:22:59 -0400 Subject: [PATCH 11/11] Update the Nix derivation --- Cargo.nix | 45 +++++++++++++++++++++++++++++++++++++++++++++ crate-hashes.json | 4 +++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Cargo.nix b/Cargo.nix index cb15d53..3778355 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -7917,6 +7917,28 @@ rec { }; resolvedDefaultFeatures = [ "default" ]; }; + "nary_tree" = rec { + crateName = "nary_tree"; + version = "0.4.3"; + edition = "2021"; + sha256 = "1iqray1a716995l9mmvz5sfqrwg9a235bvrkpcn8bcqwjnwfv1pv"; + authors = [ + "Ian " + "David Cohen " + ]; + dependencies = [ + { + name = "slab"; + packageId = "slab"; + } + { + name = "snowflake"; + packageId = "snowflake"; + } + ]; + features = { + }; + }; "native-tls" = rec { crateName = "native-tls"; version = "0.2.11"; @@ -8551,6 +8573,10 @@ rec { name = "grid"; packageId = "grid"; } + { + name = "nary_tree"; + packageId = "nary_tree"; + } { name = "serde"; packageId = "serde 1.0.193"; @@ -11570,6 +11596,10 @@ rec { packageId = "chrono"; features = [ "serde" ]; } + { + name = "nary_tree"; + packageId = "nary_tree"; + } { name = "nom"; packageId = "nom"; @@ -11803,6 +11833,21 @@ rec { }; resolvedDefaultFeatures = [ "const_generics" "const_new" "union" ]; }; + "snowflake" = rec { + crateName = "snowflake"; + version = "1.3.0"; + edition = "2015"; + sha256 = "1wadr7bxdxbmkbqkqsvzan6q1h3mxqpxningi3ss3v9jaav7n817"; + authors = [ + "Steven Allen " + ]; + features = { + "serde" = [ "dep:serde" ]; + "serde_derive" = [ "dep:serde_derive" ]; + "serde_support" = [ "serde" "serde_derive" ]; + }; + resolvedDefaultFeatures = [ "default" ]; + }; "socket2 0.4.10" = rec { crateName = "socket2"; version = "0.4.10"; diff --git a/crate-hashes.json b/crate-hashes.json index 9b7b532..12766b9 100644 --- a/crate-hashes.json +++ b/crate-hashes.json @@ -223,6 +223,7 @@ "registry+https://github.com/rust-lang/crates.io-index#mio@0.8.10": "02gyaxvaia9zzi4drrw59k9s0j6pa5d1y2kv7iplwjipdqlhngcg", "registry+https://github.com/rust-lang/crates.io-index#modifier@0.1.0": "0n3fmgli1nsskl0whrfzm1gk0rmwwl6pw1q4nb9sqqmn5h8wkxa1", "registry+https://github.com/rust-lang/crates.io-index#multer@2.1.0": "1hjiphaypj3phqaj5igrzcia9xfmf4rr4ddigbh8zzb96k1bvb01", + "registry+https://github.com/rust-lang/crates.io-index#nary_tree@0.4.3": "1iqray1a716995l9mmvz5sfqrwg9a235bvrkpcn8bcqwjnwfv1pv", "registry+https://github.com/rust-lang/crates.io-index#native-tls@0.2.11": "0bmrlg0fmzxaycjpkgkchi93av07v2yf9k33gc12ca9gqdrn28h7", "registry+https://github.com/rust-lang/crates.io-index#nix@0.27.1": "0ly0kkmij5f0sqz35lx9czlbk6zpihb7yh1bsy4irzwfd2f4xc1f", "registry+https://github.com/rust-lang/crates.io-index#no-std-compat@0.4.1": "132vrf710zsdp40yp1z3kgc2ss8pi0z4gmihsz3y7hl4dpd56f5r", @@ -342,6 +343,7 @@ "registry+https://github.com/rust-lang/crates.io-index#siphasher@0.3.11": "03axamhmwsrmh0psdw3gf7c0zc4fyl5yjxfifz9qfka6yhkqid9q", "registry+https://github.com/rust-lang/crates.io-index#slab@0.4.9": "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg", "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.11.2": "0w79x38f7c0np7hqfmzrif9zmn0avjvvm31b166zdk9d1aad1k2d", + "registry+https://github.com/rust-lang/crates.io-index#snowflake@1.3.0": "1wadr7bxdxbmkbqkqsvzan6q1h3mxqpxningi3ss3v9jaav7n817", "registry+https://github.com/rust-lang/crates.io-index#socket2@0.4.10": "03ack54dxhgfifzsj14k7qa3r5c9wqy3v6mqhlim99cc03y1cycz", "registry+https://github.com/rust-lang/crates.io-index#socket2@0.5.5": "1sgq315f1njky114ip7wcy83qlphv9qclprfjwvxcpfblmcsqpvv", "registry+https://github.com/rust-lang/crates.io-index#spin@0.5.2": "0b84m6dbzrwf2kxylnw82d3dr8w06av7rfkr8s85fb5f43rwyqvf", @@ -469,4 +471,4 @@ "registry+https://github.com/rust-lang/crates.io-index#zerocopy@0.7.31": "0gcfyrmlrhmsz16qxjp2qzr6vixyaw1p04zl28f08lxkvfz62h0w", "registry+https://github.com/rust-lang/crates.io-index#zeroize@1.7.0": "0bfvby7k9pdp6623p98yz2irqnamcyzpn7zh20nqmdn68b0lwnsj", "registry+https://github.com/rust-lang/crates.io-index#zune-inflate@0.2.54": "00kg24jh3zqa3i6rg6yksnb71bch9yi1casqydl00s7nw8pk7avk" -} \ No newline at end of file +} -- 2.44.1