summaryrefslogtreecommitdiffstats
path: root/src/server/games/slots.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-28 23:19:52 +0100
committersalmonik <salmonik@tuta.io>2026-10-28 23:19:52 +0100
commit2915d826f744612e0d975282f5bddf13270b9f3f (patch)
treea2864bfaef91070ac3b4e13b8b731e539a757cc8 /src/server/games/slots.rs
parentv1.0.0: replace slots with 5x3 reels and five paylines (diff)
downloadzagan-bet-2915d826f744612e0d975282f5bddf13270b9f3f.tar.gz
zagan-bet-2915d826f744612e0d975282f5bddf13270b9f3f.zip
v1.1.0: highlight winning symbols on the slots board
Diffstat (limited to '')
-rw-r--r--src/server/games/slots.rs49
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)]
26pub struct LineWin {
27 pub line: usize,
28 /// Column-major cell indices of the matched symbols.
29 pub cells: Vec<usize>,
30}
31
25pub struct SlotsGame { 32pub 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(),