Set up SGF reading and start on the game database #47

Merged
savanni merged 20 commits from kifu/sgf into main 2023-07-26 13:54:28 +00:00
4 changed files with 135 additions and 5 deletions
Showing only changes of commit 90b0f830e0 - Show all commits

50
kifu/core/Cargo.lock generated
View File

@ -45,6 +45,7 @@ dependencies = [
"js-sys",
"num-integer",
"num-traits",
"time",
"wasm-bindgen",
"winapi",
]
@ -109,6 +110,15 @@ dependencies = [
"syn 2.0.12",
]
[[package]]
name = "go-sgf"
version = "0.1.0"
dependencies = [
"chrono",
"nom",
"thiserror",
]
[[package]]
name = "grid"
version = "0.9.0"
@ -161,6 +171,7 @@ dependencies = [
name = "kifu-core"
version = "0.1.0"
dependencies = [
"go-sgf",
"grid",
"serde",
"serde_json",
@ -192,12 +203,34 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "no-std-compat"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-integer"
version = "0.1.45"
@ -335,6 +368,17 @@ dependencies = [
"syn 2.0.12",
]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi",
"winapi",
]
[[package]]
name = "typeshare"
version = "1.0.1"
@ -369,6 +413,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"

View File

@ -6,8 +6,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
go-sgf = { path = "../../go-sgf" }
grid = { version = "0.9" }
serde = { version = "1", features = [ "derive" ] }
serde_json = { version = "1" }
serde = { version = "1", features = [ "derive" ] }
thiserror = { version = "1" }
typeshare = { version = "1" }

77
kifu/core/src/database.rs Normal file
View File

@ -0,0 +1,77 @@
use std::{ffi::OsStr, io::Read, os::unix::ffi::OsStrExt, path::PathBuf};
use go_sgf::go::{parse_sgf, GameTree, GameType};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("Database permission denied")]
PermissionDenied,
#[error("An IO error occurred: {0}")]
IOError(std::io::Error),
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::IOError(err)
}
}
pub struct Database {
path: PathBuf,
games: Vec<GameTree>,
}
impl Database {
pub fn open_path(path: PathBuf) -> Result<Database, Error> {
let mut games: Vec<GameTree> = Vec::new();
let extension = PathBuf::from("sgf").into_os_string();
let path_iter = std::fs::read_dir(path.clone())?;
for entry in path_iter {
match entry {
Ok(entry) => {
if entry.path().extension() == Some(&extension) {
let mut buffer = String::new();
std::fs::File::open(entry.path())
.unwrap()
.read_to_string(&mut buffer)
.unwrap();
let sgf = parse_sgf(&buffer).unwrap();
games.extend(sgf);
}
}
Err(err) => println!("failed entry: {:?}", err),
}
}
Ok(Database { path, games })
}
pub fn all_games(&self) -> impl Iterator<Item = &GameTree> {
self.games.iter()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn it_reads_empty_database() {
let db = Database::open_path(PathBuf::from("fixtures/empty_database/"))
.expect("database to open");
assert_eq!(db.all_games().count(), 0);
}
#[test]
fn it_reads_five_games_from_database() {
let db =
Database::open_path(PathBuf::from("fixtures/five_games/")).expect("database to open");
assert_eq!(db.all_games().count(), 5);
for game in db.all_games() {
assert_eq!(game.game_type, GameType::Go);
}
}
}

View File

@ -3,12 +3,14 @@ pub use api::{
CoreApp, CoreRequest, CoreResponse, CreateGameRequest, HotseatPlayerRequest, PlayerInfoRequest,
};
mod types;
pub use types::{BoardError, Color, Rank, Size};
pub mod ui;
mod board;
pub use board::*;
mod config;
pub use config::*;
mod database;
mod types;
pub use types::{BoardError, Color, Rank, Size};
pub mod ui;