summaryrefslogtreecommitdiffstats
path: root/src/client/view.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/client/view.rs182
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
6use crate::blackjack::BlackjackState;
6use crate::client::games::roulette::{Cursor, Direction}; 7use crate::client::games::roulette::{Cursor, Direction};
7use crate::client::games::traits::GameState; 8use crate::client::games::traits::{GamePhase, GameState};
8use crate::roulette::{BetTarget, Bets}; 9use 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. 86pub enum SlotsPhase {
87 Ready,
88 Spinning,
89 Result,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub 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
105fn 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
115impl 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)]
153pub 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
161impl 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)]
190pub 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
201impl 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)]
87pub enum View { 246pub 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)]
94pub enum Command { 255pub 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}