From 37dedc342c087506e502c78a952b45b1c91602fb Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 18:03:24 +0100 Subject: v4.0.2: port slots, blackjack and crash to structured views --- src/client/view.rs | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 176 insertions(+), 6 deletions(-) (limited to 'src/client/view.rs') diff --git a/src/client/view.rs b/src/client/view.rs index 3683ac2..77efec6 100644 --- a/src/client/view.rs +++ b/src/client/view.rs @@ -3,8 +3,9 @@ //! The core owns state and navigation; adapters map input to `Command` and //! render `View`. Terminal text lives in [`crate::client::tui`], not here. +use crate::blackjack::BlackjackState; use crate::client::games::roulette::{Cursor, Direction}; -use crate::client::games::traits::GameState; +use crate::client::games::traits::{GamePhase, GameState}; use crate::roulette::{BetTarget, Bets}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -81,23 +82,192 @@ impl RouletteView { } } -/// Everything a frontend can draw. `Text` is the bridge for games that have -/// not been ported to a structured view yet. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SlotsPhase { + Ready, + Spinning, + Result, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SlotsView { + pub bet: u64, + pub balance: Option, + pub reels: Vec, + pub win_cells: Vec, + pub win_lines: Vec, + pub win_type: String, + pub winnings: Option, + pub phase: SlotsPhase, + pub error: Option, +} + +fn parse_indices(raw: Option<&String>) -> Vec { + raw.map(|value| { + value + .split(',') + .filter_map(|part| part.trim().parse().ok()) + .collect() + }) + .unwrap_or_default() +} + +impl SlotsView { + pub fn from_state(state: &GameState) -> Self { + let reels = state + .data + .get("reels") + .map(|raw| raw.chars().collect()) + .unwrap_or_default(); + let phase = match state.phase { + GamePhase::Playing => SlotsPhase::Spinning, + GamePhase::Finished => SlotsPhase::Result, + GamePhase::WaitingToStart => SlotsPhase::Ready, + }; + + Self { + bet: state.bet_amount, + balance: state + .data + .get("balance") + .and_then(|value| value.parse().ok()), + reels, + win_cells: parse_indices(state.data.get("win_cells")), + win_lines: parse_indices(state.data.get("win_lines")), + win_type: state.data.get("win_type").cloned().unwrap_or_default(), + winnings: state + .data + .get("winnings") + .and_then(|value| value.parse().ok()), + phase, + error: state + .data + .get("error") + .cloned() + .filter(|message| !message.is_empty()), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlackjackView { + pub status: String, + pub balance: Option, + pub bet: u64, + pub hand: Option, + pub error: Option, +} + +impl BlackjackView { + pub fn from_state(state: &GameState) -> Self { + let hand = state + .data + .get("blackjack") + .and_then(|raw| serde_json::from_str(raw).ok()); + + Self { + status: state + .data + .get("status") + .cloned() + .unwrap_or_else(|| "ready".to_string()), + balance: state + .data + .get("balance") + .and_then(|value| value.parse().ok()), + bet: state.bet_amount, + hand, + error: state + .data + .get("error") + .cloned() + .filter(|message| !message.is_empty()), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct CrashView { + pub status: String, + pub balance: Option, + pub bet: u64, + pub multiplier: f64, + pub potential: u64, + pub crash_point: Option, + pub payout: u64, + pub error: Option, +} + +impl CrashView { + pub fn from_state(state: &GameState) -> Self { + let bet = state.bet_amount; + + Self { + status: state + .data + .get("status") + .cloned() + .unwrap_or_else(|| "ready".to_string()), + balance: state + .data + .get("balance") + .and_then(|value| value.parse().ok()), + bet, + multiplier: state + .data + .get("multiplier") + .and_then(|value| value.parse::().ok()) + .unwrap_or(1.0), + potential: state + .data + .get("potential") + .and_then(|value| value.parse::().ok()) + .unwrap_or(bet), + crash_point: state + .data + .get("crash_point") + .and_then(|value| value.parse::().ok()), + payout: state + .data + .get("payout") + .and_then(|value| value.parse::().ok()) + .unwrap_or(0), + error: state + .data + .get("error") + .cloned() + .filter(|message| !message.is_empty()), + } + } +} + +/// Everything a frontend can draw. +#[derive(Debug, Clone, PartialEq)] pub enum View { + Slots(SlotsView), Roulette(RouletteView), - Text(Vec), + Blackjack(BlackjackView), + Crash(CrashView), } /// Semantic input. Frontends turn keys and clicks into these. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Command { + SetBet(u64), + AdjustBet(i64), + Leave, + Spin, + Deal, + Hit, + Stand, + Double, + Start, + CashOut, + Forfeit, MoveCursor(Direction), PlaceBet, PlaceBetAt(BetTarget), ClearBets, SetChip(u64), AdjustChip(i64), - Spin, - Leave, } -- cgit v1.2.3