diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-29 17:23:23 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-29 17:23:23 +0100 |
| commit | ffa1de686ce4ec6e3548047a2c5a158b99dc8af3 (patch) | |
| tree | a125c9b40ee0a59674460c40802cd69430787eec | |
| parent | v2.1.0: add European roulette with split bets and wheel animation (diff) | |
| download | zagan-bet-ffa1de686ce4ec6e3548047a2c5a158b99dc8af3.tar.gz zagan-bet-ffa1de686ce4ec6e3548047a2c5a158b99dc8af3.zip | |
v2.1.1: color-code roulette numbers red and green
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | docs/GAMES.md | 3 | ||||
| -rw-r--r-- | docs/TESTING.md | 4 | ||||
| -rw-r--r-- | src/bin/client/repl.rs | 76 |
4 files changed, 81 insertions, 4 deletions
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | name = "zagan-bet" | 2 | name = "zagan-bet" |
| 3 | version = "2.1.0" | 3 | version = "2.1.1" |
| 4 | edition = "2024" | 4 | edition = "2024" |
| 5 | 5 | ||
| 6 | [dependencies] | 6 | [dependencies] |
diff --git a/docs/GAMES.md b/docs/GAMES.md index 52ab1bf..12b0e7c 100644 --- a/docs/GAMES.md +++ b/docs/GAMES.md | |||
| @@ -25,6 +25,9 @@ redesign the loop first. | |||
| 25 | - Bets can be split across all of those; every winning bet is added together. | 25 | - Bets can be split across all of those; every winning bet is added together. |
| 26 | - Straight pays 35:1 (36x returned), outside bets pay 1:1 (2x returned). | 26 | - Straight pays 35:1 (36x returned), outside bets pay 1:1 (2x returned). |
| 27 | - 0 loses all outside bets. RTP is 36/37 = 97.30%. | 27 | - 0 loses all outside bets. RTP is 36/37 = 97.30%. |
| 28 | - Numbers are color-coded in the TUI: red numbers are red, 0 is green, and | ||
| 29 | black numbers stay at the default terminal color. Bet amounts and balances | ||
| 30 | are not colored. | ||
| 28 | 31 | ||
| 29 | Client controls inside roulette: | 32 | Client controls inside roulette: |
| 30 | 33 | ||
diff --git a/docs/TESTING.md b/docs/TESTING.md index 2dcb01f..d944b42 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md | |||
| @@ -74,7 +74,9 @@ Use an 80-column terminal because several output lines are width-sensitive. | |||
| 74 | 4. Check a split result: | 74 | 4. Check a split result: |
| 75 | - `17 black odd`: black+odd pay, red+even lose, straight 17 pays 36x. | 75 | - `17 black odd`: black+odd pay, red+even lose, straight 17 pays 36x. |
| 76 | - `0`: all outside bets lose; a straight 0 pays 36x. | 76 | - `0`: all outside bets lose; a straight 0 pays 36x. |
| 77 | 5. The result board shows the winning number/color/parity, the payout, and the | 77 | 5. Numbers are color-coded: red numbers red, 0 green, black default. Check the |
| 78 | bets line, the rolling strip, and the winning number. | ||
| 79 | 6. The result board shows the winning number/color/parity, the payout, and the | ||
| 78 | new balance. The next spin reuses the placed bets. | 80 | new balance. The next spin reuses the placed bets. |
| 79 | 81 | ||
| 80 | ## Manual server checks | 82 | ## Manual server checks |
diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs index e028cc2..f85af47 100644 --- a/src/bin/client/repl.rs +++ b/src/bin/client/repl.rs | |||
| @@ -20,7 +20,7 @@ use zagan_bet::client::ui::{ | |||
| 20 | visual_line_count, | 20 | visual_line_count, |
| 21 | }; | 21 | }; |
| 22 | use zagan_bet::protocol::io_failure_message; | 22 | use zagan_bet::protocol::io_failure_message; |
| 23 | use zagan_bet::roulette::WHEEL; | 23 | use zagan_bet::roulette::{color as roulette_color, WHEEL}; |
| 24 | 24 | ||
| 25 | use crate::connection::TlsStream; | 25 | use crate::connection::TlsStream; |
| 26 | 26 | ||
| @@ -247,11 +247,22 @@ impl ClientState { | |||
| 247 | execute!(stdout(), MoveUp(lines_to_move_up(self.game_lines))).unwrap(); | 247 | execute!(stdout(), MoveUp(lines_to_move_up(self.game_lines))).unwrap(); |
| 248 | } | 248 | } |
| 249 | execute!(stdout(), MoveToColumn(0), Clear(ClearType::FromCursorDown)).unwrap(); | 249 | execute!(stdout(), MoveToColumn(0), Clear(ClearType::FromCursorDown)).unwrap(); |
| 250 | let roulette = lines | ||
| 251 | .first() | ||
| 252 | .is_some_and(|first| first.contains("ROULETTE")); | ||
| 250 | let rendered: Vec<String> = lines | 253 | let rendered: Vec<String> = lines |
| 251 | .iter() | 254 | .iter() |
| 252 | .map(|line| { | 255 | .map(|line| { |
| 253 | if line.starts_with("===") { | 256 | if line.starts_with("===") { |
| 254 | line.as_str().bold().cyan().to_string() | 257 | line.as_str().bold().cyan().to_string() |
| 258 | } else if roulette { | ||
| 259 | if line.starts_with("Won ") { | ||
| 260 | line.as_str().bold().green().to_string() | ||
| 261 | } else if line.starts_with("ERROR:") { | ||
| 262 | line.as_str().red().to_string() | ||
| 263 | } else { | ||
| 264 | style_roulette_numbers(line) | ||
| 265 | } | ||
| 255 | } else if line.contains('{') { | 266 | } else if line.contains('{') { |
| 256 | style_winning_cells(line) | 267 | style_winning_cells(line) |
| 257 | } else if line.contains("WIN!") || line.starts_with("Won ") { | 268 | } else if line.contains("WIN!") || line.starts_with("Won ") { |
| @@ -309,7 +320,13 @@ impl ClientState { | |||
| 309 | let strip = (0..9) | 320 | let strip = (0..9) |
| 310 | .map(|offset| { | 321 | .map(|offset| { |
| 311 | let index = (center - 4 + offset).rem_euclid(WHEEL.len() as isize) as usize; | 322 | let index = (center - 4 + offset).rem_euclid(WHEEL.len() as isize) as usize; |
| 312 | format!("[ {:>2} ]", WHEEL[index]) | 323 | let number = WHEEL[index]; |
| 324 | let cell = format!("[ {:>2} ]", number); | ||
| 325 | match roulette_color(number) { | ||
| 326 | "red" => cell.red().to_string(), | ||
| 327 | "green" => cell.green().to_string(), | ||
| 328 | _ => cell, | ||
| 329 | } | ||
| 313 | }) | 330 | }) |
| 314 | .collect::<Vec<_>>() | 331 | .collect::<Vec<_>>() |
| 315 | .join(" "); | 332 | .join(" "); |
| @@ -498,6 +515,61 @@ impl ClientState { | |||
| 498 | } | 515 | } |
| 499 | } | 516 | } |
| 500 | 517 | ||
| 518 | fn style_roulette_number(text: &str, number: u8) -> String { | ||
| 519 | match roulette_color(number) { | ||
| 520 | "red" => text.red().to_string(), | ||
| 521 | "green" => text.green().to_string(), | ||
| 522 | _ => text.to_string(), | ||
| 523 | } | ||
| 524 | } | ||
| 525 | |||
| 526 | fn style_roulette_numbers(line: &str) -> String { | ||
| 527 | if line.starts_with("Bets:") { | ||
| 528 | let mut out = String::new(); | ||
| 529 | let mut rest = line; | ||
| 530 | while let Some(index) = rest.find('#') { | ||
| 531 | out.push_str(&rest[..=index]); | ||
| 532 | let after = &rest[index + 1..]; | ||
| 533 | let digits: String = after | ||
| 534 | .chars() | ||
| 535 | .take_while(|character| character.is_ascii_digit()) | ||
| 536 | .collect(); | ||
| 537 | if let Ok(number) = digits.parse::<u8>() { | ||
| 538 | if number <= 36 { | ||
| 539 | out.push_str(&style_roulette_number(&digits, number)); | ||
| 540 | } else { | ||
| 541 | out.push_str(&digits); | ||
| 542 | } | ||
| 543 | } | ||
| 544 | rest = &after[digits.len()..]; | ||
| 545 | } | ||
| 546 | out.push_str(rest); | ||
| 547 | return out; | ||
| 548 | } | ||
| 549 | |||
| 550 | for label in ["Winning: ", "Number: "] { | ||
| 551 | if let Some(index) = line.find(label) { | ||
| 552 | let start = index + label.len(); | ||
| 553 | let digits: String = line[start..] | ||
| 554 | .chars() | ||
| 555 | .take_while(|character| character.is_ascii_digit()) | ||
| 556 | .collect(); | ||
| 557 | if let Ok(number) = digits.parse::<u8>() { | ||
| 558 | if number <= 36 { | ||
| 559 | return format!( | ||
| 560 | "{}{}{}", | ||
| 561 | &line[..start], | ||
| 562 | style_roulette_number(&digits, number), | ||
| 563 | &line[start + digits.len()..] | ||
| 564 | ); | ||
| 565 | } | ||
| 566 | } | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | line.to_string() | ||
| 571 | } | ||
| 572 | |||
| 501 | fn style_winning_cells(line: &str) -> String { | 573 | fn style_winning_cells(line: &str) -> String { |
| 502 | let chars: Vec<char> = line.chars().collect(); | 574 | let chars: Vec<char> = line.chars().collect(); |
| 503 | let mut out = String::new(); | 575 | let mut out = String::new(); |
