summaryrefslogtreecommitdiffstats
path: root/src/client/games
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/client/games
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/client/games/manager.rs11
-rw-r--r--src/client/games/response.rs18
-rw-r--r--src/client/games/slots.rs29
3 files changed, 48 insertions, 10 deletions
diff --git a/src/client/games/manager.rs b/src/client/games/manager.rs
index 8436142..168c49d 100644
--- a/src/client/games/manager.rs
+++ b/src/client/games/manager.rs
@@ -76,6 +76,7 @@ impl GameManager {
76 session.data.remove("reels"); 76 session.data.remove("reels");
77 session.data.remove("win_type"); 77 session.data.remove("win_type");
78 session.data.remove("win_lines"); 78 session.data.remove("win_lines");
79 session.data.remove("win_cells");
79 session.data.remove("winnings"); 80 session.data.remove("winnings");
80 Ok(()) 81 Ok(())
81 } 82 }
@@ -101,6 +102,15 @@ impl GameManager {
101 .collect::<Vec<_>>() 102 .collect::<Vec<_>>()
102 .join(","), 103 .join(","),
103 ); 104 );
105 session.data.insert(
106 "win_cells".to_string(),
107 result
108 .win_cells
109 .iter()
110 .map(usize::to_string)
111 .collect::<Vec<_>>()
112 .join(","),
113 );
104 session 114 session
105 .data 115 .data
106 .insert("winnings".to_string(), result.winnings.to_string()); 116 .insert("winnings".to_string(), result.winnings.to_string());
@@ -118,6 +128,7 @@ impl GameManager {
118 session.data.remove("reels"); 128 session.data.remove("reels");
119 session.data.remove("win_type"); 129 session.data.remove("win_type");
120 session.data.remove("win_lines"); 130 session.data.remove("win_lines");
131 session.data.remove("win_cells");
121 session.data.remove("winnings"); 132 session.data.remove("winnings");
122 session.data.insert("error".to_string(), message.into()); 133 session.data.insert("error".to_string(), message.into());
123 } 134 }
diff --git a/src/client/games/response.rs b/src/client/games/response.rs
index de22a75..d7f9d45 100644
--- a/src/client/games/response.rs
+++ b/src/client/games/response.rs
@@ -7,6 +7,7 @@ pub struct SpinResult {
7 pub reels: String, 7 pub reels: String,
8 pub win_type: String, 8 pub win_type: String,
9 pub win_lines: Vec<usize>, 9 pub win_lines: Vec<usize>,
10 pub win_cells: Vec<usize>,
10} 11}
11 12
12/// Parse one server reply to `PLAY_START`. 13/// Parse one server reply to `PLAY_START`.
@@ -52,11 +53,11 @@ pub fn parse_spin_response(response: &str) -> Result<SpinResult, String> {
52 53
53 let win_lines = parts 54 let win_lines = parts
54 .get(7) 55 .get(7)
55 .map(|raw| { 56 .map(|raw| parse_indices(raw))
56 raw.split(',') 57 .unwrap_or_default();
57 .filter_map(|value| value.trim().parse::<usize>().ok()) 58 let win_cells = parts
58 .collect() 59 .get(8)
59 }) 60 .map(|raw| parse_indices(raw))
60 .unwrap_or_default(); 61 .unwrap_or_default();
61 62
62 Ok(SpinResult { 63 Ok(SpinResult {
@@ -67,5 +68,12 @@ pub fn parse_spin_response(response: &str) -> Result<SpinResult, String> {
67 reels: parts[5].to_string(), 68 reels: parts[5].to_string(),
68 win_type: parts[6].to_string(), 69 win_type: parts[6].to_string(),
69 win_lines, 70 win_lines,
71 win_cells,
70 }) 72 })
71} 73}
74
75fn parse_indices(raw: &str) -> Vec<usize> {
76 raw.split(',')
77 .filter_map(|value| value.trim().parse::<usize>().ok())
78 .collect()
79}
diff --git a/src/client/games/slots.rs b/src/client/games/slots.rs
index 39c43b2..c367fcb 100644
--- a/src/client/games/slots.rs
+++ b/src/client/games/slots.rs
@@ -13,22 +13,40 @@ impl SlotsGame {
13 Self 13 Self
14 } 14 }
15 15
16 fn cell(chars: &[char], row: usize, col: usize) -> char { 16 fn cell(chars: &[char], row: usize, col: usize, winning: &[usize]) -> String {
17 if chars.len() == REELS * ROWS { 17 let symbol = if chars.len() == REELS * ROWS {
18 chars.get(col * ROWS + row).copied().unwrap_or('?') 18 chars.get(col * ROWS + row).copied().unwrap_or('?')
19 } else if chars.len() == 3 && row == 0 { 19 } else if chars.len() == 3 && row == 0 {
20 // Legacy 3-symbol server: degrade to the first row. 20 // Legacy 3-symbol server: degrade to the first row.
21 chars.get(col).copied().unwrap_or('?') 21 chars.get(col).copied().unwrap_or('?')
22 } else { 22 } else {
23 '?' 23 '?'
24 };
25
26 if winning.contains(&(col * ROWS + row)) {
27 format!("{{ {} }}", symbol)
28 } else {
29 format!("[ {} ]", symbol)
24 } 30 }
25 } 31 }
26 32
27 fn render_grid(&self, reels: &str) -> [String; ROWS] { 33 fn highlighted_cells(state: &GameState) -> Vec<usize> {
34 state
35 .data
36 .get("win_cells")
37 .map(|raw| {
38 raw.split(',')
39 .filter_map(|value| value.trim().parse::<usize>().ok())
40 .collect()
41 })
42 .unwrap_or_default()
43 }
44
45 fn render_grid(&self, reels: &str, winning: &[usize]) -> [String; ROWS] {
28 let chars: Vec<char> = reels.chars().collect(); 46 let chars: Vec<char> = reels.chars().collect();
29 std::array::from_fn(|row| { 47 std::array::from_fn(|row| {
30 (0..REELS) 48 (0..REELS)
31 .map(|col| format!("[ {} ]", Self::cell(&chars, row, col))) 49 .map(|col| Self::cell(&chars, row, col, winning))
32 .collect::<Vec<_>>() 50 .collect::<Vec<_>>()
33 .join(" ") 51 .join(" ")
34 }) 52 })
@@ -84,7 +102,8 @@ impl Game for SlotsGame {
84 .get("balance") 102 .get("balance")
85 .map(String::as_str) 103 .map(String::as_str)
86 .unwrap_or("?"); 104 .unwrap_or("?");
87 let grid = self.render_grid(&reels); 105 let winning = Self::highlighted_cells(state);
106 let grid = self.render_grid(&reels, &winning);
88 107
89 format!( 108 format!(
90 "=== SLOTS ===\r\nBet: {} PLN Lines: {} Balance: {} PLN\r\n{}\r\n{}\r\n{}\r\n\r\n{}\r\n{}\r\n[+/-] Bet [SPACE] Spin [Q] Quit", 109 "=== SLOTS ===\r\nBet: {} PLN Lines: {} Balance: {} PLN\r\n{}\r\n{}\r\n{}\r\n\r\n{}\r\n{}\r\n[+/-] Bet [SPACE] Spin [Q] Quit",