Start setting up properties for Move and Setup nodes
This commit is contained in:
parent
9007ecdead
commit
0384d789a4
|
@ -6,6 +6,7 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
cool_asserts = { version = "*" }
|
||||||
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" ] }
|
||||||
|
|
252
sgf/src/game.rs
252
sgf/src/game.rs
|
@ -1,6 +1,90 @@
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use crate::{Color, Position};
|
use crate::{Color, Position};
|
||||||
use uuid::Uuid;
|
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)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum GameNode {
|
pub enum GameNode {
|
||||||
MoveNode(MoveNode),
|
MoveNode(MoveNode),
|
||||||
|
@ -8,8 +92,6 @@ pub enum GameNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Node {
|
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.
|
/// Provide a pre-order traversal of all of the nodes in the game tree.
|
||||||
fn nodes<'a>(&'a self) -> Vec<&'a GameNode> {
|
fn nodes<'a>(&'a self) -> Vec<&'a GameNode> {
|
||||||
self.children()
|
self.children()
|
||||||
|
@ -24,8 +106,8 @@ pub trait Node {
|
||||||
.collect::<Vec<&'a GameNode>>()
|
.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_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode;
|
||||||
fn add_annotation(&mut self, label: String, note: String);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameNode {
|
impl GameNode {
|
||||||
|
@ -35,10 +117,6 @@ impl GameNode {
|
||||||
GameNode::SetupNode(node) => node.id,
|
GameNode::SetupNode(node) => node.id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children<'a>(&'a self) -> Vec<&'a GameNode> {
|
|
||||||
unimplemented!("GameNode::children")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for GameNode {
|
impl Node for GameNode {
|
||||||
|
@ -62,10 +140,6 @@ impl Node for GameNode {
|
||||||
GameNode::SetupNode(node) => node.add_child(new_node),
|
GameNode::SetupNode(node) => node.add_child(new_node),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_annotation(&mut self, label: String, note: String) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root node
|
// Root node
|
||||||
|
@ -77,11 +151,6 @@ impl GameTree {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self { children: vec![] }
|
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 {
|
impl Node for GameTree {
|
||||||
|
@ -90,11 +159,8 @@ impl Node for GameTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode {
|
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode {
|
||||||
unimplemented!()
|
self.children.push(node);
|
||||||
}
|
self.children.last_mut().unwrap()
|
||||||
|
|
||||||
fn add_annotation(&mut self, label: String, note: String) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +170,7 @@ pub struct MoveNode {
|
||||||
|
|
||||||
color: Color,
|
color: Color,
|
||||||
position: Position,
|
position: Position,
|
||||||
annotations: Vec<(String, String)>,
|
properties: MoveProperties,
|
||||||
children: Vec<GameNode>,
|
children: Vec<GameNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,10 +181,14 @@ impl MoveNode {
|
||||||
|
|
||||||
color,
|
color,
|
||||||
position,
|
position,
|
||||||
annotations: Vec::new(),
|
properties: MoveProperties::default(),
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_property(&mut self, prop: MoveProperty) -> Result<(), PropertyError> {
|
||||||
|
self.properties.add_property(prop)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for MoveNode {
|
impl Node for MoveNode {
|
||||||
|
@ -130,29 +200,31 @@ impl Node for MoveNode {
|
||||||
self.children.push(node);
|
self.children.push(node);
|
||||||
self.children.last_mut().unwrap()
|
self.children.last_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_annotation(&mut self, label: String, note: String) {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct SetupNode {
|
pub struct SetupNode {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
|
|
||||||
positions: Vec<(Color, Position)>,
|
positions: Vec<(Option<Color>, Position)>,
|
||||||
annotations: Vec<(String, String)>,
|
|
||||||
children: Vec<GameNode>,
|
children: Vec<GameNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetupNode {
|
impl SetupNode {
|
||||||
pub fn new(positions: Vec<(Color, Position)>) -> Self {
|
pub fn new(positions: Vec<(Option<Color>, Position)>) -> Result<Self, SetupError> {
|
||||||
Self {
|
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(),
|
id: Uuid::new_v4(),
|
||||||
positions,
|
positions,
|
||||||
annotations: Vec::new(),
|
|
||||||
children: 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 {
|
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode {
|
||||||
unimplemented!()
|
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> {
|
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() {
|
fn it_can_add_moves_to_a_game() {
|
||||||
let mut tree = GameTree::new();
|
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 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()));
|
first_.add_child(GameNode::MoveNode(second_move.clone()));
|
||||||
|
|
||||||
let nodes = tree.nodes();
|
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)]
|
#[cfg(test)]
|
||||||
mod root_node_tests {
|
mod root_node_tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -242,6 +329,9 @@ mod root_node_tests {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod move_node_tests {
|
mod move_node_tests {
|
||||||
|
use super::*;
|
||||||
|
use cool_asserts::assert_matches;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_rejects_setup_properties() {
|
fn it_rejects_setup_properties() {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
|
@ -258,22 +348,38 @@ mod move_node_tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_rejects_an_sgf_setup_node() {
|
fn it_rejects_an_sgf_setup_property() {
|
||||||
unimplemented!()
|
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)]
|
#[cfg(test)]
|
||||||
mod setup_node_tests {
|
mod setup_node_tests {
|
||||||
#[test]
|
use super::*;
|
||||||
fn it_rejects_move_properties() {
|
use cool_asserts::assert_matches;
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_rejects_root_properties() {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_can_parse_an_sgf_setup_node() {
|
fn it_can_parse_an_sgf_setup_node() {
|
||||||
|
@ -281,8 +387,52 @@ mod setup_node_tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_rejects_an_sgf_move_node() {
|
fn it_rejects_conflicting_placement_properties() {
|
||||||
unimplemented!()
|
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,
|
White,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Position {}
|
pub struct Position {
|
||||||
|
row: char,
|
||||||
|
column: char,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
||||||
|
|
Loading…
Reference in New Issue