summaryrefslogtreecommitdiffstats
path: root/src/client/games/roulette.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/client/games/roulette.rs141
1 files changed, 58 insertions, 83 deletions
diff --git a/src/client/games/roulette.rs b/src/client/games/roulette.rs
index b3a8d9c..ec9573c 100644
--- a/src/client/games/roulette.rs
+++ b/src/client/games/roulette.rs
@@ -1,4 +1,5 @@
1use super::traits::{Game, GameState}; 1use super::traits::{Game, GameState};
2use crate::client::view::{RoulettePhase, RouletteView};
2use crate::roulette::{BetTarget, Bets, Outside, is_even, is_odd}; 3use crate::roulette::{BetTarget, Bets, Outside, is_even, is_odd};
3 4
4pub const ROULETTE_BOARD_LINES: usize = 12; 5pub const ROULETTE_BOARD_LINES: usize = 12;
@@ -232,22 +233,6 @@ impl RouletteGame {
232 Self 233 Self
233 } 234 }
234 235
235 fn bets(state: &GameState) -> Bets {
236 state
237 .data
238 .get("bets")
239 .and_then(|raw| Bets::from_json(raw).ok())
240 .unwrap_or_default()
241 }
242
243 fn cursor(state: &GameState) -> Cursor {
244 state
245 .data
246 .get("cursor")
247 .map(|raw| Cursor::parse(raw))
248 .unwrap_or_default()
249 }
250
251 fn zero_cell(cursor: Cursor) -> String { 236 fn zero_cell(cursor: Cursor) -> String {
252 let cell = if cursor == Cursor::Zero { 237 let cell = if cursor == Cursor::Zero {
253 "[ 0 ]".to_string() 238 "[ 0 ]".to_string()
@@ -329,92 +314,82 @@ impl RouletteGame {
329 format!("Bets: {list} Total: {} Kr", bets.total()) 314 format!("Bets: {list} Total: {} Kr", bets.total())
330 } 315 }
331 316
332 fn status_line(state: &GameState) -> String { 317 fn status_line(view: &RouletteView) -> String {
333 if let Some(error) = state.data.get("error") { 318 if let Some(error) = &view.error {
334 if !error.is_empty() { 319 return format!("ERROR: {error}");
335 return format!("ERROR: {error}");
336 }
337 } 320 }
338 321
339 match state.data.get("status").map(String::as_str) { 322 match view.phase {
340 Some("result") => { 323 RoulettePhase::Result => {
341 let number = state.data.get("number").map(String::as_str).unwrap_or("?"); 324 let number = view.number.unwrap_or(0);
342 let color = state 325 let color = view
343 .data 326 .color
344 .get("color") 327 .as_deref()
345 .map(|value| value.to_uppercase()) 328 .map(str::to_uppercase)
346 .unwrap_or_default(); 329 .unwrap_or_default();
347 let parity = number 330 let parity = if is_even(number) {
348 .parse::<u8>() 331 " EVEN"
349 .ok() 332 } else if is_odd(number) {
350 .map(|number| { 333 " ODD"
351 if is_even(number) {
352 " EVEN"
353 } else if is_odd(number) {
354 " ODD"
355 } else {
356 ""
357 }
358 })
359 .unwrap_or("");
360 let winnings = state
361 .data
362 .get("winnings")
363 .map(String::as_str)
364 .unwrap_or("0");
365 let stake = state.bet_amount;
366 if winnings == "0" {
367 format!("No win at {number} {color}{parity}")
368 } else { 334 } else {
369 let profit = winnings.parse::<u64>().unwrap_or(0).saturating_sub(stake); 335 ""
370 format!("Won {winnings} Kr at {number} {color}{parity} (+{profit})") 336 };
371 } 337 match view.winnings.unwrap_or(0) {
372 } 338 0 => format!("No win at {number} {color}{parity}"),
373 Some("spinning") => "Spinning...".to_string(), 339 winnings => {
374 _ => { 340 let profit = winnings.saturating_sub(view.bet_amount);
375 let chip = state.data.get("chip").map(String::as_str).unwrap_or("10"); 341 format!("Won {winnings} Kr at {number} {color}{parity} (+{profit})")
376 match Self::cursor(state).target() { 342 }
377 Some(target) => format!(
378 "Bet: {} · pays {} · ENTER places {} Kr",
379 target.label(),
380 target.return_label(),
381 chip
382 ),
383 None => "Arrows move, ENTER places the chip, SPACE spins.".to_string(),
384 } 343 }
385 } 344 }
345 RoulettePhase::Spinning => "Spinning...".to_string(),
346 RoulettePhase::Placing => match view.cursor.target() {
347 Some(target) => format!(
348 "Bet: {} · pays {} · ENTER places {} Kr",
349 target.label(),
350 target.return_label(),
351 view.chip
352 ),
353 None => "Arrows move, ENTER places the chip, SPACE spins.".to_string(),
354 },
386 } 355 }
387 } 356 }
388}
389
390impl Game for RouletteGame {
391 fn get_name(&self) -> &'static str {
392 "Roulette"
393 }
394 357
395 fn render(&self, state: &GameState) -> String { 358 /// Render a roulette view to terminal lines. The GUI draws the same view
396 let bets = Self::bets(state); 359 /// with a painter instead.
397 let chip = state.data.get("chip").map(String::as_str).unwrap_or("10"); 360 pub fn render_view(view: &RouletteView) -> String {
398 let balance = state.data.get("balance").map(String::as_str).unwrap_or("?"); 361 let covered = view.cursor.numbers();
399 let cursor = Self::cursor(state); 362 let balance = view
400 let covered = cursor.numbers(); 363 .balance
364 .map(|balance| balance.to_string())
365 .unwrap_or_else(|| "?".to_string());
401 366
402 let lines = [ 367 let lines = [
403 "=== ROULETTE ===".to_string(), 368 "=== ROULETTE ===".to_string(),
404 format!("Balance: {balance} Kr Chip: {chip} Kr"), 369 format!("Balance: {balance} Kr Chip: {} Kr", view.chip),
405 Self::zero_cell(cursor), 370 Self::zero_cell(view.cursor),
406 Self::number_row(0, &covered), 371 Self::number_row(0, &covered),
407 Self::number_row(1, &covered), 372 Self::number_row(1, &covered),
408 Self::number_row(2, &covered), 373 Self::number_row(2, &covered),
409 Self::dozens_row(cursor), 374 Self::dozens_row(view.cursor),
410 Self::columns_row(cursor), 375 Self::columns_row(view.cursor),
411 Self::outside_row(cursor), 376 Self::outside_row(view.cursor),
412 Self::bets_line(&bets), 377 Self::bets_line(&view.bets),
413 Self::status_line(state), 378 Self::status_line(view),
414 "[ARROWS] Move [ENTER] Bet [C]lear [+/-] Chip [SPACE] Spin [Q] Quit".to_string(), 379 "[ARROWS] Move [ENTER] Bet [C]lear [+/-] Chip [SPACE] Spin [Q] Quit".to_string(),
415 ]; 380 ];
416 lines.join("\r\n") 381 lines.join("\r\n")
417 } 382 }
383}
384
385impl Game for RouletteGame {
386 fn get_name(&self) -> &'static str {
387 "Roulette"
388 }
389
390 fn render(&self, state: &GameState) -> String {
391 Self::render_view(&RouletteView::from_state(state))
392 }
418 393
419 fn render_lines(&self, _state: &GameState) -> usize { 394 fn render_lines(&self, _state: &GameState) -> usize {
420 ROULETTE_BOARD_LINES 395 ROULETTE_BOARD_LINES