Start setting up properties for Move and Setup nodes

This commit is contained in:
Savanni D'Gerinel 2023-09-02 22:24:27 -04:00
parent aa053cd10f
commit a6be0129d6
3 changed files with 213 additions and 59 deletions

View File

@ -6,6 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cool_asserts = { version = "*" }
chrono = { version = "0.4", features = [ "serde" ] }
nom = { version = "7" }
serde = { version = "1", features = [ "derive" ] }

View File

@ -1,6 +1,90 @@
use std::collections::HashSet;
use crate::{Color, Position};
use uuid::Uuid;
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyError {
ConflictingProperty,
}
#[derive(Clone, Debug, PartialEq)]
pub enum SetupError {
ConflictingPosition,
}
#[derive(Clone, Debug, PartialEq)]
pub enum NodeProperty {
Comment(String),
EvenResult(f64),
GoodForBlack(f64),
GoodForWhite(f64),
Hotspot(f64),
NodeName(String),
Unclear(f64),
Value(f64),
}
#[derive(Clone, Debug, PartialEq)]
pub enum TimingProperty {
BlackTimeLeft(f64),
BlackMovesLeft(f64),
WhiteMovesLeft(f64),
WhiteTimeLeft(f64),
}
#[derive(Clone, Debug, PartialEq)]
pub enum MoveAnnotationProperty {
BadMove(f64),
DoubtfulMove(f64),
InterestingMove(f64),
Tesuji(f64),
}
#[derive(Clone, Debug, PartialEq)]
pub enum MoveProperty {
NodeProperty(NodeProperty),
TimingProperty(TimingProperty),
MoveAnnotationProperty(MoveAnnotationProperty),
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct MoveProperties(Vec<MoveProperty>);
impl MoveProperties {
pub fn add_property(&mut self, prop: MoveProperty) -> Result<(), PropertyError> {
match prop {
MoveProperty::NodeProperty(_) => {}
MoveProperty::TimingProperty(_) => {}
MoveProperty::MoveAnnotationProperty(_) => {
if contains_move_annotation_property(&self.0) {
return Err(PropertyError::ConflictingProperty);
}
self.0.push(prop);
}
}
return Ok(());
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum SetupProperty {
NodeProperty(NodeProperty),
AddBlack(Vec<Position>),
AddWhite(Vec<Position>),
ClearPoints(Vec<Position>),
PlayerTurn(Color),
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct SetupProperties(Vec<SetupProperty>);
impl SetupProperties {
pub fn add_property(&mut self, prop: SetupProperty) {
unimplemented!()
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum GameNode {
MoveNode(MoveNode),
@ -8,8 +92,6 @@ pub enum GameNode {
}
pub trait Node {
fn children<'a>(&'a self) -> Vec<&'a GameNode>;
/// Provide a pre-order traversal of all of the nodes in the game tree.
fn nodes<'a>(&'a self) -> Vec<&'a GameNode> {
self.children()
@ -24,8 +106,8 @@ pub trait Node {
.collect::<Vec<&'a GameNode>>()
}
fn children<'a>(&'a self) -> Vec<&'a GameNode>;
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode;
fn add_annotation(&mut self, label: String, note: String);
}
impl GameNode {
@ -35,10 +117,6 @@ impl GameNode {
GameNode::SetupNode(node) => node.id,
}
}
fn children<'a>(&'a self) -> Vec<&'a GameNode> {
unimplemented!("GameNode::children")
}
}
impl Node for GameNode {
@ -62,10 +140,6 @@ impl Node for GameNode {
GameNode::SetupNode(node) => node.add_child(new_node),
}
}
fn add_annotation(&mut self, label: String, note: String) {
unimplemented!()
}
}
// Root node
@ -77,11 +151,6 @@ impl GameTree {
fn new() -> Self {
Self { children: vec![] }
}
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode {
self.children.push(node);
self.children.last_mut().unwrap()
}
}
impl Node for GameTree {
@ -90,11 +159,8 @@ impl Node for GameTree {
}
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode {
unimplemented!()
}
fn add_annotation(&mut self, label: String, note: String) {
unimplemented!()
self.children.push(node);
self.children.last_mut().unwrap()
}
}
@ -104,7 +170,7 @@ pub struct MoveNode {
color: Color,
position: Position,
annotations: Vec<(String, String)>,
properties: MoveProperties,
children: Vec<GameNode>,
}
@ -115,10 +181,14 @@ impl MoveNode {
color,
position,
annotations: Vec::new(),
properties: MoveProperties::default(),
children: Vec::new(),
}
}
fn add_property(&mut self, prop: MoveProperty) -> Result<(), PropertyError> {
self.properties.add_property(prop)
}
}
impl Node for MoveNode {
@ -130,29 +200,31 @@ impl Node for MoveNode {
self.children.push(node);
self.children.last_mut().unwrap()
}
fn add_annotation(&mut self, label: String, note: String) {
unimplemented!()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SetupNode {
id: Uuid,
positions: Vec<(Color, Position)>,
annotations: Vec<(String, String)>,
positions: Vec<(Option<Color>, Position)>,
children: Vec<GameNode>,
}
impl SetupNode {
pub fn new(positions: Vec<(Color, Position)>) -> Self {
Self {
pub fn new(positions: Vec<(Option<Color>, Position)>) -> Result<Self, SetupError> {
let mut coords: HashSet<Position> = HashSet::new();
for coord in positions.iter().map(|p| p.1.clone()) {
if coords.contains(&coord) {
return Err(SetupError::ConflictingPosition);
}
coords.insert(coord);
}
Ok(Self {
id: Uuid::new_v4(),
positions,
annotations: Vec::new(),
children: Vec::new(),
}
})
}
}
@ -164,10 +236,6 @@ impl Node for SetupNode {
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut 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> {
@ -200,9 +268,21 @@ mod test {
fn it_can_add_moves_to_a_game() {
let mut tree = GameTree::new();
let first_move = MoveNode::new(Color::Black, Position {});
let first_move = MoveNode::new(
Color::Black,
Position {
row: 'd',
column: 'd',
},
);
let first_ = tree.add_child(GameNode::MoveNode(first_move.clone()));
let second_move = MoveNode::new(Color::White, Position {});
let second_move = MoveNode::new(
Color::White,
Position {
row: 'q',
column: 'q',
},
);
first_.add_child(GameNode::MoveNode(second_move.clone()));
let nodes = tree.nodes();
@ -222,6 +302,13 @@ mod test {
}
}
fn contains_move_annotation_property(lst: &Vec<MoveProperty>) -> bool {
lst.iter().any(|item| match item {
MoveProperty::MoveAnnotationProperty(_) => true,
_ => false,
})
}
#[cfg(test)]
mod root_node_tests {
#[test]
@ -242,6 +329,9 @@ mod root_node_tests {
#[cfg(test)]
mod move_node_tests {
use super::*;
use cool_asserts::assert_matches;
#[test]
fn it_rejects_setup_properties() {
unimplemented!()
@ -258,22 +348,38 @@ mod move_node_tests {
}
#[test]
fn it_rejects_an_sgf_setup_node() {
fn it_rejects_an_sgf_setup_property() {
unimplemented!()
}
#[test]
fn it_prevents_multiple_move_annotation_properties() {
let mut node = MoveNode::new(
Color::Black,
Position {
row: 'a',
column: 'b',
},
);
assert_matches!(
node.add_property(MoveProperty::MoveAnnotationProperty(
MoveAnnotationProperty::BadMove(5.),
)),
Ok(_)
);
assert_matches!(
node.add_property(MoveProperty::MoveAnnotationProperty(
MoveAnnotationProperty::Tesuji(5.),
)),
Err(PropertyError::ConflictingProperty)
);
}
}
#[cfg(test)]
mod setup_node_tests {
#[test]
fn it_rejects_move_properties() {
unimplemented!()
}
#[test]
fn it_rejects_root_properties() {
unimplemented!()
}
use super::*;
use cool_asserts::assert_matches;
#[test]
fn it_can_parse_an_sgf_setup_node() {
@ -281,8 +387,52 @@ mod setup_node_tests {
}
#[test]
fn it_rejects_an_sgf_move_node() {
unimplemented!()
fn it_rejects_conflicting_placement_properties() {
assert_matches!(
SetupNode::new(vec![
(
Some(Color::Black),
Position {
row: 'd',
column: 'd',
},
),
(
Some(Color::Black),
Position {
row: 'd',
column: 'd',
},
),
]),
Err(SetupError::ConflictingPosition)
);
assert_matches!(
SetupNode::new(vec![
(
Some(Color::Black),
Position {
row: 'd',
column: 'd',
},
),
(
Some(Color::Black),
Position {
row: 'e',
column: 'e',
},
),
(
Some(Color::White),
Position {
row: 'e',
column: 'e',
},
),
]),
Err(SetupError::ConflictingPosition)
);
}
}

View File

@ -24,8 +24,11 @@ pub enum Color {
White,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Position {}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Position {
row: char,
column: char,
}
#[derive(Debug)]
pub struct VerboseNomError(nom::error::VerboseError<String>);