use super::traits::{Game, GamePhase, GameState}; /// Height of the game board. It must never depend on the spin result, or an /// in-place redraw would move to the wrong line. pub const SLOT_BOARD_LINES: usize = 7; pub struct SlotsGame; impl SlotsGame { pub fn new() -> Self { Self } fn render_reels(&self, reels: &str) -> String { let mut chars = reels.chars(); format!( "[ {} ] [ {} ] [ {} ]", chars.next().unwrap_or('?'), chars.next().unwrap_or('?'), chars.next().unwrap_or('?') ) } /// The fifth board line: current status, result, or error. fn render_status(&self, state: &GameState) -> String { if let Some(error) = state.data.get("error") { return format!("ERROR: {error}"); } if state.phase == GamePhase::Playing { return "Spinning...".to_string(); } if state.phase == GamePhase::Finished { let winnings = state .data .get("winnings") .map(String::as_str) .unwrap_or("0"); return match state.data.get("win_type").map(String::as_str) { Some("jackpot") => format!("JACKPOT! Winnings: {winnings} PLN"), Some("two") => format!("Two of a kind! Winnings: {winnings} PLN"), _ => "No win.".to_string(), }; } "Press [SPACE] to spin!".to_string() } } impl Game for SlotsGame { fn get_name(&self) -> &'static str { "Slots" } fn render(&self, state: &GameState) -> String { let reels = state.data.get("reels").cloned().unwrap_or_default(); let balance = state .data .get("balance") .map(String::as_str) .unwrap_or("?"); format!( "=== SLOTS ===\r\nBet: {} PLN Balance: {} PLN\r\n{}\r\n\r\n{}\r\n\r\n[+/-] Adjust Bet [Q] Quit", state.bet_amount, balance, self.render_reels(&reels), self.render_status(state) ) } fn render_lines(&self, _state: &GameState) -> usize { SLOT_BOARD_LINES } fn get_help(&self) -> String { "SLOTS GAME\n\ =========\n\ Match symbols to win!\n\ \n\ Payouts:\n\ Three of a kind: 2x-28x bet\n\ Two of a kind: 1x-3x bet\n\ \n\ Symbols: C=Cherry, L=Lemon, O=Orange, G=Grapes, D=Diamond, S=Seven\n\ \n\ Controls:\n\ SPACE - Spin the reels\n\ +/- - Adjust bet amount\n\ Q - Exit the game" .to_string() } }