Start on a second-level representation for the game tree

This commit is contained in:
Savanni D'Gerinel 2023-09-01 09:49:00 -04:00
parent d6c2a9519b
commit 0700f13429
4 changed files with 284 additions and 0 deletions

2
Cargo.lock generated
View File

@ -2309,6 +2309,7 @@ dependencies = [
"serde", "serde",
"thiserror", "thiserror",
"typeshare", "typeshare",
"uuid 1.4.1",
] ]
[[package]] [[package]]
@ -2726,6 +2727,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"serde",
] ]
[[package]] [[package]]

View File

@ -11,6 +11,7 @@ nom = { version = "7" }
serde = { version = "1", features = [ "derive" ] } serde = { version = "1", features = [ "derive" ] }
thiserror = { version = "1"} thiserror = { version = "1"}
typeshare = { version = "1" } typeshare = { version = "1" }
uuid = { version = "1.4", features = ["v4", "serde"] }
[dev-dependencies] [dev-dependencies]
cool_asserts = { version = "2" } cool_asserts = { version = "2" }

272
sgf/src/game.rs Normal file
View File

@ -0,0 +1,272 @@
use crate::{Color, Position};
use uuid::Uuid;
pub trait Node {
fn path_to_node<'a>(&'a self, id: Uuid) -> Vec<&'a GameNode>;
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a GameNode;
fn add_annotation(&mut self, label: String, note: String);
}
pub enum GameNode {
MoveNode(MoveNode),
SetupNode(SetupNode),
}
impl GameNode {
fn id(&self) -> Uuid {
match self {
GameNode::MoveNode(node) => node.id,
GameNode::SetupNode(node) => node.id,
}
}
fn children<'a>(&'a self) -> Vec<&'a GameNode> {
unimplemented!("GameNode::children")
}
}
impl Node for GameNode {
fn path_to_node<'a>(&'a self, id: Uuid) -> Vec<&'a GameNode> {
unimplemented!()
}
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a GameNode {
unimplemented!()
}
fn add_annotation(&mut self, label: String, note: String) {
unimplemented!()
}
}
// Root node
pub struct GameTree {
children: Vec<GameNode>,
}
impl GameTree {
fn new() -> Self {
Self { children: vec![] }
}
fn nodes<'a>(&'a self) -> impl Iterator<Item = &'a GameNode> {
vec![].into_iter()
}
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a GameNode {
// self.children.push(node);
unimplemented!()
}
}
impl Node for GameTree {
fn path_to_node<'a>(&'a self, id: Uuid) -> Vec<&'a GameNode> {
unimplemented!()
}
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a GameNode {
unimplemented!()
}
fn add_annotation(&mut self, label: String, note: String) {
unimplemented!()
}
}
pub struct MoveNode {
id: Uuid,
color: Color,
position: Position,
annotations: Vec<(String, String)>,
children: Vec<GameNode>,
}
impl MoveNode {
pub fn new(color: Color, position: Position) -> Self {
Self {
id: Uuid::new_v4(),
color,
position,
annotations: Vec::new(),
children: Vec::new(),
}
}
}
impl Node for MoveNode {
fn path_to_node<'a>(&'a self, id: Uuid) -> Vec<&'a GameNode> {
unimplemented!()
}
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a GameNode {
unimplemented!()
}
fn add_annotation(&mut self, label: String, note: String) {
unimplemented!()
}
}
pub struct SetupNode {
id: Uuid,
positions: Vec<(Color, Position)>,
annotations: Vec<(String, String)>,
children: Vec<GameNode>,
}
impl SetupNode {
pub fn new(positions: Vec<(Color, Position)>) -> Self {
Self {
id: Uuid::new_v4(),
positions,
annotations: Vec::new(),
children: Vec::new(),
}
}
}
impl Node for SetupNode {
fn path_to_node<'a>(&'a self, id: Uuid) -> Vec<&'a GameNode> {
unimplemented!()
}
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a GameNode {
unimplemented!()
}
fn add_annotation(&mut self, label: String, note: String) {
unimplemented!()
}
}
pub fn path_to_node<'a>(node: &'a GameNode, id: Uuid) -> Vec<&'a GameNode> {
if node.id() == id {
return vec![node];
}
for child in node.children() {
let mut path = path_to_node(child, id);
if path.len() > 1 {
path.push(child);
return path;
}
}
Vec::new()
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn it_can_create_an_empty_game_tree() {
let tree = GameTree::new();
assert_eq!(tree.nodes().count(), 0);
}
#[test]
fn it_can_add_moves_to_a_game() {
let mut tree = GameTree::new();
let node = MoveNode::new(Color::Black, Position {});
let mut node = tree.add_child(GameNode::MoveNode(node));
let new_node = MoveNode::new(Color::White, Position {});
node.add_child(new_node);
}
#[test]
fn it_can_set_up_a_game() {
unimplemented!()
}
#[test]
fn it_can_load_tree_from_sgf() {
unimplemented!()
}
}
#[cfg(test)]
mod root_node_tests {
#[test]
fn it_rejects_move_properties() {
unimplemented!()
}
#[test]
fn it_rejects_setup_properties() {
unimplemented!()
}
#[test]
fn it_can_parse_a_root_sgf() {
unimplemented!()
}
}
#[cfg(test)]
mod move_node_tests {
#[test]
fn it_rejects_setup_properties() {
unimplemented!()
}
#[test]
fn it_rejects_root_properties() {
unimplemented!()
}
#[test]
fn it_can_parse_an_sgf_move_node() {
unimplemented!()
}
#[test]
fn it_rejects_an_sgf_setup_node() {
unimplemented!()
}
}
#[cfg(test)]
mod setup_node_tests {
#[test]
fn it_rejects_move_properties() {
unimplemented!()
}
#[test]
fn it_rejects_root_properties() {
unimplemented!()
}
#[test]
fn it_can_parse_an_sgf_setup_node() {
unimplemented!()
}
#[test]
fn it_rejects_an_sgf_move_node() {
unimplemented!()
}
}
#[cfg(test)]
mod path_test {
#[test]
fn returns_empty_list_if_no_game_nodes() {
unimplemented!()
}
#[test]
fn returns_empty_list_if_node_not_found() {
unimplemented!()
}
#[test]
fn path_excludes_root_node() {
unimplemented!()
}
}

View File

@ -6,6 +6,8 @@ pub mod go;
mod tree; mod tree;
use tree::parse_collection; use tree::parse_collection;
mod game;
use thiserror::Error; use thiserror::Error;
#[derive(Debug)] #[derive(Debug)]
@ -16,6 +18,13 @@ pub enum Error {
InvalidSgf(VerboseNomError), InvalidSgf(VerboseNomError),
} }
pub enum Color {
Black,
White,
}
pub struct Position {}
#[derive(Debug)] #[derive(Debug)]
pub struct VerboseNomError(nom::error::VerboseError<String>); pub struct VerboseNomError(nom::error::VerboseError<String>);