diff options
Diffstat (limited to '')
| -rw-r--r-- | src/client/view.rs | 182 |
1 files changed, 176 insertions, 6 deletions
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 @@ | |||
| 3 | //! The core owns state and navigation; adapters map input to `Command` and | 3 | //! The core owns state and navigation; adapters map input to `Command` and |
| 4 | //! render `View`. Terminal text lives in [`crate::client::tui`], not here. | 4 | //! render `View`. Terminal text lives in [`crate::client::tui`], not here. |
| 5 | 5 | ||
| 6 | use crate::blackjack::BlackjackState; | ||
| 6 | use crate::client::games::roulette::{Cursor, Direction}; | 7 | use crate::client::games::roulette::{Cursor, Direction}; |
| 7 | use crate::client::games::traits::GameState; | 8 | use crate::client::games::traits::{GamePhase, GameState}; |
| 8 | use crate::roulette::{BetTarget, Bets}; | 9 | use crate::roulette::{BetTarget, Bets}; |
| 9 | 10 | ||
| 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 11 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| @@ -81,23 +82,192 @@ impl RouletteView { | |||
| 81 | } | 82 | } |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | /// Everything a frontend can draw. `Text` is the bridge for games that have | 85 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 85 | /// not been ported to a structured view yet. | 86 | pub enum SlotsPhase { |
| 87 | Ready, | ||
| 88 | Spinning, | ||
| 89 | Result, | ||
| 90 | } | ||
| 91 | |||
| 92 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 93 | pub struct SlotsView { | ||
| 94 | pub bet: u64, | ||
| 95 | pub balance: Option<i64>, | ||
| 96 | pub reels: Vec<char>, | ||
| 97 | pub win_cells: Vec<usize>, | ||
| 98 | pub win_lines: Vec<usize>, | ||
| 99 | pub win_type: String, | ||
| 100 | pub winnings: Option<u64>, | ||
| 101 | pub phase: SlotsPhase, | ||
| 102 | pub error: Option<String>, | ||
| 103 | } | ||
| 104 | |||
| 105 | fn parse_indices(raw: Option<&String>) -> Vec<usize> { | ||
| 106 | raw.map(|value| { | ||
| 107 | value | ||
| 108 | .split(',') | ||
| 109 | .filter_map(|part| part.trim().parse().ok()) | ||
| 110 | .collect() | ||
| 111 | }) | ||
| 112 | .unwrap_or_default() | ||
| 113 | } | ||
| 114 | |||
| 115 | impl SlotsView { | ||
| 116 | pub fn from_state(state: &GameState) -> Self { | ||
| 117 | let reels = state | ||
| 118 | .data | ||
| 119 | .get("reels") | ||
| 120 | .map(|raw| raw.chars().collect()) | ||
| 121 | .unwrap_or_default(); | ||
| 122 | let phase = match state.phase { | ||
| 123 | GamePhase::Playing => SlotsPhase::Spinning, | ||
| 124 | GamePhase::Finished => SlotsPhase::Result, | ||
| 125 | GamePhase::WaitingToStart => SlotsPhase::Ready, | ||
| 126 | }; | ||
| 127 | |||
| 128 | Self { | ||
| 129 | bet: state.bet_amount, | ||
| 130 | balance: state | ||
| 131 | .data | ||
| 132 | .get("balance") | ||
| 133 | .and_then(|value| value.parse().ok()), | ||
| 134 | reels, | ||
| 135 | win_cells: parse_indices(state.data.get("win_cells")), | ||
| 136 | win_lines: parse_indices(state.data.get("win_lines")), | ||
| 137 | win_type: state.data.get("win_type").cloned().unwrap_or_default(), | ||
| 138 | winnings: state | ||
| 139 | .data | ||
| 140 | .get("winnings") | ||
| 141 | .and_then(|value| value.parse().ok()), | ||
| 142 | phase, | ||
| 143 | error: state | ||
| 144 | .data | ||
| 145 | .get("error") | ||
| 146 | .cloned() | ||
| 147 | .filter(|message| !message.is_empty()), | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 86 | #[derive(Debug, Clone, PartialEq, Eq)] | 152 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 153 | pub struct BlackjackView { | ||
| 154 | pub status: String, | ||
| 155 | pub balance: Option<i64>, | ||
| 156 | pub bet: u64, | ||
| 157 | pub hand: Option<BlackjackState>, | ||
| 158 | pub error: Option<String>, | ||
| 159 | } | ||
| 160 | |||
| 161 | impl BlackjackView { | ||
| 162 | pub fn from_state(state: &GameState) -> Self { | ||
| 163 | let hand = state | ||
| 164 | .data | ||
| 165 | .get("blackjack") | ||
| 166 | .and_then(|raw| serde_json::from_str(raw).ok()); | ||
| 167 | |||
| 168 | Self { | ||
| 169 | status: state | ||
| 170 | .data | ||
| 171 | .get("status") | ||
| 172 | .cloned() | ||
| 173 | .unwrap_or_else(|| "ready".to_string()), | ||
| 174 | balance: state | ||
| 175 | .data | ||
| 176 | .get("balance") | ||
| 177 | .and_then(|value| value.parse().ok()), | ||
| 178 | bet: state.bet_amount, | ||
| 179 | hand, | ||
| 180 | error: state | ||
| 181 | .data | ||
| 182 | .get("error") | ||
| 183 | .cloned() | ||
| 184 | .filter(|message| !message.is_empty()), | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | #[derive(Debug, Clone, PartialEq)] | ||
| 190 | pub struct CrashView { | ||
| 191 | pub status: String, | ||
| 192 | pub balance: Option<i64>, | ||
| 193 | pub bet: u64, | ||
| 194 | pub multiplier: f64, | ||
| 195 | pub potential: u64, | ||
| 196 | pub crash_point: Option<f64>, | ||
| 197 | pub payout: u64, | ||
| 198 | pub error: Option<String>, | ||
| 199 | } | ||
| 200 | |||
| 201 | impl CrashView { | ||
| 202 | pub fn from_state(state: &GameState) -> Self { | ||
| 203 | let bet = state.bet_amount; | ||
| 204 | |||
| 205 | Self { | ||
| 206 | status: state | ||
| 207 | .data | ||
| 208 | .get("status") | ||
| 209 | .cloned() | ||
| 210 | .unwrap_or_else(|| "ready".to_string()), | ||
| 211 | balance: state | ||
| 212 | .data | ||
| 213 | .get("balance") | ||
| 214 | .and_then(|value| value.parse().ok()), | ||
| 215 | bet, | ||
| 216 | multiplier: state | ||
| 217 | .data | ||
| 218 | .get("multiplier") | ||
| 219 | .and_then(|value| value.parse::<f64>().ok()) | ||
| 220 | .unwrap_or(1.0), | ||
| 221 | potential: state | ||
| 222 | .data | ||
| 223 | .get("potential") | ||
| 224 | .and_then(|value| value.parse::<u64>().ok()) | ||
| 225 | .unwrap_or(bet), | ||
| 226 | crash_point: state | ||
| 227 | .data | ||
| 228 | .get("crash_point") | ||
| 229 | .and_then(|value| value.parse::<f64>().ok()), | ||
| 230 | payout: state | ||
| 231 | .data | ||
| 232 | .get("payout") | ||
| 233 | .and_then(|value| value.parse::<u64>().ok()) | ||
| 234 | .unwrap_or(0), | ||
| 235 | error: state | ||
| 236 | .data | ||
| 237 | .get("error") | ||
| 238 | .cloned() | ||
| 239 | .filter(|message| !message.is_empty()), | ||
| 240 | } | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | /// Everything a frontend can draw. | ||
| 245 | #[derive(Debug, Clone, PartialEq)] | ||
| 87 | pub enum View { | 246 | pub enum View { |
| 247 | Slots(SlotsView), | ||
| 88 | Roulette(RouletteView), | 248 | Roulette(RouletteView), |
| 89 | Text(Vec<String>), | 249 | Blackjack(BlackjackView), |
| 250 | Crash(CrashView), | ||
| 90 | } | 251 | } |
| 91 | 252 | ||
| 92 | /// Semantic input. Frontends turn keys and clicks into these. | 253 | /// Semantic input. Frontends turn keys and clicks into these. |
| 93 | #[derive(Debug, Clone, PartialEq, Eq)] | 254 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 94 | pub enum Command { | 255 | pub enum Command { |
| 256 | SetBet(u64), | ||
| 257 | AdjustBet(i64), | ||
| 258 | Leave, | ||
| 259 | Spin, | ||
| 260 | Deal, | ||
| 261 | Hit, | ||
| 262 | Stand, | ||
| 263 | Double, | ||
| 264 | Start, | ||
| 265 | CashOut, | ||
| 266 | Forfeit, | ||
| 95 | MoveCursor(Direction), | 267 | MoveCursor(Direction), |
| 96 | PlaceBet, | 268 | PlaceBet, |
| 97 | PlaceBetAt(BetTarget), | 269 | PlaceBetAt(BetTarget), |
| 98 | ClearBets, | 270 | ClearBets, |
| 99 | SetChip(u64), | 271 | SetChip(u64), |
| 100 | AdjustChip(i64), | 272 | AdjustChip(i64), |
| 101 | Spin, | ||
| 102 | Leave, | ||
| 103 | } | 273 | } |
