Thoroughly process move nodes, start processing setup nodes
This commit is contained in:
parent
349f71fc81
commit
7fc1530245
285
sgf/src/game.rs
285
sgf/src/game.rs
|
@ -1,175 +1,34 @@
|
||||||
use crate::{parser, Color};
|
use crate::{
|
||||||
|
parser::{self, Annotation, Evaluation, UnknownProperty},
|
||||||
|
Color,
|
||||||
|
};
|
||||||
use std::{collections::HashSet, time::Duration};
|
use std::{collections::HashSet, time::Duration};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
/*
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum PropertyError {
|
pub enum PropertyError {
|
||||||
ConflictingProperty,
|
ConflictingProperty,
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum SetupError {
|
pub enum MoveNodeError {
|
||||||
|
IncompatibleProperty,
|
||||||
|
ConflictingProperty,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum SetupNodeError {
|
||||||
|
IncompatibleProperty,
|
||||||
ConflictingPosition,
|
ConflictingPosition,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum ConversionError {
|
pub enum GameNodeError {
|
||||||
IncompatibleNodeType,
|
UnsupportedGameNode,
|
||||||
InvalidPositionSyntax,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Evaluation {
|
|
||||||
Even,
|
|
||||||
GoodForBlack,
|
|
||||||
GoodForWhite,
|
|
||||||
Unclear,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum Annotation {
|
|
||||||
BadMove,
|
|
||||||
DoubtfulMove,
|
|
||||||
InterestingMove,
|
|
||||||
Tesuji,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum NodeProperty {
|
|
||||||
Comment(String),
|
|
||||||
EvenResult(Double),
|
|
||||||
GoodForBlack(Double),
|
|
||||||
GoodForWhite(Double),
|
|
||||||
Hotspot(Double),
|
|
||||||
NodeName(String),
|
|
||||||
Unclear(Double),
|
|
||||||
Value(f64),
|
|
||||||
Unknown((String, String)),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct NodeProperties(Vec<NodeProperty>);
|
|
||||||
|
|
||||||
impl NodeProperties {
|
|
||||||
fn property_conflicts(&self, prop: &NodeProperty) -> bool {
|
|
||||||
match prop {
|
|
||||||
NodeProperty::EvenResult(_)
|
|
||||||
| NodeProperty::GoodForBlack(_)
|
|
||||||
| NodeProperty::GoodForWhite(_)
|
|
||||||
| NodeProperty::Unclear(_) => self.contains_exclusive(),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contains_exclusive(&self) -> bool {
|
|
||||||
self.0.iter().any(|prop| match prop {
|
|
||||||
NodeProperty::EvenResult(_)
|
|
||||||
| NodeProperty::GoodForBlack(_)
|
|
||||||
| NodeProperty::GoodForWhite(_)
|
|
||||||
| NodeProperty::Unclear(_) => true,
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&MoveProperties> for NodeProperties {
|
|
||||||
fn from(props: &MoveProperties) -> NodeProperties {
|
|
||||||
let props: Vec<NodeProperty> = props
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.filter_map(|prop| {
|
|
||||||
if let MoveProperty::NodeProperty(p) = prop {
|
|
||||||
Some(p.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
NodeProperties(props)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 len(&self) -> usize {
|
|
||||||
self.0.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_property(&mut self, prop: MoveProperty) -> Result<(), PropertyError> {
|
|
||||||
match prop {
|
|
||||||
MoveProperty::NodeProperty(node_prop) => {
|
|
||||||
if NodeProperties::from(self).property_conflicts(&node_prop) {
|
|
||||||
self.0.push(prop);
|
|
||||||
} else {
|
|
||||||
return Err(PropertyError::ConflictingProperty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MoveProperty::TimingProperty(_) => {}
|
|
||||||
MoveProperty::MoveAnnotationProperty(_) => {
|
|
||||||
if self
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.filter(|prop| match prop {
|
|
||||||
MoveProperty::MoveAnnotationProperty(_) => true,
|
|
||||||
_ => false,
|
|
||||||
})
|
|
||||||
.count()
|
|
||||||
> 0
|
|
||||||
{
|
|
||||||
return Err(PropertyError::ConflictingProperty);
|
|
||||||
}
|
|
||||||
self.0.push(prop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum SetupProperty {
|
|
||||||
NodeProperty(NodeProperty),
|
|
||||||
PlayerTurn(Color),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Default)]
|
|
||||||
pub struct SetupProperties(Vec<SetupProperty>);
|
|
||||||
|
|
||||||
impl SetupProperties {
|
|
||||||
pub fn add_property(&mut self, prop: SetupProperty) {
|
|
||||||
match prop {
|
|
||||||
SetupProperty::NodeProperty(_) => {}
|
|
||||||
SetupProperty::PlayerTurn(_) => self.0.push(prop),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum GameNode {
|
pub enum GameNode {
|
||||||
MoveNode(MoveNode),
|
MoveNode(MoveNode),
|
||||||
|
@ -228,9 +87,12 @@ impl Node for GameNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&parser::Node> for GameNode {
|
impl TryFrom<&parser::Node> for GameNode {
|
||||||
type Error = ConversionError;
|
type Error = GameNodeError;
|
||||||
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
||||||
unimplemented!()
|
MoveNode::try_from(n)
|
||||||
|
.map(GameNode::MoveNode)
|
||||||
|
.or_else(|_| SetupNode::try_from(n).map(GameNode::SetupNode))
|
||||||
|
.map_err(|_| Self::Error::UnsupportedGameNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,19 +167,57 @@ impl Node for MoveNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&parser::Node> for MoveNode {
|
impl TryFrom<&parser::Node> for MoveNode {
|
||||||
type Error = ConversionError;
|
type Error = MoveNodeError;
|
||||||
|
|
||||||
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
||||||
match n.move_() {
|
match n.mv() {
|
||||||
Some((color, position)) => {
|
Some((color, position)) => {
|
||||||
let mut s = Self::new(color, position);
|
let mut s = Self::new(color, position);
|
||||||
|
|
||||||
s.time_left = n.time_left();
|
for prop in n.properties.iter() {
|
||||||
s.comments = n.comments();
|
match prop {
|
||||||
|
parser::Property::Move((color, position)) => {
|
||||||
|
if s.color != *color || s.position != *position {
|
||||||
|
return Err(Self::Error::ConflictingProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parser::Property::TimeLeft((color, duration)) => {
|
||||||
|
if s.color != *color {
|
||||||
|
return Err(Self::Error::ConflictingProperty);
|
||||||
|
}
|
||||||
|
if s.time_left.is_some() {
|
||||||
|
return Err(Self::Error::ConflictingProperty);
|
||||||
|
}
|
||||||
|
s.time_left = Some(duration.clone());
|
||||||
|
}
|
||||||
|
parser::Property::Comment(cmt) => {
|
||||||
|
if s.comments.is_some() {
|
||||||
|
return Err(Self::Error::ConflictingProperty);
|
||||||
|
}
|
||||||
|
s.comments = Some(cmt.clone());
|
||||||
|
}
|
||||||
|
parser::Property::Evaluation(evaluation) => {
|
||||||
|
if s.evaluation.is_some() {
|
||||||
|
return Err(Self::Error::ConflictingProperty);
|
||||||
|
}
|
||||||
|
s.evaluation = Some(*evaluation)
|
||||||
|
}
|
||||||
|
parser::Property::Annotation(annotation) => {
|
||||||
|
if s.annotation.is_some() {
|
||||||
|
return Err(Self::Error::ConflictingProperty);
|
||||||
|
}
|
||||||
|
s.annotation = Some(*annotation)
|
||||||
|
}
|
||||||
|
parser::Property::Unknown(UnknownProperty { ident, value }) => {
|
||||||
|
s.unknown_props.push((ident.clone(), value.clone()));
|
||||||
|
}
|
||||||
|
_ => return Err(Self::Error::IncompatibleProperty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(s)
|
Ok(s)
|
||||||
}
|
}
|
||||||
None => Err(ConversionError::IncompatibleNodeType),
|
None => Err(MoveNodeError::IncompatibleProperty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -339,20 +239,12 @@ fn parse_position(s: &str) -> Result<Position, ConversionError> {
|
||||||
pub struct SetupNode {
|
pub struct SetupNode {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
|
|
||||||
positions: Vec<(Option<Color>, String)>,
|
positions: Vec<parser::SetupInstr>,
|
||||||
children: Vec<GameNode>,
|
children: Vec<GameNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SetupNode {
|
impl SetupNode {
|
||||||
pub fn new(positions: Vec<(Option<Color>, String)>) -> Result<Self, SetupError> {
|
pub fn new(positions: Vec<parser::SetupInstr>) -> Result<Self, SetupNodeError> {
|
||||||
let mut coords: HashSet<String> = 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 {
|
Ok(Self {
|
||||||
id: Uuid::new_v4(),
|
id: Uuid::new_v4(),
|
||||||
positions,
|
positions,
|
||||||
|
@ -371,6 +263,17 @@ impl Node for SetupNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&parser::Node> for SetupNode {
|
||||||
|
type Error = SetupNodeError;
|
||||||
|
|
||||||
|
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
||||||
|
match n.setup() {
|
||||||
|
Some(elements) => Self::new(elements),
|
||||||
|
None => Err(Self::Error::IncompatibleProperty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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> {
|
||||||
if node.id() == id {
|
if node.id() == id {
|
||||||
return vec![node];
|
return vec![node];
|
||||||
|
@ -452,6 +355,8 @@ mod root_node_tests {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod move_node_tests {
|
mod move_node_tests {
|
||||||
|
use crate::parser::PositionList;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use cool_asserts::assert_matches;
|
use cool_asserts::assert_matches;
|
||||||
|
|
||||||
|
@ -476,12 +381,28 @@ mod move_node_tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_rejects_an_sgf_setup_node() {
|
fn it_rejects_an_sgf_setup_node() {
|
||||||
unimplemented!()
|
let n = parser::Node {
|
||||||
|
properties: vec![
|
||||||
|
parser::Property::Move((Color::White, "dp".to_owned())),
|
||||||
|
parser::Property::TimeLeft((Color::White, Duration::from_secs(176))),
|
||||||
|
parser::Property::SetupBlackStones(PositionList(vec![
|
||||||
|
"dd".to_owned(),
|
||||||
|
"de".to_owned(),
|
||||||
|
])),
|
||||||
|
],
|
||||||
|
next: vec![],
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
MoveNode::try_from(&n),
|
||||||
|
Err(MoveNodeError::IncompatibleProperty)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod setup_node_tests {
|
mod setup_node_tests {
|
||||||
|
use crate::parser::SetupInstr;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use cool_asserts::assert_matches;
|
use cool_asserts::assert_matches;
|
||||||
|
|
||||||
|
@ -493,18 +414,18 @@ 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(),),
|
SetupInstr::Piece((Color::Black, "dd".to_owned())),
|
||||||
(Some(Color::Black), "dd".to_owned(),),
|
SetupInstr::Piece((Color::Black, "dd".to_owned())),
|
||||||
]),
|
]),
|
||||||
Err(SetupError::ConflictingPosition)
|
Err(SetupNodeError::ConflictingPosition)
|
||||||
);
|
);
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
SetupNode::new(vec![
|
SetupNode::new(vec![
|
||||||
(Some(Color::Black), "dd".to_owned(),),
|
SetupInstr::Piece((Color::Black, "dd".to_owned())),
|
||||||
(Some(Color::Black), "ee".to_owned(),),
|
SetupInstr::Piece((Color::Black, "ee".to_owned())),
|
||||||
(Some(Color::White), "ee".to_owned(),),
|
SetupInstr::Piece((Color::White, "ee".to_owned())),
|
||||||
]),
|
]),
|
||||||
Err(SetupError::ConflictingPosition)
|
Err(SetupNodeError::ConflictingPosition)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,28 @@ impl From<ParseIntError> for ParseSizeError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Double {
|
pub enum Double {
|
||||||
Normal,
|
Normal,
|
||||||
Emphasized,
|
Emphasized,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum Annotation {
|
||||||
|
BadMove,
|
||||||
|
DoubtfulMove,
|
||||||
|
InterestingMove,
|
||||||
|
Tesuji,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum Evaluation {
|
||||||
|
Even,
|
||||||
|
GoodForBlack,
|
||||||
|
GoodForWhite,
|
||||||
|
Unclear,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum GameType {
|
pub enum GameType {
|
||||||
Go,
|
Go,
|
||||||
|
@ -209,13 +225,13 @@ impl TryFrom<&str> for Size {
|
||||||
pub struct Position(String);
|
pub struct Position(String);
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct PositionList(Vec<Position>);
|
pub struct PositionList(pub Vec<String>);
|
||||||
|
|
||||||
impl PositionList {
|
impl PositionList {
|
||||||
pub fn compressed_list(&self) -> String {
|
pub fn compressed_list(&self) -> String {
|
||||||
self.0
|
self.0
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| v.0.clone())
|
.map(|v| v.clone())
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(":")
|
.join(":")
|
||||||
}
|
}
|
||||||
|
@ -238,26 +254,59 @@ pub struct Node {
|
||||||
pub next: Vec<Node>,
|
pub next: Vec<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum SetupInstr {
|
||||||
|
Piece((Color, String)),
|
||||||
|
Clear(String),
|
||||||
|
}
|
||||||
|
|
||||||
impl Node {
|
impl Node {
|
||||||
pub fn move_(&self) -> Option<(Color, String)> {
|
pub fn mv(&self) -> Option<(Color, String)> {
|
||||||
self.find_by(|prop| match prop {
|
self.find_by(|prop| match prop {
|
||||||
Property::Move(val) => Some(val.clone()),
|
Property::Move(val) => Some(val.clone()),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn time_left(&self) -> Option<Duration> {
|
pub fn setup(&self) -> Option<Vec<SetupInstr>> {
|
||||||
self.find_by(|prop| match prop {
|
let mut setup = Vec::new();
|
||||||
Property::TimeLeft((_, duration)) => Some(duration.clone()),
|
for prop in self.properties.iter() {
|
||||||
_ => None,
|
match prop {
|
||||||
})
|
Property::SetupBlackStones(positions) => {
|
||||||
}
|
setup.append(
|
||||||
|
&mut positions
|
||||||
pub fn comments(&self) -> Option<String> {
|
.0
|
||||||
self.find_by(|prop| match prop {
|
.iter()
|
||||||
Property::Comment(c) => Some(c.clone()),
|
.map(|pos| SetupInstr::Piece((Color::Black, pos.clone())))
|
||||||
_ => None,
|
.collect::<Vec<SetupInstr>>(),
|
||||||
})
|
);
|
||||||
|
}
|
||||||
|
Property::SetupWhiteStones(positions) => {
|
||||||
|
setup.append(
|
||||||
|
&mut positions
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|pos| SetupInstr::Piece((Color::White, pos.clone())))
|
||||||
|
.collect::<Vec<SetupInstr>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Property::ClearStones(positions) => {
|
||||||
|
setup.append(
|
||||||
|
&mut positions
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.map(|pos| SetupInstr::Clear(pos.clone()))
|
||||||
|
.collect::<Vec<SetupInstr>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if setup.len() > 0 {
|
||||||
|
Some(setup)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_by<F, R>(&self, f: F) -> Option<R>
|
fn find_by<F, R>(&self, f: F) -> Option<R>
|
||||||
|
@ -318,17 +367,8 @@ pub enum Property {
|
||||||
// C
|
// C
|
||||||
Comment(String),
|
Comment(String),
|
||||||
|
|
||||||
// BM
|
// BM, DO, IT, TE
|
||||||
BadMove,
|
Annotation(Annotation),
|
||||||
|
|
||||||
// DO
|
|
||||||
DoubtfulMove,
|
|
||||||
|
|
||||||
// IT
|
|
||||||
InterestingMove,
|
|
||||||
|
|
||||||
// TE
|
|
||||||
Tesuji,
|
|
||||||
|
|
||||||
// AP
|
// AP
|
||||||
Application(String),
|
Application(String),
|
||||||
|
@ -360,17 +400,8 @@ pub enum Property {
|
||||||
// PL
|
// PL
|
||||||
NextPlayer(Color),
|
NextPlayer(Color),
|
||||||
|
|
||||||
// DM
|
// DM, GB, GW, UC
|
||||||
EvenResult,
|
Evaluation(Evaluation),
|
||||||
|
|
||||||
// GB
|
|
||||||
GoodForBlack,
|
|
||||||
|
|
||||||
// GW
|
|
||||||
GoodForWhite,
|
|
||||||
|
|
||||||
// UC
|
|
||||||
UnclearResult,
|
|
||||||
|
|
||||||
// HO
|
// HO
|
||||||
Hotspot,
|
Hotspot,
|
||||||
|
@ -449,8 +480,8 @@ pub enum Property {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct UnknownProperty {
|
pub struct UnknownProperty {
|
||||||
ident: String,
|
pub ident: String,
|
||||||
value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Property {
|
impl ToString for Property {
|
||||||
|
@ -463,10 +494,10 @@ impl ToString for Property {
|
||||||
format!("{}[{}]", color.abbreviation(), time.as_secs())
|
format!("{}[{}]", color.abbreviation(), time.as_secs())
|
||||||
}
|
}
|
||||||
Property::Comment(value) => format!("C[{}]", value),
|
Property::Comment(value) => format!("C[{}]", value),
|
||||||
Property::BadMove => "BM[]".to_owned(),
|
Property::Annotation(Annotation::BadMove) => "BM[]".to_owned(),
|
||||||
Property::DoubtfulMove => "DO[]".to_owned(),
|
Property::Annotation(Annotation::DoubtfulMove) => "DO[]".to_owned(),
|
||||||
Property::InterestingMove => "IT[]".to_owned(),
|
Property::Annotation(Annotation::InterestingMove) => "IT[]".to_owned(),
|
||||||
Property::Tesuji => "TE[]".to_owned(),
|
Property::Annotation(Annotation::Tesuji) => "TE[]".to_owned(),
|
||||||
Property::Application(app) => format!("AP[{}]", app),
|
Property::Application(app) => format!("AP[{}]", app),
|
||||||
Property::Charset(set) => format!("CA[{}]", set),
|
Property::Charset(set) => format!("CA[{}]", set),
|
||||||
Property::FileFormat(ff) => format!("FF[{}]", ff),
|
Property::FileFormat(ff) => format!("FF[{}]", ff),
|
||||||
|
@ -489,10 +520,10 @@ impl ToString for Property {
|
||||||
format!("AW[{}]", positions.compressed_list(),)
|
format!("AW[{}]", positions.compressed_list(),)
|
||||||
}
|
}
|
||||||
Property::NextPlayer(color) => format!("PL[{}]", color.abbreviation()),
|
Property::NextPlayer(color) => format!("PL[{}]", color.abbreviation()),
|
||||||
Property::EvenResult => "DM[]".to_owned(),
|
Property::Evaluation(Evaluation::Even) => "DM[]".to_owned(),
|
||||||
Property::GoodForBlack => "GB[]".to_owned(),
|
Property::Evaluation(Evaluation::GoodForBlack) => "GB[]".to_owned(),
|
||||||
Property::GoodForWhite => "GW[]".to_owned(),
|
Property::Evaluation(Evaluation::GoodForWhite) => "GW[]".to_owned(),
|
||||||
Property::UnclearResult => "UC[]".to_owned(),
|
Property::Evaluation(Evaluation::Unclear) => "UC[]".to_owned(),
|
||||||
Property::Hotspot => "HO[]".to_owned(),
|
Property::Hotspot => "HO[]".to_owned(),
|
||||||
Property::Value(value) => format!("V[{}]", value),
|
Property::Value(value) => format!("V[{}]", value),
|
||||||
Property::Annotator(value) => format!("AN[{}]", value),
|
Property::Annotator(value) => format!("AN[{}]", value),
|
||||||
|
@ -574,14 +605,18 @@ fn parse_property<'a, E: nom::error::ParseError<&'a str>>(
|
||||||
"C" => parse_propval(parse_comment())(input)?,
|
"C" => parse_propval(parse_comment())(input)?,
|
||||||
"WL" => parse_propval(parse_time_left(Color::White))(input)?,
|
"WL" => parse_propval(parse_time_left(Color::White))(input)?,
|
||||||
"BL" => parse_propval(parse_time_left(Color::Black))(input)?,
|
"BL" => parse_propval(parse_time_left(Color::Black))(input)?,
|
||||||
"BM" => discard_propval().map(|_| Property::BadMove).parse(input)?,
|
"BM" => discard_propval()
|
||||||
|
.map(|_| Property::Annotation(Annotation::BadMove))
|
||||||
|
.parse(input)?,
|
||||||
"DO" => discard_propval()
|
"DO" => discard_propval()
|
||||||
.map(|_| Property::DoubtfulMove)
|
.map(|_| Property::Annotation(Annotation::DoubtfulMove))
|
||||||
.parse(input)?,
|
.parse(input)?,
|
||||||
"IT" => discard_propval()
|
"IT" => discard_propval()
|
||||||
.map(|_| Property::InterestingMove)
|
.map(|_| Property::Annotation(Annotation::InterestingMove))
|
||||||
|
.parse(input)?,
|
||||||
|
"TE" => discard_propval()
|
||||||
|
.map(|_| Property::Annotation(Annotation::Tesuji))
|
||||||
.parse(input)?,
|
.parse(input)?,
|
||||||
"TE" => discard_propval().map(|_| Property::Tesuji).parse(input)?,
|
|
||||||
"AP" => parse_propval(parse_simple_text().map(Property::Application))(input)?,
|
"AP" => parse_propval(parse_simple_text().map(Property::Application))(input)?,
|
||||||
"CA" => parse_propval(parse_simple_text().map(Property::Charset))(input)?,
|
"CA" => parse_propval(parse_simple_text().map(Property::Charset))(input)?,
|
||||||
"FF" => parse_propval(parse_number().map(Property::FileFormat))(input)?,
|
"FF" => parse_propval(parse_number().map(Property::FileFormat))(input)?,
|
||||||
|
@ -589,16 +624,16 @@ fn parse_property<'a, E: nom::error::ParseError<&'a str>>(
|
||||||
"ST" => unimplemented!(),
|
"ST" => unimplemented!(),
|
||||||
"SZ" => unimplemented!(),
|
"SZ" => unimplemented!(),
|
||||||
"DM" => discard_propval()
|
"DM" => discard_propval()
|
||||||
.map(|_| Property::EvenResult)
|
.map(|_| Property::Evaluation(Evaluation::Even))
|
||||||
.parse(input)?,
|
.parse(input)?,
|
||||||
"GB" => discard_propval()
|
"GB" => discard_propval()
|
||||||
.map(|_| Property::GoodForBlack)
|
.map(|_| Property::Evaluation(Evaluation::GoodForBlack))
|
||||||
.parse(input)?,
|
.parse(input)?,
|
||||||
"GW" => discard_propval()
|
"GW" => discard_propval()
|
||||||
.map(|_| Property::GoodForWhite)
|
.map(|_| Property::Evaluation(Evaluation::GoodForWhite))
|
||||||
.parse(input)?,
|
.parse(input)?,
|
||||||
"UC" => discard_propval()
|
"UC" => discard_propval()
|
||||||
.map(|_| Property::UnclearResult)
|
.map(|_| Property::Evaluation(Evaluation::Unclear))
|
||||||
.parse(input)?,
|
.parse(input)?,
|
||||||
"V" => unimplemented!(),
|
"V" => unimplemented!(),
|
||||||
"AN" => parse_propval(parse_simple_text().map(Property::Annotator))(input)?,
|
"AN" => parse_propval(parse_simple_text().map(Property::Annotator))(input)?,
|
||||||
|
|
Loading…
Reference in New Issue