Switch to a standardized tree library in the game tree and depth tree #236

Merged
savanni merged 11 commits from savanni/improved-tree-structure into main 2024-05-01 02:26:47 +00:00
9 changed files with 180 additions and 99 deletions
Showing only changes of commit bc5042c004 - Show all commits

1
Cargo.lock generated
View File

@ -2708,6 +2708,7 @@ dependencies = [
"serde 1.0.193", "serde 1.0.193",
"serde_json", "serde_json",
"sgf", "sgf",
"slab_tree",
"thiserror", "thiserror",
"uuid 0.8.2", "uuid 0.8.2",
] ]

View File

@ -14,6 +14,7 @@ sgf = { path = "../../sgf" }
grid = { version = "0.9" } grid = { version = "0.9" }
serde_json = { version = "1" } serde_json = { version = "1" }
serde = { version = "1", features = [ "derive" ] } serde = { version = "1", features = [ "derive" ] }
slab_tree = { version = "0.3" }
thiserror = { version = "1" } thiserror = { version = "1" }
uuid = { version = "0.8", features = ["v4", "serde"] } uuid = { version = "0.8", features = ["v4", "serde"] }

View File

@ -81,7 +81,7 @@ impl From<HotseatPlayerRequest> for Player {
} }
*/ */
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug)]
pub enum CoreResponse { pub enum CoreResponse {
Library(library::LibraryResponse), Library(library::LibraryResponse),
Settings(settings::SettingsResponse), Settings(settings::SettingsResponse),

View File

@ -42,7 +42,8 @@ impl Database {
.unwrap(); .unwrap();
match parse_sgf(&buffer) { match parse_sgf(&buffer) {
Ok(sgfs) => { Ok(sgfs) => {
let mut sgfs = sgfs.into_iter().flatten().collect::<Vec<sgf::GameRecord>>(); let mut sgfs =
sgfs.into_iter().flatten().collect::<Vec<sgf::GameRecord>>();
games.append(&mut sgfs); games.append(&mut sgfs);
} }
Err(err) => println!("Error parsing {:?}: {:?}", entry.path(), err), Err(err) => println!("Error parsing {:?}: {:?}", entry.path(), err),

View File

@ -29,5 +29,5 @@ pub mod library;
pub mod settings; pub mod settings;
mod types; 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};

View File

@ -23,7 +23,7 @@ pub enum LibraryRequest {
ListGames ListGames
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug)]
pub enum LibraryResponse { pub enum LibraryResponse {
Games(Vec<GameRecord>) Games(Vec<GameRecord>)
} }

View File

@ -2,10 +2,9 @@ use crate::goban::{Coordinate, Goban};
use config::define_config; use config::define_config;
use config_derive::ConfigOption; use config_derive::ConfigOption;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sgf::GameNode; use sgf::GameTree;
use std::{cell::RefCell, collections::VecDeque, fmt, path::PathBuf, time::Duration}; use std::{cell::RefCell, collections::VecDeque, fmt, path::PathBuf, time::Duration};
use thiserror::Error; use thiserror::Error;
use uuid::Uuid;
define_config! { define_config! {
LibraryPath(LibraryPath), 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 // 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. // 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. // 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<T> { pub struct Tree<T> {
nodes: Vec<Node<T>>, nodes: Vec<Node<T>>,
} }
*/
struct DepthTree(slab_tree::Tree<SizeNode>);
#[derive(Debug)] #[derive(Debug)]
pub struct Node<T> { pub struct SizeNode {
pub id: usize, pub id: usize,
node: T, node_id: slab_tree::NodeId,
parent: Option<usize>, parent: Option<usize>,
depth: usize, depth: usize,
width: RefCell<Option<usize>>, width: RefCell<Option<usize>>,
children: Vec<usize>, children: Vec<usize>,
} }
impl<T> Tree<T> { impl DepthTree {
/*
fn new(root: T) -> Self { fn new(root: T) -> Self {
Tree { Tree {
nodes: vec![Node { nodes: vec![Node {
@ -286,12 +290,16 @@ impl<T> Tree<T> {
parent.children.push(next_idx); parent.children.push(next_idx);
next_idx next_idx
} }
*/
pub fn max_depth(&self) -> usize { pub fn max_depth(&self) -> usize {
unimplemented!()
/*
self.nodes.iter().fold( self.nodes.iter().fold(
0, 0,
|max, node| if node.depth > max { node.depth } else { max }, |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 // Since I know the width of a node, now I want to figure out its placement in the larger
@ -310,6 +318,8 @@ impl<T> Tree<T> {
// //
// When drawing nodes, I don't know how to persist the level of indent. // When drawing nodes, I don't know how to persist the level of indent.
pub fn position(&self, idx: usize) -> (usize, usize) { pub fn position(&self, idx: usize) -> (usize, usize) {
unimplemented!()
/*
let node = &self.nodes[idx]; let node = &self.nodes[idx];
match node.parent { match node.parent {
Some(parent_idx) => { Some(parent_idx) => {
@ -326,8 +336,10 @@ impl<T> Tree<T> {
// Root nodes won't have a parent, so just put them in the first column // Root nodes won't have a parent, so just put them in the first column
None => (0, 0), None => (0, 0),
} }
*/
} }
/*
// Given a node, do a postorder traversal to figure out the width of the node based on all of // 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. // its children. This is equivalent to the widest of all of its children at all depths.
// //
@ -359,8 +371,16 @@ impl<T> Tree<T> {
queue.push_back(&self.nodes[0]); queue.push_back(&self.nodes[0]);
BFSIter { tree: self, queue } 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<Uuid> { impl<'a> From<&'a GameNode> for Tree<Uuid> {
fn from(root: &'a GameNode) -> Self { fn from(root: &'a GameNode) -> Self {
fn add_subtree(tree: &mut Tree<Uuid>, parent_idx: usize, node: &GameNode) { fn add_subtree(tree: &mut Tree<Uuid>, parent_idx: usize, node: &GameNode) {
@ -390,7 +410,9 @@ impl<'a> From<&'a GameNode> for Tree<Uuid> {
tree tree
} }
} }
*/
/*
pub struct BFSIter<'a, T> { pub struct BFSIter<'a, T> {
tree: &'a Tree<T>, tree: &'a Tree<T>,
queue: VecDeque<&'a Node<T>>, queue: VecDeque<&'a Node<T>>,
@ -410,12 +432,13 @@ impl<'a, T> Iterator for BFSIter<'a, T> {
retval retval
} }
} }
*/
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use cool_asserts::assert_matches; // use sgf::{GameRecord, GameTree, GameType, Move, MoveNode};
use sgf::{Move, MoveNode}; use sgf::{GameNode, GameTree, Move, MoveNode};
#[test] #[test]
fn current_player_changes_after_move() { fn current_player_changes_after_move() {
@ -474,69 +497,91 @@ mod test {
// B G H // B G H
// C I // C I
// D E F // 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] #[test]
fn it_can_calculate_depth_from_game_tree() { fn it_can_calculate_depth_from_game_tree() {
let mut node_a = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); let game_tree = branching_tree();
let mut node_b = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); let tree = DepthTree::from(&game_tree);
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);
assert_eq!(tree.max_depth(), 3); assert_eq!(tree.max_depth(), 3);
} }
// A
// B G H
// C I
// D E F
#[test] #[test]
fn it_calculates_horizontal_position_of_nodes() { fn it_calculates_horizontal_position_of_nodes() {
let mut node_a = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); let game_tree = branching_tree();
let mut node_b = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned())); let tree = DepthTree::from(&game_tree);
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);
assert_eq!(tree.position(2), (2, 0)); assert_eq!(tree.position(2), (2, 0));
assert_eq!(tree.position(1), (1, 0)); assert_eq!(tree.position(1), (1, 0));
assert_eq!(tree.position(0), (0, 0)); assert_eq!(tree.position(0), (0, 0));
@ -546,8 +591,10 @@ mod test {
assert_eq!(tree.position(7), (1, 4)); assert_eq!(tree.position(7), (1, 4));
} }
#[ignore]
#[test] #[test]
fn breadth_first_iter() { fn breadth_first_iter() {
/*
let mut node_a = 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_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 mut node_c = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned()));
@ -558,6 +605,24 @@ mod test {
let mut node_h = 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 node_i = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned()));
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_c.children.push(GameNode::MoveNode(node_d.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_e.clone()));
node_c.children.push(GameNode::MoveNode(node_f.clone())); node_c.children.push(GameNode::MoveNode(node_f.clone()));
@ -585,5 +650,6 @@ mod test {
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_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_e.id));
assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_f.id)); assert_matches!(iter.next(), Some(Node { node: uuid, .. }) => assert_eq!(*uuid, node_f.id));
*/
} }
} }

View File

@ -7,7 +7,7 @@ use slab_tree::{NodeId, NodeMut, NodeRef, Tree};
use std::{ use std::{
collections::{HashMap, HashSet, VecDeque}, collections::{HashMap, HashSet, VecDeque},
fmt::Debug, fmt::Debug,
ops::Deref, ops::{Deref, DerefMut},
time::Duration, time::Duration,
}; };
use uuid::Uuid; use uuid::Uuid;
@ -309,6 +309,12 @@ impl<'a> Iterator for TreeIter<'a> {
pub struct GameTree(Tree<GameNode>); pub struct GameTree(Tree<GameNode>);
impl Default for GameTree {
fn default() -> Self {
Self(Tree::new())
}
}
impl Clone for GameTree { impl Clone for GameTree {
fn clone(&self) -> Self { fn clone(&self) -> Self {
match self.0.root() { 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 { impl PartialEq for GameTree {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
// Get pre-order iterators over both trees, zip them, and ensure that the data contents are // Get pre-order iterators over both trees, zip them, and ensure that the data contents are

View File

@ -1,7 +1,7 @@
mod date; mod date;
mod game; mod game;
pub use game::{GameNode, GameRecord, MoveNode, Player}; pub use game::{GameNode, GameRecord, GameTree, MoveNode, Player};
mod parser; mod parser;
pub use parser::{parse_collection, Move}; pub use parser::{parse_collection, Move};