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
2 changed files with 27 additions and 17 deletions
Showing only changes of commit 3aac3b8393 - Show all commits

View File

@ -49,11 +49,11 @@ pub struct Player {
/// level, the interpreter will reject any games that have setup properties and move properties /// level, the interpreter will reject any games that have setup properties and move properties
/// mixed in a single node. If there are other semantic problems, the interpreter will reject /// mixed in a single node. If there are other semantic problems, the interpreter will reject
/// those, as well. Where the function of the parser is to understand and correct fundamental /// those, as well. Where the function of the parser is to understand and correct fundamental
/// syntax issues, the result of the Game is to have a fully-understood game. However, this doesn't /// syntax issues, the result of the GameRecord is to have a fully-understood game. However, this
/// (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, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Game { pub struct GameRecord {
pub game_type: GameType, pub game_type: GameType,
// TODO: board size is not necessary in all games. Hive has no defined board size. // TODO: board size is not necessary in all games. Hive has no defined board size.
@ -81,7 +81,7 @@ pub struct Game {
pub children: Vec<GameNode>, pub children: Vec<GameNode>,
} }
impl Game { impl GameRecord {
pub fn new( pub fn new(
game_type: GameType, game_type: GameType,
board_size: Size, board_size: Size,
@ -116,7 +116,7 @@ impl Game {
} }
} }
impl Node for Game { impl Node for GameRecord {
fn children<'a>(&'a self) -> Vec<&'a GameNode> { fn children<'a>(&'a self) -> Vec<&'a GameNode> {
self.children.iter().collect::<Vec<&'a GameNode>>() self.children.iter().collect::<Vec<&'a GameNode>>()
} }
@ -127,7 +127,7 @@ impl Node for Game {
} }
} }
impl TryFrom<&parser::Tree> for Game { impl TryFrom<&parser::Tree> for GameRecord {
type Error = GameError; type Error = GameError;
fn try_from(tree: &parser::Tree) -> Result<Self, Self::Error> { fn try_from(tree: &parser::Tree) -> Result<Self, Self::Error> {
@ -503,7 +503,7 @@ mod test {
#[test] #[test]
fn it_can_create_an_empty_game_tree() { fn it_can_create_an_empty_game_tree() {
let tree = Game::new( let tree = GameRecord::new(
GameType::Go, GameType::Go,
Size { Size {
width: 19, width: 19,
@ -517,7 +517,7 @@ mod test {
#[test] #[test]
fn it_can_add_moves_to_a_game() { fn it_can_add_moves_to_a_game() {
let mut game = Game::new( let mut game = GameRecord::new(
GameType::Go, GameType::Go,
Size { Size {
width: 19, width: 19,
@ -693,16 +693,16 @@ mod file_test {
use parser::parse_collection; use parser::parse_collection;
use std::{fs::File, io::Read}; use std::{fs::File, io::Read};
fn with_text(text: &str, f: impl FnOnce(Vec<Game>)) { fn with_text(text: &str, f: impl FnOnce(Vec<GameRecord>)) {
let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(text).unwrap(); let (_, games) = parse_collection::<nom::error::VerboseError<&str>>(text).unwrap();
let games = games let games = games
.into_iter() .into_iter()
.map(|game| Game::try_from(&game).expect("game to parse")) .map(|game| GameRecord::try_from(&game).expect("game to parse"))
.collect::<Vec<Game>>(); .collect::<Vec<GameRecord>>();
f(games); f(games);
} }
fn with_file(path: &std::path::Path, f: impl FnOnce(Vec<Game>)) { fn with_file(path: &std::path::Path, f: impl FnOnce(Vec<GameRecord>)) {
let mut file = File::open(path).unwrap(); let mut file = File::open(path).unwrap();
let mut text = String::new(); let mut text = String::new();
let _ = file.read_to_string(&mut text); let _ = file.read_to_string(&mut text);

View File

@ -5,7 +5,7 @@ mod types;
use std::{fs::File, io::Read}; use std::{fs::File, io::Read};
pub use date::Date; pub use date::Date;
pub use game::Game; pub use game::GameRecord;
pub use parser::parse_collection; pub use parser::parse_collection;
use thiserror::Error; use thiserror::Error;
pub use types::*; pub use types::*;
@ -58,16 +58,26 @@ impl From<nom::error::Error<&str>> for ParseError {
} }
} }
pub fn parse_sgf(input: &str) -> Result<Vec<Result<Game, game::GameError>>, Error> { /// Given raw text of an SGF file, parse all of the games within that file.
///
/// The outermost Result is for any errors that happen in opening and reading the file, or if hte
/// outermost part of the file format is invalid.
///
/// The inner Result is for errors in each individual game in the file. All of the other games can
/// still be kept as valid.
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.into_iter() let games = games.into_iter()
.map(|game| Game::try_from(&game)) .map(|game| GameRecord::try_from(&game))
.collect::<Vec<Result<Game, game::GameError>>>(); .collect::<Vec<Result<GameRecord, game::GameError>>>();
Ok(games) Ok(games)
} }
pub fn parse_sgf_file(path: &std::path::Path) -> Result<Vec<Result<Game, game::GameError>>, Error> { /// Given a path, parse all of the games stored in that file.
///
/// See also `parse_sgf`
pub fn parse_sgf_file(path: &std::path::Path) -> Result<Vec<Result<GameRecord, game::GameError>>, Error> {
let mut file = File::open(path).unwrap(); let mut file = File::open(path).unwrap();
let mut text = String::new(); let mut text = String::new();
let _ = file.read_to_string(&mut text); let _ = file.read_to_string(&mut text);