summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-28 23:31:54 +0100
committersalmonik <salmonik@tuta.io>2026-10-28 23:31:54 +0100
commit7d18a268547734daf19a740b6533e9c1697baf1a (patch)
tree789f519a19b053b8ad0ff41d2828fe22c06fb21a /src
parentv1.2.0: show the symbol paytable on the slots board (diff)
downloadzagan-bet-7d18a268547734daf19a740b6533e9c1697baf1a.tar.gz
zagan-bet-7d18a268547734daf19a740b6533e9c1697baf1a.zip
v1.2.1: track wrapped board lines and split the paytable
Diffstat (limited to '')
-rw-r--r--src/bin/client/repl.rs8
-rw-r--r--src/client/games/slots.rs10
-rw-r--r--src/client/ui.rs17
-rw-r--r--src/slots.rs18
4 files changed, 44 insertions, 9 deletions
diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs
index 58cb541..1ed3073 100644
--- a/src/bin/client/repl.rs
+++ b/src/bin/client/repl.rs
@@ -17,6 +17,7 @@ use zagan_bet::client::core::{ClientCore, Effect, InteractiveCommand, Key};
17use zagan_bet::client::database; 17use zagan_bet::client::database;
18use zagan_bet::client::ui::{ 18use zagan_bet::client::ui::{
19 display_width, format_pong, host_lines, input_cursor_column, lines_to_move_up, 19 display_width, format_pong, host_lines, input_cursor_column, lines_to_move_up,
20 visual_line_count,
20}; 21};
21use zagan_bet::protocol::io_failure_message; 22use zagan_bet::protocol::io_failure_message;
22 23
@@ -29,7 +30,7 @@ pub struct ClientState {
29 pub default_port: u16, 30 pub default_port: u16,
30 core: ClientCore, 31 core: ClientCore,
31 cleanup_done: Arc<Mutex<bool>>, 32 cleanup_done: Arc<Mutex<bool>>,
32 /// Number of terminal lines currently occupied by the game board. 33 /// Number of visual terminal lines currently occupied by the game board.
33 game_lines: usize, 34 game_lines: usize,
34 /// Text last drawn after the `> ` prompt, used to keep the command echo 35 /// Text last drawn after the `> ` prompt, used to keep the command echo
35 /// in the transcript before printing command output. 36 /// in the transcript before printing command output.
@@ -255,7 +256,10 @@ impl ClientState {
255 .collect(); 256 .collect();
256 print!("{}", rendered.join("\r\n")); 257 print!("{}", rendered.join("\r\n"));
257 stdout().flush().unwrap(); 258 stdout().flush().unwrap();
258 self.game_lines = lines.len().max(1); 259 let width = crossterm::terminal::size()
260 .map(|(columns, _)| columns as usize)
261 .unwrap_or(80);
262 self.game_lines = visual_line_count(lines, width).max(1);
259 } 263 }
260 264
261 fn clear_region(&mut self) { 265 fn clear_region(&mut self) {
diff --git a/src/client/games/slots.rs b/src/client/games/slots.rs
index 7e59919..a417e7d 100644
--- a/src/client/games/slots.rs
+++ b/src/client/games/slots.rs
@@ -1,8 +1,8 @@
1use super::traits::{Game, GamePhase, GameState}; 1use super::traits::{Game, GamePhase, GameState};
2use crate::slots::{paytable_line, REELS, ROWS}; 2use crate::slots::{paytable_lines, REELS, ROWS};
3 3
4/// Height of the game board. It must never depend on the spin result. 4/// Height of the game board. It must never depend on the spin result.
5pub const SLOT_BOARD_LINES: usize = 9; 5pub const SLOT_BOARD_LINES: usize = 10;
6 6
7pub struct SlotsGame; 7pub struct SlotsGame;
8 8
@@ -102,15 +102,17 @@ impl Game for SlotsGame {
102 .unwrap_or("?"); 102 .unwrap_or("?");
103 let winning = Self::highlighted_cells(state); 103 let winning = Self::highlighted_cells(state);
104 let grid = self.render_grid(&reels, &winning); 104 let grid = self.render_grid(&reels, &winning);
105 let paytable = paytable_lines();
105 106
106 format!( 107 format!(
107 "=== SLOTS ===\r\nBet: {} PLN Balance: {} PLN\r\n{}\r\n{}\r\n{}\r\n{}\r\n{}\r\n{}\r\n[+/-] Bet [SPACE] Spin [Q] Quit", 108 "=== SLOTS ===\r\nBet: {} PLN Balance: {} PLN\r\n{}\r\n{}\r\n{}\r\n{}\r\n{}\r\n{}\r\n{}\r\n[+/-] Bet [SPACE] Spin [Q] Quit",
108 state.bet_amount, 109 state.bet_amount,
109 balance, 110 balance,
110 grid[0], 111 grid[0],
111 grid[1], 112 grid[1],
112 grid[2], 113 grid[2],
113 paytable_line(), 114 paytable[0],
115 paytable[1],
114 self.render_status(state), 116 self.render_status(state),
115 self.render_win_lines(state) 117 self.render_win_lines(state)
116 ) 118 )
diff --git a/src/client/ui.rs b/src/client/ui.rs
index 493f660..11cedc7 100644
--- a/src/client/ui.rs
+++ b/src/client/ui.rs
@@ -257,6 +257,23 @@ pub fn input_cursor_column(prompt_len: usize, input_len: usize) -> u16 {
257 .min(u16::MAX as usize) as u16 257 .min(u16::MAX as usize) as u16
258} 258}
259 259
260/// Number of terminal rows a block of logical lines occupies after wrapping.
261/// Empty lines still occupy one row.
262pub fn visual_line_count(lines: &[String], width: usize) -> usize {
263 let width = width.max(1);
264 lines
265 .iter()
266 .map(|line| {
267 let columns = display_width(line);
268 if columns == 0 {
269 1
270 } else {
271 columns.div_ceil(width)
272 }
273 })
274 .sum()
275}
276
260/// Format a ping round-trip time with one decimal place. 277/// Format a ping round-trip time with one decimal place.
261pub fn format_pong(elapsed: std::time::Duration) -> String { 278pub fn format_pong(elapsed: std::time::Duration) -> String {
262 format!("Pong ({:.1}ms)", elapsed.as_secs_f64() * 1000.0) 279 format!("Pong ({:.1}ms)", elapsed.as_secs_f64() * 1000.0)
diff --git a/src/slots.rs b/src/slots.rs
index 561c449..a3b4ef4 100644
--- a/src/slots.rs
+++ b/src/slots.rs
@@ -23,11 +23,23 @@ pub const PAYTABLE: [(char, u64, u64, u64); 6] = [
23 ('S', 9, 30, 150), 23 ('S', 9, 30, 150),
24]; 24];
25 25
26/// One-line paytable rendered under the reels. 26/// Paytable rendered under the reels, split so it fits a narrow terminal.
27pub fn paytable_line() -> String { 27pub fn paytable_lines() -> Vec<String> {
28 let entries: Vec<String> = PAYTABLE 28 let entries: Vec<String> = PAYTABLE
29 .iter() 29 .iter()
30 .map(|(symbol, three, four, five)| format!("{symbol} {three}/{four}/{five}")) 30 .map(|(symbol, three, four, five)| format!("{symbol} {three}/{four}/{five}"))
31 .collect(); 31 .collect();
32 format!("Pays 3/4/5 x bet: {}", entries.join(" ")) 32
33 entries
34 .chunks(3)
35 .enumerate()
36 .map(|(index, chunk)| {
37 let prefix = if index == 0 {
38 "Pays 3/4/5 x bet: "
39 } else {
40 ""
41 };
42 format!("{}{}", prefix, chunk.join(" "))
43 })
44 .collect()
33} 45}