Compare commits

..

No commits in common. "4590bc6688b22058dec944b00b811c58d56dadb4" and "a8a9c49f027f2327eedd55bde603c2d6c66c5293" have entirely different histories.

14 changed files with 181 additions and 384 deletions

View File

@ -76,7 +76,7 @@
inherit pkgs;
buildRustCrateForPkgs = customBuildInfo;
rootFeatures = [ "screenplay" ];
release = true;
release = false;
}).rootCrate.build;
};
};

View File

@ -2,19 +2,11 @@ use crate::types::AppState;
use crate::ui::{playing_field, PlayingFieldView};
use std::sync::{Arc, RwLock};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Request {
PlayingField,
PlayStoneRequest(PlayStoneRequest),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PlayStoneRequest {
pub column: u8,
pub row: u8,
}
#[derive(Clone, Debug)]
#[derive(Debug)]
pub enum Response {
PlayingFieldView(PlayingFieldView),
}
@ -33,18 +25,7 @@ impl CoreApp {
pub async fn dispatch(&self, request: Request) -> Response {
match request {
Request::PlayingField => {
let app_state = self.state.read().unwrap();
let game = app_state.game.as_ref().unwrap();
Response::PlayingFieldView(playing_field(game))
}
Request::PlayStoneRequest(request) => {
let mut app_state = self.state.write().unwrap();
app_state.place_stone(request);
let game = app_state.game.as_ref().unwrap();
Response::PlayingFieldView(playing_field(game))
}
Request::PlayingField => Response::PlayingFieldView(playing_field()),
}
}

View File

@ -1,3 +1,5 @@
use std::time::Duration;
mod api;
pub use api::{CoreApp, Request, Response};

View File

@ -1,7 +1,6 @@
use crate::api::PlayStoneRequest;
use std::time::Duration;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug)]
pub enum Color {
Black,
White,
@ -22,24 +21,13 @@ impl Default for Size {
}
}
pub struct AppState {
pub game: Option<GameState>,
pub(crate) struct AppState {
game: Option<GameState>,
}
impl AppState {
pub fn new() -> Self {
Self {
game: Some(GameState::new()),
}
}
pub fn place_stone(&mut self, req: PlayStoneRequest) {
match self.game {
Some(ref mut game) => {
game.place_stone(req.column, req.row);
}
None => {}
}
Self { game: None }
}
}
@ -67,82 +55,18 @@ impl From<Rank> for String {
}
}
pub struct Player {
struct Player {
name: String,
rank: Rank,
}
pub struct GameState {
pub board: Board,
pub conversation: Vec<String>,
pub current_player: Color,
pub(crate) struct GameState {
goban: Vec<Vec<Color>>,
conversation: Vec<String>,
pub white_player: Player,
pub black_player: Player,
white_player: Player,
black_player: Player,
pub white_clock: Duration,
pub black_clock: Duration,
}
impl GameState {
fn new() -> GameState {
GameState {
board: Board::new(),
conversation: vec![],
current_player: Color::Black,
white_player: Player {
name: "Savanni".to_owned(),
rank: Rank::Kyu(10),
},
black_player: Player {
name: "Opal".to_owned(),
rank: Rank::Kyu(10),
},
white_clock: Duration::from_secs(600),
black_clock: Duration::from_secs(600),
}
}
fn place_stone(&mut self, column: u8, row: u8) {
self.board.place_stone(column, row, self.current_player);
match self.current_player {
Color::White => self.current_player = Color::Black,
Color::Black => self.current_player = Color::White,
}
}
}
pub struct Board {
pub size: Size,
pub spaces: Vec<Option<Color>>,
}
impl Board {
fn new() -> Self {
let mut spaces = Vec::new();
for _ in 0..19 * 19 {
spaces.push(None);
}
Self {
size: Size {
width: 19,
height: 19,
},
spaces,
}
}
pub fn place_stone(&mut self, column: u8, row: u8, stone: Color) {
let addr = self.addr(column, row);
self.spaces[addr] = Some(stone);
}
pub fn stone(&self, column: u8, row: u8) -> Option<Color> {
let addr = self.addr(column, row);
self.spaces[addr]
}
fn addr(&self, column: u8, row: u8) -> usize {
((row as usize) * (self.size.width as usize) + (column as usize)) as usize
}
white_clock: Duration,
black_clock: Duration,
}

View File

@ -2,7 +2,4 @@ mod playing_field;
pub use playing_field::{playing_field, PlayingFieldView};
mod types;
pub use types::{
BoardElement, ChatElement, IntersectionElement, PlayerCardElement, StoneElement,
TextFieldElement,
};
pub use types::{ChatElement, GobanElement, PlayerCardElement, StoneElement, TextFieldElement};

View File

@ -1,9 +1,9 @@
use crate::types::Color;
use crate::{types::GameState, ui::types};
use crate::types::{Color, Size};
use crate::ui::types;
#[derive(Clone, Debug)]
pub struct PlayingFieldView {
pub board: types::BoardElement,
pub board: types::GobanElement,
pub player_card_black: types::PlayerCardElement,
pub player_card_white: types::PlayerCardElement,
pub chat: types::ChatElement,
@ -11,8 +11,8 @@ pub struct PlayingFieldView {
pub current_player: Color,
}
pub fn playing_field(game: &GameState) -> PlayingFieldView {
let board = types::BoardElement::from(&game.board);
pub fn playing_field() -> PlayingFieldView {
let board = types::GobanElement::default();
let player_card_black = types::PlayerCardElement {
color: Color::Black,
@ -42,6 +42,6 @@ pub fn playing_field(game: &GameState) -> PlayingFieldView {
player_card_white,
chat,
message,
current_player: game.current_player,
current_player: Color::White,
}
}

View File

@ -1,16 +1,12 @@
use crate::types::{Color, Size};
use crate::{
api::{PlayStoneRequest, Request},
types::Board,
};
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub struct Jitter {
pub x: i8,
pub y: i8,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(Clone, Debug)]
pub struct StoneElement {
pub color: Color,
pub jitter: Jitter,
@ -25,73 +21,37 @@ impl StoneElement {
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum IntersectionElement {
Unplayable,
Empty(Request),
Filled(StoneElement),
}
#[derive(Clone, Debug)]
pub struct BoardElement {
pub struct GobanElement {
pub size: Size,
pub spaces: Vec<IntersectionElement>,
pub spaces: Vec<Option<StoneElement>>,
}
impl BoardElement {
impl GobanElement {
pub fn new(size: Size) -> Self {
let spaces: Vec<IntersectionElement> = (0..size.height)
.map(|row| {
(0..size.width)
.map(|column| {
IntersectionElement::Empty(Request::PlayStoneRequest(PlayStoneRequest {
column,
row,
}))
})
.collect::<Vec<IntersectionElement>>()
})
.collect::<Vec<Vec<IntersectionElement>>>()
.concat();
Self { size, spaces }
}
pub fn stone(&self, column: u8, row: u8) -> IntersectionElement {
let addr = self.addr(column, row);
self.spaces[addr]
}
fn addr(&self, column: u8, row: u8) -> usize {
((row as usize) * (self.size.width as usize) + (column as usize)) as usize
}
}
impl From<&Board> for BoardElement {
fn from(board: &Board) -> Self {
let spaces: Vec<IntersectionElement> = (0..board.size.height)
.map(|row| {
(0..board.size.width)
.map(|column| match board.stone(column, row) {
Some(color) => IntersectionElement::Filled(StoneElement {
jitter: Jitter { x: 0, y: 0 },
color,
}),
None => IntersectionElement::Empty(Request::PlayStoneRequest(
PlayStoneRequest { column, row },
)),
})
.collect::<Vec<IntersectionElement>>()
})
.collect::<Vec<Vec<IntersectionElement>>>()
.concat();
Self {
size: board.size,
spaces,
size: size.clone(),
spaces: (0..((size.width as usize) * (size.height as usize)))
.map(|_| None)
.collect::<Vec<Option<StoneElement>>>(),
}
}
pub fn stone_mut<'a>(&'a mut self, row: u8, col: u8) -> &'a mut Option<StoneElement> {
let addr = self.addr(row, col);
&mut self.spaces[addr]
}
pub fn stone(&self, row: u8, col: u8) -> Option<StoneElement> {
self.spaces[self.addr(row, col)].clone()
}
fn addr(&self, row: u8, col: u8) -> usize {
((row as usize) * (self.size.width as usize) + (col as usize)) as usize
}
}
impl Default for BoardElement {
impl Default for GobanElement {
fn default() -> Self {
Self::new(Size::default())
}

View File

@ -1,21 +1 @@
pub mod ui;
use kifu_core::{CoreApp, Request, Response};
use std::sync::Arc;
use tokio::runtime::Runtime;
#[derive(Clone)]
pub struct CoreApi {
pub gtk_tx: gtk::glib::Sender<Response>,
pub rt: Arc<Runtime>,
pub core: CoreApp,
}
impl CoreApi {
pub fn dispatch(&self, request: Request) {
self.rt.spawn({
let gtk_tx = self.gtk_tx.clone();
let core = self.core.clone();
async move { gtk_tx.send(core.dispatch(request).await) }
});
}
}

View File

@ -1,7 +1,32 @@
use gio::resources_lookup_data;
use gtk::prelude::*;
use kifu_core::{CoreApp, Request, Response};
use kifu_gtk::{ui::PlayingField, CoreApi};
use std::sync::{Arc, RwLock};
use kifu_gtk::ui::PlayingField;
use std::{
sync::{Arc, Mutex},
time::Duration,
};
use tokio::{
runtime::Runtime,
sync::mpsc::{Receiver, Sender},
};
#[derive(Clone)]
pub struct CoreApi {
gtk_tx: gtk::glib::Sender<Response>,
rt: Arc<Runtime>,
core: CoreApp,
}
impl CoreApi {
pub fn dispatch(&self, request: Request) {
self.rt.spawn({
let gtk_tx = self.gtk_tx.clone();
let core = self.core.clone();
async move { gtk_tx.send(core.dispatch(request).await) }
});
}
}
fn main() {
gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource")
@ -42,29 +67,15 @@ fn main() {
let window = gtk::ApplicationWindow::new(app);
window.present();
gtk_rx.attach(None, {
let api = api.clone();
let playing_field = Arc::new(RwLock::new(None));
move |message| {
gtk_rx.attach(None, move |message| {
println!("message: {:?}", message);
match message {
Response::PlayingFieldView(view) => {
let api = api.clone();
let start = std::time::Instant::now();
let mut playing_field = playing_field.write().unwrap();
if playing_field.is_none() {
let field = PlayingField::new(api, view);
window.set_child(Some(&field));
*playing_field = Some(field);
} else {
playing_field.as_ref().map(|field| field.update_view(view));
}
let end = std::time::Instant::now();
println!("Time to render the playing field: {:?}", end - start);
let playing_field = PlayingField::new(view);
window.set_child(Some(&playing_field));
}
}
Continue(true)
}
});
api.dispatch(Request::PlayingField);

View File

@ -1,4 +1,3 @@
use crate::CoreApi;
use gio::resources_lookup_data;
use glib::Object;
use gtk::{
@ -7,15 +6,11 @@ use gtk::{
subclass::prelude::*,
};
use image::io::Reader as ImageReader;
use kifu_core::{
ui::{BoardElement, IntersectionElement},
Color,
};
use kifu_core::{ui::GobanElement, Color};
use std::{cell::RefCell, io::Cursor, rc::Rc};
const WIDTH: i32 = 800;
const HEIGHT: i32 = 800;
const MARGIN: i32 = 20;
#[derive(Clone, Default, PartialEq)]
struct Addr {
@ -23,39 +18,36 @@ struct Addr {
column: u8,
}
pub struct BoardPrivate {
pub struct GobanPrivate {
drawing_area: gtk::DrawingArea,
current_player: Rc<RefCell<Color>>,
board: Rc<RefCell<BoardElement>>,
cursor_location: Rc<RefCell<Option<Addr>>>,
api: Rc<RefCell<Option<CoreApi>>>,
goban: Rc<RefCell<GobanElement>>,
cursor_location: Rc<RefCell<Addr>>,
}
#[glib::object_subclass]
impl ObjectSubclass for BoardPrivate {
const NAME: &'static str = "Board";
type Type = Board;
impl ObjectSubclass for GobanPrivate {
const NAME: &'static str = "Goban";
type Type = Goban;
type ParentType = gtk::Grid;
fn new() -> BoardPrivate {
BoardPrivate {
fn new() -> GobanPrivate {
GobanPrivate {
drawing_area: Default::default(),
current_player: Rc::new(RefCell::new(Color::Black)),
board: Default::default(),
goban: Default::default(),
cursor_location: Default::default(),
api: Default::default(),
}
}
}
impl ObjectImpl for BoardPrivate {
impl ObjectImpl for GobanPrivate {
fn constructed(&self) {
self.drawing_area.set_width_request(WIDTH);
self.drawing_area.set_height_request(HEIGHT);
let board = self.board.clone();
let goban = self.goban.clone();
let cursor_location = self.cursor_location.clone();
let current_player = self.current_player.clone();
@ -83,9 +75,7 @@ impl ObjectImpl for BoardPrivate {
self.drawing_area
.set_draw_func(move |_, context, width, height| {
let render_start = std::time::Instant::now();
let board = board.borrow();
let goban = goban.borrow();
match background {
Ok(Some(ref background)) => {
context.set_source_pixbuf(&background, 0., 0.);
@ -97,36 +87,24 @@ impl ObjectImpl for BoardPrivate {
context.set_source_rgb(0.1, 0.1, 0.1);
context.set_line_width(2.);
let hspace_between = ((width - 40) as f64) / ((board.size.width - 1) as f64);
let vspace_between = ((height - 40) as f64) / ((board.size.height - 1) as f64);
let hspace_between = ((width - 40) as f64) / ((goban.size.width - 1) as f64);
let vspace_between = ((height - 40) as f64) / ((goban.size.height - 1) as f64);
let pen = Pen {
x_offset: MARGIN as f64,
y_offset: MARGIN as f64,
x_offset: 20.,
y_offset: 20.,
hspace_between,
vspace_between,
};
(0..board.size.width).for_each(|col| {
context.move_to(
(MARGIN as f64) + (col as f64) * hspace_between,
MARGIN as f64,
);
context.line_to(
(MARGIN as f64) + (col as f64) * hspace_between,
(height as f64) - (MARGIN as f64),
);
(0..goban.size.width).for_each(|col| {
context.move_to(20.0 + (col as f64) * hspace_between, 20.0);
context.line_to(20.0 + (col as f64) * hspace_between, (height as f64) - 20.0);
let _ = context.stroke();
});
(0..board.size.height).for_each(|row| {
context.move_to(
MARGIN as f64,
(MARGIN as f64) + (row as f64) * vspace_between,
);
context.line_to(
(width - MARGIN) as f64,
(MARGIN as f64) + (row as f64) * vspace_between,
);
(0..goban.size.height).for_each(|row| {
context.move_to(20.0, 20.0 + (row as f64) * vspace_between);
context.line_to((width - 20) as f64, 20.0 + (row as f64) * vspace_between);
let _ = context.stroke();
});
@ -139,110 +117,61 @@ impl ObjectImpl for BoardPrivate {
(0..19).for_each(|col| {
(0..19).for_each(|row| {
match board.stone(row, col) {
IntersectionElement::Filled(stone) => {
pen.stone(&context, row, col, stone.color);
match goban.stone(row, col) {
None => {}
Some(element) => {
pen.stone(&context, row, col, element.color);
}
_ => {}
};
})
});
let cursor = cursor_location.borrow();
match *cursor {
None => {}
Some(ref cursor) => match board.stone(cursor.row, cursor.column) {
IntersectionElement::Empty(_) => pen.ghost_stone(
context,
cursor.row,
cursor.column,
*current_player.borrow(),
),
_ => {}
},
}
let render_end = std::time::Instant::now();
println!("board rendering time: {:?}", render_end - render_start);
pen.ghost_stone(context, cursor.row, cursor.column, *current_player.borrow());
});
let motion_controller = gtk::EventControllerMotion::new();
{
let board = self.board.clone();
let goban = self.goban.clone();
let cursor = self.cursor_location.clone();
let drawing_area = self.drawing_area.clone();
motion_controller.connect_motion(move |_, x, y| {
let board = board.borrow();
let goban = goban.borrow();
let mut cursor = cursor.borrow_mut();
let hspace_between = ((WIDTH - 40) as f64) / ((board.size.width - 1) as f64);
let vspace_between = ((HEIGHT - 40) as f64) / ((board.size.height - 1) as f64);
let hspace_between = ((WIDTH - 40) as f64) / ((goban.size.width - 1) as f64);
let vspace_between = ((HEIGHT - 40) as f64) / ((goban.size.height - 1) as f64);
let addr = if x.round() < MARGIN as f64
|| x.round() > (WIDTH - MARGIN) as f64
|| y.round() < MARGIN as f64
|| y.round() > (HEIGHT - MARGIN) as f64
{
None
} else {
Some(Addr {
column: ((x.round() - MARGIN as f64) / hspace_between).round() as u8,
row: ((y.round() - MARGIN as f64) / vspace_between).round() as u8,
})
let addr = Addr {
column: ((x.round() - 20.) / hspace_between).round() as u8,
row: ((y.round() - 20.) / vspace_between).round() as u8,
};
if *cursor != addr.clone() {
if *cursor != addr {
*cursor = addr;
drawing_area.queue_draw();
}
});
}
let gesture = gtk::GestureClick::new();
{
let board = self.board.clone();
let cursor = self.cursor_location.clone();
let api = self.api.clone();
gesture.connect_released(move |_, _, _, _| {
let board = board.borrow();
let cursor = cursor.borrow();
match *cursor {
None => {}
Some(ref cursor) => match board.stone(cursor.row, cursor.column) {
IntersectionElement::Empty(request) => {
println!("need to send request: {:?}", request);
api.borrow()
.as_ref()
.expect("API must exist")
.dispatch(request);
}
_ => {}
},
}
});
}
self.drawing_area.add_controller(motion_controller);
self.drawing_area.add_controller(gesture);
}
}
impl WidgetImpl for BoardPrivate {}
impl GridImpl for BoardPrivate {}
impl WidgetImpl for GobanPrivate {}
impl GridImpl for GobanPrivate {}
glib::wrapper! {
pub struct Board(ObjectSubclass<BoardPrivate>) @extends gtk::Grid, gtk::Widget;
pub struct Goban(ObjectSubclass<GobanPrivate>) @extends gtk::Grid, gtk::Widget;
}
impl Board {
pub fn new(api: CoreApi) -> Self {
impl Goban {
pub fn new() -> Self {
let s: Self = Object::builder().build();
*s.imp().api.borrow_mut() = Some(api);
s.attach(&s.imp().drawing_area, 1, 1, 1, 1);
s
}
pub fn set_board(&self, board: BoardElement) {
*self.imp().board.borrow_mut() = board;
pub fn set_board(&self, goban: GobanElement) {
*self.imp().goban.borrow_mut() = goban;
self.imp().drawing_area.queue_draw();
}
@ -263,7 +192,7 @@ impl Pen {
context.arc(
self.x_offset + (col as f64) * self.hspace_between,
self.y_offset + (row as f64) * self.vspace_between,
5.,
10.,
0.,
2. * std::f64::consts::PI,
);
@ -275,7 +204,14 @@ impl Pen {
Color::White => context.set_source_rgb(0.9, 0.9, 0.9),
Color::Black => context.set_source_rgb(0.0, 0.0, 0.0),
};
self.draw_stone(context, row, col);
context.arc(
20.0 + (col as f64) * self.hspace_between,
20.0 + (row as f64) * self.vspace_between,
25.0,
0.0,
2.0 * std::f64::consts::PI,
);
let _ = context.fill();
}
fn ghost_stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) {
@ -283,15 +219,10 @@ impl Pen {
Color::White => context.set_source_rgba(0.9, 0.9, 0.9, 0.5),
Color::Black => context.set_source_rgba(0.0, 0.0, 0.0, 0.5),
};
self.draw_stone(context, row, col);
}
fn draw_stone(&self, context: &cairo::Context, row: u8, col: u8) {
let radius = self.hspace_between / 2. - 2.;
context.arc(
self.x_offset + (col as f64) * self.hspace_between,
self.y_offset + (row as f64) * self.vspace_between,
radius,
20.0 + (col as f64) * self.hspace_between,
20.0 + (row as f64) * self.vspace_between,
25.0,
0.0,
2.0 * std::f64::consts::PI,
);

View File

@ -7,8 +7,8 @@ pub use chat::Chat;
mod playing_field;
pub use playing_field::PlayingField;
mod board;
pub use board::Board;
mod goban;
pub use goban::Goban;
#[cfg(feature = "screenplay")]
pub use playing_field::playing_field_view;

View File

@ -1,6 +1,6 @@
use glib::Object;
use gtk::{prelude::*, subclass::prelude::*};
use kifu_core::ui::PlayerCardElement;
use kifu_core::{ui::PlayerCardElement, Color};
#[derive(Default)]
pub struct PlayerCardPrivate {

View File

@ -1,12 +1,29 @@
use crate::ui::{Board, Chat, PlayerCard};
use crate::CoreApi;
use crate::ui::{Chat, Goban, PlayerCard};
use glib::Object;
use gtk::{prelude::*, subclass::prelude::*};
use kifu_core::ui::PlayingFieldView;
use kifu_core::{
ui::{
ChatElement, GobanElement, PlayerCardElement, PlayingFieldView, StoneElement,
TextFieldElement,
},
Color, Size,
};
use std::{cell::RefCell, rc::Rc};
/*
#[derive(Clone, Debug)]
pub struct PlayingFieldView {
pub board: types::GobanElement,
pub player_card_black: types::PlayerCardElement,
pub player_card_white: types::PlayerCardElement,
pub chat: types::ChatElement,
pub message: types::TextFieldElement,
pub current_player: Color,
}
*/
pub struct PlayingFieldPrivate {
board: Rc<RefCell<Option<Board>>>,
goban: Goban,
player_card_white: Rc<RefCell<Option<PlayerCard>>>,
player_card_black: Rc<RefCell<Option<PlayerCard>>>,
chat: Rc<RefCell<Option<Chat>>>,
@ -15,7 +32,7 @@ pub struct PlayingFieldPrivate {
impl Default for PlayingFieldPrivate {
fn default() -> Self {
Self {
board: Default::default(),
goban: Goban::new(),
player_card_white: Rc::new(RefCell::new(None)),
player_card_black: Rc::new(RefCell::new(None)),
chat: Rc::new(RefCell::new(None)),
@ -28,6 +45,15 @@ impl ObjectSubclass for PlayingFieldPrivate {
const NAME: &'static str = "PlayingField";
type Type = PlayingField;
type ParentType = gtk::Grid;
fn new() -> Self {
Self {
goban: Goban::new(),
player_card_white: Rc::new(RefCell::new(None)),
player_card_black: Rc::new(RefCell::new(None)),
chat: Rc::new(RefCell::new(None)),
}
}
}
impl ObjectImpl for PlayingFieldPrivate {}
@ -39,19 +65,16 @@ glib::wrapper! {
}
impl PlayingField {
pub fn new(api: CoreApi, view: PlayingFieldView) -> PlayingField {
pub fn new(view: PlayingFieldView) -> PlayingField {
let s: Self = Object::builder().build();
let player_card_white = PlayerCard::new(view.player_card_white);
let player_card_black = PlayerCard::new(view.player_card_black);
let chat = Chat::new(view.chat);
*s.imp().board.borrow_mut() = Some(Board::new(api));
s.imp()
.board
.borrow()
.as_ref()
.map(|board| s.attach(board, 1, 1, 1, 2));
s.imp().goban.set_board(view.board);
s.attach(&s.imp().goban, 1, 1, 1, 2);
s.attach(&player_card_black, 2, 1, 1, 1);
s.attach(&player_card_white, 3, 1, 1, 1);
s.attach(&chat, 2, 2, 2, 1);
@ -59,32 +82,17 @@ impl PlayingField {
*s.imp().player_card_white.borrow_mut() = Some(player_card_white);
*s.imp().player_card_black.borrow_mut() = Some(player_card_black);
*s.imp().chat.borrow_mut() = Some(chat);
s.imp().board.borrow().as_ref().map(|board| {
board.set_board(view.board);
board.set_current_player(view.current_player);
});
s.imp().goban.set_current_player(view.current_player);
s
}
pub fn update_view(&self, view: PlayingFieldView) {
self.imp().board.borrow().as_ref().map(|board| {
board.set_board(view.board);
board.set_current_player(view.current_player);
});
}
}
#[cfg(feature = "screenplay")]
pub fn playing_field_view() -> PlayingFieldView {
let mut board = BoardElement::new(Size {
width: 19,
height: 19,
});
*board.stone_mut(4, 4) = IntersectionElement::Filled(StoneElement::new(Color::White));
*board.stone_mut(15, 15) = IntersectionElement::Filled(StoneElement::new(Color::Black));
*board.stone_mut(18, 18) = IntersectionElement::Unplayable;
let mut board = GobanElement::default();
*board.stone_mut(4, 4) = Some(StoneElement::new(Color::White));
*board.stone_mut(15, 15) = Some(StoneElement::new(Color::Black));
let player_card_black = PlayerCardElement {
color: Color::Black,
name: "Savanni".to_owned(),

3
rust-toolchain.toml Normal file
View File

@ -0,0 +1,3 @@
[toolchain]
channel = "1.68.2"
components = ["rust-src"]