diff options
Diffstat (limited to 'src/server/games/slots.rs')
| -rw-r--r-- | src/server/games/slots.rs | 49 |
1 files changed, 41 insertions, 8 deletions
diff --git a/src/server/games/slots.rs b/src/server/games/slots.rs index 2ef1b46..1d71c9d 100644 --- a/src/server/games/slots.rs +++ b/src/server/games/slots.rs | |||
| @@ -22,6 +22,13 @@ struct Payout { | |||
| 22 | five: u64, | 22 | five: u64, |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 26 | pub struct LineWin { | ||
| 27 | pub line: usize, | ||
| 28 | /// Column-major cell indices of the matched symbols. | ||
| 29 | pub cells: Vec<usize>, | ||
| 30 | } | ||
| 31 | |||
| 25 | pub struct SlotsGame { | 32 | pub struct SlotsGame { |
| 26 | payouts: HashMap<char, Payout>, | 33 | payouts: HashMap<char, Payout>, |
| 27 | /// Deterministic grid for tests and demos, in column-major order. | 34 | /// Deterministic grid for tests and demos, in column-major order. |
| @@ -65,10 +72,11 @@ impl SlotsGame { | |||
| 65 | .collect() | 72 | .collect() |
| 66 | } | 73 | } |
| 67 | 74 | ||
| 68 | /// Evaluate one spin. Returns the total win and the 1-based winning lines. | 75 | /// Evaluate one spin. Returns the total win and the winning lines with |
| 69 | pub fn calculate_payout(&self, grid: &[char], bet: u64) -> (u64, Vec<usize>) { | 76 | /// the column-major cells that should be highlighted. |
| 77 | pub fn calculate_payout(&self, grid: &[char], bet: u64) -> (u64, Vec<LineWin>) { | ||
| 70 | let mut total = 0u64; | 78 | let mut total = 0u64; |
| 71 | let mut winning_lines = Vec::new(); | 79 | let mut wins = Vec::new(); |
| 72 | 80 | ||
| 73 | for (index, pattern) in PAYLINES.iter().enumerate() { | 81 | for (index, pattern) in PAYLINES.iter().enumerate() { |
| 74 | let Some(&first) = grid.get(pattern[0]) else { | 82 | let Some(&first) = grid.get(pattern[0]) else { |
| @@ -99,10 +107,19 @@ impl SlotsGame { | |||
| 99 | }; | 107 | }; |
| 100 | 108 | ||
| 101 | total = total.saturating_add(bet.saturating_mul(multiplier)); | 109 | total = total.saturating_add(bet.saturating_mul(multiplier)); |
| 102 | winning_lines.push(index + 1); | 110 | let cells = pattern |
| 111 | .iter() | ||
| 112 | .enumerate() | ||
| 113 | .take(run) | ||
| 114 | .map(|(col, row)| col * ROWS + row) | ||
| 115 | .collect(); | ||
| 116 | wins.push(LineWin { | ||
| 117 | line: index + 1, | ||
| 118 | cells, | ||
| 119 | }); | ||
| 103 | } | 120 | } |
| 104 | 121 | ||
| 105 | (total, winning_lines) | 122 | (total, wins) |
| 106 | } | 123 | } |
| 107 | 124 | ||
| 108 | /// Exact theoretical return to player for the current paytable. | 125 | /// Exact theoretical return to player for the current paytable. |
| @@ -213,12 +230,27 @@ impl Game for SlotsGame { | |||
| 213 | ))); | 230 | ))); |
| 214 | } | 231 | } |
| 215 | 232 | ||
| 216 | let (winnings, winning_lines) = self.calculate_payout(&grid, state.bet_amount); | 233 | let (winnings, wins) = self.calculate_payout(&grid, state.bet_amount); |
| 217 | let win_type = classify_win(winnings, state.bet_amount); | 234 | let win_type = classify_win(winnings, state.bet_amount); |
| 218 | let win_lines = if winning_lines.is_empty() { | 235 | let win_lines = if wins.is_empty() { |
| 236 | "-".to_string() | ||
| 237 | } else { | ||
| 238 | wins.iter() | ||
| 239 | .map(|win| win.line.to_string()) | ||
| 240 | .collect::<Vec<_>>() | ||
| 241 | .join(",") | ||
| 242 | }; | ||
| 243 | |||
| 244 | let mut cells: Vec<usize> = wins | ||
| 245 | .iter() | ||
| 246 | .flat_map(|win| win.cells.iter().copied()) | ||
| 247 | .collect(); | ||
| 248 | cells.sort_unstable(); | ||
| 249 | cells.dedup(); | ||
| 250 | let win_cells = if cells.is_empty() { | ||
| 219 | "-".to_string() | 251 | "-".to_string() |
| 220 | } else { | 252 | } else { |
| 221 | winning_lines | 253 | cells |
| 222 | .iter() | 254 | .iter() |
| 223 | .map(usize::to_string) | 255 | .map(usize::to_string) |
| 224 | .collect::<Vec<_>>() | 256 | .collect::<Vec<_>>() |
| @@ -229,6 +261,7 @@ impl Game for SlotsGame { | |||
| 229 | result_data.insert("reels".to_string(), reel_string.clone()); | 261 | result_data.insert("reels".to_string(), reel_string.clone()); |
| 230 | result_data.insert("win_type".to_string(), win_type.to_string()); | 262 | result_data.insert("win_type".to_string(), win_type.to_string()); |
| 231 | result_data.insert("win_lines".to_string(), win_lines); | 263 | result_data.insert("win_lines".to_string(), win_lines); |
| 264 | result_data.insert("win_cells".to_string(), win_cells); | ||
| 232 | 265 | ||
| 233 | Ok(GameResult { | 266 | Ok(GameResult { |
| 234 | session_id: state.session_id.clone(), | 267 | session_id: state.session_id.clone(), |
