|
|
|
@ -3,7 +3,11 @@ use crate::{
|
|
|
|
|
Color, Date, GameResult, GameType,
|
|
|
|
|
};
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use std::{collections::HashSet, time::Duration};
|
|
|
|
|
use slab_tree::{NodeId, NodeMut, NodeRef, Tree};
|
|
|
|
|
use std::{
|
|
|
|
|
collections::{HashSet, VecDeque},
|
|
|
|
|
time::Duration,
|
|
|
|
|
};
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
@ -32,7 +36,7 @@ pub enum SetupNodeError {
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
|
pub enum GameNodeError {
|
|
|
|
|
UnsupportedGameNode(MoveNodeError, SetupNodeError),
|
|
|
|
|
UnsupportedGameNode(MoveNodeError, SetupNodeError, parser::Node),
|
|
|
|
|
ConflictingProperty,
|
|
|
|
|
ConflictingPosition,
|
|
|
|
|
}
|
|
|
|
@ -52,7 +56,6 @@ pub struct Player {
|
|
|
|
|
/// syntax issues, the result of the GameRecord is to have a fully-understood game. However, this
|
|
|
|
|
/// doesn't (yet?) go quite to the level of apply the game type (i.e., this is Go, Chess, Yinsh, or
|
|
|
|
|
/// whatever).
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
|
pub struct GameRecord {
|
|
|
|
|
pub game_type: GameType,
|
|
|
|
|
|
|
|
|
@ -78,7 +81,7 @@ pub struct GameRecord {
|
|
|
|
|
pub overtime: Option<String>,
|
|
|
|
|
pub transcriber: Option<String>,
|
|
|
|
|
|
|
|
|
|
pub children: Vec<GameNode>,
|
|
|
|
|
pub trees: Vec<Tree<GameNode>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl GameRecord {
|
|
|
|
@ -111,55 +114,41 @@ impl GameRecord {
|
|
|
|
|
overtime: None,
|
|
|
|
|
transcriber: None,
|
|
|
|
|
|
|
|
|
|
children: vec![],
|
|
|
|
|
trees: vec![],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn nodes(&self) -> Vec<&GameNode> {
|
|
|
|
|
self.iter().collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = &'_ GameNode> {
|
|
|
|
|
self.trees
|
|
|
|
|
.iter()
|
|
|
|
|
.flat_map(|tree| tree.root().unwrap().traverse_pre_order())
|
|
|
|
|
.map(|nr| nr.data())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Generate a list of moves which constitute the main line of the game. This is the game as it
|
|
|
|
|
/// was actually played out, and by convention consists of the first node in each list of
|
|
|
|
|
/// children.
|
|
|
|
|
pub fn mainline(&self) -> Vec<&GameNode> {
|
|
|
|
|
let mut moves: Vec<&GameNode> = vec![];
|
|
|
|
|
|
|
|
|
|
let mut next = self.children.first();
|
|
|
|
|
while let Some(node) = next {
|
|
|
|
|
// Given that I know that I have a node, and I know that I'm going to push a reference
|
|
|
|
|
// to it onto my final list, I want to get the first of its children. And I want to
|
|
|
|
|
// keep doing that until there are no more first children.
|
|
|
|
|
//
|
|
|
|
|
// Just going to push references onto the list. No need to copy the nodes for this.
|
|
|
|
|
//
|
|
|
|
|
// Pushing a reference onto the list implicitely clones the reference, but not the data
|
|
|
|
|
// it is pointing to. This means that each time through the loop, `next` points to
|
|
|
|
|
// something else. This isn't being described very well, though, so it's worth
|
|
|
|
|
// reviewing in the future.
|
|
|
|
|
moves.push(node);
|
|
|
|
|
|
|
|
|
|
next = match node {
|
|
|
|
|
GameNode::MoveNode(node) => node.children.first(),
|
|
|
|
|
GameNode::SetupNode(node) => node.children.first(),
|
|
|
|
|
};
|
|
|
|
|
pub fn mainline(&self) -> Option<impl Iterator<Item = &'_ GameNode>> {
|
|
|
|
|
println!("number of trees: {}", self.trees.len());
|
|
|
|
|
if self.trees.len() > 0 {
|
|
|
|
|
Some(MainlineIter {
|
|
|
|
|
next: self.trees[0].root(),
|
|
|
|
|
tree: &self.trees[0],
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
moves
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Node for GameRecord {
|
|
|
|
|
fn children<'a>(&'a self) -> Vec<&'a GameNode> {
|
|
|
|
|
self.children.iter().collect::<Vec<&'a GameNode>>()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_child(&mut self, node: GameNode) -> &mut GameNode {
|
|
|
|
|
self.children.push(node);
|
|
|
|
|
self.children.last_mut().unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<&parser::Tree> for GameRecord {
|
|
|
|
|
impl TryFrom<parser::Tree> for GameRecord {
|
|
|
|
|
type Error = GameError;
|
|
|
|
|
|
|
|
|
|
fn try_from(tree: &parser::Tree) -> Result<Self, Self::Error> {
|
|
|
|
|
fn try_from(tree: parser::Tree) -> Result<Self, Self::Error> {
|
|
|
|
|
let mut ty = None;
|
|
|
|
|
let mut size = None;
|
|
|
|
|
let mut black_player = Player {
|
|
|
|
@ -234,6 +223,7 @@ impl TryFrom<&parser::Tree> for GameRecord {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
s.children = tree
|
|
|
|
|
.root
|
|
|
|
|
.next
|
|
|
|
@ -241,35 +231,100 @@ impl TryFrom<&parser::Tree> for GameRecord {
|
|
|
|
|
.map(GameNode::try_from)
|
|
|
|
|
.collect::<Result<Vec<GameNode>, GameNodeError>>()
|
|
|
|
|
.map_err(GameError::InvalidGameNode)?;
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
s.trees = tree.root.next.into_iter()
|
|
|
|
|
.map(recursive_tree_to_slab_tree)
|
|
|
|
|
.collect::<Result<Vec<Tree<GameNode>>, GameError>>()?;
|
|
|
|
|
|
|
|
|
|
Ok(s)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn recursive_tree_to_slab_tree(node: parser::Node) -> Result<Tree<GameNode>, GameError> {
|
|
|
|
|
let mut slab = Tree::new();
|
|
|
|
|
let mut nodes: VecDeque<(NodeId, parser::Node)> = VecDeque::new();
|
|
|
|
|
|
|
|
|
|
let root_id =
|
|
|
|
|
slab.set_root(GameNode::try_from(node.clone()).map_err(GameError::InvalidGameNode)?);
|
|
|
|
|
nodes.push_back((root_id, node));
|
|
|
|
|
|
|
|
|
|
// I need to keep track of the current parent, and I need to keep on digging deeper into the
|
|
|
|
|
// tree. Given that I have the root, I can then easily find out all of the children.
|
|
|
|
|
//
|
|
|
|
|
// So, maybe I take the list of children. Assign each one of them to a place in the slab tree.
|
|
|
|
|
// Then push the child *and* its ID into a dequeue. So long as the dequeue is not empty, I want
|
|
|
|
|
// to pop a node and its ID from the dequeue. The retrieve the NodeMut for it and work on the
|
|
|
|
|
// node's children.
|
|
|
|
|
while let Some((node_id, node)) = nodes.pop_front() {
|
|
|
|
|
let mut game_node: NodeMut<GameNode> = slab
|
|
|
|
|
.get_mut(node_id)
|
|
|
|
|
.expect("invalid node_id when retrieving nodes from the game");
|
|
|
|
|
// I have a node that is in the tree. Now run across all of its children, adding each one
|
|
|
|
|
// to the tree and pushing them into the deque along with their IDs.
|
|
|
|
|
for child in node.next {
|
|
|
|
|
let slab_child = game_node
|
|
|
|
|
.append(GameNode::try_from(child.clone()).map_err(GameError::InvalidGameNode)?);
|
|
|
|
|
nodes.push_back((slab_child.node_id(), child));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(slab)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct TreeIter<'a> {
|
|
|
|
|
queue: VecDeque<NodeRef<'a, &'a GameNode>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Default for TreeIter<'a> {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
TreeIter {
|
|
|
|
|
queue: VecDeque::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Iterator for TreeIter<'a> {
|
|
|
|
|
type Item = &'a GameNode;
|
|
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
let retval = self.queue.pop_front();
|
|
|
|
|
if let Some(ref retval) = retval {
|
|
|
|
|
retval
|
|
|
|
|
.children()
|
|
|
|
|
.for_each(|node| self.queue.push_back(node));
|
|
|
|
|
}
|
|
|
|
|
retval.map(|rv| *rv.data())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct MainlineIter<'a> {
|
|
|
|
|
next: Option<NodeRef<'a, GameNode>>,
|
|
|
|
|
tree: &'a Tree<GameNode>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Iterator for MainlineIter<'a> {
|
|
|
|
|
type Item = &'a GameNode;
|
|
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
if let Some(next) = self.next.take() {
|
|
|
|
|
let ret = self.tree.get(next.node_id())?;
|
|
|
|
|
self.next = next
|
|
|
|
|
.first_child()
|
|
|
|
|
.and_then(|child| self.tree.get(child.node_id()));
|
|
|
|
|
Some(ret.data())
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
|
|
|
|
pub enum GameNode {
|
|
|
|
|
MoveNode(MoveNode),
|
|
|
|
|
SetupNode(SetupNode),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait Node {
|
|
|
|
|
/// Provide a pre-order traversal of all of the nodes in the game tree.
|
|
|
|
|
fn nodes<'a>(&'a self) -> Vec<&'a GameNode> {
|
|
|
|
|
self.children()
|
|
|
|
|
.iter()
|
|
|
|
|
.flat_map(|node| {
|
|
|
|
|
let mut children = node.nodes();
|
|
|
|
|
let mut v = vec![*node];
|
|
|
|
|
v.append(&mut children);
|
|
|
|
|
v
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<&'a GameNode>>()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn children(&self) -> Vec<&GameNode>;
|
|
|
|
|
fn add_child(&mut self, node: GameNode) -> &mut GameNode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl GameNode {
|
|
|
|
|
pub fn id(&self) -> Uuid {
|
|
|
|
|
match self {
|
|
|
|
@ -279,33 +334,10 @@ impl GameNode {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Node for GameNode {
|
|
|
|
|
fn children(&self) -> Vec<&GameNode> {
|
|
|
|
|
match self {
|
|
|
|
|
GameNode::MoveNode(node) => node.children(),
|
|
|
|
|
GameNode::SetupNode(node) => node.children(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn nodes(&self) -> Vec<&GameNode> {
|
|
|
|
|
match self {
|
|
|
|
|
GameNode::MoveNode(node) => node.nodes(),
|
|
|
|
|
GameNode::SetupNode(node) => node.nodes(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_child(&mut self, new_node: GameNode) -> &mut GameNode {
|
|
|
|
|
match self {
|
|
|
|
|
GameNode::MoveNode(node) => node.add_child(new_node),
|
|
|
|
|
GameNode::SetupNode(node) => node.add_child(new_node),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<&parser::Node> for GameNode {
|
|
|
|
|
impl TryFrom<parser::Node> for GameNode {
|
|
|
|
|
type Error = GameNodeError;
|
|
|
|
|
|
|
|
|
|
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
|
|
|
|
fn try_from(n: parser::Node) -> Result<Self, Self::Error> {
|
|
|
|
|
// I originally wrote this recursively. However, on an ordinary game of a couple hundred
|
|
|
|
|
// moves, that meant that I was recursing 500 functions, and that exceeded the stack limit.
|
|
|
|
|
// So, instead, I need to unroll everything to non-recursive form.
|
|
|
|
@ -314,35 +346,29 @@ impl TryFrom<&parser::Node> for GameNode {
|
|
|
|
|
// only use the MoveNode::try_from and SetupNode::try_from if those functions don't
|
|
|
|
|
// recurse. Instead, I'm going to process just that node, then return to here and process
|
|
|
|
|
// the children.
|
|
|
|
|
let move_node = MoveNode::try_from(n);
|
|
|
|
|
let setup_node = SetupNode::try_from(n);
|
|
|
|
|
let move_node = MoveNode::try_from(n.clone());
|
|
|
|
|
let setup_node = SetupNode::try_from(n.clone());
|
|
|
|
|
|
|
|
|
|
// I'm much too tired when writing this. I'm still recursing, but I did cut the number of
|
|
|
|
|
// recursions in half. This helps, but it still doesn't guarantee that I'm going to be able
|
|
|
|
|
// to parse all possible games. So, still, treat each branch of the game as a single line.
|
|
|
|
|
// Iterate over that line, don't recurse. Create bookmarks at each branch point, and then
|
|
|
|
|
// come back to each one.
|
|
|
|
|
/*
|
|
|
|
|
let children = n
|
|
|
|
|
.next
|
|
|
|
|
.iter()
|
|
|
|
|
.map(GameNode::try_from)
|
|
|
|
|
.collect::<Result<Vec<Self>, Self::Error>>()?;
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
let node = match (move_node, setup_node) {
|
|
|
|
|
(Ok(mut node), _) => {
|
|
|
|
|
node.children = children;
|
|
|
|
|
Ok(Self::MoveNode(node))
|
|
|
|
|
}
|
|
|
|
|
(Err(_), Ok(mut node)) => {
|
|
|
|
|
node.children = children;
|
|
|
|
|
Ok(Self::SetupNode(node))
|
|
|
|
|
}
|
|
|
|
|
match (move_node, setup_node) {
|
|
|
|
|
(Ok(mut node), _) => Ok(Self::MoveNode(node)),
|
|
|
|
|
(Err(_), Ok(mut node)) => Ok(Self::SetupNode(node)),
|
|
|
|
|
(Err(move_err), Err(setup_err)) => {
|
|
|
|
|
Err(Self::Error::UnsupportedGameNode(move_err, setup_err))
|
|
|
|
|
Err(Self::Error::UnsupportedGameNode(move_err, setup_err, n))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}?;
|
|
|
|
|
|
|
|
|
|
Ok(node)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -351,7 +377,6 @@ pub struct MoveNode {
|
|
|
|
|
pub id: Uuid,
|
|
|
|
|
pub color: Color,
|
|
|
|
|
pub mv: Move,
|
|
|
|
|
pub children: Vec<GameNode>,
|
|
|
|
|
|
|
|
|
|
pub time_left: Option<Duration>,
|
|
|
|
|
pub moves_left: Option<usize>,
|
|
|
|
@ -369,7 +394,6 @@ impl MoveNode {
|
|
|
|
|
id: Uuid::new_v4(),
|
|
|
|
|
color,
|
|
|
|
|
mv,
|
|
|
|
|
children: Vec::new(),
|
|
|
|
|
|
|
|
|
|
time_left: None,
|
|
|
|
|
moves_left: None,
|
|
|
|
@ -383,21 +407,10 @@ impl MoveNode {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Node for MoveNode {
|
|
|
|
|
fn children<'a>(&'a self) -> Vec<&'a GameNode> {
|
|
|
|
|
self.children.iter().collect::<Vec<&'a GameNode>>()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_child(&mut self, node: GameNode) -> &mut GameNode {
|
|
|
|
|
self.children.push(node);
|
|
|
|
|
self.children.last_mut().unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<&parser::Node> for MoveNode {
|
|
|
|
|
impl TryFrom<parser::Node> for MoveNode {
|
|
|
|
|
type Error = MoveNodeError;
|
|
|
|
|
|
|
|
|
|
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
|
|
|
|
fn try_from(n: parser::Node) -> Result<Self, Self::Error> {
|
|
|
|
|
let s = match n.mv() {
|
|
|
|
|
Some((color, mv)) => {
|
|
|
|
|
let mut s = Self::new(color, mv);
|
|
|
|
@ -460,7 +473,6 @@ pub struct SetupNode {
|
|
|
|
|
id: Uuid,
|
|
|
|
|
|
|
|
|
|
pub positions: Vec<parser::SetupInstr>,
|
|
|
|
|
pub children: Vec<GameNode>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SetupNode {
|
|
|
|
@ -480,26 +492,14 @@ impl SetupNode {
|
|
|
|
|
Ok(Self {
|
|
|
|
|
id: Uuid::new_v4(),
|
|
|
|
|
positions,
|
|
|
|
|
children: Vec::new(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Node for SetupNode {
|
|
|
|
|
fn children<'a>(&'a self) -> Vec<&'a GameNode> {
|
|
|
|
|
self.children.iter().collect::<Vec<&'a GameNode>>()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
fn add_child(&mut self, _node: GameNode) -> &mut GameNode {
|
|
|
|
|
unimplemented!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<&parser::Node> for SetupNode {
|
|
|
|
|
impl TryFrom<parser::Node> for SetupNode {
|
|
|
|
|
type Error = SetupNodeError;
|
|
|
|
|
|
|
|
|
|
fn try_from(n: &parser::Node) -> Result<Self, Self::Error> {
|
|
|
|
|
fn try_from(n: parser::Node) -> Result<Self, Self::Error> {
|
|
|
|
|
match n.setup() {
|
|
|
|
|
Some(elements) => Self::new(elements),
|
|
|
|
|
None => Err(Self::Error::NotASetupNode),
|
|
|
|
@ -507,6 +507,7 @@ impl TryFrom<&parser::Node> for SetupNode {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
pub fn path_to_node(node: &GameNode, id: Uuid) -> Vec<&GameNode> {
|
|
|
|
|
if node.id() == id {
|
|
|
|
@ -523,6 +524,7 @@ pub fn path_to_node(node: &GameNode, id: Uuid) -> Vec<&GameNode> {
|
|
|
|
|
|
|
|
|
|
Vec::new()
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod test {
|
|
|
|
@ -555,15 +557,19 @@ mod test {
|
|
|
|
|
Player::default(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
let first_move = MoveNode::new(Color::Black, Move::Move("dd".to_owned()));
|
|
|
|
|
let first_ = game.add_child(GameNode::MoveNode(first_move.clone()));
|
|
|
|
|
let second_move = MoveNode::new(Color::White, Move::Move("qq".to_owned()));
|
|
|
|
|
first_.add_child(GameNode::MoveNode(second_move.clone()));
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
let nodes = game.nodes();
|
|
|
|
|
assert_eq!(nodes.len(), 2);
|
|
|
|
|
assert_eq!(nodes[0].id(), first_move.id);
|
|
|
|
|
assert_eq!(nodes[1].id(), second_move.id);
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[ignore]
|
|
|
|
@ -588,7 +594,7 @@ mod test {
|
|
|
|
|
],
|
|
|
|
|
next: vec![],
|
|
|
|
|
};
|
|
|
|
|
assert_matches!(GameNode::try_from(&n), Ok(GameNode::MoveNode(_)));
|
|
|
|
|
assert_matches!(GameNode::try_from(n), Ok(GameNode::MoveNode(_)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -630,10 +636,10 @@ mod move_node_tests {
|
|
|
|
|
],
|
|
|
|
|
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.mv, Move::Move("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.comments, Some("Comments in the game".to_owned()));
|
|
|
|
|
});
|
|
|
|
@ -653,7 +659,7 @@ mod move_node_tests {
|
|
|
|
|
next: vec![],
|
|
|
|
|
};
|
|
|
|
|
assert_matches!(
|
|
|
|
|
MoveNode::try_from(&n),
|
|
|
|
|
MoveNode::try_from(n),
|
|
|
|
|
Err(MoveNodeError::IncompatibleProperty(_))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
@ -703,7 +709,7 @@ mod path_test {
|
|
|
|
|
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(text).unwrap();
|
|
|
|
|
let games = games
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|game| GameRecord::try_from(&game).expect("game to parse"))
|
|
|
|
|
.map(|game| GameRecord::try_from(game).expect("game to parse"))
|
|
|
|
|
.collect::<Vec<GameRecord>>();
|
|
|
|
|
f(games);
|
|
|
|
|
}
|
|
|
|
@ -722,7 +728,10 @@ mod path_test {
|
|
|
|
|
|games| {
|
|
|
|
|
let game = &games[0];
|
|
|
|
|
|
|
|
|
|
let moves = game.mainline();
|
|
|
|
|
let moves = game
|
|
|
|
|
.mainline()
|
|
|
|
|
.expect("there should be a mainline in this file")
|
|
|
|
|
.collect::<Vec<&GameNode>>();
|
|
|
|
|
assert_matches!(moves[0], GameNode::MoveNode(node) => {
|
|
|
|
|
assert_eq!(node.color, Color::Black);
|
|
|
|
|
assert_eq!(node.mv, Move::Move("pp".to_owned()));
|
|
|
|
@ -744,7 +753,10 @@ mod path_test {
|
|
|
|
|
with_file(std::path::Path::new("test_data/branch_test.sgf"), |games| {
|
|
|
|
|
let game = &games[0];
|
|
|
|
|
|
|
|
|
|
let moves = game.mainline();
|
|
|
|
|
let moves = game
|
|
|
|
|
.mainline()
|
|
|
|
|
.expect("there should be a mainline in this file")
|
|
|
|
|
.collect::<Vec<&GameNode>>();
|
|
|
|
|
assert_matches!(moves[1], GameNode::MoveNode(node) => {
|
|
|
|
|
assert_eq!(node.color, Color::White);
|
|
|
|
|
assert_eq!(node.mv, Move::Move("dd".to_owned()));
|
|
|
|
@ -791,7 +803,7 @@ mod file_test {
|
|
|
|
|
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(text).unwrap();
|
|
|
|
|
let games = games
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|game| GameRecord::try_from(&game).expect("game to parse"))
|
|
|
|
|
.map(|game| GameRecord::try_from(game).expect("game to parse"))
|
|
|
|
|
.collect::<Vec<GameRecord>>();
|
|
|
|
|
f(games);
|
|
|
|
|
}
|
|
|
|
@ -875,6 +887,7 @@ mod file_test {
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
let children = game.children();
|
|
|
|
|
let node = children.first().unwrap();
|
|
|
|
|
assert_matches!(node, GameNode::MoveNode(node) => {
|
|
|
|
@ -892,6 +905,7 @@ mod file_test {
|
|
|
|
|
assert_eq!(node.time_left, Some(Duration::from_secs(1765)));
|
|
|
|
|
assert_eq!(node.comments, None);
|
|
|
|
|
});
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
let node = node.next().unwrap();
|
|
|
|
|
let expected_properties = vec![
|
|
|
|
@ -911,4 +925,29 @@ mod file_test {
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn it_can_load_a_file_with_multiple_roots() {
|
|
|
|
|
with_file(std::path::Path::new("test_data/multi-tree.sgf"), |games| {
|
|
|
|
|
assert_eq!(games.len(), 1);
|
|
|
|
|
let game = &games[0];
|
|
|
|
|
assert_eq!(game.game_type, GameType::Go);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
game.board_size,
|
|
|
|
|
Size {
|
|
|
|
|
width: 19,
|
|
|
|
|
height: 19
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(game.trees.len(), 2);
|
|
|
|
|
assert_matches!(game.trees[0].root().unwrap().data(), GameNode::MoveNode(node) => {
|
|
|
|
|
assert_eq!(node.color, Color::Black);
|
|
|
|
|
assert_eq!(node.mv, Move::Move("pd".to_owned()));
|
|
|
|
|
});
|
|
|
|
|
assert_matches!(game.trees[1].root().unwrap().data(), GameNode::MoveNode(node) => {
|
|
|
|
|
assert_eq!(node.color, Color::Black);
|
|
|
|
|
assert_eq!(node.mv, Move::Move("pc".to_owned()));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|