From ea5e7d336719571e51ef14478ab603e71126e3d8 Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 28 Oct 2026 22:28:05 +0100 Subject: v0.2.2: split client/server logic into the library --- src/bin/server/games/slots.rs | 179 ------------------------------------------ 1 file changed, 179 deletions(-) delete mode 100644 src/bin/server/games/slots.rs (limited to 'src/bin/server/games/slots.rs') diff --git a/src/bin/server/games/slots.rs b/src/bin/server/games/slots.rs deleted file mode 100644 index 04ac175..0000000 --- a/src/bin/server/games/slots.rs +++ /dev/null @@ -1,179 +0,0 @@ -use crate::games::traits::{Game, GameError, GamePhase, GameResult, GameState}; -use rand::Rng; -use std::collections::HashMap; - -pub struct SlotsGame { - symbols: Vec, - payouts: HashMap, -} - -impl SlotsGame { - pub fn new() -> Self { - let symbols = vec!['C', 'L', 'O', 'G', 'D', 'S']; - let mut payouts = HashMap::new(); - - // Balanced payouts: EV = 210/216 ≈ 0.972 (2.8% house edge) - payouts.insert("CCC".to_string(), 2); - payouts.insert("LLL".to_string(), 3); - payouts.insert("OOO".to_string(), 5); - payouts.insert("GGG".to_string(), 8); - payouts.insert("DDD".to_string(), 14); - payouts.insert("SSS".to_string(), 28); - - payouts.insert("CC".to_string(), 1); - payouts.insert("LL".to_string(), 1); - payouts.insert("OO".to_string(), 1); - payouts.insert("GG".to_string(), 2); - payouts.insert("DD".to_string(), 2); - payouts.insert("SS".to_string(), 3); - - Self { symbols, payouts } - } - - fn spin_reels(&self) -> Vec { - let mut rng = rand::thread_rng(); - (0..3) - .map(|_| self.symbols[rng.gen_range(0..self.symbols.len())]) - .collect() - } - - pub fn calculate_payout(&self, reels: &[char], bet: u64) -> u64 { - let reel_string: String = reels.iter().collect(); - - if let Some(&multiplier) = self.payouts.get(&reel_string) { - return bet * multiplier; - } - - // Check all three pairs including non-adjacent (e.g. [C,D,C]) - let pairs: [(usize, usize); 3] = [(0, 1), (1, 2), (0, 2)]; - let mut best: u64 = 0; - for &(a, b) in &pairs { - if reels[a] == reels[b] { - let two_of_kind = format!("{}{}", reels[a], reels[a]); - if let Some(&multiplier) = self.payouts.get(&two_of_kind) { - best = best.max(bet * multiplier); - } - } - } - best - } -} - -impl Game for SlotsGame { - fn game_type(&self) -> &'static str { - "slots" - } - - fn get_name(&self) -> &'static str { - "Slots" - } - - fn get_description(&self) -> &'static str { - "Spin the reels and match symbols to win!" - } - - fn prepare_state(&self, username: &str, bet_amount: u64) -> GameState { - let mut data = HashMap::new(); - data.insert("username".to_string(), username.to_string()); - data.insert("status".to_string(), "ready".to_string()); - - GameState { - session_id: String::new(), - game_type: "slots".to_string(), - bet_amount, - phase: GamePhase::WaitingToStart, - data, - } - } - - fn process_action( - &self, - state: &GameState, - action: &str, - _params: &[&str], - ) -> Result { - if state.phase == GamePhase::Finished { - return Err(GameError::GameOver); - } - - match action { - "spin" => { - if state.phase != GamePhase::WaitingToStart && state.phase != GamePhase::Playing { - return Err(GameError::InvalidAction( - "Cannot spin in current phase".to_string(), - )); - } - - let reels = self.spin_reels(); - let reel_string: String = reels.iter().collect(); - - let mut new_data = state.data.clone(); - new_data.insert("reels".to_string(), reel_string.clone()); - new_data.insert("status".to_string(), "spun".to_string()); - - let is_jackpot = reels[0] == reels[1] && reels[1] == reels[2]; - let is_two = reels[0] == reels[1] || reels[1] == reels[2] || reels[0] == reels[2]; - new_data.insert( - "win_type".to_string(), - if is_jackpot { - "jackpot".to_string() - } else if is_two { - "two".to_string() - } else { - "none".to_string() - }, - ); - - Ok(GameState { - session_id: state.session_id.clone(), - game_type: state.game_type.clone(), - bet_amount: state.bet_amount, - phase: GamePhase::Finished, - data: new_data, - }) - } - _ => Err(GameError::InvalidAction(format!( - "Unknown action: {}", - action - ))), - } - } - - fn calculate_result(&self, state: &GameState) -> Result { - if state.phase != GamePhase::Finished { - return Err(GameError::InternalError("Game not finished".to_string())); - } - - let reel_string = state - .data - .get("reels") - .ok_or(GameError::InternalError("No reels found".to_string()))?; - - let reels: Vec = reel_string.chars().collect(); - let winnings = self.calculate_payout(&reels, state.bet_amount); - - let mut result_data = HashMap::new(); - result_data.insert("reels".to_string(), reel_string.clone()); - result_data.insert( - "payout_multiplier".to_string(), - if winnings > 0 && state.bet_amount > 0 { - (winnings / state.bet_amount).to_string() - } else { - "0".to_string() - }, - ); - result_data.insert( - "win_type".to_string(), - state.data.get("win_type").cloned().unwrap_or_default(), - ); - - Ok(GameResult { - session_id: state.session_id.clone(), - game_type: state.game_type.clone(), - bet_amount: state.bet_amount, - winnings, - final_balance: 0, - result_data, - }) - } -} -- cgit v1.2.3