Compare commits
2 Commits
9fbc630500
...
b982f2c1cc
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | b982f2c1cc | |
Savanni D'Gerinel | 46b25cc6c5 |
|
@ -3,9 +3,9 @@ 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::GameNode;
|
||||||
use uuid::Uuid;
|
use std::{cell::RefCell, collections::VecDeque, fmt, path::PathBuf, time::Duration};
|
||||||
use std::{cell::RefCell, fmt, path::PathBuf, time::Duration};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
define_config! {
|
define_config! {
|
||||||
LibraryPath(LibraryPath),
|
LibraryPath(LibraryPath),
|
||||||
|
@ -235,13 +235,13 @@ impl GameState {
|
||||||
//
|
//
|
||||||
// So, what is the maximum depth of the tree? Follow all paths and see how far I get in every case.
|
// So, what is the maximum depth of the tree? Follow all paths and see how far I get in every case.
|
||||||
// I could do this by just generating an intermediate tree and numbering each level.
|
// I could do this by just generating an intermediate tree and numbering each level.
|
||||||
|
|
||||||
pub struct Tree<T> {
|
pub struct Tree<T> {
|
||||||
nodes: Vec<Node<T>>,
|
nodes: Vec<Node<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Node<T> {
|
#[derive(Debug)]
|
||||||
id: usize,
|
pub struct Node<T> {
|
||||||
|
pub id: usize,
|
||||||
node: T,
|
node: T,
|
||||||
parent: Option<usize>,
|
parent: Option<usize>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
|
@ -294,6 +294,17 @@ impl<T> Tree<T> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since I know the width of a node, now I want to figure out its placement in the larger
|
||||||
|
// scheme of things.
|
||||||
|
//
|
||||||
|
// One thought I have is that I could just develop a grid virtually and start placing nodes.
|
||||||
|
// Whenever I notice a collision, I can just move the node over. But I'd like to see if I can
|
||||||
|
// be a bit smarter than doing it as just a vec into which I place things, as though it's a
|
||||||
|
// game board. So, given a game node, I want to figure out it's position along the X axis.
|
||||||
|
//
|
||||||
|
// Just having the node is greatly insufficient. I can get better results if I'm calculating
|
||||||
|
// the position of its children.
|
||||||
|
//
|
||||||
// indent represents the indentation that should be applied to all children in this tree. It
|
// indent represents the indentation that should be applied to all children in this tree. It
|
||||||
// amounts to the position of the parent node.
|
// amounts to the position of the parent node.
|
||||||
pub fn position(&self, indent: usize, idx: usize) -> (usize, usize) {
|
pub fn position(&self, indent: usize, idx: usize) -> (usize, usize) {
|
||||||
|
@ -310,12 +321,22 @@ impl<T> Tree<T> {
|
||||||
println!("[{}] sibling width {}", idx, sibling_width);
|
println!("[{}] sibling width {}", idx, sibling_width);
|
||||||
(node.depth, indent + sibling_width)
|
(node.depth, indent + sibling_width)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
// its children. This is equivalent to the widest of all of its children at all depths.
|
||||||
|
//
|
||||||
|
// There are some collapse rules that I could take into account here, but that I haven't
|
||||||
|
// figured out yet. If two nodes are side by side, and one of them has some wide children but
|
||||||
|
// the other has no children, then they are effectively the same width. The second node only
|
||||||
|
// needs to be moved out if it has children that would overlap the children of the first node.
|
||||||
|
//
|
||||||
|
// My algorithm right now is likely to generate unnecessarily wide trees in a complex game
|
||||||
|
// review.
|
||||||
fn width(&self, id: usize) -> usize {
|
fn width(&self, id: usize) -> usize {
|
||||||
println!("[{}] calculating width", id);
|
println!("[{}] calculating width", id);
|
||||||
let node = &self.nodes[id];
|
let node = &self.nodes[id];
|
||||||
|
@ -333,6 +354,12 @@ impl<T> Tree<T> {
|
||||||
|
|
||||||
width
|
width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bfs_iter<'a>(&'a self) -> BFSIter<T> {
|
||||||
|
let mut queue = VecDeque::new();
|
||||||
|
queue.push_back(&self.nodes[0]);
|
||||||
|
BFSIter { tree: self, queue }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a GameNode> for Tree<Uuid> {
|
impl<'a> From<&'a GameNode> for Tree<Uuid> {
|
||||||
|
@ -365,9 +392,30 @@ impl<'a> From<&'a GameNode> for Tree<Uuid> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BFSIter<'a, T> {
|
||||||
|
tree: &'a Tree<T>,
|
||||||
|
queue: VecDeque<&'a Node<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Iterator for BFSIter<'a, T> {
|
||||||
|
type Item = &'a Node<T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let retval = self.queue.pop_front();
|
||||||
|
if let Some(ref retval) = retval {
|
||||||
|
retval
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.for_each(|idx| self.queue.push_back(&self.tree.nodes[*idx]));
|
||||||
|
}
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use cool_asserts::assert_matches;
|
||||||
use sgf::{Move, MoveNode};
|
use sgf::{Move, MoveNode};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -498,4 +546,45 @@ mod test {
|
||||||
assert_eq!(tree.position(0, 6), (1, 3));
|
assert_eq!(tree.position(0, 6), (1, 3));
|
||||||
assert_eq!(tree.position(0, 7), (1, 4));
|
assert_eq!(tree.position(0, 7), (1, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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()));
|
||||||
|
|
||||||
|
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_b.children.push(GameNode::MoveNode(node_c.clone()));
|
||||||
|
|
||||||
|
node_h.children.push(GameNode::MoveNode(node_i.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 game_tree = GameNode::MoveNode(node_a.clone());
|
||||||
|
|
||||||
|
let tree = Tree::from(&game_tree);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,8 +66,29 @@ impl ReviewTree {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redraw(&self, _ctx: &Context, _width: i32, _height: i32) {
|
pub fn redraw(&self, ctx: &Context, _width: i32, _height: i32) {
|
||||||
// Implement the tree-drawing algorithm here
|
println!("redraw");
|
||||||
|
let tree: &Option<Tree<Uuid>> = &self.imp().tree.borrow();
|
||||||
|
match tree {
|
||||||
|
Some(ref tree) => {
|
||||||
|
for node in tree.bfs_iter() {
|
||||||
|
// draw a circle given the coordinates of the nodes
|
||||||
|
// I don't know the indent. How do I keep track of that? Do I track the position of
|
||||||
|
// 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(0, node.id);
|
||||||
|
println!("[{}] {} x {}", node.id, row, column);
|
||||||
|
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);
|
||||||
|
let _ = ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// if there is no tree present, then there's nothing to draw!
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,43 +131,6 @@ struct Tree {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// There are some collapse rules that I could take into account here, but that I haven't figured
|
|
||||||
// out yet. If two nodes are side by side, and one of them has some wide children but the other has
|
|
||||||
// no children, then they are effectively the same width. The second node only needs to be moved
|
|
||||||
// out if it has children that would overlap the children of the first node.
|
|
||||||
//
|
|
||||||
// My algorithm right now is likely to generate unnecessarily wide trees in a complex game review.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn node_width(node: &GameNode) -> usize {
|
|
||||||
let children: &Vec<GameNode> = match node {
|
|
||||||
GameNode::MoveNode(mn) => &mn.children,
|
|
||||||
GameNode::SetupNode(sn) => &sn.children,
|
|
||||||
};
|
|
||||||
|
|
||||||
if children.is_empty() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is more than one child, run node_width on each one and add them together.
|
|
||||||
children
|
|
||||||
.iter()
|
|
||||||
.fold(0, |acc, child| acc + node_width(child))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since I know the width of a node, now I want to figure out its placement in the larger scheme of
|
|
||||||
// things.
|
|
||||||
//
|
|
||||||
// One thought I have is that I could just develop a grid virtually and start placing nodes.
|
|
||||||
// Whenever I notice a collision, I can just move the node over. But I'd like to see if I can be a
|
|
||||||
// bit smarter than doing it as just a vec into which I place things, as though it's a game board.
|
|
||||||
// So, given a game node, I want to figure out it's position along the X axis.
|
|
||||||
//
|
|
||||||
// Just having the node is greatly insufficient. I can get better results if I'm calculating the
|
|
||||||
// position of its children.
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Reference in New Issue