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
7 changed files with 47 additions and 30 deletions
Showing only changes of commit d964ab0d2f - Show all commits

View File

@ -17,19 +17,11 @@ You should have received a copy of the GNU General Public License along with On
use crate::{
database::Database,
library, settings,
types::{AppState, Config, ConfigOption, GameState, LibraryPath, Player, Rank},
};
use async_std::{
channel::{Receiver, Sender},
stream,
task::spawn,
types::{Config, LibraryPath},
};
use async_std::channel::{Receiver, Sender};
use serde::{Deserialize, Serialize};
use std::{
future::Future,
path::PathBuf,
sync::{Arc, RwLock, RwLockReadGuard},
};
use std::sync::{Arc, RwLock, RwLockReadGuard};
pub trait Observable<T> {
fn subscribe(&self) -> Receiver<T>;
@ -168,14 +160,14 @@ impl Core {
*self.library.write().unwrap() = Some(db);
}
pub fn library<'a>(&'a self) -> RwLockReadGuard<'_, Option<Database>> {
pub fn library(&self) -> RwLockReadGuard<'_, Option<Database>> {
self.library.read().unwrap()
}
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
match request {
CoreRequest::Library(request) => library::handle(&self, request).await.into(),
CoreRequest::Settings(request) => settings::handle(&self, request).await.into(),
CoreRequest::Library(request) => library::handle(self, request).await.into(),
CoreRequest::Settings(request) => settings::handle(self, request).await.into(),
}
}

View File

@ -42,13 +42,10 @@ impl Database {
.unwrap();
match parse_sgf(&buffer) {
Ok(sgfs) => {
for sgf in sgfs {
if let Ok(sgf) = sgf {
games.push(sgf);
}
}
let mut sgfs = sgfs.into_iter().flatten().collect::<Vec<sgf::GameRecord>>();
games.append(&mut sgfs);
}
Err(err) => println!("Error parsing {:?}", entry.path()),
Err(err) => println!("Error parsing {:?}: {:?}", entry.path(), err),
}
}
}

View File

@ -95,7 +95,7 @@ impl Goban {
/// This would not work at all if we wanted to set up an impossible board state, given that
/// groups of stones get automatically removed once surrounded.
pub fn from_coordinates(
mut coordinates: impl IntoIterator<Item = (Coordinate, Color)>,
coordinates: impl IntoIterator<Item = (Coordinate, Color)>,
) -> Result<Self, BoardError> {
coordinates.into_iter().try_fold(Self::new(), |board, (coordinate, color)| {
board.place_stone(coordinate, color)

View File

@ -19,8 +19,8 @@ extern crate config_derive;
mod api;
pub use api::{Core, CoreNotification, CoreRequest, CoreResponse, Observable};
mod board;
pub use board::*;
mod goban;
pub use goban::*;
mod database;

View File

@ -14,7 +14,7 @@ General Public License for more details.
You should have received a copy of the GNU General Public License along with On the Grid. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{Core, Config};
use crate::{Core};
use serde::{Deserialize, Serialize};
use sgf::GameRecord;
@ -32,7 +32,7 @@ async fn handle_list_games(model: &Core) -> LibraryResponse {
let library = model.library();
match *library {
Some(ref library) => {
let info = library.all_games().map(|g| g.clone()).collect::<Vec<GameRecord>>();
let info = library.all_games().cloned().collect::<Vec<GameRecord>>();
LibraryResponse::Games(info)
}
None => LibraryResponse::Games(vec![]),

View File

@ -14,7 +14,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with On the Grid. If not, see <https://www.gnu.org/licenses/>.
*/
use crate::{types::LibraryPath, Core, Config};
use crate::{Core, Config};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]

View File

@ -1,7 +1,4 @@
use crate::{
board::{Coordinate, Goban},
database::Database,
};
use crate::goban::{Coordinate, Goban};
use config::define_config;
use config_derive::ConfigOption;
use serde::{Deserialize, Serialize};
@ -70,6 +67,32 @@ impl Default for Size {
}
}
/// AppState stores all of the important state to a full running version of the application.
/// However, this version of AppState is in pretty sorry shape.
///
/// What are the states of the app?
///
/// - in review
/// - in a game
/// - connections to the internet
/// - connections to local applications, such as Leela Zero
/// - the current configuration
/// - the games database
/// - If in a game, the current state of the game. Delegated to GameState.
/// - If in review, the current state of the review.
///
/// Some of these states are concurrent. It's quite possible to have online connections running and
/// to be reviewing a game while, for instance, waiting for an opponent on OGS to make a move.
///
/// I get to ignore a lot of these things for now. Not playing online. Not playing at all,
/// actually. We'll come back to that.
///
/// Plus, it gets more fuzzy, because some of the application state is really UI state. For
/// instance, the state of a game review is purely UI.
///
/// So... AppState probably isn't great for now, but maybe it will become so later. I think I'm
/// going to ignore it until I need it.
/*
#[derive(Debug)]
pub struct AppState {
pub game: Option<GameState>,
@ -95,9 +118,11 @@ impl AppState {
}
*/
}
*/
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Rank {
Kyu(u8),
Dan(u8),
Pro(u8),
@ -164,6 +189,9 @@ impl Default for GameState {
}
impl GameState {
// Legacy code. I recall that this is no longer used (but will be used again) because I
// commented out so much code when I was overhauling the architecture of this app.
#[allow(dead_code)]
fn place_stone(&mut self, coordinate: Coordinate) -> Result<(), BoardError> {
let board = self.board.clone();
let new_board = board.place_stone(coordinate, self.current_player)?;