Start setting up properties for Move and Setup nodes
This commit is contained in:
parent
aa053cd10f
commit
a6be0129d6
|
@ -6,12 +6,13 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4", features = [ "serde" ] }
|
||||
nom = { version = "7" }
|
||||
serde = { version = "1", features = [ "derive" ] }
|
||||
thiserror = { version = "1"}
|
||||
typeshare = { version = "1" }
|
||||
uuid = { version = "1.4", features = ["v4", "serde"] }
|
||||
cool_asserts = { version = "*" }
|
||||
chrono = { version = "0.4", features = [ "serde" ] }
|
||||
nom = { version = "7" }
|
||||
serde = { version = "1", features = [ "derive" ] }
|
||||
thiserror = { version = "1"}
|
||||
typeshare = { version = "1" }
|
||||
uuid = { version = "1.4", features = ["v4", "serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
cool_asserts = { version = "2" }
|
||||
|
|
252
sgf/src/game.rs
252
sgf/src/game.rs
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>);
|
||||
|
|
Loading…
Reference in New Issue