Compare commits
No commits in common. "9fbc6305005a45903619bbc828efbcd805bab0b0" and "3192c0a14224eaed87c74eaf4f9b2ec43602e5b0" have entirely different histories.
9fbc630500
...
3192c0a142
|
@ -2709,7 +2709,6 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sgf",
|
"sgf",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"uuid 0.8.2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2729,7 +2728,6 @@ dependencies = [
|
||||||
"pango",
|
"pango",
|
||||||
"sgf",
|
"sgf",
|
||||||
"tokio",
|
"tokio",
|
||||||
"uuid 0.8.2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -15,7 +15,6 @@ grid = { version = "0.9" }
|
||||||
serde_json = { version = "1" }
|
serde_json = { version = "1" }
|
||||||
serde = { version = "1", features = [ "derive" ] }
|
serde = { version = "1", features = [ "derive" ] }
|
||||||
thiserror = { version = "1" }
|
thiserror = { version = "1" }
|
||||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
cool_asserts = { version = "2" }
|
cool_asserts = { version = "2" }
|
||||||
|
|
|
@ -26,8 +26,7 @@ mod database;
|
||||||
|
|
||||||
pub mod library;
|
pub mod library;
|
||||||
|
|
||||||
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};
|
||||||
|
|
||||||
|
pub mod settings;
|
||||||
|
|
|
@ -2,9 +2,7 @@ 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 std::{path::PathBuf, time::Duration, fmt};
|
||||||
use uuid::Uuid;
|
|
||||||
use std::{cell::RefCell, fmt, path::PathBuf, time::Duration};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
define_config! {
|
define_config! {
|
||||||
|
@ -142,6 +140,7 @@ impl AppState {
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum Rank {
|
pub enum Rank {
|
||||||
|
|
||||||
Kyu(u8),
|
Kyu(u8),
|
||||||
Dan(u8),
|
Dan(u8),
|
||||||
Pro(u8),
|
Pro(u8),
|
||||||
|
@ -229,146 +228,9 @@ 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.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
pub struct Tree<T> {
|
|
||||||
nodes: Vec<Node<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Node<T> {
|
|
||||||
id: usize,
|
|
||||||
node: T,
|
|
||||||
parent: Option<usize>,
|
|
||||||
depth: usize,
|
|
||||||
width: RefCell<Option<usize>>,
|
|
||||||
children: Vec<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Tree<T> {
|
|
||||||
fn new(root: T) -> Self {
|
|
||||||
Tree {
|
|
||||||
nodes: vec![Node {
|
|
||||||
id: 0,
|
|
||||||
node: root,
|
|
||||||
parent: None,
|
|
||||||
depth: 0,
|
|
||||||
width: RefCell::new(None),
|
|
||||||
children: vec![],
|
|
||||||
}],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn node(&self, idx: usize) -> &T {
|
|
||||||
&self.nodes[idx].node
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a node to the parent specified by parent_idx. Return the new index. This cannot be used
|
|
||||||
// to add the root node, but the constructor should handle that, anyway.
|
|
||||||
fn add_node(&mut self, parent_idx: usize, node: T) -> usize {
|
|
||||||
let next_idx = self.nodes.len();
|
|
||||||
let parent = &self.nodes[parent_idx];
|
|
||||||
|
|
||||||
self.nodes.push(Node {
|
|
||||||
id: next_idx,
|
|
||||||
node,
|
|
||||||
parent: Some(parent_idx),
|
|
||||||
depth: parent.depth + 1,
|
|
||||||
width: RefCell::new(None),
|
|
||||||
children: vec![],
|
|
||||||
});
|
|
||||||
|
|
||||||
let parent = &mut self.nodes[parent_idx];
|
|
||||||
parent.children.push(next_idx);
|
|
||||||
next_idx
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn max_depth(&self) -> usize {
|
|
||||||
self.nodes.iter().fold(
|
|
||||||
0,
|
|
||||||
|max, node| if node.depth > max { node.depth } else { max },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// indent represents the indentation that should be applied to all children in this tree. It
|
|
||||||
// amounts to the position of the parent node.
|
|
||||||
pub fn position(&self, indent: usize, idx: usize) -> (usize, usize) {
|
|
||||||
println!("[{}]", idx);
|
|
||||||
let node = &self.nodes[idx];
|
|
||||||
match node.parent {
|
|
||||||
Some(parent_idx) => {
|
|
||||||
let parent = &self.nodes[parent_idx];
|
|
||||||
let sibling_width = parent
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.take_while(|n| **n != node.id)
|
|
||||||
.fold(0, |acc, n| acc + self.width(*n));
|
|
||||||
println!("[{}] sibling width {}", idx, sibling_width);
|
|
||||||
(node.depth, indent + sibling_width)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root nodes won't have a parent, so just put them in the first column
|
|
||||||
None => (0, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn width(&self, id: usize) -> usize {
|
|
||||||
println!("[{}] calculating width", id);
|
|
||||||
let node = &self.nodes[id];
|
|
||||||
if let Some(width) = *node.width.borrow() {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
let width = node
|
|
||||||
.children
|
|
||||||
.iter()
|
|
||||||
.fold(0, |acc, child| acc + self.width(*child));
|
|
||||||
let width = if width == 0 { 1 } else { width };
|
|
||||||
println!("[{}] width: {}", id, width);
|
|
||||||
*node.width.borrow_mut() = Some(width);
|
|
||||||
|
|
||||||
width
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a GameNode> for Tree<Uuid> {
|
|
||||||
fn from(root: &'a GameNode) -> Self {
|
|
||||||
fn add_subtree<'a>(tree: &mut Tree<Uuid>, parent_idx: usize, node: &'a GameNode) {
|
|
||||||
let idx = tree.add_node(parent_idx, node.id());
|
|
||||||
|
|
||||||
let children = match node {
|
|
||||||
GameNode::MoveNode(node) => &node.children,
|
|
||||||
GameNode::SetupNode(node) => &node.children,
|
|
||||||
};
|
|
||||||
|
|
||||||
for child in children {
|
|
||||||
add_subtree(tree, idx, child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tree = Tree::new(root.id());
|
|
||||||
|
|
||||||
let children = match root {
|
|
||||||
GameNode::MoveNode(node) => &node.children,
|
|
||||||
GameNode::SetupNode(node) => &node.children,
|
|
||||||
};
|
|
||||||
|
|
||||||
for node in children {
|
|
||||||
add_subtree(&mut tree, 0, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
tree
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use sgf::{Move, MoveNode};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn current_player_changes_after_move() {
|
fn current_player_changes_after_move() {
|
||||||
|
@ -422,80 +284,4 @@ mod test {
|
||||||
Err(BoardError::Ko)
|
Err(BoardError::Ko)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A
|
|
||||||
// B G H
|
|
||||||
// C I
|
|
||||||
// D E F
|
|
||||||
#[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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
assert_eq!(tree.position(0, 2), (2, 0));
|
|
||||||
assert_eq!(tree.position(0, 1), (1, 0));
|
|
||||||
assert_eq!(tree.position(0, 0), (0, 0));
|
|
||||||
assert_eq!(tree.position(0, 4), (3, 1));
|
|
||||||
assert_eq!(tree.position(0, 5), (3, 2));
|
|
||||||
assert_eq!(tree.position(0, 6), (1, 3));
|
|
||||||
assert_eq!(tree.position(0, 7), (1, 4));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ otg-core = { path = "../core" }
|
||||||
pango = { version = "*" }
|
pango = { version = "*" }
|
||||||
sgf = { path = "../../sgf" }
|
sgf = { path = "../../sgf" }
|
||||||
tokio = { version = "1.26", features = [ "full" ] }
|
tokio = { version = "1.26", features = [ "full" ] }
|
||||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
glib-build-tools = "0.17"
|
glib-build-tools = "0.17"
|
||||||
|
|
|
@ -17,18 +17,14 @@ You should have received a copy of the GNU General Public License along with On
|
||||||
use cairo::Context;
|
use cairo::Context;
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{prelude::*, subclass::prelude::*};
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
use otg_core::Tree;
|
|
||||||
use sgf::{GameNode, GameRecord};
|
use sgf::{GameNode, GameRecord};
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
const WIDTH: i32 = 200;
|
const WIDTH: i32 = 200;
|
||||||
const HEIGHT: i32 = 800;
|
const HEIGHT: i32 = 800;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ReviewTreePrivate {
|
pub struct ReviewTreePrivate {
|
||||||
record: Rc<RefCell<Option<GameRecord>>>,
|
// record: Rc<RefCell<Option<GameRecord>>>,
|
||||||
tree: Rc<RefCell<Option<Tree<Uuid>>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -47,12 +43,9 @@ glib::wrapper! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReviewTree {
|
impl ReviewTree {
|
||||||
pub fn new(record: GameRecord) -> Self {
|
pub fn new(_record: GameRecord) -> Self {
|
||||||
let s: Self = Object::new();
|
let s: Self = Object::new();
|
||||||
|
|
||||||
*s.imp().tree.borrow_mut() = Some(Tree::from(&record.children[0]));
|
|
||||||
*s.imp().record.borrow_mut() = Some(record);
|
|
||||||
|
|
||||||
s.set_width_request(WIDTH);
|
s.set_width_request(WIDTH);
|
||||||
s.set_height_request(HEIGHT);
|
s.set_height_request(HEIGHT);
|
||||||
|
|
||||||
|
@ -131,9 +124,7 @@ fn node_width(node: &GameNode) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is more than one child, run node_width on each one and add them together.
|
// If there is more than one child, run node_width on each one and add them together.
|
||||||
children
|
children.iter().fold(0, |acc, child| acc + node_width(child))
|
||||||
.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
|
// Since I know the width of a node, now I want to figure out its placement in the larger scheme of
|
||||||
|
@ -146,6 +137,10 @@ fn node_width(node: &GameNode) -> usize {
|
||||||
//
|
//
|
||||||
// Just having the node is greatly insufficient. I can get better results if I'm calculating the
|
// Just having the node is greatly insufficient. I can get better results if I'm calculating the
|
||||||
// position of its children.
|
// position of its children.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn node_children_columns(_node: &GameNode) -> Vec<usize> {
|
||||||
|
vec![0, 1, 2]
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -180,9 +175,9 @@ mod test {
|
||||||
fn it_calculates_width_with_one_deep_child() {
|
fn it_calculates_width_with_one_deep_child() {
|
||||||
let mut node_a = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_a = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let mut node_b = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_b = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let node_c = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_c = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let node_d = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_d = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let node_e = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_e = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
|
|
||||||
node_b.children.push(GameNode::MoveNode(node_c));
|
node_b.children.push(GameNode::MoveNode(node_c));
|
||||||
node_b.children.push(GameNode::MoveNode(node_d));
|
node_b.children.push(GameNode::MoveNode(node_d));
|
||||||
|
@ -202,12 +197,12 @@ mod test {
|
||||||
let mut node_a = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_a = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let mut node_b = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_b = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let mut node_c = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_c = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let node_d = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_d = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let node_e = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_e = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let node_f = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_f = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let node_g = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_g = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let mut node_h = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_h = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
let node_i = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
let mut node_i = MoveNode::new(Color::Black, Move::Move("dp".to_owned()));
|
||||||
|
|
||||||
node_c.children.push(GameNode::MoveNode(node_d));
|
node_c.children.push(GameNode::MoveNode(node_d));
|
||||||
node_c.children.push(GameNode::MoveNode(node_e));
|
node_c.children.push(GameNode::MoveNode(node_e));
|
||||||
|
@ -238,14 +233,6 @@ mod test {
|
||||||
node_a.children.push(node_c.clone());
|
node_a.children.push(node_c.clone());
|
||||||
node_a.children.push(node_d.clone());
|
node_a.children.push(node_d.clone());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(node_children_columns(&GameNode::MoveNode(node_a)), vec![0, 1, 2]);
|
||||||
node_children_columns(&GameNode::MoveNode(node_a)),
|
|
||||||
vec![0, 1, 2]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn text_renderer() {
|
|
||||||
assert!(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue