summaryrefslogtreecommitdiffstats
path: root/src/bin/client/repl.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-10-29 17:18:34 +0100
committersalmonik <salmonik@tuta.io>2026-10-29 17:18:34 +0100
commitd9f1fe513e6f7696809d3a9b4d44ca485fe99c97 (patch)
tree9da99200fdfe004a47056d10340b3f59c7ccc52e /src/bin/client/repl.rs
parentv2.0.0: reset plays audit to a generic per-game schema (diff)
downloadzagan-bet-d9f1fe513e6f7696809d3a9b4d44ca485fe99c97.tar.gz
zagan-bet-d9f1fe513e6f7696809d3a9b4d44ca485fe99c97.zip
v2.1.0: add European roulette with split bets and wheel animation
Diffstat (limited to '')
-rw-r--r--src/bin/client/repl.rs54
1 files changed, 54 insertions, 0 deletions
diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs
index 1ed3073..e028cc2 100644
--- a/src/bin/client/repl.rs
+++ b/src/bin/client/repl.rs
@@ -20,6 +20,7 @@ use zagan_bet::client::ui::{
20 visual_line_count, 20 visual_line_count,
21}; 21};
22use zagan_bet::protocol::io_failure_message; 22use zagan_bet::protocol::io_failure_message;
23use zagan_bet::roulette::WHEEL;
23 24
24use crate::connection::TlsStream; 25use crate::connection::TlsStream;
25 26
@@ -150,6 +151,16 @@ impl ClientState {
150 } 151 }
151 } 152 }
152 Effect::ReplaceRegion(lines) => self.draw_region(&lines), 153 Effect::ReplaceRegion(lines) => self.draw_region(&lines),
154 Effect::AnimateRoulette {
155 winning,
156 final_lines,
157 } => {
158 self.animate_wheel(winning);
159 self.draw_region(&final_lines);
160 while crossterm::event::poll(std::time::Duration::ZERO).unwrap_or(false) {
161 let _ = crossterm::event::read();
162 }
163 }
153 Effect::ClearRegion => self.clear_region(), 164 Effect::ClearRegion => self.clear_region(),
154 Effect::SetPrompt { text, cursor_col } => self.draw_prompt(&text, cursor_col), 165 Effect::SetPrompt { text, cursor_col } => self.draw_prompt(&text, cursor_col),
155 Effect::SendLine(line) => { 166 Effect::SendLine(line) => {
@@ -276,6 +287,49 @@ impl ClientState {
276 self.game_lines = 0; 287 self.game_lines = 0;
277 } 288 }
278 289
290 /// CS:GO-style wheel strip: the ball marker is fixed and the numbers move
291 /// left-to-right under it, ending with the winning number in the center.
292 fn animate_wheel(&mut self, winning: u8) {
293 use std::thread;
294 use std::time::Duration;
295
296 let target = WHEEL
297 .iter()
298 .position(|number| *number == winning)
299 .unwrap_or(0) as f64;
300 let wheel_len = WHEEL.len() as f64;
301 let start = target - 4.0 * wheel_len - 7.0;
302 let frames = 32;
303
304 for frame in 0..frames {
305 let t = frame as f64 / (frames - 1) as f64;
306 let ease = 1.0 - (1.0 - t).powi(3);
307 let center = (start + (target - start) * ease).round() as isize;
308
309 let strip = (0..9)
310 .map(|offset| {
311 let index = (center - 4 + offset).rem_euclid(WHEEL.len() as isize) as usize;
312 format!("[ {:>2} ]", WHEEL[index])
313 })
314 .collect::<Vec<_>>()
315 .join(" ");
316 let marker = format!("{}▼", " ".repeat(4 * 7 + 3));
317 let lines = vec![
318 "=== ROULETTE ===".to_string(),
319 strip,
320 marker,
321 "Spinning...".to_string(),
322 ];
323
324 self.draw_region(&lines);
325 thread::sleep(Duration::from_millis(35));
326 }
327
328 // Let the winning number sit under the static ball before the result
329 // board replaces the wheel.
330 thread::sleep(Duration::from_millis(450));
331 }
332
279 fn draw_prompt(&mut self, text: &str, cursor_col: u16) { 333 fn draw_prompt(&mut self, text: &str, cursor_col: u16) {
280 execute!(stdout(), MoveToColumn(0), Clear(ClearType::CurrentLine)).unwrap(); 334 execute!(stdout(), MoveToColumn(0), Clear(ClearType::CurrentLine)).unwrap();
281 print!("{}", "> ".cyan()); 335 print!("{}", "> ".cyan());