Compare commits
11 Commits
3c94f906a6
...
20b02fbd90
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | 20b02fbd90 | |
Savanni D'Gerinel | 278ec27b4e | |
Savanni D'Gerinel | 8b7add37c1 | |
Savanni D'Gerinel | 5441a3c441 | |
Savanni D'Gerinel | b1374229f3 | |
Savanni D'Gerinel | bc5042c004 | |
Savanni D'Gerinel | 0534143d6b | |
Savanni D'Gerinel | d7f5269e15 | |
Savanni D'Gerinel | c913e9da37 | |
Savanni D'Gerinel | c50bd652f1 | |
Savanni D'Gerinel | 093e1f7f8a |
|
@ -2503,6 +2503,16 @@ dependencies = [
|
||||||
"version_check 0.9.4",
|
"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]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
@ -2705,6 +2715,7 @@ dependencies = [
|
||||||
"config-derive",
|
"config-derive",
|
||||||
"cool_asserts",
|
"cool_asserts",
|
||||||
"grid",
|
"grid",
|
||||||
|
"nary_tree",
|
||||||
"serde 1.0.193",
|
"serde 1.0.193",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sgf",
|
"sgf",
|
||||||
|
@ -3685,6 +3696,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"cool_asserts",
|
"cool_asserts",
|
||||||
|
"nary_tree",
|
||||||
"nom",
|
"nom",
|
||||||
"serde 1.0.193",
|
"serde 1.0.193",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -3766,6 +3778,12 @@ version = "1.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "snowflake"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4.10"
|
version = "0.4.10"
|
||||||
|
|
45
Cargo.nix
45
Cargo.nix
|
@ -7917,6 +7917,28 @@ rec {
|
||||||
};
|
};
|
||||||
resolvedDefaultFeatures = [ "default" ];
|
resolvedDefaultFeatures = [ "default" ];
|
||||||
};
|
};
|
||||||
|
"nary_tree" = rec {
|
||||||
|
crateName = "nary_tree";
|
||||||
|
version = "0.4.3";
|
||||||
|
edition = "2021";
|
||||||
|
sha256 = "1iqray1a716995l9mmvz5sfqrwg9a235bvrkpcn8bcqwjnwfv1pv";
|
||||||
|
authors = [
|
||||||
|
"Ian <iwburns8@gmail.com>"
|
||||||
|
"David Cohen <dacohen@pm.me>"
|
||||||
|
];
|
||||||
|
dependencies = [
|
||||||
|
{
|
||||||
|
name = "slab";
|
||||||
|
packageId = "slab";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "snowflake";
|
||||||
|
packageId = "snowflake";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
features = {
|
||||||
|
};
|
||||||
|
};
|
||||||
"native-tls" = rec {
|
"native-tls" = rec {
|
||||||
crateName = "native-tls";
|
crateName = "native-tls";
|
||||||
version = "0.2.11";
|
version = "0.2.11";
|
||||||
|
@ -8551,6 +8573,10 @@ rec {
|
||||||
name = "grid";
|
name = "grid";
|
||||||
packageId = "grid";
|
packageId = "grid";
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "nary_tree";
|
||||||
|
packageId = "nary_tree";
|
||||||
|
}
|
||||||
{
|
{
|
||||||
name = "serde";
|
name = "serde";
|
||||||
packageId = "serde 1.0.193";
|
packageId = "serde 1.0.193";
|
||||||
|
@ -11570,6 +11596,10 @@ rec {
|
||||||
packageId = "chrono";
|
packageId = "chrono";
|
||||||
features = [ "serde" ];
|
features = [ "serde" ];
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
name = "nary_tree";
|
||||||
|
packageId = "nary_tree";
|
||||||
|
}
|
||||||
{
|
{
|
||||||
name = "nom";
|
name = "nom";
|
||||||
packageId = "nom";
|
packageId = "nom";
|
||||||
|
@ -11803,6 +11833,21 @@ rec {
|
||||||
};
|
};
|
||||||
resolvedDefaultFeatures = [ "const_generics" "const_new" "union" ];
|
resolvedDefaultFeatures = [ "const_generics" "const_new" "union" ];
|
||||||
};
|
};
|
||||||
|
"snowflake" = rec {
|
||||||
|
crateName = "snowflake";
|
||||||
|
version = "1.3.0";
|
||||||
|
edition = "2015";
|
||||||
|
sha256 = "1wadr7bxdxbmkbqkqsvzan6q1h3mxqpxningi3ss3v9jaav7n817";
|
||||||
|
authors = [
|
||||||
|
"Steven Allen <steven@stebalien.com>"
|
||||||
|
];
|
||||||
|
features = {
|
||||||
|
"serde" = [ "dep:serde" ];
|
||||||
|
"serde_derive" = [ "dep:serde_derive" ];
|
||||||
|
"serde_support" = [ "serde" "serde_derive" ];
|
||||||
|
};
|
||||||
|
resolvedDefaultFeatures = [ "default" ];
|
||||||
|
};
|
||||||
"socket2 0.4.10" = rec {
|
"socket2 0.4.10" = rec {
|
||||||
crateName = "socket2";
|
crateName = "socket2";
|
||||||
version = "0.4.10";
|
version = "0.4.10";
|
||||||
|
|
|
@ -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#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#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#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#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#nix@0.27.1": "0ly0kkmij5f0sqz35lx9czlbk6zpihb7yh1bsy4irzwfd2f4xc1f",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#no-std-compat@0.4.1": "132vrf710zsdp40yp1z3kgc2ss8pi0z4gmihsz3y7hl4dpd56f5r",
|
"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#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#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#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.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#socket2@0.5.5": "1sgq315f1njky114ip7wcy83qlphv9qclprfjwvxcpfblmcsqpvv",
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#spin@0.5.2": "0b84m6dbzrwf2kxylnw82d3dr8w06av7rfkr8s85fb5f43rwyqvf",
|
"registry+https://github.com/rust-lang/crates.io-index#spin@0.5.2": "0b84m6dbzrwf2kxylnw82d3dr8w06av7rfkr8s85fb5f43rwyqvf",
|
||||||
|
|
|
@ -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" ] }
|
||||||
|
nary_tree = { version = "0.4" }
|
||||||
thiserror = { version = "1" }
|
thiserror = { version = "1" }
|
||||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
uuid = { version = "0.8", features = ["v4", "serde"] }
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -29,5 +29,6 @@ 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, DepthTree, LibraryPath, Player, Rank, Size,
|
||||||
|
};
|
||||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
You should have received a copy of the GNU General Public License along with On the Grid. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::{Core};
|
use crate::Core;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sgf::GameRecord;
|
use sgf::GameRecord;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum LibraryRequest {
|
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>),
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_list_games(model: &Core) -> LibraryResponse {
|
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 {
|
pub async fn handle(model: &Core, request: LibraryRequest) -> LibraryResponse {
|
||||||
match request {
|
match request {
|
||||||
LibraryRequest::ListGames => handle_list_games(model).await,
|
LibraryRequest::ListGames => handle_list_games(model).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,11 @@ 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::{
|
||||||
|
collections::{HashMap, VecDeque}, fmt, ops::Deref, path::PathBuf, time::Duration
|
||||||
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
define_config! {
|
define_config! {
|
||||||
LibraryPath(LibraryPath),
|
LibraryPath(LibraryPath),
|
||||||
|
@ -229,6 +230,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,19 +240,49 @@ impl GameState {
|
||||||
pub struct Tree<T> {
|
pub struct Tree<T> {
|
||||||
nodes: Vec<Node<T>>,
|
nodes: Vec<Node<T>>,
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct DepthTree(nary_tree::Tree<SizeNode>);
|
||||||
pub struct Node<T> {
|
|
||||||
pub id: usize,
|
impl Deref for DepthTree {
|
||||||
node: T,
|
type Target = nary_tree::Tree<SizeNode>;
|
||||||
parent: Option<usize>,
|
|
||||||
depth: usize,
|
fn deref(&self) -> &Self::Target {
|
||||||
width: RefCell<Option<usize>>,
|
&self.0
|
||||||
children: Vec<usize>,
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Tree<T> {
|
#[derive(Debug)]
|
||||||
fn new(root: T) -> Self {
|
pub struct SizeNode {
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SizeNode {
|
||||||
|
pub fn position(&self) -> (usize, usize) {
|
||||||
|
(self.depth, self.width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DepthTree {
|
||||||
|
// 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(nary_tree::Tree::new())
|
||||||
|
/*
|
||||||
Tree {
|
Tree {
|
||||||
nodes: vec![Node {
|
nodes: vec![Node {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
@ -261,8 +293,10 @@ impl<T> Tree<T> {
|
||||||
children: vec![],
|
children: vec![],
|
||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
pub fn node(&self, idx: usize) -> &T {
|
pub fn node(&self, idx: usize) -> &T {
|
||||||
&self.nodes[idx].node
|
&self.nodes[idx].node
|
||||||
}
|
}
|
||||||
|
@ -286,12 +320,21 @@ 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 {
|
||||||
self.nodes.iter().fold(
|
self.0
|
||||||
0,
|
.root()
|
||||||
|max, node| if node.depth > max { node.depth } else { max },
|
.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
|
// Since I know the width of a node, now I want to figure out its placement in the larger
|
||||||
|
@ -309,7 +352,9 @@ impl<T> Tree<T> {
|
||||||
// amounts to the position of the parent node.
|
// amounts to the position of the parent node.
|
||||||
//
|
//
|
||||||
// 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) {
|
|
||||||
|
// 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 +371,9 @@ 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.
|
||||||
//
|
//
|
||||||
|
@ -353,14 +399,100 @@ impl<T> Tree<T> {
|
||||||
|
|
||||||
width
|
width
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn bfs_iter(&self) -> BFSIter<T> {
|
pub fn bfs_iter(&self) -> BFSIter<'_, SizeNode> {
|
||||||
let mut queue = VecDeque::new();
|
let mut queue = VecDeque::new();
|
||||||
queue.push_back(&self.nodes[0]);
|
queue.push_back(self.0.root().unwrap());
|
||||||
BFSIter { tree: self, queue }
|
BFSIter { queue }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a GameTree> for DepthTree {
|
||||||
|
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<nary_tree::NodeId, nary_tree::NodeId> = 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
|
||||||
|
// 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 {
|
||||||
|
game_node_id: source_root_node.node_id(),
|
||||||
|
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 {
|
||||||
|
game_node_id: source_node.node_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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
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,32 +522,31 @@ impl<'a> From<&'a GameNode> for Tree<Uuid> {
|
||||||
tree
|
tree
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub struct BFSIter<'a, T> {
|
pub struct BFSIter<'a, T> {
|
||||||
tree: &'a Tree<T>,
|
queue: VecDeque<nary_tree::NodeRef<'a, T>>,
|
||||||
queue: VecDeque<&'a Node<T>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Iterator for BFSIter<'a, T> {
|
impl<'a, T> Iterator for BFSIter<'a, T> {
|
||||||
type Item = &'a Node<T>;
|
type Item = &'a T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let retval = self.queue.pop_front();
|
let retval = self.queue.pop_front();
|
||||||
if let Some(retval) = retval {
|
if let Some(ref retval) = retval {
|
||||||
retval
|
retval
|
||||||
.children
|
.children()
|
||||||
.iter()
|
.for_each(|noderef| self.queue.push_back(noderef));
|
||||||
.for_each(|idx| self.queue.push_back(&self.tree.nodes[*idx]));
|
|
||||||
}
|
}
|
||||||
retval
|
retval.map(|retval| retval.data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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,116 +605,193 @@ 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()),
|
||||||
|
)))
|
||||||
|
.node_id();
|
||||||
|
|
||||||
|
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()));
|
assert_eq!(
|
||||||
let node_d = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned()));
|
game_tree.root().unwrap().traverse_pre_order().count(),
|
||||||
let node_e = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned()));
|
tree.0.root().unwrap().traverse_pre_order().count()
|
||||||
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));
|
let node_a = tree.root().unwrap();
|
||||||
node_c.children.push(GameNode::MoveNode(node_e));
|
assert_eq!(node_a.data().position(), (0, 0));
|
||||||
node_c.children.push(GameNode::MoveNode(node_f));
|
|
||||||
|
|
||||||
node_b.children.push(GameNode::MoveNode(node_c));
|
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));
|
||||||
|
|
||||||
node_h.children.push(GameNode::MoveNode(node_i));
|
let node_c = node_b.first_child().unwrap();
|
||||||
|
assert_eq!(node_c.data().position(), (2, 0));
|
||||||
|
|
||||||
node_a.children.push(GameNode::MoveNode(node_b));
|
let node_d = node_c.first_child().unwrap();
|
||||||
node_a.children.push(GameNode::MoveNode(node_g));
|
assert_eq!(node_d.data().position(), (3, 0));
|
||||||
node_a.children.push(GameNode::MoveNode(node_h));
|
|
||||||
|
|
||||||
let game_tree = GameNode::MoveNode(node_a);
|
let node_i = node_h.first_child().unwrap();
|
||||||
|
assert_eq!(node_i.data().position(), (2, 2));
|
||||||
|
|
||||||
let tree = Tree::from(&game_tree);
|
/*
|
||||||
|
assert_eq!(tree.position(test_tree.node_c), (2, 0));
|
||||||
assert_eq!(tree.position(2), (2, 0));
|
assert_eq!(tree.position(test_tree.node_b), (1, 0));
|
||||||
assert_eq!(tree.position(1), (1, 0));
|
assert_eq!(tree.position(test_tree.node_a), (0, 0));
|
||||||
assert_eq!(tree.position(0), (0, 0));
|
assert_eq!(tree.position(test_tree.node_d), (3, 1));
|
||||||
assert_eq!(tree.position(4), (3, 1));
|
assert_eq!(tree.position(test_tree.node_e), (3, 2));
|
||||||
assert_eq!(tree.position(5), (3, 2));
|
assert_eq!(tree.position(test_tree.node_f), (1, 3));
|
||||||
assert_eq!(tree.position(6), (1, 3));
|
assert_eq!(tree.position(test_tree.node_g), (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_b = 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_c = 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 node_d = 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_e = 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_f = 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_g = MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned()));
|
let node_f = 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_g = 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_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()));
|
let game = GameRecord::new(
|
||||||
node_c.children.push(GameNode::MoveNode(node_e.clone()));
|
GameType::Go,
|
||||||
node_c.children.push(GameNode::MoveNode(node_f.clone()));
|
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_h.children.push(GameNode::MoveNode(node_i.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());
|
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));
|
let mut iter = tree.bfs_iter();
|
||||||
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_a.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_b.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_g.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_h.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_c.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_i.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_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));
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ 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 otg_core::DepthTree;
|
||||||
use sgf::GameRecord;
|
use sgf::GameRecord;
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -28,7 +28,7 @@ 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>>>>,
|
tree: Rc<RefCell<Option<DepthTree>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -50,7 +50,9 @@ 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]));
|
// 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.imp().record.borrow_mut() = Some(record);
|
||||||
|
|
||||||
s.set_width_request(WIDTH);
|
s.set_width_request(WIDTH);
|
||||||
|
@ -67,7 +69,7 @@ impl ReviewTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redraw(&self, ctx: &Context, _width: i32, _height: i32) {
|
pub fn redraw(&self, ctx: &Context, _width: i32, _height: i32) {
|
||||||
let tree: &Option<Tree<Uuid>> = &self.imp().tree.borrow();
|
let tree: &Option<DepthTree> = &self.imp().tree.borrow();
|
||||||
match tree {
|
match tree {
|
||||||
Some(ref tree) => {
|
Some(ref tree) => {
|
||||||
for node in tree.bfs_iter() {
|
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
|
// the parent? do I need to just make it more intrinsically a part of the position
|
||||||
// code?
|
// code?
|
||||||
ctx.set_source_rgb(0.7, 0.7, 0.7);
|
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 y = (row as f64) * 20. + 10.;
|
||||||
let x = (column as f64) * 20. + 10.;
|
let x = (column as f64) * 20. + 10.;
|
||||||
ctx.arc(x, y, 5., 0., 2. * std::f64::consts::PI);
|
ctx.arc(x, y, 5., 0., 2. * std::f64::consts::PI);
|
||||||
|
|
|
@ -55,9 +55,10 @@ impl GameReview {
|
||||||
// It's actually really bad to be just throwing away errors. Panics make everyone unhappy.
|
// 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
|
// 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.
|
// renders the board and notifies the user of a problem that cannot be resolved.
|
||||||
let board_repr = otg_core::Goban::default()
|
let board_repr = match record.mainline() {
|
||||||
.apply_moves(record.mainline())
|
Some(iter) => otg_core::Goban::default().apply_moves(iter).unwrap(),
|
||||||
.unwrap();
|
None => otg_core::Goban::default(),
|
||||||
|
};
|
||||||
let board = Goban::new(board_repr, resources);
|
let board = Goban::new(board_repr, resources);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -9,6 +9,7 @@ edition = "2021"
|
||||||
chrono = { version = "0.4", features = [ "serde" ] }
|
chrono = { version = "0.4", features = [ "serde" ] }
|
||||||
nom = { version = "7" }
|
nom = { version = "7" }
|
||||||
serde = { version = "1", features = [ "derive" ] }
|
serde = { version = "1", features = [ "derive" ] }
|
||||||
|
nary_tree = { version = "0.4" }
|
||||||
thiserror = { version = "1"}
|
thiserror = { version = "1"}
|
||||||
typeshare = { version = "1" }
|
typeshare = { version = "1" }
|
||||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
uuid = { version = "0.8", features = ["v4", "serde"] }
|
||||||
|
|
444
sgf/src/game.rs
444
sgf/src/game.rs
|
@ -3,7 +3,14 @@ use crate::{
|
||||||
Color, Date, GameResult, GameType,
|
Color, Date, GameResult, GameType,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{collections::HashSet, time::Duration};
|
use nary_tree::{NodeId, NodeMut, NodeRef, Tree};
|
||||||
|
use std::{
|
||||||
|
collections::{HashMap, HashSet, VecDeque},
|
||||||
|
fmt,
|
||||||
|
fmt::Debug,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -32,7 +39,7 @@ pub enum SetupNodeError {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum GameNodeError {
|
pub enum GameNodeError {
|
||||||
UnsupportedGameNode(MoveNodeError, SetupNodeError),
|
UnsupportedGameNode(MoveNodeError, SetupNodeError, parser::Node),
|
||||||
ConflictingProperty,
|
ConflictingProperty,
|
||||||
ConflictingPosition,
|
ConflictingPosition,
|
||||||
}
|
}
|
||||||
|
@ -52,7 +59,7 @@ pub struct Player {
|
||||||
/// syntax issues, the result of the GameRecord is to have a fully-understood game. However, this
|
/// 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
|
/// doesn't (yet?) go quite to the level of apply the game type (i.e., this is Go, Chess, Yinsh, or
|
||||||
/// whatever).
|
/// whatever).
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct GameRecord {
|
pub struct GameRecord {
|
||||||
pub game_type: GameType,
|
pub game_type: GameType,
|
||||||
|
|
||||||
|
@ -78,7 +85,7 @@ pub struct GameRecord {
|
||||||
pub overtime: Option<String>,
|
pub overtime: Option<String>,
|
||||||
pub transcriber: Option<String>,
|
pub transcriber: Option<String>,
|
||||||
|
|
||||||
pub children: Vec<GameNode>,
|
pub trees: Vec<GameTree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameRecord {
|
impl GameRecord {
|
||||||
|
@ -111,55 +118,40 @@ impl GameRecord {
|
||||||
overtime: None,
|
overtime: None,
|
||||||
transcriber: None,
|
transcriber: None,
|
||||||
|
|
||||||
children: vec![],
|
trees: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn nodes(&self) -> Vec<&GameNode> {
|
||||||
|
self.iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &'_ GameNode> {
|
||||||
|
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
|
/// 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
|
/// was actually played out, and by convention consists of the first node in each list of
|
||||||
/// children.
|
/// children.
|
||||||
pub fn mainline(&self) -> Vec<&GameNode> {
|
pub fn mainline(&self) -> Option<impl Iterator<Item = &'_ GameNode>> {
|
||||||
let mut moves: Vec<&GameNode> = vec![];
|
if !self.trees.is_empty() {
|
||||||
|
Some(MainlineIter {
|
||||||
let mut next = self.children.first();
|
next: self.trees[0].root(),
|
||||||
while let Some(node) = next {
|
tree: &self.trees[0],
|
||||||
// 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
|
} else {
|
||||||
// keep doing that until there are no more first children.
|
None
|
||||||
//
|
|
||||||
// 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(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
moves
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for GameRecord {
|
impl TryFrom<parser::Tree> for GameRecord {
|
||||||
fn children<'a>(&'a self) -> Vec<&'a GameNode> {
|
|
||||||
self.children.iter().collect::<Vec<&'a GameNode>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_child(&mut self, node: GameNode) -> &mut GameNode {
|
|
||||||
self.children.push(node);
|
|
||||||
self.children.last_mut().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&parser::Tree> for GameRecord {
|
|
||||||
type Error = GameError;
|
type Error = GameError;
|
||||||
|
|
||||||
fn try_from(tree: &parser::Tree) -> Result<Self, Self::Error> {
|
fn try_from(tree: parser::Tree) -> Result<Self, Self::Error> {
|
||||||
let mut ty = None;
|
let mut ty = None;
|
||||||
let mut size = None;
|
let mut size = None;
|
||||||
let mut black_player = Player {
|
let mut black_player = Player {
|
||||||
|
@ -234,6 +226,7 @@ impl TryFrom<&parser::Tree> for GameRecord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
s.children = tree
|
s.children = tree
|
||||||
.root
|
.root
|
||||||
.next
|
.next
|
||||||
|
@ -241,33 +234,205 @@ impl TryFrom<&parser::Tree> for GameRecord {
|
||||||
.map(GameNode::try_from)
|
.map(GameNode::try_from)
|
||||||
.collect::<Result<Vec<GameNode>, GameNodeError>>()
|
.collect::<Result<Vec<GameNode>, GameNodeError>>()
|
||||||
.map_err(GameError::InvalidGameNode)?;
|
.map_err(GameError::InvalidGameNode)?;
|
||||||
|
*/
|
||||||
|
|
||||||
|
s.trees = tree
|
||||||
|
.root
|
||||||
|
.next
|
||||||
|
.into_iter()
|
||||||
|
.map(recursive_tree_to_slab_tree)
|
||||||
|
.collect::<Result<Vec<GameTree>, GameError>>()?;
|
||||||
|
|
||||||
Ok(s)
|
Ok(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recursive_tree_to_slab_tree(node: parser::Node) -> Result<GameTree, 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<GameNode> = 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(GameTree(slab))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TreeIter<'a> {
|
||||||
|
queue: VecDeque<NodeRef<'a, &'a GameNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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<Self::Item> {
|
||||||
|
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 GameTree(Tree<GameNode>);
|
||||||
|
|
||||||
|
impl Default for GameTree {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Tree::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<NodeId, NodeId> = HashMap::new();
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<GameNode>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
// 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<NodeRef<'a, GameNode>>,
|
||||||
|
tree: &'a Tree<GameNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for MainlineIter<'a> {
|
||||||
|
type Item = &'a GameNode;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
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)]
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub enum GameNode {
|
pub enum GameNode {
|
||||||
MoveNode(MoveNode),
|
MoveNode(MoveNode),
|
||||||
SetupNode(SetupNode),
|
SetupNode(SetupNode),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Node {
|
impl fmt::Display for GameNode {
|
||||||
/// Provide a pre-order traversal of all of the nodes in the game tree.
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
fn nodes<'a>(&'a self) -> Vec<&'a GameNode> {
|
match self {
|
||||||
self.children()
|
GameNode::MoveNode(_) => write!(f, "MoveNode"),
|
||||||
.iter()
|
GameNode::SetupNode(_) => write!(f, "SetupNode"),
|
||||||
.flat_map(|node| {
|
}
|
||||||
let mut children = node.nodes();
|
|
||||||
let mut v = vec![*node];
|
|
||||||
v.append(&mut children);
|
|
||||||
v
|
|
||||||
})
|
|
||||||
.collect::<Vec<&'a GameNode>>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> Vec<&GameNode>;
|
|
||||||
fn add_child(&mut self, node: GameNode) -> &mut GameNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameNode {
|
impl GameNode {
|
||||||
|
@ -279,70 +444,20 @@ impl GameNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for GameNode {
|
impl TryFrom<parser::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;
|
type Error = GameNodeError;
|
||||||
|
|
||||||
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
fn try_from(n: parser::Node) -> Result<Self, Self::Error> {
|
||||||
// I originally wrote this recursively. However, on an ordinary game of a couple hundred
|
let move_node = MoveNode::try_from(n.clone());
|
||||||
// moves, that meant that I was recursing 500 functions, and that exceeded the stack limit.
|
let setup_node = SetupNode::try_from(n.clone());
|
||||||
// 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);
|
|
||||||
let setup_node = SetupNode::try_from(n);
|
|
||||||
|
|
||||||
// I'm much too tired when writing this. I'm still recursing, but I did cut the number of
|
match (move_node, setup_node) {
|
||||||
// recursions in half. This helps, but it still doesn't guarantee that I'm going to be able
|
(Ok(node), _) => Ok(Self::MoveNode(node)),
|
||||||
// to parse all possible games. So, still, treat each branch of the game as a single line.
|
(Err(_), Ok(node)) => Ok(Self::SetupNode(node)),
|
||||||
// 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::<Result<Vec<Self>, 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))
|
|
||||||
}
|
|
||||||
(Err(move_err), Err(setup_err)) => {
|
(Err(move_err), Err(setup_err)) => {
|
||||||
Err(Self::Error::UnsupportedGameNode(move_err, setup_err))
|
Err(Self::Error::UnsupportedGameNode(move_err, setup_err, n))
|
||||||
}
|
}
|
||||||
}?;
|
}
|
||||||
|
|
||||||
Ok(node)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +466,6 @@ pub struct MoveNode {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub mv: Move,
|
pub mv: Move,
|
||||||
pub children: Vec<GameNode>,
|
|
||||||
|
|
||||||
pub time_left: Option<Duration>,
|
pub time_left: Option<Duration>,
|
||||||
pub moves_left: Option<usize>,
|
pub moves_left: Option<usize>,
|
||||||
|
@ -369,7 +483,6 @@ impl MoveNode {
|
||||||
id: Uuid::new_v4(),
|
id: Uuid::new_v4(),
|
||||||
color,
|
color,
|
||||||
mv,
|
mv,
|
||||||
children: Vec::new(),
|
|
||||||
|
|
||||||
time_left: None,
|
time_left: None,
|
||||||
moves_left: None,
|
moves_left: None,
|
||||||
|
@ -383,21 +496,10 @@ impl MoveNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for MoveNode {
|
impl TryFrom<parser::Node> for MoveNode {
|
||||||
fn children<'a>(&'a self) -> Vec<&'a GameNode> {
|
|
||||||
self.children.iter().collect::<Vec<&'a GameNode>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
type Error = MoveNodeError;
|
||||||
|
|
||||||
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
fn try_from(n: parser::Node) -> Result<Self, Self::Error> {
|
||||||
let s = match n.mv() {
|
let s = match n.mv() {
|
||||||
Some((color, mv)) => {
|
Some((color, mv)) => {
|
||||||
let mut s = Self::new(color, mv);
|
let mut s = Self::new(color, mv);
|
||||||
|
@ -460,7 +562,6 @@ pub struct SetupNode {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
|
|
||||||
pub positions: Vec<parser::SetupInstr>,
|
pub positions: Vec<parser::SetupInstr>,
|
||||||
pub children: Vec<GameNode>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetupNode {
|
impl SetupNode {
|
||||||
|
@ -480,26 +581,14 @@ impl SetupNode {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
id: Uuid::new_v4(),
|
id: Uuid::new_v4(),
|
||||||
positions,
|
positions,
|
||||||
children: Vec::new(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for SetupNode {
|
impl TryFrom<parser::Node> for SetupNode {
|
||||||
fn children<'a>(&'a self) -> Vec<&'a GameNode> {
|
|
||||||
self.children.iter().collect::<Vec<&'a GameNode>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn add_child(&mut self, _node: GameNode) -> &mut GameNode {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&parser::Node> for SetupNode {
|
|
||||||
type Error = SetupNodeError;
|
type Error = SetupNodeError;
|
||||||
|
|
||||||
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
fn try_from(n: parser::Node) -> Result<Self, Self::Error> {
|
||||||
match n.setup() {
|
match n.setup() {
|
||||||
Some(elements) => Self::new(elements),
|
Some(elements) => Self::new(elements),
|
||||||
None => Err(Self::Error::NotASetupNode),
|
None => Err(Self::Error::NotASetupNode),
|
||||||
|
@ -507,6 +596,7 @@ impl TryFrom<&parser::Node> for SetupNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn path_to_node(node: &GameNode, id: Uuid) -> Vec<&GameNode> {
|
pub fn path_to_node(node: &GameNode, id: Uuid) -> Vec<&GameNode> {
|
||||||
if node.id() == id {
|
if node.id() == id {
|
||||||
|
@ -523,6 +613,7 @@ pub fn path_to_node(node: &GameNode, id: Uuid) -> Vec<&GameNode> {
|
||||||
|
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -555,15 +646,19 @@ mod test {
|
||||||
Player::default(),
|
Player::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
let first_move = MoveNode::new(Color::Black, Move::Move("dd".to_owned()));
|
let first_move = MoveNode::new(Color::Black, Move::Move("dd".to_owned()));
|
||||||
let first_ = game.add_child(GameNode::MoveNode(first_move.clone()));
|
let first_ = game.add_child(GameNode::MoveNode(first_move.clone()));
|
||||||
let second_move = MoveNode::new(Color::White, Move::Move("qq".to_owned()));
|
let second_move = MoveNode::new(Color::White, Move::Move("qq".to_owned()));
|
||||||
first_.add_child(GameNode::MoveNode(second_move.clone()));
|
first_.add_child(GameNode::MoveNode(second_move.clone()));
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
let nodes = game.nodes();
|
let nodes = game.nodes();
|
||||||
assert_eq!(nodes.len(), 2);
|
assert_eq!(nodes.len(), 2);
|
||||||
assert_eq!(nodes[0].id(), first_move.id);
|
assert_eq!(nodes[0].id(), first_move.id);
|
||||||
assert_eq!(nodes[1].id(), second_move.id);
|
assert_eq!(nodes[1].id(), second_move.id);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ignore]
|
#[ignore]
|
||||||
|
@ -588,7 +683,7 @@ mod test {
|
||||||
],
|
],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
};
|
};
|
||||||
assert_matches!(GameNode::try_from(&n), Ok(GameNode::MoveNode(_)));
|
assert_matches!(GameNode::try_from(n), Ok(GameNode::MoveNode(_)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -630,10 +725,10 @@ mod move_node_tests {
|
||||||
],
|
],
|
||||||
next: vec![],
|
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.color, Color::White);
|
||||||
assert_eq!(node.mv, Move::Move("dp".to_owned()));
|
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.time_left, Some(Duration::from_secs(176)));
|
||||||
assert_eq!(node.comments, Some("Comments in the game".to_owned()));
|
assert_eq!(node.comments, Some("Comments in the game".to_owned()));
|
||||||
});
|
});
|
||||||
|
@ -653,7 +748,7 @@ mod move_node_tests {
|
||||||
next: vec![],
|
next: vec![],
|
||||||
};
|
};
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
MoveNode::try_from(&n),
|
MoveNode::try_from(n),
|
||||||
Err(MoveNodeError::IncompatibleProperty(_))
|
Err(MoveNodeError::IncompatibleProperty(_))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -703,7 +798,7 @@ mod path_test {
|
||||||
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(text).unwrap();
|
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(text).unwrap();
|
||||||
let games = games
|
let games = games
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|game| GameRecord::try_from(&game).expect("game to parse"))
|
.map(|game| GameRecord::try_from(game).expect("game to parse"))
|
||||||
.collect::<Vec<GameRecord>>();
|
.collect::<Vec<GameRecord>>();
|
||||||
f(games);
|
f(games);
|
||||||
}
|
}
|
||||||
|
@ -722,7 +817,10 @@ mod path_test {
|
||||||
|games| {
|
|games| {
|
||||||
let game = &games[0];
|
let game = &games[0];
|
||||||
|
|
||||||
let moves = game.mainline();
|
let moves = game
|
||||||
|
.mainline()
|
||||||
|
.expect("there should be a mainline in this file")
|
||||||
|
.collect::<Vec<&GameNode>>();
|
||||||
assert_matches!(moves[0], GameNode::MoveNode(node) => {
|
assert_matches!(moves[0], GameNode::MoveNode(node) => {
|
||||||
assert_eq!(node.color, Color::Black);
|
assert_eq!(node.color, Color::Black);
|
||||||
assert_eq!(node.mv, Move::Move("pp".to_owned()));
|
assert_eq!(node.mv, Move::Move("pp".to_owned()));
|
||||||
|
@ -744,7 +842,10 @@ mod path_test {
|
||||||
with_file(std::path::Path::new("test_data/branch_test.sgf"), |games| {
|
with_file(std::path::Path::new("test_data/branch_test.sgf"), |games| {
|
||||||
let game = &games[0];
|
let game = &games[0];
|
||||||
|
|
||||||
let moves = game.mainline();
|
let moves = game
|
||||||
|
.mainline()
|
||||||
|
.expect("there should be a mainline in this file")
|
||||||
|
.collect::<Vec<&GameNode>>();
|
||||||
assert_matches!(moves[1], GameNode::MoveNode(node) => {
|
assert_matches!(moves[1], GameNode::MoveNode(node) => {
|
||||||
assert_eq!(node.color, Color::White);
|
assert_eq!(node.color, Color::White);
|
||||||
assert_eq!(node.mv, Move::Move("dd".to_owned()));
|
assert_eq!(node.mv, Move::Move("dd".to_owned()));
|
||||||
|
@ -791,7 +892,7 @@ mod file_test {
|
||||||
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(text).unwrap();
|
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(text).unwrap();
|
||||||
let games = games
|
let games = games
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|game| GameRecord::try_from(&game).expect("game to parse"))
|
.map(|game| GameRecord::try_from(game).expect("game to parse"))
|
||||||
.collect::<Vec<GameRecord>>();
|
.collect::<Vec<GameRecord>>();
|
||||||
f(games);
|
f(games);
|
||||||
}
|
}
|
||||||
|
@ -875,6 +976,7 @@ mod file_test {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
let children = game.children();
|
let children = game.children();
|
||||||
let node = children.first().unwrap();
|
let node = children.first().unwrap();
|
||||||
assert_matches!(node, GameNode::MoveNode(node) => {
|
assert_matches!(node, GameNode::MoveNode(node) => {
|
||||||
|
@ -892,6 +994,7 @@ mod file_test {
|
||||||
assert_eq!(node.time_left, Some(Duration::from_secs(1765)));
|
assert_eq!(node.time_left, Some(Duration::from_secs(1765)));
|
||||||
assert_eq!(node.comments, None);
|
assert_eq!(node.comments, None);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
/*
|
/*
|
||||||
let node = node.next().unwrap();
|
let node = node.next().unwrap();
|
||||||
let expected_properties = vec![
|
let expected_properties = vec![
|
||||||
|
@ -911,4 +1014,39 @@ 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()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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]);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
@ -22,6 +22,7 @@ pub enum Error {
|
||||||
InvalidSgf(VerboseNomError),
|
InvalidSgf(VerboseNomError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ pub fn parse_sgf(input: &str) -> Result<Vec<Result<GameRecord, game::GameError>>
|
||||||
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(input)?;
|
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(input)?;
|
||||||
let games = games
|
let games = games
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|game| GameRecord::try_from(&game))
|
.map(GameRecord::try_from)
|
||||||
.collect::<Vec<Result<GameRecord, game::GameError>>>();
|
.collect::<Vec<Result<GameRecord, game::GameError>>>();
|
||||||
|
|
||||||
Ok(games)
|
Ok(games)
|
||||||
|
|
|
@ -56,6 +56,7 @@ pub enum Error {
|
||||||
InvalidSgf(VerboseNomError),
|
InvalidSgf(VerboseNomError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
||||||
|
|
||||||
|
|
|
@ -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]))
|
|
@ -9,6 +9,10 @@ use std::{
|
||||||
rc::Rc,
|
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)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub enum Tree<T> {
|
pub enum Tree<T> {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -55,6 +59,16 @@ impl<T> Tree<T> {
|
||||||
None
|
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<F>(&self, f: F) -> Vec<Node<T>>
|
||||||
|
where
|
||||||
|
F: FnOnce(&T) -> bool + Copy,
|
||||||
|
{
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert each node of a tree from type T to type U
|
/// Convert each node of a tree from type T to type U
|
||||||
pub fn map<F, U>(&self, op: F) -> Tree<U>
|
pub fn map<F, U>(&self, op: F) -> Tree<U>
|
||||||
where
|
where
|
||||||
|
@ -146,6 +160,13 @@ impl<T> Node<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialEq for Node<T> {
|
||||||
|
fn eq(&self, other: &Node<T>) -> bool {
|
||||||
|
self.0.borrow().value == other.0.borrow().value
|
||||||
|
&& self.0.borrow().children == other.0.borrow().children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -184,4 +205,31 @@ mod tests {
|
||||||
assert!(tree2.find_bfs(|val| *val == "16").is_some());
|
assert!(tree2.find_bfs(|val| *val == "16").is_some());
|
||||||
assert!(tree2.find_bfs(|val| *val == "17").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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue