Start building the second layer of game validation
This commit is contained in:
parent
69e4605d71
commit
2bf0b3d782
121
sgf/src/game.rs
121
sgf/src/game.rs
|
@ -1,4 +1,4 @@
|
||||||
use crate::{tree, Color, Position};
|
use crate::{parser, Color};
|
||||||
use std::{collections::HashSet, time::Duration};
|
use std::{collections::HashSet, time::Duration};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
@ -227,9 +227,9 @@ impl Node for GameNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&tree::Node> for GameNode {
|
impl TryFrom<&parser::Node> for GameNode {
|
||||||
type Error = ConversionError;
|
type Error = ConversionError;
|
||||||
fn try_from(n: &tree::Node) -> Result<Self, Self::Error> {
|
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,7 +260,7 @@ impl Node for GameTree {
|
||||||
pub struct MoveNode {
|
pub struct MoveNode {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
color: Color,
|
color: Color,
|
||||||
position: Position,
|
position: String,
|
||||||
children: Vec<GameNode>,
|
children: Vec<GameNode>,
|
||||||
|
|
||||||
time_left: Option<Duration>,
|
time_left: Option<Duration>,
|
||||||
|
@ -274,7 +274,7 @@ pub struct MoveNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MoveNode {
|
impl MoveNode {
|
||||||
pub fn new(color: Color, position: Position) -> Self {
|
pub fn new(color: Color, position: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: Uuid::new_v4(),
|
id: Uuid::new_v4(),
|
||||||
color,
|
color,
|
||||||
|
@ -304,42 +304,41 @@ impl Node for MoveNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&tree::Node> for MoveNode {
|
impl TryFrom<&parser::Node> for MoveNode {
|
||||||
type Error = ConversionError;
|
type Error = ConversionError;
|
||||||
|
|
||||||
fn try_from(n: &tree::Node) -> Result<Self, Self::Error> {
|
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
||||||
let move_ = match (n.find_prop("W"), n.find_prop("B")) {
|
match n.move_() {
|
||||||
(Some(white_move), _) => Some((Color::White, Position{ row: white_move
|
Some((color, position)) => Ok(MoveNode::new(color, position)),
|
||||||
(None, Some(black_move)) => unimplemented!(),
|
|
||||||
(None, None) => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
match move_ {
|
|
||||||
Some((color, position)) => unimplemented!(),
|
|
||||||
None => Err(ConversionError::IncompatibleNodeType),
|
None => Err(ConversionError::IncompatibleNodeType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
fn parse_position(s: &str) -> Result<Position, ConversionError> {
|
fn parse_position(s: &str) -> Result<Position, ConversionError> {
|
||||||
if s.len() == 2 {
|
if s.len() == 2 {
|
||||||
Ok(Position{ row: s[0], column: s[1] })
|
Ok(Position {
|
||||||
|
row: s[0],
|
||||||
|
column: s[1],
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(ConversionError::InvalidPositionSyntax)
|
Err(ConversionError::InvalidPositionSyntax)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct SetupNode {
|
pub struct SetupNode {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
|
|
||||||
positions: Vec<(Option<Color>, Position)>,
|
positions: Vec<(Option<Color>, String)>,
|
||||||
children: Vec<GameNode>,
|
children: Vec<GameNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetupNode {
|
impl SetupNode {
|
||||||
pub fn new(positions: Vec<(Option<Color>, Position)>) -> Result<Self, SetupError> {
|
pub fn new(positions: Vec<(Option<Color>, String)>) -> Result<Self, SetupError> {
|
||||||
let mut coords: HashSet<Position> = HashSet::new();
|
let mut coords: HashSet<String> = HashSet::new();
|
||||||
for coord in positions.iter().map(|p| p.1.clone()) {
|
for coord in positions.iter().map(|p| p.1.clone()) {
|
||||||
if coords.contains(&coord) {
|
if coords.contains(&coord) {
|
||||||
return Err(SetupError::ConflictingPosition);
|
return Err(SetupError::ConflictingPosition);
|
||||||
|
@ -396,21 +395,9 @@ 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(
|
let first_move = MoveNode::new(Color::Black, "dd".to_owned());
|
||||||
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(
|
let second_move = MoveNode::new(Color::White, "qq".to_owned());
|
||||||
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();
|
||||||
|
@ -429,20 +416,19 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn game_node_can_parse_sgf_move_node() {
|
fn game_node_can_parse_sgf_move_node() {
|
||||||
let n = tree::Node {
|
let n = parser::Node {
|
||||||
properties: vec![
|
properties: vec![
|
||||||
tree::Property {
|
parser::Property::Move((Color::White, "dp".to_owned())),
|
||||||
ident: "W".to_owned(),
|
/*
|
||||||
values: vec!["dp".to_owned()],
|
parser::Property {
|
||||||
},
|
|
||||||
tree::Property {
|
|
||||||
ident: "WL".to_owned(),
|
ident: "WL".to_owned(),
|
||||||
values: vec!["176.099".to_owned()],
|
values: vec!["176.099".to_owned()],
|
||||||
},
|
},
|
||||||
tree::Property {
|
parser::Property {
|
||||||
ident: "C".to_owned(),
|
ident: "C".to_owned(),
|
||||||
values: vec!["Comments in the game".to_owned()],
|
values: vec!["Comments in the game".to_owned()],
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
],
|
],
|
||||||
next: vec![],
|
next: vec![],
|
||||||
};
|
};
|
||||||
|
@ -472,26 +458,25 @@ mod move_node_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_can_parse_an_sgf_move_node() {
|
fn it_can_parse_an_sgf_move_node() {
|
||||||
let n = tree::Node {
|
let n = parser::Node {
|
||||||
properties: vec![
|
properties: vec![
|
||||||
tree::Property {
|
parser::Property::Move((Color::White, "dp".to_owned())),
|
||||||
ident: "W".to_owned(),
|
/*
|
||||||
values: vec!["dp".to_owned()],
|
parser::Property {
|
||||||
},
|
|
||||||
tree::Property {
|
|
||||||
ident: "WL".to_owned(),
|
ident: "WL".to_owned(),
|
||||||
values: vec!["176.099".to_owned()],
|
values: vec!["176.099".to_owned()],
|
||||||
},
|
},
|
||||||
tree::Property {
|
parser::Property {
|
||||||
ident: "C".to_owned(),
|
ident: "C".to_owned(),
|
||||||
values: vec!["Comments in the game".to_owned()],
|
values: vec!["Comments in the game".to_owned()],
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
],
|
],
|
||||||
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.position, Position{ row: 'd', column: 'p' });
|
assert_eq!(node.position, "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, vec!["Comments in the game".to_owned()]);
|
assert_eq!(node.comments, vec!["Comments in the game".to_owned()]);
|
||||||
|
@ -517,46 +502,16 @@ mod setup_node_tests {
|
||||||
fn it_rejects_conflicting_placement_properties() {
|
fn it_rejects_conflicting_placement_properties() {
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
SetupNode::new(vec![
|
SetupNode::new(vec![
|
||||||
(
|
(Some(Color::Black), "dd".to_owned(),),
|
||||||
Some(Color::Black),
|
(Some(Color::Black), "dd".to_owned(),),
|
||||||
Position {
|
|
||||||
row: 'd',
|
|
||||||
column: 'd',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Some(Color::Black),
|
|
||||||
Position {
|
|
||||||
row: 'd',
|
|
||||||
column: 'd',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
Err(SetupError::ConflictingPosition)
|
Err(SetupError::ConflictingPosition)
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
SetupNode::new(vec![
|
SetupNode::new(vec![
|
||||||
(
|
(Some(Color::Black), "dd".to_owned(),),
|
||||||
Some(Color::Black),
|
(Some(Color::Black), "ee".to_owned(),),
|
||||||
Position {
|
(Some(Color::White), "ee".to_owned(),),
|
||||||
row: 'd',
|
|
||||||
column: 'd',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Some(Color::Black),
|
|
||||||
Position {
|
|
||||||
row: 'e',
|
|
||||||
column: 'e',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Some(Color::White),
|
|
||||||
Position {
|
|
||||||
row: 'e',
|
|
||||||
column: 'e',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]),
|
]),
|
||||||
Err(SetupError::ConflictingPosition)
|
Err(SetupError::ConflictingPosition)
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,10 +3,10 @@ pub use date::Date;
|
||||||
|
|
||||||
// pub mod go;
|
// pub mod go;
|
||||||
|
|
||||||
mod tree;
|
mod game;
|
||||||
use tree::{parse_collection, Tree};
|
|
||||||
|
|
||||||
// mod game;
|
mod parser;
|
||||||
|
use parser::{parse_collection, Tree};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
|
@ -238,6 +238,18 @@ pub struct Node {
|
||||||
pub next: Vec<Node>,
|
pub next: Vec<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
pub fn move_(&self) -> Option<(Color, String)> {
|
||||||
|
self.properties
|
||||||
|
.iter()
|
||||||
|
.filter_map(|prop| match prop {
|
||||||
|
Property::Move(val) => Some(val.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToString for Node {
|
impl ToString for Node {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
let props = self
|
let props = self
|
||||||
|
@ -263,21 +275,6 @@ impl ToString for Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
impl Node {
|
|
||||||
pub fn find_prop(&self, ident: &str) -> Option<Property> {
|
|
||||||
self.properties
|
|
||||||
.iter()
|
|
||||||
.find(|prop| prop.ident == ident)
|
|
||||||
.cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next(&self) -> Option<&Node> {
|
|
||||||
self.next.get(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// KO
|
// KO
|
||||||
// MN
|
// MN
|
||||||
// N
|
// N
|
||||||
|
@ -437,14 +434,6 @@ pub struct UnknownProperty {
|
||||||
value: String,
|
value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct Property {
|
|
||||||
pub ident: String,
|
|
||||||
pub values: Vec<String>,
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
impl ToString for Property {
|
impl ToString for Property {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
@ -509,10 +498,6 @@ impl ToString for Property {
|
||||||
format!("{}[{}]", ident, value)
|
format!("{}[{}]", ident, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
let values = self
|
|
||||||
.collect::<String>();
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,12 +626,6 @@ fn parse_move<'a, E: nom::error::ParseError<&'a str>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_propvals<'a, E: nom::error::ParseError<&'a str>>(
|
|
||||||
parser: impl Parser<&'a str, Property, E>,
|
|
||||||
) -> impl FnMut(&'a str) -> IResult<&'a str, Vec<Property>, E> {
|
|
||||||
many1(parse_propval(parser))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_propval<'a, E: nom::error::ParseError<&'a str>>(
|
fn parse_propval<'a, E: nom::error::ParseError<&'a str>>(
|
||||||
mut parser: impl Parser<&'a str, Property, E>,
|
mut parser: impl Parser<&'a str, Property, E>,
|
||||||
) -> impl FnMut(&'a str) -> IResult<&'a str, Property, E> {
|
) -> impl FnMut(&'a str) -> IResult<&'a str, Property, E> {
|
||||||
|
@ -660,10 +639,6 @@ fn parse_propval<'a, E: nom::error::ParseError<&'a str>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discard_propvals<'a, E: nom::error::ParseError<&'a str>>() -> impl Parser<&'a str, (), E> {
|
|
||||||
many1(discard_propval()).map(|_| ())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn discard_propval<'a, E: nom::error::ParseError<&'a str>>() -> impl Parser<&'a str, (), E> {
|
fn discard_propval<'a, E: nom::error::ParseError<&'a str>>() -> impl Parser<&'a str, (), E> {
|
||||||
|input| {
|
|input| {
|
||||||
let (input, _) = multispace0(input)?;
|
let (input, _) = multispace0(input)?;
|
Loading…
Reference in New Issue