Compare commits
No commits in common. "f899fdb6910ff87583b62cd4879e0b114bdf0f99" and "8fc9f18d842865ec73a54be6f886bc73caed97e8" have entirely different histories.
f899fdb691
...
8fc9f18d84
138
sgf/src/game.rs
138
sgf/src/game.rs
|
@ -5,9 +5,7 @@ use crate::{
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use slab_tree::{NodeId, NodeMut, NodeRef, Tree};
|
use slab_tree::{NodeId, NodeMut, NodeRef, Tree};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet, VecDeque},
|
collections::{HashSet, VecDeque},
|
||||||
fmt::Debug,
|
|
||||||
ops::Deref,
|
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -58,7 +56,6 @@ pub struct Player {
|
||||||
/// syntax issues, the result of the GameRecord is to have a fully-understood game. However, this
|
/// 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
|
/// doesn't (yet?) go quite to the level of apply the game type (i.e., this is Go, Chess, Yinsh, or
|
||||||
/// whatever).
|
/// whatever).
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct GameRecord {
|
pub struct GameRecord {
|
||||||
pub game_type: GameType,
|
pub game_type: GameType,
|
||||||
|
|
||||||
|
@ -84,7 +81,7 @@ pub struct GameRecord {
|
||||||
pub overtime: Option<String>,
|
pub overtime: Option<String>,
|
||||||
pub transcriber: Option<String>,
|
pub transcriber: Option<String>,
|
||||||
|
|
||||||
pub trees: Vec<GameTree>,
|
pub trees: Vec<Tree<GameNode>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameRecord {
|
impl GameRecord {
|
||||||
|
@ -137,7 +134,7 @@ impl GameRecord {
|
||||||
/// children.
|
/// children.
|
||||||
pub fn mainline(&self) -> Option<impl Iterator<Item = &'_ GameNode>> {
|
pub fn mainline(&self) -> Option<impl Iterator<Item = &'_ GameNode>> {
|
||||||
println!("number of trees: {}", self.trees.len());
|
println!("number of trees: {}", self.trees.len());
|
||||||
if !self.trees.is_empty(){
|
if self.trees.len() > 0 {
|
||||||
Some(MainlineIter {
|
Some(MainlineIter {
|
||||||
next: self.trees[0].root(),
|
next: self.trees[0].root(),
|
||||||
tree: &self.trees[0],
|
tree: &self.trees[0],
|
||||||
|
@ -236,18 +233,15 @@ impl TryFrom<parser::Tree> for GameRecord {
|
||||||
.map_err(GameError::InvalidGameNode)?;
|
.map_err(GameError::InvalidGameNode)?;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
s.trees = tree
|
s.trees = tree.root.next.into_iter()
|
||||||
.root
|
|
||||||
.next
|
|
||||||
.into_iter()
|
|
||||||
.map(recursive_tree_to_slab_tree)
|
.map(recursive_tree_to_slab_tree)
|
||||||
.collect::<Result<Vec<GameTree>, GameError>>()?;
|
.collect::<Result<Vec<Tree<GameNode>>, GameError>>()?;
|
||||||
|
|
||||||
Ok(s)
|
Ok(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn recursive_tree_to_slab_tree(node: parser::Node) -> Result<GameTree, GameError> {
|
fn recursive_tree_to_slab_tree(node: parser::Node) -> Result<Tree<GameNode>, GameError> {
|
||||||
let mut slab = Tree::new();
|
let mut slab = Tree::new();
|
||||||
let mut nodes: VecDeque<(NodeId, parser::Node)> = VecDeque::new();
|
let mut nodes: VecDeque<(NodeId, parser::Node)> = VecDeque::new();
|
||||||
|
|
||||||
|
@ -275,15 +269,13 @@ fn recursive_tree_to_slab_tree(node: parser::Node) -> Result<GameTree, GameError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(GameTree(slab))
|
Ok(slab)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct TreeIter<'a> {
|
pub struct TreeIter<'a> {
|
||||||
queue: VecDeque<NodeRef<'a, &'a GameNode>>,
|
queue: VecDeque<NodeRef<'a, &'a GameNode>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
impl<'a> Default for TreeIter<'a> {
|
impl<'a> Default for TreeIter<'a> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
TreeIter {
|
TreeIter {
|
||||||
|
@ -291,7 +283,6 @@ impl<'a> Default for TreeIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
impl<'a> Iterator for TreeIter<'a> {
|
impl<'a> Iterator for TreeIter<'a> {
|
||||||
type Item = &'a GameNode;
|
type Item = &'a GameNode;
|
||||||
|
@ -307,86 +298,6 @@ impl<'a> Iterator for TreeIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GameTree(Tree<GameNode>);
|
|
||||||
|
|
||||||
impl Clone for GameTree {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
match self.0.root() {
|
|
||||||
None => Self(Tree::new()),
|
|
||||||
Some(source_root_node) => {
|
|
||||||
let mut dest = Tree::new();
|
|
||||||
let dest_root_id = dest.set_root(source_root_node.data().clone());
|
|
||||||
|
|
||||||
// In order to add a node to the new tree, I need to know the ID of the parent in
|
|
||||||
// the source tree and the ID of the parent in the destination tree. So I want a
|
|
||||||
// lookup table that maps source IDs to destination IDs. But is that sufficient?
|
|
||||||
// Perhaps I can just keep a mapping from a source noderef to a destination ID.
|
|
||||||
// I don't think I can keep more than one mutable destination node.
|
|
||||||
|
|
||||||
let mut mapping: HashMap<NodeId, NodeId> = HashMap::new();
|
|
||||||
mapping.insert(source_root_node.node_id(), dest_root_id);
|
|
||||||
|
|
||||||
for source_node in source_root_node.traverse_level_order() {
|
|
||||||
match source_node.parent() {
|
|
||||||
None => {}
|
|
||||||
Some(parent) => {
|
|
||||||
let source_node_parent_id = parent.node_id();
|
|
||||||
let target_node_parent_id = mapping.get(&source_node_parent_id).expect("node should have been added to the source to dest mapping when being cloned");
|
|
||||||
|
|
||||||
let mut parent = dest.get_mut(*target_node_parent_id).expect(
|
|
||||||
"destination parent node to exist before reaching potential children",
|
|
||||||
);
|
|
||||||
let dest_id = parent.append(source_node.data().clone()).node_id();
|
|
||||||
mapping.insert(source_node.node_id(), dest_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Self(dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for GameTree {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
|
||||||
self.write_formatted(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for GameTree {
|
|
||||||
type Target = Tree<GameNode>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for GameTree {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
// Get pre-order iterators over both trees, zip them, and ensure that the data contents are
|
|
||||||
// the same between them
|
|
||||||
let left_root = self.root();
|
|
||||||
let right_root = other.root();
|
|
||||||
|
|
||||||
match (left_root, right_root) {
|
|
||||||
(Some(left_root), Some(right_root)) => {
|
|
||||||
for (left_node, right_node) in std::iter::zip(
|
|
||||||
left_root.traverse_pre_order(),
|
|
||||||
right_root.traverse_pre_order(),
|
|
||||||
) {
|
|
||||||
if left_node.data() != right_node.data() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(None, None) => return true,
|
|
||||||
_ => return false,
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MainlineIter<'a> {
|
pub struct MainlineIter<'a> {
|
||||||
next: Option<NodeRef<'a, GameNode>>,
|
next: Option<NodeRef<'a, GameNode>>,
|
||||||
tree: &'a Tree<GameNode>,
|
tree: &'a Tree<GameNode>,
|
||||||
|
@ -427,12 +338,33 @@ impl TryFrom<parser::Node> for GameNode {
|
||||||
type Error = GameNodeError;
|
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.
|
||||||
|
//
|
||||||
|
// So, I can treat each branch of the tree as a single line. Iterate over that line. I can
|
||||||
|
// 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.clone());
|
let move_node = MoveNode::try_from(n.clone());
|
||||||
let setup_node = SetupNode::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>>()?;
|
||||||
|
*/
|
||||||
|
|
||||||
match (move_node, setup_node) {
|
match (move_node, setup_node) {
|
||||||
(Ok(node), _) => Ok(Self::MoveNode(node)),
|
(Ok(mut node), _) => Ok(Self::MoveNode(node)),
|
||||||
(Err(_), Ok(node)) => Ok(Self::SetupNode(node)),
|
(Err(_), Ok(mut node)) => Ok(Self::SetupNode(node)),
|
||||||
(Err(move_err), Err(setup_err)) => {
|
(Err(move_err), Err(setup_err)) => {
|
||||||
Err(Self::Error::UnsupportedGameNode(move_err, setup_err, n))
|
Err(Self::Error::UnsupportedGameNode(move_err, setup_err, n))
|
||||||
}
|
}
|
||||||
|
@ -1018,14 +950,4 @@ mod file_test {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_can_copy_a_game_record() {
|
|
||||||
with_file(std::path::Path::new("test_data/multi-tree.sgf"), |games| {
|
|
||||||
let dest = games.clone();
|
|
||||||
|
|
||||||
assert_eq!(games.len(), dest.len());
|
|
||||||
assert_eq!(games[0], dest[0]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ pub enum Error {
|
||||||
InvalidSgf(VerboseNomError),
|
InvalidSgf(VerboseNomError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
||||||
|
|
||||||
|
@ -74,7 +73,7 @@ pub fn parse_sgf(input: &str) -> Result<Vec<Result<GameRecord, game::GameError>>
|
||||||
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(input)?;
|
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(input)?;
|
||||||
let games = games
|
let games = games
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(GameRecord::try_from)
|
.map(|game| GameRecord::try_from(game))
|
||||||
.collect::<Vec<Result<GameRecord, game::GameError>>>();
|
.collect::<Vec<Result<GameRecord, game::GameError>>>();
|
||||||
|
|
||||||
Ok(games)
|
Ok(games)
|
||||||
|
|
|
@ -56,7 +56,6 @@ pub enum Error {
|
||||||
InvalidSgf(VerboseNomError),
|
InvalidSgf(VerboseNomError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
pub struct VerboseNomError(nom::error::VerboseError<String>);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue