Return the mainline of a game that has no branches in it.
This commit is contained in:
parent
d9bb9d92e5
commit
642351f248
|
@ -114,6 +114,35 @@ impl GameRecord {
|
|||
children: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<'a>(&'a self) -> Vec<&'a GameNode> {
|
||||
let mut moves: Vec<&GameNode> = vec![];
|
||||
|
||||
let mut next = self.children.get(0);
|
||||
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.get(0),
|
||||
GameNode::SetupNode(node) => node.children.get(0),
|
||||
};
|
||||
}
|
||||
|
||||
moves
|
||||
}
|
||||
}
|
||||
|
||||
impl Node for GameRecord {
|
||||
|
@ -666,6 +695,51 @@ mod setup_node_tests {
|
|||
|
||||
#[cfg(test)]
|
||||
mod path_test {
|
||||
use super::*;
|
||||
use cool_asserts::assert_matches;
|
||||
use parser::parse_collection;
|
||||
use std::{fs::File, io::Read};
|
||||
|
||||
fn with_text(text: &str, f: impl FnOnce(Vec<GameRecord>)) {
|
||||
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"))
|
||||
.collect::<Vec<GameRecord>>();
|
||||
f(games);
|
||||
}
|
||||
|
||||
fn with_file(path: &std::path::Path, f: impl FnOnce(Vec<GameRecord>)) {
|
||||
let mut file = File::open(path).unwrap();
|
||||
let mut text = String::new();
|
||||
let _ = file.read_to_string(&mut text);
|
||||
with_text(&text, f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn returns_the_mainline_of_a_game_without_branches() {
|
||||
with_file(
|
||||
std::path::Path::new("test_data/2020 USGO DDK, Round 1.sgf"),
|
||||
|games| {
|
||||
let game = &games[0];
|
||||
|
||||
let moves = game.mainline();
|
||||
assert_matches!(moves[0], GameNode::MoveNode(node) => {
|
||||
assert_eq!(node.color, Color::Black);
|
||||
assert_eq!(node.mv, Move::Move("pp".to_owned()));
|
||||
});
|
||||
assert_matches!(moves[1], GameNode::MoveNode(node) => {
|
||||
assert_eq!(node.color, Color::White);
|
||||
assert_eq!(node.mv, Move::Move("dp".to_owned()));
|
||||
});
|
||||
assert_matches!(moves[2], GameNode::MoveNode(node) => {
|
||||
assert_eq!(node.color, Color::Black);
|
||||
assert_eq!(node.mv, Move::Move("pd".to_owned()));
|
||||
});
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn returns_empty_list_if_no_game_nodes() {
|
||||
|
|
Loading…
Reference in New Issue