feat(client_web): display checkers stacks
This commit is contained in:
parent
01fa837b84
commit
d3a20eb6b6
2 changed files with 104 additions and 74 deletions
|
|
@ -3,15 +3,15 @@ use leptos::prelude::*;
|
|||
use crate::trictrac::types::{SerTurnStage, ViewState};
|
||||
|
||||
/// Field numbers in visual display order (left-to-right for each quarter), white's perspective.
|
||||
const TOP_LEFT_W: [u8; 6] = [13, 14, 15, 16, 17, 18];
|
||||
const TOP_LEFT_W: [u8; 6] = [13, 14, 15, 16, 17, 18];
|
||||
const TOP_RIGHT_W: [u8; 6] = [19, 20, 21, 22, 23, 24];
|
||||
const BOT_LEFT_W: [u8; 6] = [12, 11, 10, 9, 8, 7];
|
||||
const BOT_RIGHT_W: [u8; 6] = [ 6, 5, 4, 3, 2, 1];
|
||||
const BOT_LEFT_W: [u8; 6] = [12, 11, 10, 9, 8, 7];
|
||||
const BOT_RIGHT_W: [u8; 6] = [6, 5, 4, 3, 2, 1];
|
||||
|
||||
/// 180° rotation of white's layout: black's pieces (field 24) appear at the bottom.
|
||||
const TOP_LEFT_B: [u8; 6] = [ 1, 2, 3, 4, 5, 6];
|
||||
const TOP_RIGHT_B: [u8; 6] = [ 7, 8, 9, 10, 11, 12];
|
||||
const BOT_LEFT_B: [u8; 6] = [24, 23, 22, 21, 20, 19];
|
||||
const TOP_LEFT_B: [u8; 6] = [1, 2, 3, 4, 5, 6];
|
||||
const TOP_RIGHT_B: [u8; 6] = [7, 8, 9, 10, 11, 12];
|
||||
const BOT_LEFT_B: [u8; 6] = [24, 23, 22, 21, 20, 19];
|
||||
const BOT_RIGHT_B: [u8; 6] = [18, 17, 16, 15, 14, 13];
|
||||
|
||||
/// Returns the displayed board value for `field_num` after applying `staged_moves`.
|
||||
|
|
@ -25,8 +25,12 @@ fn displayed_value(
|
|||
let mut val = base_board[(field_num - 1) as usize];
|
||||
let delta: i8 = if is_white { 1 } else { -1 };
|
||||
for &(from, to) in staged_moves {
|
||||
if from == field_num { val -= delta; }
|
||||
if to == field_num { val += delta; }
|
||||
if from == field_num {
|
||||
val -= delta;
|
||||
}
|
||||
if to == field_num {
|
||||
val += delta;
|
||||
}
|
||||
}
|
||||
val
|
||||
}
|
||||
|
|
@ -42,66 +46,83 @@ pub fn Board(
|
|||
) -> impl IntoView {
|
||||
let board = view_state.board;
|
||||
let is_move_stage = view_state.active_mp_player == Some(player_id)
|
||||
&& matches!(view_state.turn_stage, SerTurnStage::Move | SerTurnStage::HoldOrGoChoice);
|
||||
&& matches!(
|
||||
view_state.turn_stage,
|
||||
SerTurnStage::Move | SerTurnStage::HoldOrGoChoice
|
||||
);
|
||||
let is_white = player_id == 0;
|
||||
|
||||
let fields_from = |nums: &[u8]| -> Vec<AnyView> {
|
||||
nums.iter().map(|&field_num| {
|
||||
view! {
|
||||
<div
|
||||
class=move || {
|
||||
let moves = staged_moves.get();
|
||||
let val = displayed_value(board, &moves, is_white, field_num);
|
||||
let is_mine = if is_white { val > 0 } else { val < 0 };
|
||||
let can_stage = is_move_stage && moves.len() < 2;
|
||||
let sel = selected_origin.get();
|
||||
let fields_from = |nums: &[u8], is_top_row: bool| -> Vec<AnyView> {
|
||||
nums.iter()
|
||||
.map(|&field_num| {
|
||||
view! {
|
||||
<div
|
||||
class=move || {
|
||||
let moves = staged_moves.get();
|
||||
let val = displayed_value(board, &moves, is_white, field_num);
|
||||
let is_mine = if is_white { val > 0 } else { val < 0 };
|
||||
let can_stage = is_move_stage && moves.len() < 2;
|
||||
let sel = selected_origin.get();
|
||||
|
||||
let mut cls = "field".to_string();
|
||||
if can_stage && (sel.is_some() || is_mine) {
|
||||
cls.push_str(" clickable");
|
||||
}
|
||||
if sel == Some(field_num) { cls.push_str(" selected"); }
|
||||
if can_stage && sel.is_some() && sel != Some(field_num) {
|
||||
cls.push_str(" dest");
|
||||
}
|
||||
cls
|
||||
}
|
||||
on:click=move |_| {
|
||||
if !is_move_stage { return; }
|
||||
if staged_moves.get_untracked().len() >= 2 { return; }
|
||||
|
||||
let moves = staged_moves.get_untracked();
|
||||
let val = displayed_value(board, &moves, is_white, field_num);
|
||||
let is_mine = if is_white { val > 0 } else { val < 0 };
|
||||
|
||||
match selected_origin.get_untracked() {
|
||||
Some(origin) if origin == field_num => {
|
||||
selected_origin.set(None);
|
||||
let mut cls = "field".to_string();
|
||||
if can_stage && (sel.is_some() || is_mine) {
|
||||
cls.push_str(" clickable");
|
||||
}
|
||||
Some(origin) => {
|
||||
staged_moves.update(|v| v.push((origin, field_num)));
|
||||
selected_origin.set(None);
|
||||
if sel == Some(field_num) { cls.push_str(" selected"); }
|
||||
if can_stage && sel.is_some() && sel != Some(field_num) {
|
||||
cls.push_str(" dest");
|
||||
}
|
||||
None if is_mine => selected_origin.set(Some(field_num)),
|
||||
None => {}
|
||||
cls
|
||||
}
|
||||
}
|
||||
>
|
||||
<span class="field-num">{field_num}</span>
|
||||
{move || {
|
||||
let moves = staged_moves.get();
|
||||
let val = displayed_value(board, &moves, is_white, field_num);
|
||||
let count = val.unsigned_abs();
|
||||
(count > 0).then(|| {
|
||||
let color = if val > 0 { "white" } else { "black" };
|
||||
view! { <span class=format!("checkers {color}")>{count}</span> }
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
})
|
||||
.collect()
|
||||
on:click=move |_| {
|
||||
if !is_move_stage { return; }
|
||||
if staged_moves.get_untracked().len() >= 2 { return; }
|
||||
|
||||
let moves = staged_moves.get_untracked();
|
||||
let val = displayed_value(board, &moves, is_white, field_num);
|
||||
let is_mine = if is_white { val > 0 } else { val < 0 };
|
||||
|
||||
match selected_origin.get_untracked() {
|
||||
Some(origin) if origin == field_num => {
|
||||
selected_origin.set(None);
|
||||
}
|
||||
Some(origin) => {
|
||||
staged_moves.update(|v| v.push((origin, field_num)));
|
||||
selected_origin.set(None);
|
||||
}
|
||||
None if is_mine => selected_origin.set(Some(field_num)),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
>
|
||||
<span class="field-num">{field_num}</span>
|
||||
{move || {
|
||||
let moves = staged_moves.get();
|
||||
let val = displayed_value(board, &moves, is_white, field_num);
|
||||
let count = val.unsigned_abs();
|
||||
(count > 0).then(|| {
|
||||
let color = if val > 0 { "white" } else { "black" };
|
||||
let display_n = (count as usize).min(4);
|
||||
// outermost index: last for top rows, first for bottom rows.
|
||||
let outer_idx = if is_top_row { display_n - 1 } else { 0 };
|
||||
let chips: Vec<AnyView> = (0..display_n).map(|i| {
|
||||
let label = if i == outer_idx && count >= 5 {
|
||||
count.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
view! {
|
||||
<div class=format!("checker {color}")>{label}</div>
|
||||
}.into_any()
|
||||
}).collect();
|
||||
view! { <div class="checker-stack">{chips}</div> }
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
}
|
||||
.into_any()
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let (tl, tr, bl, br) = if is_white {
|
||||
|
|
@ -113,15 +134,15 @@ pub fn Board(
|
|||
view! {
|
||||
<div class="board">
|
||||
<div class="board-row top-row">
|
||||
<div class="board-quarter">{fields_from(tl)}</div>
|
||||
<div class="board-quarter">{fields_from(tl, true)}</div>
|
||||
<div class="board-bar"></div>
|
||||
<div class="board-quarter">{fields_from(tr)}</div>
|
||||
<div class="board-quarter">{fields_from(tr, true)}</div>
|
||||
</div>
|
||||
<div class="board-center-bar"></div>
|
||||
<div class="board-row bot-row">
|
||||
<div class="board-quarter">{fields_from(bl)}</div>
|
||||
<div class="board-quarter">{fields_from(bl, false)}</div>
|
||||
<div class="board-bar"></div>
|
||||
<div class="board-quarter">{fields_from(br)}</div>
|
||||
<div class="board-quarter">{fields_from(br, false)}</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue