Set up the game review page along with #229

Merged
savanni merged 24 commits from otg/game-review into main 2024-03-31 23:37:51 +00:00
4 changed files with 33 additions and 40 deletions
Showing only changes of commit 64138b9e90 - Show all commits

View File

@ -11,9 +11,5 @@ fn main() {
println!("{:?}", file); println!("{:?}", file);
let games = parse_sgf_file(&file).unwrap(); let games = parse_sgf_file(&file).unwrap();
for sgf in games { games.into_iter().flatten().for_each(|sgf| println!("{:?}", sgf.white_player));
if let Ok(sgf) = sgf {
println!("{:?}", sgf.white_player);
}
}
} }

View File

@ -118,7 +118,7 @@ impl GameRecord {
/// Generate a list of moves which constitute the main line of the game. This is the game as it /// 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 /// was actually played out, and by convention consists of the first node in each list of
/// children. /// children.
pub fn mainline<'a>(&'a self) -> Vec<&'a GameNode> { pub fn mainline(&self) -> Vec<&GameNode> {
let mut moves: Vec<&GameNode> = vec![]; let mut moves: Vec<&GameNode> = vec![];
let mut next = self.children.get(0); let mut next = self.children.get(0);
@ -150,7 +150,7 @@ impl Node for GameRecord {
self.children.iter().collect::<Vec<&'a GameNode>>() self.children.iter().collect::<Vec<&'a GameNode>>()
} }
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode { fn add_child(&mut self, node: GameNode) -> &mut GameNode {
self.children.push(node); self.children.push(node);
self.children.last_mut().unwrap() self.children.last_mut().unwrap()
} }
@ -227,7 +227,7 @@ impl TryFrom<&parser::Tree> for GameRecord {
parser::Property::Round(v) => s.round = Some(v.clone()), parser::Property::Round(v) => s.round = Some(v.clone()),
parser::Property::Ruleset(v) => s.rules = Some(v.clone()), parser::Property::Ruleset(v) => s.rules = Some(v.clone()),
parser::Property::Source(v) => s.source = Some(v.clone()), parser::Property::Source(v) => s.source = Some(v.clone()),
parser::Property::TimeLimit(v) => s.time_limit = Some(v.clone()), parser::Property::TimeLimit(v) => s.time_limit = Some(*v),
parser::Property::Overtime(v) => s.overtime = Some(v.clone()), parser::Property::Overtime(v) => s.overtime = Some(v.clone()),
// parser::Property::Data(v) => s.transcriber = Some(v.clone()), // parser::Property::Data(v) => s.transcriber = Some(v.clone()),
_ => {} _ => {}
@ -238,7 +238,7 @@ impl TryFrom<&parser::Tree> for GameRecord {
.root .root
.next .next
.iter() .iter()
.map(|node| GameNode::try_from(node)) .map(GameNode::try_from)
.collect::<Result<Vec<GameNode>, GameNodeError>>() .collect::<Result<Vec<GameNode>, GameNodeError>>()
.map_err(GameError::InvalidGameNode)?; .map_err(GameError::InvalidGameNode)?;
@ -257,18 +257,17 @@ pub trait Node {
fn nodes<'a>(&'a self) -> Vec<&'a GameNode> { fn nodes<'a>(&'a self) -> Vec<&'a GameNode> {
self.children() self.children()
.iter() .iter()
.map(|node| { .flat_map(|node| {
let mut children = node.nodes(); let mut children = node.nodes();
let mut v = vec![*node]; let mut v = vec![*node];
v.append(&mut children); v.append(&mut children);
v v
}) })
.flatten()
.collect::<Vec<&'a GameNode>>() .collect::<Vec<&'a GameNode>>()
} }
fn children<'a>(&'a self) -> Vec<&'a GameNode>; fn children(&self) -> Vec<&GameNode>;
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode; fn add_child(&mut self, node: GameNode) -> &mut GameNode;
} }
impl GameNode { impl GameNode {
@ -281,21 +280,21 @@ impl GameNode {
} }
impl Node for GameNode { impl Node for GameNode {
fn children<'a>(&'a self) -> Vec<&'a GameNode> { fn children(&self) -> Vec<&GameNode> {
match self { match self {
GameNode::MoveNode(node) => node.children(), GameNode::MoveNode(node) => node.children(),
GameNode::SetupNode(node) => node.children(), GameNode::SetupNode(node) => node.children(),
} }
} }
fn nodes<'a>(&'a self) -> Vec<&'a GameNode> { fn nodes(&self) -> Vec<&GameNode> {
match self { match self {
GameNode::MoveNode(node) => node.nodes(), GameNode::MoveNode(node) => node.nodes(),
GameNode::SetupNode(node) => node.nodes(), GameNode::SetupNode(node) => node.nodes(),
} }
} }
fn add_child<'a>(&'a mut self, new_node: GameNode) -> &'a mut GameNode { fn add_child(&mut self, new_node: GameNode) -> &mut GameNode {
match self { match self {
GameNode::MoveNode(node) => node.add_child(new_node), GameNode::MoveNode(node) => node.add_child(new_node),
GameNode::SetupNode(node) => node.add_child(new_node), GameNode::SetupNode(node) => node.add_child(new_node),
@ -326,7 +325,7 @@ impl TryFrom<&parser::Node> for GameNode {
let children = n let children = n
.next .next
.iter() .iter()
.map(|n| GameNode::try_from(n)) .map(GameNode::try_from)
.collect::<Result<Vec<Self>, Self::Error>>()?; .collect::<Result<Vec<Self>, Self::Error>>()?;
let node = match (move_node, setup_node) { let node = match (move_node, setup_node) {
@ -389,7 +388,7 @@ impl Node for MoveNode {
self.children.iter().collect::<Vec<&'a GameNode>>() self.children.iter().collect::<Vec<&'a GameNode>>()
} }
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode { fn add_child(&mut self, node: GameNode) -> &mut GameNode {
self.children.push(node); self.children.push(node);
self.children.last_mut().unwrap() self.children.last_mut().unwrap()
} }
@ -417,7 +416,7 @@ impl TryFrom<&parser::Node> for MoveNode {
if s.time_left.is_some() { if s.time_left.is_some() {
return Err(Self::Error::ConflictingProperty); return Err(Self::Error::ConflictingProperty);
} }
s.time_left = Some(duration.clone()); s.time_left = Some(*duration);
} }
parser::Property::Comment(cmt) => { parser::Property::Comment(cmt) => {
if s.comments.is_some() { if s.comments.is_some() {
@ -492,7 +491,7 @@ impl Node for SetupNode {
} }
#[allow(dead_code)] #[allow(dead_code)]
fn add_child<'a>(&'a mut self, _node: GameNode) -> &'a mut GameNode { fn add_child(&mut self, _node: GameNode) -> &mut GameNode {
unimplemented!() unimplemented!()
} }
} }
@ -509,7 +508,7 @@ impl TryFrom<&parser::Node> for SetupNode {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn path_to_node<'a>(node: &'a GameNode, id: Uuid) -> Vec<&'a GameNode> { pub fn path_to_node(node: &GameNode, id: Uuid) -> Vec<&GameNode> {
if node.id() == id { if node.id() == id {
return vec![node]; return vec![node];
} }

View File

@ -70,7 +70,7 @@ impl From<nom::error::Error<&str>> for ParseError {
/// The inner Result is for errors in each individual game in the file. All of the other games can /// The inner Result is for errors in each individual game in the file. All of the other games can
/// still be kept as valid. /// still be kept as valid.
pub fn parse_sgf(input: &str) -> Result<Vec<Result<GameRecord, game::GameError>>, Error> { pub fn parse_sgf(input: &str) -> Result<Vec<Result<GameRecord, game::GameError>>, Error> {
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(|game| GameRecord::try_from(&game)) .map(|game| GameRecord::try_from(&game))

View File

@ -10,7 +10,7 @@ use nom::{
IResult, Parser, IResult, Parser,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{num::ParseIntError, time::Duration}; use std::{fmt::Write, num::ParseIntError, time::Duration};
impl From<ParseSizeError> for Error { impl From<ParseSizeError> for Error {
fn from(_: ParseSizeError) -> Self { fn from(_: ParseSizeError) -> Self {
@ -303,9 +303,9 @@ impl Move {
if s.len() == 2 { if s.len() == 2 {
let mut parts = s.chars(); let mut parts = s.chars();
let row_char = parts.next().unwrap(); let row_char = parts.next().unwrap();
let row = row_char as u8 - 'a' as u8; let row = row_char as u8 - b'a';
let column_char = parts.next().unwrap(); let column_char = parts.next().unwrap();
let column = column_char as u8 - 'a' as u8; let column = column_char as u8 - b'a';
Some((row, column)) Some((row, column))
} else { } else {
unimplemented!("moves must contain exactly two characters"); unimplemented!("moves must contain exactly two characters");
@ -527,22 +527,20 @@ impl ToString for Property {
Property::WhiteRank(value) => format!("WR[{}]", value), Property::WhiteRank(value) => format!("WR[{}]", value),
Property::WhiteTeam(value) => format!("WT[{}]", value), Property::WhiteTeam(value) => format!("WT[{}]", value),
Property::Territory(Color::White, positions) => { Property::Territory(Color::White, positions) => {
format!( positions
"TW{}", .iter()
positions .fold("TW".to_owned(), |mut output, Position(p)| {
.iter() let _ = write!(output, "{}", p);
.map(|Position(p)| format!("[{}]", p)) output
.collect::<String>() })
)
} }
Property::Territory(Color::Black, positions) => { Property::Territory(Color::Black, positions) => {
format!( positions
"TB{}", .iter()
positions .fold("TB".to_owned(), |mut output, Position(p)| {
.iter() let _ = write!(output, "{}", p);
.map(|Position(p)| format!("[{}]", p)) output
.collect::<String>() })
)
} }
Property::Unknown(UnknownProperty { ident, value }) => { Property::Unknown(UnknownProperty { ident, value }) => {
format!("{}[{}]", ident, value) format!("{}[{}]", ident, value)
@ -965,7 +963,7 @@ fn parse_win_score<'a, E: nom::error::ParseError<&'a str>>() -> impl Parser<&'a
mod test { mod test {
use super::*; use super::*;
const EXAMPLE: &'static str = "(;FF[4]C[root](;C[a];C[b](;C[c]) const EXAMPLE: &str = "(;FF[4]C[root](;C[a];C[b](;C[c])
(;C[d];C[e])) (;C[d];C[e]))
(;C[f](;C[g];C[h];C[i]) (;C[f](;C[g];C[h];C[i])
(;C[j])))"; (;C[j])))";