Compare commits
13 Commits
3192c0a142
...
5486c3ce37
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | 5486c3ce37 | |
Savanni D'Gerinel | 372f1e1e64 | |
Savanni D'Gerinel | 045daea774 | |
Savanni D'Gerinel | d15613c40e | |
Savanni D'Gerinel | 6fc2487937 | |
Savanni D'Gerinel | d6b424d335 | |
Savanni D'Gerinel | 965223d227 | |
Savanni D'Gerinel | f5429340a1 | |
Savanni D'Gerinel | d929afed2d | |
Savanni D'Gerinel | c30d4e6714 | |
Savanni D'Gerinel | 91e27ced6b | |
Savanni D'Gerinel | fae6d0f94a | |
Savanni D'Gerinel | 95dc194d5d |
|
@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with On
|
||||||
// documenting) my code from almost a year ago.
|
// documenting) my code from almost a year ago.
|
||||||
//
|
//
|
||||||
use crate::{BoardError, Color, Size};
|
use crate::{BoardError, Color, Size};
|
||||||
use sgf::{GameNode, MoveNode};
|
use sgf::{GameNode, Move, MoveNode};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
|
@ -221,7 +221,7 @@ impl Goban {
|
||||||
) -> Result<Goban, BoardError> {
|
) -> Result<Goban, BoardError> {
|
||||||
let mut s = self;
|
let mut s = self;
|
||||||
for m in moves.into_iter() {
|
for m in moves.into_iter() {
|
||||||
match m {
|
let s = match m {
|
||||||
GameNode::MoveNode(node) => s = s.apply_move_node(node)?,
|
GameNode::MoveNode(node) => s = s.apply_move_node(node)?,
|
||||||
GameNode::SetupNode(_n) => unimplemented!("setup nodes aren't processed yet"),
|
GameNode::SetupNode(_n) => unimplemented!("setup nodes aren't processed yet"),
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,22 +16,20 @@ You should have received a copy of the GNU General Public License along with On
|
||||||
|
|
||||||
use crate::CoreApi;
|
use crate::CoreApi;
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
|
use async_std::task::{block_on, spawn};
|
||||||
use otg_core::{
|
use otg_core::{
|
||||||
settings::{SettingsRequest, SettingsResponse},
|
settings::{SettingsRequest, SettingsResponse},
|
||||||
CoreRequest, CoreResponse,
|
Config, CoreRequest, CoreResponse,
|
||||||
};
|
};
|
||||||
use sgf::GameRecord;
|
use sgf::GameRecord;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use crate::views::{GameReview, HomeView, SettingsView};
|
use crate::views::{GameReview, HomeView, SettingsView};
|
||||||
|
|
||||||
/*
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum AppView {
|
enum AppView {
|
||||||
Home,
|
Home,
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// An application window should generally contain
|
// An application window should generally contain
|
||||||
// - an overlay widget
|
// - an overlay widget
|
||||||
|
@ -48,7 +46,7 @@ pub struct AppWindow {
|
||||||
// we can maintain the state of previous views. Since the two of these work together, they are
|
// we can maintain the state of previous views. Since the two of these work together, they are
|
||||||
// a candidate for extraction into a new widget or a new struct.
|
// a candidate for extraction into a new widget or a new struct.
|
||||||
stack: adw::NavigationView,
|
stack: adw::NavigationView,
|
||||||
// view_states: Vec<AppView>,
|
view_states: Vec<AppView>,
|
||||||
|
|
||||||
// Overlays are for transient content, such as about and settings, which can be accessed from
|
// Overlays are for transient content, such as about and settings, which can be accessed from
|
||||||
// anywhere but shouldn't be part of the main application flow.
|
// anywhere but shouldn't be part of the main application flow.
|
||||||
|
@ -65,7 +63,7 @@ impl AppWindow {
|
||||||
let window = Self::setup_window(app);
|
let window = Self::setup_window(app);
|
||||||
let overlay = Self::setup_overlay();
|
let overlay = Self::setup_overlay();
|
||||||
let stack = adw::NavigationView::new();
|
let stack = adw::NavigationView::new();
|
||||||
// let view_states = vec![];
|
let view_states = vec![];
|
||||||
|
|
||||||
window.set_content(Some(&overlay));
|
window.set_content(Some(&overlay));
|
||||||
overlay.set_child(Some(&stack));
|
overlay.set_child(Some(&stack));
|
||||||
|
@ -73,7 +71,7 @@ impl AppWindow {
|
||||||
let s = Self {
|
let s = Self {
|
||||||
window,
|
window,
|
||||||
stack,
|
stack,
|
||||||
// view_states,
|
view_states,
|
||||||
overlay,
|
overlay,
|
||||||
core,
|
core,
|
||||||
settings_view_model: Default::default(),
|
settings_view_model: Default::default(),
|
||||||
|
@ -81,7 +79,7 @@ impl AppWindow {
|
||||||
|
|
||||||
let home = s.setup_home();
|
let home = s.setup_home();
|
||||||
|
|
||||||
s.stack.push(&home);
|
let _ = s.stack.push(&home);
|
||||||
|
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
@ -151,18 +149,23 @@ impl AppWindow {
|
||||||
|
|
||||||
pub fn close_overlay(&self) {
|
pub fn close_overlay(&self) {
|
||||||
let mut view = self.settings_view_model.write().unwrap();
|
let mut view = self.settings_view_model.write().unwrap();
|
||||||
if let Some(ref mut settings) = *view {
|
match *view {
|
||||||
self.overlay.remove_overlay(settings);
|
Some(ref mut settings) => {
|
||||||
*view = None;
|
self.overlay.remove_overlay(settings);
|
||||||
|
*view = None;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_window(app: &adw::Application) -> adw::ApplicationWindow {
|
fn setup_window(app: &adw::Application) -> adw::ApplicationWindow {
|
||||||
adw::ApplicationWindow::builder()
|
let window = adw::ApplicationWindow::builder()
|
||||||
.application(app)
|
.application(app)
|
||||||
.width_request(800)
|
.width_request(800)
|
||||||
.height_request(500)
|
.height_request(500)
|
||||||
.build()
|
.build();
|
||||||
|
|
||||||
|
window
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_header() -> adw::HeaderBar {
|
fn setup_header() -> adw::HeaderBar {
|
||||||
|
|
|
@ -36,15 +36,16 @@ You should have received a copy of the GNU General Public License along with On
|
||||||
// that.
|
// that.
|
||||||
|
|
||||||
use crate::perftrace;
|
use crate::perftrace;
|
||||||
|
use gio::resources_lookup_data;
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{
|
use gtk::{
|
||||||
|
gdk_pixbuf::{InterpType, Pixbuf},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
subclass::prelude::*,
|
subclass::prelude::*,
|
||||||
};
|
};
|
||||||
|
use image::io::Reader as ImageReader;
|
||||||
use otg_core::{Color, Coordinate};
|
use otg_core::{Color, Coordinate};
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, io::Cursor, rc::Rc};
|
||||||
|
|
||||||
const WIDTH: i32 = 800;
|
const WIDTH: i32 = 800;
|
||||||
const HEIGHT: i32 = 800;
|
const HEIGHT: i32 = 800;
|
||||||
|
@ -240,7 +241,6 @@ impl Pen {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn ghost_stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) {
|
fn ghost_stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) {
|
||||||
match color {
|
match color {
|
||||||
Color::White => context.set_source_rgba(0.9, 0.9, 0.9, 0.5),
|
Color::White => context.set_source_rgba(0.9, 0.9, 0.9, 0.5),
|
||||||
|
|
|
@ -18,13 +18,14 @@ use cairo::Context;
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{prelude::*, subclass::prelude::*};
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
use sgf::{GameNode, GameRecord};
|
use sgf::{GameNode, GameRecord};
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
const WIDTH: i32 = 200;
|
const WIDTH: i32 = 200;
|
||||||
const HEIGHT: i32 = 800;
|
const HEIGHT: i32 = 800;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ReviewTreePrivate {
|
pub struct ReviewTreePrivate {
|
||||||
// record: Rc<RefCell<Option<GameRecord>>>,
|
record: Rc<RefCell<Option<GameRecord>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
@ -43,7 +44,7 @@ glib::wrapper! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReviewTree {
|
impl ReviewTree {
|
||||||
pub fn new(_record: GameRecord) -> Self {
|
pub fn new(record: GameRecord) -> Self {
|
||||||
let s: Self = Object::new();
|
let s: Self = Object::new();
|
||||||
|
|
||||||
s.set_width_request(WIDTH);
|
s.set_width_request(WIDTH);
|
||||||
|
@ -59,7 +60,7 @@ impl ReviewTree {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redraw(&self, _ctx: &Context, _width: i32, _height: i32) {
|
pub fn redraw(&self, ctx: &Context, width: i32, height: i32) {
|
||||||
// Implement the tree-drawing algorithm here
|
// Implement the tree-drawing algorithm here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,14 +113,13 @@ struct Tree {
|
||||||
// out if it has children that would overlap the children of the first node.
|
// out if it has children that would overlap the children of the first node.
|
||||||
//
|
//
|
||||||
// My algorithm right now is likely to generate unnecessarily wide trees in a complex game review.
|
// My algorithm right now is likely to generate unnecessarily wide trees in a complex game review.
|
||||||
#[allow(dead_code)]
|
|
||||||
fn node_width(node: &GameNode) -> usize {
|
fn node_width(node: &GameNode) -> usize {
|
||||||
let children: &Vec<GameNode> = match node {
|
let children: &Vec<GameNode> = match node {
|
||||||
GameNode::MoveNode(mn) => &mn.children,
|
GameNode::MoveNode(mn) => &mn.children,
|
||||||
GameNode::SetupNode(sn) => &sn.children,
|
GameNode::SetupNode(sn) => &sn.children,
|
||||||
};
|
};
|
||||||
|
|
||||||
if children.is_empty() {
|
if children.len() == 0 {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,8 +137,7 @@ fn node_width(node: &GameNode) -> usize {
|
||||||
//
|
//
|
||||||
// Just having the node is greatly insufficient. I can get better results if I'm calculating the
|
// Just having the node is greatly insufficient. I can get better results if I'm calculating the
|
||||||
// position of its children.
|
// position of its children.
|
||||||
#[allow(dead_code)]
|
fn node_children_columns(node: &GameNode) -> Vec<usize> {
|
||||||
fn node_children_columns(_node: &GameNode) -> Vec<usize> {
|
|
||||||
vec![0, 1, 2]
|
vec![0, 1, 2]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,10 @@ pub use app_window::AppWindow;
|
||||||
|
|
||||||
mod views;
|
mod views;
|
||||||
|
|
||||||
use async_std::task::{yield_now};
|
use async_std::task::{spawn, yield_now};
|
||||||
use otg_core::{Core, Observable, CoreRequest, CoreResponse};
|
use otg_core::{Core, Observable, CoreRequest, CoreResponse};
|
||||||
use std::{rc::Rc};
|
use std::{rc::Rc, sync::Arc};
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CoreApi {
|
pub struct CoreApi {
|
||||||
|
@ -51,7 +51,6 @@ where
|
||||||
/// LocalObserver creates a task on the current thread which watches the specified observer for notifications and calls the handler function with each one.
|
/// LocalObserver creates a task on the current thread which watches the specified observer for notifications and calls the handler function with each one.
|
||||||
///
|
///
|
||||||
/// The LocalObserver starts a task which listens for notifications during the constructor. When the observer goes out of scope, it will make a point of aborting the task. This combination means that anything which uses the observer can create it, hold on to a reference of it, and then drop it when done, and not have to do anything else with the observer object.
|
/// The LocalObserver starts a task which listens for notifications during the constructor. When the observer goes out of scope, it will make a point of aborting the task. This combination means that anything which uses the observer can create it, hold on to a reference of it, and then drop it when done, and not have to do anything else with the observer object.
|
||||||
#[allow(dead_code)]
|
|
||||||
struct LocalObserver<T> {
|
struct LocalObserver<T> {
|
||||||
join_handle: glib::JoinHandle<()>,
|
join_handle: glib::JoinHandle<()>,
|
||||||
handler: Rc<dyn Fn(T)>,
|
handler: Rc<dyn Fn(T)>,
|
||||||
|
@ -62,7 +61,6 @@ impl<T: 'static> LocalObserver<T> {
|
||||||
///
|
///
|
||||||
/// observable -- any object which emits events
|
/// observable -- any object which emits events
|
||||||
/// handler -- a function which can process events
|
/// handler -- a function which can process events
|
||||||
#[allow(dead_code)]
|
|
||||||
fn new(observable: &dyn Observable<T>, handler: impl Fn(T) + 'static) -> Self {
|
fn new(observable: &dyn Observable<T>, handler: impl Fn(T) + 'static) -> Self {
|
||||||
let listener = observable.subscribe();
|
let listener = observable.subscribe();
|
||||||
let handler = Rc::new(handler);
|
let handler = Rc::new(handler);
|
||||||
|
|
|
@ -4,15 +4,17 @@ use async_std::task::spawn;
|
||||||
use gio::ActionEntry;
|
use gio::ActionEntry;
|
||||||
use otg_core::{Config, ConfigOption, Core, CoreNotification, LibraryPath, Observable};
|
use otg_core::{Config, ConfigOption, Core, CoreNotification, LibraryPath, Observable};
|
||||||
use otg_gtk::{
|
use otg_gtk::{
|
||||||
|
perftrace,
|
||||||
|
// ui::{ConfigurationPage, Home, PlayingField},
|
||||||
AppWindow,
|
AppWindow,
|
||||||
CoreApi,
|
CoreApi,
|
||||||
};
|
};
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
const APP_ID_DEV: &str = "com.luminescent-dreams.otg.dev";
|
const APP_ID_DEV: &str = "com.luminescent-dreams.otg.dev";
|
||||||
const APP_ID_PROD: &str = "com.luminescent-dreams.otg";
|
const APP_ID_PROD: &str = "com.luminescent-dreams.otg";
|
||||||
|
|
||||||
// const RESOURCE_BASE_PATH: &str = "/com/luminescent-dreams/otg/";
|
const RESOURCE_BASE_PATH: &str = "/com/luminescent-dreams/otg/";
|
||||||
|
|
||||||
async fn handler(notifications: Receiver<CoreNotification>, app_id: String) {
|
async fn handler(notifications: Receiver<CoreNotification>, app_id: String) {
|
||||||
loop {
|
loop {
|
||||||
|
@ -106,7 +108,7 @@ fn main() {
|
||||||
APP_ID_PROD
|
APP_ID_PROD
|
||||||
};
|
};
|
||||||
|
|
||||||
let config = load_config(app_id);
|
let config = load_config(&app_id);
|
||||||
|
|
||||||
let core = Core::new(config.clone());
|
let core = Core::new(config.clone());
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,13 @@ use gtk::{prelude::*, subclass::prelude::*};
|
||||||
use otg_core::Color;
|
use otg_core::Color;
|
||||||
use sgf::GameRecord;
|
use sgf::GameRecord;
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct GameReviewPrivate {}
|
pub struct GameReviewPrivate {}
|
||||||
|
|
||||||
|
impl Default for GameReviewPrivate {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
impl ObjectSubclass for GameReviewPrivate {
|
impl ObjectSubclass for GameReviewPrivate {
|
||||||
|
@ -49,7 +52,7 @@ glib::wrapper! {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameReview {
|
impl GameReview {
|
||||||
pub fn new(_api: CoreApi, record: GameRecord) -> Self {
|
pub fn new(api: CoreApi, record: GameRecord) -> Self {
|
||||||
let s: Self = Object::builder().build();
|
let s: Self = Object::builder().build();
|
||||||
|
|
||||||
// It's actually really bad to be just throwing away errors. Panics make everyone unhappy.
|
// It's actually really bad to be just throwing away errors. Panics make everyone unhappy.
|
||||||
|
|
|
@ -22,7 +22,7 @@ use otg_core::{
|
||||||
CoreRequest, CoreResponse,
|
CoreRequest, CoreResponse,
|
||||||
};
|
};
|
||||||
use sgf::GameRecord;
|
use sgf::GameRecord;
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
struct PlayerDataEntryPrivate {
|
struct PlayerDataEntryPrivate {
|
||||||
|
@ -101,13 +101,19 @@ impl PlayerDataEntry {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct HomePrivate {
|
pub struct HomePrivate {
|
||||||
// black_player: Rc<RefCell<Option<PlayerDataEntry>>>,
|
// black_player: Rc<RefCell<Option<PlayerDataEntry>>>,
|
||||||
// white_player: Rc<RefCell<Option<PlayerDataEntry>>>,
|
// white_player: Rc<RefCell<Option<PlayerDataEntry>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for HomePrivate {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
// black_player: Rc::new(RefCell::new(None)),
|
||||||
|
// white_player: Rc::new(RefCell::new(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
impl ObjectSubclass for HomePrivate {
|
impl ObjectSubclass for HomePrivate {
|
||||||
|
@ -152,7 +158,7 @@ impl HomeView {
|
||||||
s.append(&new_game_button);
|
s.append(&new_game_button);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let library = Library::new(on_select_game);
|
let library = Library::new(move |game| on_select_game(game));
|
||||||
let library_view = gtk::ScrolledWindow::builder()
|
let library_view = gtk::ScrolledWindow::builder()
|
||||||
.hscrollbar_policy(gtk::PolicyType::Never)
|
.hscrollbar_policy(gtk::PolicyType::Never)
|
||||||
.min_content_width(360)
|
.min_content_width(360)
|
||||||
|
|
|
@ -14,11 +14,11 @@ 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/>.
|
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 std::{cell::RefCell, rc::Rc};
|
use std::{borrow::Cow, cell::RefCell, path::Path, rc::Rc};
|
||||||
|
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{subclass::prelude::*};
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
use otg_core::{Config, ConfigOption, LibraryPath};
|
use otg_core::{Config, ConfigOption, LibraryPath};
|
||||||
|
|
||||||
fn library_chooser_row(
|
fn library_chooser_row(
|
||||||
|
@ -33,7 +33,7 @@ fn library_chooser_row(
|
||||||
.valign(gtk::Align::Center)
|
.valign(gtk::Align::Center)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let _parent = parent.clone();
|
let parent = parent.clone();
|
||||||
|
|
||||||
let library_row = adw::ActionRow::builder()
|
let library_row = adw::ActionRow::builder()
|
||||||
.title("Library Path")
|
.title("Library Path")
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.77.0"
|
channel = "1.73.0"
|
||||||
targets = [ "wasm32-unknown-unknown" ]
|
targets = [ "wasm32-unknown-unknown" ]
|
||||||
|
|
|
@ -11,5 +11,9 @@ fn main() {
|
||||||
println!("{:?}", file);
|
println!("{:?}", file);
|
||||||
|
|
||||||
let games = parse_sgf_file(&file).unwrap();
|
let games = parse_sgf_file(&file).unwrap();
|
||||||
games.into_iter().flatten().for_each(|sgf| println!("{:?}", sgf.white_player));
|
for sgf in games {
|
||||||
|
if let Ok(sgf) = sgf {
|
||||||
|
println!("{:?}", sgf.white_player);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(&self) -> Vec<&GameNode> {
|
pub fn mainline<'a>(&'a self) -> Vec<&'a 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(&mut self, node: GameNode) -> &mut GameNode {
|
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a 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),
|
parser::Property::TimeLimit(v) => s.time_limit = Some(v.clone()),
|
||||||
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(GameNode::try_from)
|
.map(|node| GameNode::try_from(node))
|
||||||
.collect::<Result<Vec<GameNode>, GameNodeError>>()
|
.collect::<Result<Vec<GameNode>, GameNodeError>>()
|
||||||
.map_err(GameError::InvalidGameNode)?;
|
.map_err(GameError::InvalidGameNode)?;
|
||||||
|
|
||||||
|
@ -257,17 +257,18 @@ 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()
|
||||||
.flat_map(|node| {
|
.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(&self) -> Vec<&GameNode>;
|
fn children<'a>(&'a self) -> Vec<&'a GameNode>;
|
||||||
fn add_child(&mut self, node: GameNode) -> &mut GameNode;
|
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameNode {
|
impl GameNode {
|
||||||
|
@ -280,21 +281,21 @@ impl GameNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Node for GameNode {
|
impl Node for GameNode {
|
||||||
fn children(&self) -> Vec<&GameNode> {
|
fn children<'a>(&'a self) -> Vec<&'a 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(&self) -> Vec<&GameNode> {
|
fn nodes<'a>(&'a self) -> Vec<&'a 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(&mut self, new_node: GameNode) -> &mut GameNode {
|
fn add_child<'a>(&'a mut self, new_node: GameNode) -> &'a 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),
|
||||||
|
@ -325,7 +326,7 @@ impl TryFrom<&parser::Node> for GameNode {
|
||||||
let children = n
|
let children = n
|
||||||
.next
|
.next
|
||||||
.iter()
|
.iter()
|
||||||
.map(GameNode::try_from)
|
.map(|n| GameNode::try_from(n))
|
||||||
.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) {
|
||||||
|
@ -388,7 +389,7 @@ impl Node for MoveNode {
|
||||||
self.children.iter().collect::<Vec<&'a GameNode>>()
|
self.children.iter().collect::<Vec<&'a GameNode>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_child(&mut self, node: GameNode) -> &mut GameNode {
|
fn add_child<'a>(&'a mut self, node: GameNode) -> &'a mut GameNode {
|
||||||
self.children.push(node);
|
self.children.push(node);
|
||||||
self.children.last_mut().unwrap()
|
self.children.last_mut().unwrap()
|
||||||
}
|
}
|
||||||
|
@ -416,7 +417,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);
|
s.time_left = Some(duration.clone());
|
||||||
}
|
}
|
||||||
parser::Property::Comment(cmt) => {
|
parser::Property::Comment(cmt) => {
|
||||||
if s.comments.is_some() {
|
if s.comments.is_some() {
|
||||||
|
@ -491,7 +492,7 @@ impl Node for SetupNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn add_child(&mut self, _node: GameNode) -> &mut GameNode {
|
fn add_child<'a>(&'a mut self, _node: GameNode) -> &'a mut GameNode {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -508,7 +509,7 @@ impl TryFrom<&parser::Node> for SetupNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn path_to_node(node: &GameNode, id: Uuid) -> Vec<&GameNode> {
|
pub fn path_to_node<'a>(node: &'a GameNode, id: Uuid) -> Vec<&'a GameNode> {
|
||||||
if node.id() == id {
|
if node.id() == id {
|
||||||
return vec![node];
|
return vec![node];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -10,7 +10,7 @@ use nom::{
|
||||||
IResult, Parser,
|
IResult, Parser,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{fmt::Write, num::ParseIntError, time::Duration};
|
use std::{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 - b'a';
|
let row = row_char as u8 - 'a' as u8;
|
||||||
let column_char = parts.next().unwrap();
|
let column_char = parts.next().unwrap();
|
||||||
let column = column_char as u8 - b'a';
|
let column = column_char as u8 - 'a' as u8;
|
||||||
Some((row, column))
|
Some((row, column))
|
||||||
} else {
|
} else {
|
||||||
unimplemented!("moves must contain exactly two characters");
|
unimplemented!("moves must contain exactly two characters");
|
||||||
|
@ -527,20 +527,22 @@ 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) => {
|
||||||
positions
|
format!(
|
||||||
.iter()
|
"TW{}",
|
||||||
.fold("TW".to_owned(), |mut output, Position(p)| {
|
positions
|
||||||
let _ = write!(output, "{}", p);
|
.iter()
|
||||||
output
|
.map(|Position(p)| format!("[{}]", p))
|
||||||
})
|
.collect::<String>()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Property::Territory(Color::Black, positions) => {
|
Property::Territory(Color::Black, positions) => {
|
||||||
positions
|
format!(
|
||||||
.iter()
|
"TB{}",
|
||||||
.fold("TB".to_owned(), |mut output, Position(p)| {
|
positions
|
||||||
let _ = write!(output, "{}", p);
|
.iter()
|
||||||
output
|
.map(|Position(p)| format!("[{}]", p))
|
||||||
})
|
.collect::<String>()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Property::Unknown(UnknownProperty { ident, value }) => {
|
Property::Unknown(UnknownProperty { ident, value }) => {
|
||||||
format!("{}[{}]", ident, value)
|
format!("{}[{}]", ident, value)
|
||||||
|
@ -963,7 +965,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: &str = "(;FF[4]C[root](;C[a];C[b](;C[c])
|
const EXAMPLE: &'static 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])))";
|
||||||
|
|
Loading…
Reference in New Issue