Skip to content

Commit

Permalink
Allow two tentative positions
Browse files Browse the repository at this point in the history
  • Loading branch information
yescallop committed Nov 16, 2024
1 parent d260eba commit 6757c85
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 77 deletions.
1 change: 1 addition & 0 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ leptos = { version = "=0.7.0-rc1", features = ["csr"] }
paste = "1"
ron = "0.8"
serde = "1"
tinyvec = "1"
web-sys = { version = "0.3.72", features = [
"CanvasRenderingContext2d",
"DomRect",
Expand Down
27 changes: 18 additions & 9 deletions client/src/dialog.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{Confirm, ANALYZE_PREFIX};
use base64::prelude::*;
use c6ol_core::{
game::{Move, Record, Stone},
game::{Move, Point, Record, Stone},
protocol::Request,
};
use leptos::{
Expand All @@ -10,6 +10,7 @@ use leptos::{
prelude::*,
};
use serde::{Deserialize, Serialize};
use tinyvec::ArrayVec;

trait DialogImpl {
type RetVal;
Expand Down Expand Up @@ -251,6 +252,7 @@ pub struct GameMenuDialog {
pub stone: Option<Stone>,
pub online: bool,
pub record: ReadSignal<Record>,
pub tentative_pos: ReadSignal<ArrayVec<[Point; 2]>>,
pub requests: ReadSignal<[Option<Stone>; Request::VALUES.len()]>,
}

Expand All @@ -266,7 +268,7 @@ pub enum GameMenuRetVal {
End,
ClaimWin,
Resign,
Pass,
Submit,
Draw,
}

Expand All @@ -283,6 +285,7 @@ impl DialogImpl for GameMenuDialog {
stone,
online,
record,
tentative_pos,
requests,
} = self;

Expand Down Expand Up @@ -397,8 +400,14 @@ impl DialogImpl for GameMenuDialog {
<button value=ret!(ClaimWin) disabled=ended>
"Claim Win"
</button>
<button value=ret!(Resign) disabled=ended>
"Resign"
<button value=ret!(Submit) disabled=move || record.read().turn() != stone>
{move || {
if tentative_pos.read().len() < record.read().max_stones_to_play() {
"Pass"
} else {
"Submit"
}
}}
</button>
</div>
}
Expand All @@ -425,16 +434,16 @@ impl DialogImpl for GameMenuDialog {
})}
</div>
<div class="btn-group">
<button value=ret!(Pass) disabled=move || record.read().turn() != stone>
"Pass"
</button>
<button
value=ret!(Draw)
disabled=move || ended() || who_requested(Draw) == User
class:prominent=move || who_requested(Draw) == Opponent
>
"Draw"
</button>
<button value=ret!(Resign) disabled=ended>
"Resign"
</button>
</div>
}
};
Expand Down Expand Up @@ -488,8 +497,8 @@ impl DialogImpl for ConfirmDialog {
let message = match &self.0 {
Confirm::MainMenu => "Back to main menu?",
Confirm::Submit(_, _) => "Submit the move?",
Confirm::Pass => "Pass without placing stones?",
Confirm::PlaceSingleStone(_) => "Place a single stone?",
Confirm::Pass(None) => "Place no stone and pass?",
Confirm::Pass(Some(_)) => "Place one stone and pass?",
Confirm::Request(req) => match req {
Request::Draw => "Offer a draw?",
Request::Retract => "Request to retract the previous move?",
Expand Down
64 changes: 40 additions & 24 deletions client/src/game_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
collections::{HashMap, HashSet},
f64, iter,
};
use tinyvec::ArrayVec;
use web_sys::{
js_sys::Array, wasm_bindgen::prelude::*, CanvasRenderingContext2d, HtmlCanvasElement,
KeyboardEvent, MouseEvent, PointerEvent, ResizeObserver, WheelEvent,
Expand Down Expand Up @@ -227,7 +228,7 @@ pub fn GameView(
#[prop(optional)] view_center: RwSignal<Point>,
#[prop(optional)] cursor_pos: RwSignal<Option<Point>>,
#[prop(optional)] phantom_pos: RwSignal<Option<Point>>,
#[prop(optional)] tentative_pos: RwSignal<Option<Point>>,
#[prop(optional)] tentative_pos: RwSignal<ArrayVec<[Point; 2]>>,
) -> impl IntoView {
let disabled = Memo::new(move |_| disabled());

Expand Down Expand Up @@ -419,7 +420,10 @@ pub fn GameView(

if let Some(stone) = stone.get_untracked() {
// Draw the phantom stone.
if let Some(p) = phantom_pos.get().and_then(|p| calc.board_to_view_pos(p)) {
if let Some(p) = phantom_pos
.get_untracked()
.and_then(|p| calc.board_to_view_pos(p))
{
ctx.set_global_alpha(PHANTOM_MOVE_OPACITY);

set_fill_style_by_stone(stone);
Expand All @@ -428,8 +432,12 @@ pub fn GameView(
ctx.set_global_alpha(1.0);
}

// Draw the tentative stone.
if let Some(p) = tentative_pos.get().and_then(|p| calc.board_to_view_pos(p)) {
// Draw the tentative stones.
for p in tentative_pos
.get_untracked()
.into_iter()
.filter_map(|p| calc.board_to_view_pos(p))
{
set_fill_style_by_stone(stone);
draw_circle(p, stone_radius);

Expand Down Expand Up @@ -511,12 +519,13 @@ pub fn GameView(

// Hits the cursor.
//
// Hitting an empty position puts a phantom stone there. Hitting a phantom stone
// makes it tentative. Hitting a tentative stone makes it phantom. When there are
// enough tentative stones for this turn, the move is automatically submitted.
// Hitting an empty position puts a phantom stone there if there are not
// enough tentative stones for this turn. Hitting a phantom stone makes
// it tentative. Hitting a tentative stone makes it phantom. When there
// are enough tentative stones, the move is automatically submitted.
let hit_cursor = move |cursor: Point| {
let phantom = phantom_pos.get();
let tentative = tentative_pos.get();
let mut tentative = tentative_pos.get();
let calc = calc();

if calc.board_to_view_pos(cursor).is_none()
Expand All @@ -526,19 +535,18 @@ pub fn GameView(
return;
}

if tentative == Some(cursor) {
phantom_pos.set(tentative);
tentative_pos.set(None);
if let Some(i) = tentative.iter().position(|&p| p == cursor) {
phantom_pos.set(Some(tentative.remove(i)));
tentative_pos.set(tentative);
} else if phantom == Some(cursor) {
if !record.read().has_past() {
on_event(Event::Submit(cursor, None));
} else if let Some(tentative) = tentative {
on_event(Event::Submit(tentative, Some(cursor)));
} else {
tentative_pos.set(phantom);
phantom_pos.set(None);
phantom_pos.set(None);
tentative.push(cursor);
tentative_pos.set(tentative);

if tentative.len() == record.read().max_stones_to_play() {
on_event(Event::Submit);
}
} else {
} else if tentative.len() < record.read().max_stones_to_play() {
phantom_pos.set(Some(cursor));
}
};
Expand Down Expand Up @@ -883,21 +891,29 @@ pub fn GameView(
let handle = window_event_listener(ev::keydown, on_keydown);
on_cleanup(move || handle.remove());

let record_or_stone_changed = Trigger::new();
let changed = Trigger::new();

Effect::new(move || {
record.track();
stone.track();

// Clear phantom and tentative stones if the record or the stone changed.
phantom_pos.set(None);
tentative_pos.set(None);
*phantom_pos.write_untracked() = None;
*tentative_pos.write_untracked() = ArrayVec::new();

changed.notify();
});

Effect::new(move || {
phantom_pos.track();
tentative_pos.track();

record_or_stone_changed.notify();
changed.notify();
});

Effect::new(move || {
record_or_stone_changed.track();
changed.track();

draw();
});

Expand Down
89 changes: 45 additions & 44 deletions client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use c6ol_core::{
use dialog::*;
use leptos::{ev, prelude::*};
use std::sync::atomic::{AtomicU32, Ordering};
use tinyvec::ArrayVec;
use web_sys::{
js_sys::{ArrayBuffer, Uint8Array},
wasm_bindgen::prelude::*,
Expand All @@ -30,8 +31,7 @@ pub(crate) use console_log;
enum Confirm {
MainMenu,
Submit(Point, Option<Point>),
Pass,
PlaceSingleStone(Point),
Pass(Option<Point>),
Request(Request),
Accept(Request),
Resign,
Expand All @@ -41,11 +41,13 @@ enum Confirm {

enum Event {
Menu,
Submit(Point, Option<Point>),
Submit,
Undo,
Redo,
Home,
End,
Resign,
Draw,
}

const STORAGE_KEY_RECORD: &str = "record";
Expand Down Expand Up @@ -85,6 +87,8 @@ pub fn App() -> impl IntoView {
let record = RwSignal::new(Record::new());
let stone = RwSignal::new(None::<Stone>);

let tentative_pos = RwSignal::new(ArrayVec::new());

let game_id = RwSignal::new(String::new());

let requests = RwSignal::new([None::<Stone>; Request::VALUES.len()]);
Expand Down Expand Up @@ -154,6 +158,7 @@ pub fn App() -> impl IntoView {
stone: stone.get(),
online: online(),
record: record.read_only(),
tentative_pos: tentative_pos.read_only(),
requests: requests.read_only(),
}));
};
Expand Down Expand Up @@ -334,11 +339,24 @@ pub fn App() -> impl IntoView {

match ev {
Event::Menu => show_game_menu_dialog(),
Event::Submit(fst, snd) => {
Event::Submit => {
let tentative = tentative_pos.get();
if online() {
confirm(Confirm::Submit(fst, snd));
confirm(match tentative[..] {
[] => Confirm::Pass(None),
[pos] if record.read().has_past() => Confirm::Pass(Some(pos)),
[pos] => Confirm::Submit(pos, None),
[fst, snd] => Confirm::Submit(fst, Some(snd)),
_ => unreachable!(),
});
} else {
record.write().make_move(Move::Stone(fst, snd));
let mov = match tentative[..] {
[] => Move::Pass,
[pos] => Move::Stone(pos, None),
[fst, snd] => Move::Stone(fst, Some(snd)),
_ => unreachable!(),
};
record.write().make_move(mov);
record_changed = true;
}
}
Expand Down Expand Up @@ -388,15 +406,29 @@ pub fn App() -> impl IntoView {
record_changed = true;
}
}
Event::Resign => {
if online() {
confirm(Confirm::Resign);
} else if let Some(stone) = record.read().turn() {
record.write().make_move(Move::Resign(stone));
record_changed = true;
}
}
Event::Draw => {
if online() {
confirm_request(Request::Draw);
} else {
record.write().make_move(Move::Draw);
record_changed = true;
}
}
}

if record_changed {
stone.set(record.read().turn());
}
};

let tentative_pos = RwSignal::new(None);

let on_game_menu_return = move |ret_val: GameMenuRetVal| match ret_val {
GameMenuRetVal::Resume => {}
GameMenuRetVal::MainMenu => {
Expand All @@ -416,40 +448,9 @@ pub fn App() -> impl IntoView {
GameMenuRetVal::ClaimWin => {
// TODO.
}
GameMenuRetVal::Resign => {
if online() {
confirm(Confirm::Resign);
} else {
let mut record = record.write();
if let Some(stone) = record.turn() {
record.make_move(Move::Resign(stone));
}
}
}
GameMenuRetVal::Pass => {
let tentative = tentative_pos.get();
if online() {
confirm(if let Some(pos) = tentative {
Confirm::PlaceSingleStone(pos)
} else {
Confirm::Pass
});
} else {
let mov = if let Some(pos) = tentative {
Move::Stone(pos, None)
} else {
Move::Pass
};
record.write().make_move(mov);
}
}
GameMenuRetVal::Draw => {
if online() {
confirm_request(Request::Draw);
} else {
record.write().make_move(Move::Draw);
}
}
GameMenuRetVal::Resign => on_event(Event::Resign),
GameMenuRetVal::Submit => on_event(Event::Submit),
GameMenuRetVal::Draw => on_event(Event::Draw),
};

let on_dialog_return = move |id: u32, ret_val: RetVal| {
Expand Down Expand Up @@ -500,8 +501,8 @@ pub fn App() -> impl IntoView {
match confirm {
Confirm::MainMenu => set_game_id(""),
Confirm::Submit(fst, snd) => send(ClientMessage::Place(fst, snd)),
Confirm::Pass => send(ClientMessage::Pass),
Confirm::PlaceSingleStone(pos) => send(ClientMessage::Place(pos, None)),
Confirm::Pass(None) => send(ClientMessage::Pass),
Confirm::Pass(Some(pos)) => send(ClientMessage::Place(pos, None)),
Confirm::Request(req) | Confirm::Accept(req) => {
send(ClientMessage::Request(req));
}
Expand Down
Loading

0 comments on commit 6757c85

Please sign in to comment.