Start building the second layer of game validation

This commit is contained in:
Savanni D'Gerinel 2023-09-14 09:22:40 -04:00
parent 69e4605d71
commit 2bf0b3d782
3 changed files with 53 additions and 123 deletions

View File

@ -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)
); );

View File

@ -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;

View File

@ -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)?;