diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-28 23:19:52 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-28 23:19:52 +0100 |
| commit | 2915d826f744612e0d975282f5bddf13270b9f3f (patch) | |
| tree | a2864bfaef91070ac3b4e13b8b731e539a757cc8 /src/client/games | |
| parent | v1.0.0: replace slots with 5x3 reels and five paylines (diff) | |
| download | zagan-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.rs | 11 | ||||
| -rw-r--r-- | src/client/games/response.rs | 18 | ||||
| -rw-r--r-- | src/client/games/slots.rs | 29 |
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 | |||
| 75 | fn 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", |
