From 7d18a268547734daf19a740b6533e9c1697baf1a Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 28 Oct 2026 23:31:54 +0100 Subject: v1.2.1: track wrapped board lines and split the paytable --- src/bin/client/repl.rs | 8 ++++++-- src/client/games/slots.rs | 10 ++++++---- src/client/ui.rs | 17 +++++++++++++++++ src/slots.rs | 18 +++++++++++++++--- 4 files changed, 44 insertions(+), 9 deletions(-) (limited to 'src') 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}; use zagan_bet::client::database; use zagan_bet::client::ui::{ display_width, format_pong, host_lines, input_cursor_column, lines_to_move_up, + visual_line_count, }; use zagan_bet::protocol::io_failure_message; @@ -29,7 +30,7 @@ pub struct ClientState { pub default_port: u16, core: ClientCore, cleanup_done: Arc>, - /// Number of terminal lines currently occupied by the game board. + /// Number of visual terminal lines currently occupied by the game board. game_lines: usize, /// Text last drawn after the `> ` prompt, used to keep the command echo /// in the transcript before printing command output. @@ -255,7 +256,10 @@ impl ClientState { .collect(); print!("{}", rendered.join("\r\n")); stdout().flush().unwrap(); - self.game_lines = lines.len().max(1); + let width = crossterm::terminal::size() + .map(|(columns, _)| columns as usize) + .unwrap_or(80); + self.game_lines = visual_line_count(lines, width).max(1); } 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 @@ use super::traits::{Game, GamePhase, GameState}; -use crate::slots::{paytable_line, REELS, ROWS}; +use crate::slots::{paytable_lines, REELS, ROWS}; /// Height of the game board. It must never depend on the spin result. -pub const SLOT_BOARD_LINES: usize = 9; +pub const SLOT_BOARD_LINES: usize = 10; pub struct SlotsGame; @@ -102,15 +102,17 @@ impl Game for SlotsGame { .unwrap_or("?"); let winning = Self::highlighted_cells(state); let grid = self.render_grid(&reels, &winning); + let paytable = paytable_lines(); format!( - "=== 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", + "=== 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", state.bet_amount, balance, grid[0], grid[1], grid[2], - paytable_line(), + paytable[0], + paytable[1], self.render_status(state), self.render_win_lines(state) ) 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 { .min(u16::MAX as usize) as u16 } +/// Number of terminal rows a block of logical lines occupies after wrapping. +/// Empty lines still occupy one row. +pub fn visual_line_count(lines: &[String], width: usize) -> usize { + let width = width.max(1); + lines + .iter() + .map(|line| { + let columns = display_width(line); + if columns == 0 { + 1 + } else { + columns.div_ceil(width) + } + }) + .sum() +} + /// Format a ping round-trip time with one decimal place. pub fn format_pong(elapsed: std::time::Duration) -> String { 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] = [ ('S', 9, 30, 150), ]; -/// One-line paytable rendered under the reels. -pub fn paytable_line() -> String { +/// Paytable rendered under the reels, split so it fits a narrow terminal. +pub fn paytable_lines() -> Vec { let entries: Vec = PAYTABLE .iter() .map(|(symbol, three, four, five)| format!("{symbol} {three}/{four}/{five}")) .collect(); - format!("Pays 3/4/5 x bet: {}", entries.join(" ")) + + entries + .chunks(3) + .enumerate() + .map(|(index, chunk)| { + let prefix = if index == 0 { + "Pays 3/4/5 x bet: " + } else { + "" + }; + format!("{}{}", prefix, chunk.join(" ")) + }) + .collect() } -- cgit v1.2.3