diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-29 23:08:25 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-29 23:08:25 +0100 |
| commit | 80d9aa13251f7f5c3d3eeaa877bc79dac4435341 (patch) | |
| tree | 62e8b341749fa0644b9877ae621583929a9c1a29 | |
| parent | v3.1.0: warn on shrink, but let a live round finish (diff) | |
| download | zagan-bet-80d9aa13251f7f5c3d3eeaa877bc79dac4435341.tar.gz zagan-bet-80d9aa13251f7f5c3d3eeaa877bc79dac4435341.zip | |
PLN changed to Kromer
| -rw-r--r-- | docs/GAMES.md | 6 | ||||
| -rw-r--r-- | docs/SLOTS.md | 6 | ||||
| -rw-r--r-- | src/client/core.rs | 16 | ||||
| -rw-r--r-- | src/client/games/blackjack.rs | 10 | ||||
| -rw-r--r-- | src/client/games/crash.rs | 6 | ||||
| -rw-r--r-- | src/client/games/roulette.rs | 25 | ||||
| -rw-r--r-- | src/client/games/slots.rs | 10 | ||||
| -rw-r--r-- | src/client/ui.rs | 8 | ||||
| -rw-r--r-- | src/crash.rs | 2 | ||||
| -rw-r--r-- | tests/client_core.rs | 199 | ||||
| -rw-r--r-- | tests/crash.rs | 8 |
11 files changed, 153 insertions, 143 deletions
diff --git a/docs/GAMES.md b/docs/GAMES.md index 364cfef..8cc668f 100644 --- a/docs/GAMES.md +++ b/docs/GAMES.md | |||
| @@ -37,8 +37,8 @@ and broadcast, which is a different product. | |||
| 37 | - Because `P(crash = 1.00x) = 1%`, an instant cashout returns the stake 99% of | 37 | - Because `P(crash = 1.00x) = 1%`, an instant cashout returns the stake 99% of |
| 38 | the time and nothing 1% of the time. There is no risk-free window, which is | 38 | the time and nothing 1% of the time. There is no risk-free window, which is |
| 39 | why there is no cashout lockout. | 39 | why there is no cashout lockout. |
| 40 | - Payouts are `floor(bet * multiplier)` in whole PLN, so anything under 1.10x | 40 | - Payouts are `floor(bet * multiplier)` in whole Kr, so anything under 1.10x |
| 41 | on a 10 PLN bet returns 10. | 41 | on a 10 Kr bet returns 10. |
| 42 | - When the crash time passes, the server pushes `CRASH_BUSTED` on the same | 42 | - When the crash time passes, the server pushes `CRASH_BUSTED` on the same |
| 43 | connection without being asked. The client's event loop polls for keys and | 43 | connection without being asked. The client's event loop polls for keys and |
| 44 | drains the socket every 25ms. | 44 | drains the socket every 25ms. |
| @@ -58,7 +58,7 @@ Q forfeit an active round (bet is lost), or leave when idle | |||
| 58 | 58 | ||
| 59 | - Six-deck shoe, shuffled fresh for every hand and discarded afterwards. | 59 | - Six-deck shoe, shuffled fresh for every hand and discarded afterwards. |
| 60 | - Dealer stands on all 17s (including soft 17). | 60 | - Dealer stands on all 17s (including soft 17). |
| 61 | - Blackjack pays 3:2, rounded down to whole PLN. | 61 | - Blackjack pays 3:2, rounded down to whole Kr. |
| 62 | - Hit, stand, and double; no split, insurance, or surrender. | 62 | - Hit, stand, and double; no split, insurance, or surrender. |
| 63 | - The bet is reserved when the hand starts. Leaving an active hand with Q, or | 63 | - The bet is reserved when the hand starts. Leaving an active hand with Q, or |
| 64 | disconnecting mid-hand, forfeits the reserved bet; it is not refunded. A | 64 | disconnecting mid-hand, forfeits the reserved bet; it is not refunded. A |
diff --git a/docs/SLOTS.md b/docs/SLOTS.md index 1dbaba5..403f616 100644 --- a/docs/SLOTS.md +++ b/docs/SLOTS.md | |||
| @@ -80,13 +80,13 @@ The board is ten lines: | |||
| 80 | 80 | ||
| 81 | ``` | 81 | ``` |
| 82 | === SLOTS === | 82 | === SLOTS === |
| 83 | Bet: 10 PLN Balance: 1000 PLN | 83 | Bet: 10 Kr Balance: 1000 Kr |
| 84 | { G } [ L ] [ S ] [ O ] [ G ] | 84 | { G } [ L ] [ S ] [ O ] [ G ] |
| 85 | [ D ] { G } [ L ] [ S ] [ C ] | 85 | [ D ] { G } [ L ] [ S ] [ C ] |
| 86 | [ O ] [ O ] { G } [ L ] [ O ] | 86 | [ O ] [ O ] { G } [ L ] [ O ] |
| 87 | Pays bet x symbol x shape | 87 | Pays bet x symbol x shape |
| 88 | Symbol C1 L2 O3 G4 D6 S10 Shape 3:1 4:3 5:14 | 88 | Symbol C1 L2 O3 G4 D6 S10 Shape 3:1 4:3 5:14 |
| 89 | Won 40 PLN | 89 | Won 40 Kr |
| 90 | Winning lines: 4 (V) | 90 | Winning lines: 4 (V) |
| 91 | [+/-] Bet [SPACE] Spin [Q] Quit | 91 | [+/-] Bet [SPACE] Spin [Q] Quit |
| 92 | ``` | 92 | ``` |
| @@ -118,7 +118,7 @@ In the example above, line 4 (V) contains `G G G S G`: | |||
| 118 | - reels 1-3 are `G`, so three `G` symbols pay; | 118 | - reels 1-3 are `G`, so three `G` symbols pay; |
| 119 | - reel 4 is `S`, which breaks the run, so the `G` on reel 5 does not count; | 119 | - reel 4 is `S`, which breaks the run, so the `G` on reel 5 does not count; |
| 120 | - `G` has symbol multiplier 4 and 3-of-a-kind has shape multiplier 1, so the | 120 | - `G` has symbol multiplier 4 and 3-of-a-kind has shape multiplier 1, so the |
| 121 | win is `10 x 4 x 1 = 40 PLN`. | 121 | win is `10 x 4 x 1 = 40 Kr`. |
| 122 | 122 | ||
| 123 | A diagonal of `L` symbols that is not one of the five paylines does not win, | 123 | A diagonal of `L` symbols that is not one of the five paylines does not win, |
| 124 | and a run must start on reel 1. That is why only the three `G` cells are | 124 | and a run must start on reel 1. That is why only the three `G` cells are |
diff --git a/src/client/core.rs b/src/client/core.rs index 95fa050..54a6434 100644 --- a/src/client/core.rs +++ b/src/client/core.rs | |||
| @@ -2,12 +2,12 @@ use crate::client::games::manager::GameManager; | |||
| 2 | use crate::client::games::response::{ | 2 | use crate::client::games::response::{ |
| 3 | parse_blackjack_response, parse_crash_response, parse_roulette_response, parse_spin_response, | 3 | parse_blackjack_response, parse_crash_response, parse_roulette_response, parse_spin_response, |
| 4 | }; | 4 | }; |
| 5 | use crate::client::games::roulette::Direction; | ||
| 5 | use crate::client::ui::{ | 6 | use crate::client::ui::{ |
| 6 | command_names, completion_lines, display_width, help_lines, input_cursor_column, parse_bet_arg, | 7 | command_names, completion_lines, display_width, help_lines, input_cursor_column, parse_bet_arg, |
| 7 | record_history, | 8 | record_history, |
| 8 | }; | 9 | }; |
| 9 | use crate::crash::{multiplier_at, CrashState}; | 10 | use crate::crash::{CrashState, multiplier_at}; |
| 10 | use crate::client::games::roulette::Direction; | ||
| 11 | use crate::roulette::Bets; | 11 | use crate::roulette::Bets; |
| 12 | use std::time::{Duration, Instant}; | 12 | use std::time::{Duration, Instant}; |
| 13 | 13 | ||
| @@ -240,7 +240,7 @@ impl ClientCore { | |||
| 240 | Ok(state) => { | 240 | Ok(state) => { |
| 241 | self.game.end_game(); | 241 | self.game.end_game(); |
| 242 | let message = format!( | 242 | let message = format!( |
| 243 | "Game exited. Active hand forfeited ({} PLN lost).", | 243 | "Game exited. Active hand forfeited ({} Kr lost).", |
| 244 | state.bet | 244 | state.bet |
| 245 | ); | 245 | ); |
| 246 | vec![Effect::ClearRegion, Effect::Print(vec![message])] | 246 | vec![Effect::ClearRegion, Effect::Print(vec![message])] |
| @@ -273,7 +273,7 @@ impl ClientCore { | |||
| 273 | Ok(state) => { | 273 | Ok(state) => { |
| 274 | self.crash_started = None; | 274 | self.crash_started = None; |
| 275 | self.game.end_game(); | 275 | self.game.end_game(); |
| 276 | let message = format!("Game exited. Round forfeited ({} PLN lost).", state.bet); | 276 | let message = format!("Game exited. Round forfeited ({} Kr lost).", state.bet); |
| 277 | vec![Effect::ClearRegion, Effect::Print(vec![message])] | 277 | vec![Effect::ClearRegion, Effect::Print(vec![message])] |
| 278 | } | 278 | } |
| 279 | Err(_) if self.game.crash_status() != "running" => { | 279 | Err(_) if self.game.crash_status() != "running" => { |
| @@ -865,9 +865,9 @@ impl ClientCore { | |||
| 865 | Some(fingerprint) => { | 865 | Some(fingerprint) => { |
| 866 | effects.push(Effect::Print(vec![format!("Fingerprint: {fingerprint}")])) | 866 | effects.push(Effect::Print(vec![format!("Fingerprint: {fingerprint}")])) |
| 867 | } | 867 | } |
| 868 | None => effects.push(Effect::Print( | 868 | None => effects.push(Effect::Print(vec![ |
| 869 | vec!["Fingerprint: (not trusted)".to_string()], | 869 | "Fingerprint: (not trusted)".to_string(), |
| 870 | )), | 870 | ])), |
| 871 | } | 871 | } |
| 872 | 872 | ||
| 873 | self.pending = Some(Pending::Info); | 873 | self.pending = Some(Pending::Info); |
| @@ -953,7 +953,7 @@ fn info_lines(reply: &str) -> Vec<String> { | |||
| 953 | vec![ | 953 | vec![ |
| 954 | format!("User: {}", parts[1]), | 954 | format!("User: {}", parts[1]), |
| 955 | format!("Account: {}", parts[2]), | 955 | format!("Account: {}", parts[2]), |
| 956 | format!("Balance: {} PLN", parts[3]), | 956 | format!("Balance: {} Kr", parts[3]), |
| 957 | ] | 957 | ] |
| 958 | } else { | 958 | } else { |
| 959 | vec![reply.trim().to_string()] | 959 | vec![reply.trim().to_string()] |
diff --git a/src/client/games/blackjack.rs b/src/client/games/blackjack.rs index d5f45b3..8fa5693 100644 --- a/src/client/games/blackjack.rs +++ b/src/client/games/blackjack.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use super::traits::{Game, GameState}; | 1 | use super::traits::{Game, GameState}; |
| 2 | use crate::blackjack::{card_label, BlackjackState}; | 2 | use crate::blackjack::{BlackjackState, card_label}; |
| 3 | 3 | ||
| 4 | pub const BLACKJACK_BOARD_LINES: usize = 6; | 4 | pub const BLACKJACK_BOARD_LINES: usize = 6; |
| 5 | 5 | ||
| @@ -33,8 +33,8 @@ impl BlackjackGame { | |||
| 33 | 33 | ||
| 34 | fn result_text(result: &str, winnings: u64) -> String { | 34 | fn result_text(result: &str, winnings: u64) -> String { |
| 35 | match result { | 35 | match result { |
| 36 | "blackjack" => format!("BLACKJACK! Won {winnings} PLN"), | 36 | "blackjack" => format!("BLACKJACK! Won {winnings} Kr"), |
| 37 | "win" => format!("Win! Won {winnings} PLN"), | 37 | "win" => format!("Win! Won {winnings} Kr"), |
| 38 | "push" => "Push. Bet returned.".to_string(), | 38 | "push" => "Push. Bet returned.".to_string(), |
| 39 | "lose" => "Dealer wins.".to_string(), | 39 | "lose" => "Dealer wins.".to_string(), |
| 40 | "bust" => "Bust.".to_string(), | 40 | "bust" => "Bust.".to_string(), |
| @@ -65,7 +65,7 @@ impl Game for BlackjackGame { | |||
| 65 | .map(|hand| hand.bet) | 65 | .map(|hand| hand.bet) |
| 66 | .unwrap_or(state.bet_amount); | 66 | .unwrap_or(state.bet_amount); |
| 67 | return format!( | 67 | return format!( |
| 68 | "=== BLACKJACK ===\r\nBalance: {balance} PLN Bet: {bet} PLN\r\n\r\n\r\nERROR: {error}\r\n[Q] Quit" | 68 | "=== BLACKJACK ===\r\nBalance: {balance} Kr Bet: {bet} Kr\r\n\r\n\r\nERROR: {error}\r\n[Q] Quit" |
| 69 | ); | 69 | ); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| @@ -120,7 +120,7 @@ impl Game for BlackjackGame { | |||
| 120 | }; | 120 | }; |
| 121 | 121 | ||
| 122 | format!( | 122 | format!( |
| 123 | "=== BLACKJACK ===\r\nBalance: {} PLN Bet: {} PLN\r\nDealer: {} {}\r\nYou: {} {}\r\n{}\r\n{}", | 123 | "=== BLACKJACK ===\r\nBalance: {} Kr Bet: {} Kr\r\nDealer: {} {}\r\nYou: {} {}\r\n{}\r\n{}", |
| 124 | balance, bet, dealer, dealer_total, player, player_total, text, controls | 124 | balance, bet, dealer, dealer_total, player, player_total, text, controls |
| 125 | ) | 125 | ) |
| 126 | } | 126 | } |
diff --git a/src/client/games/crash.rs b/src/client/games/crash.rs index 19733f2..383e807 100644 --- a/src/client/games/crash.rs +++ b/src/client/games/crash.rs | |||
| @@ -19,7 +19,7 @@ impl CrashGame { | |||
| 19 | "starting" => "Starting...".to_string(), | 19 | "starting" => "Starting...".to_string(), |
| 20 | "running" => "Rising... cash out before it crashes.".to_string(), | 20 | "running" => "Rising... cash out before it crashes.".to_string(), |
| 21 | "cashed" => format!( | 21 | "cashed" => format!( |
| 22 | "Cashed out at {}x Won {} PLN", | 22 | "Cashed out at {}x Won {} Kr", |
| 23 | Self::value(state, "multiplier").unwrap_or("1.00"), | 23 | Self::value(state, "multiplier").unwrap_or("1.00"), |
| 24 | Self::value(state, "payout").unwrap_or("0") | 24 | Self::value(state, "payout").unwrap_or("0") |
| 25 | ), | 25 | ), |
| @@ -55,13 +55,13 @@ impl Game for CrashGame { | |||
| 55 | 55 | ||
| 56 | if let Some(error) = Self::value(state, "error") { | 56 | if let Some(error) = Self::value(state, "error") { |
| 57 | return format!( | 57 | return format!( |
| 58 | "=== CRASH ===\r\nBalance: {balance} PLN Bet: {} PLN\r\n\r\nERROR: {error}\r\n\r\n[SPACE] Play [+/=/-] Bet [Q] Quit", | 58 | "=== CRASH ===\r\nBalance: {balance} Kr Bet: {} Kr\r\n\r\nERROR: {error}\r\n\r\n[SPACE] Play [+/=/-] Bet [Q] Quit", |
| 59 | state.bet_amount | 59 | state.bet_amount |
| 60 | ); | 60 | ); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | format!( | 63 | format!( |
| 64 | "=== CRASH ===\r\nBalance: {} PLN Bet: {} PLN\r\nMultiplier: {}x\r\nPotential: {} PLN\r\n{}\r\n{}", | 64 | "=== CRASH ===\r\nBalance: {} Kr Bet: {} Kr\r\nMultiplier: {}x\r\nPotential: {} Kr\r\n{}\r\n{}", |
| 65 | balance, | 65 | balance, |
| 66 | state.bet_amount, | 66 | state.bet_amount, |
| 67 | multiplier, | 67 | multiplier, |
diff --git a/src/client/games/roulette.rs b/src/client/games/roulette.rs index 3cb1c58..96f8791 100644 --- a/src/client/games/roulette.rs +++ b/src/client/games/roulette.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use super::traits::{Game, GameState}; | 1 | use super::traits::{Game, GameState}; |
| 2 | use crate::roulette::{is_even, is_odd, BetTarget, Bets, Outside}; | 2 | use crate::roulette::{BetTarget, Bets, Outside, is_even, is_odd}; |
| 3 | 3 | ||
| 4 | pub const ROULETTE_BOARD_LINES: usize = 12; | 4 | pub const ROULETTE_BOARD_LINES: usize = 12; |
| 5 | 5 | ||
| @@ -96,7 +96,9 @@ impl Cursor { | |||
| 96 | 96 | ||
| 97 | /// Numbers the cursor would cover, for highlighting the felt. | 97 | /// Numbers the cursor would cover, for highlighting the felt. |
| 98 | pub fn numbers(&self) -> Vec<u8> { | 98 | pub fn numbers(&self) -> Vec<u8> { |
| 99 | self.target().map(|target| target.numbers()).unwrap_or_default() | 99 | self.target() |
| 100 | .map(|target| target.numbers()) | ||
| 101 | .unwrap_or_default() | ||
| 100 | } | 102 | } |
| 101 | 103 | ||
| 102 | pub fn moved(&self, direction: Direction) -> Cursor { | 104 | pub fn moved(&self, direction: Direction) -> Cursor { |
| @@ -130,9 +132,7 @@ impl Cursor { | |||
| 130 | (Cursor::Column(index), Direction::Up) => Cursor::Dozen(index), | 132 | (Cursor::Column(index), Direction::Up) => Cursor::Dozen(index), |
| 131 | (Cursor::Column(index), Direction::Down) => Cursor::Outside(index * 2), | 133 | (Cursor::Column(index), Direction::Down) => Cursor::Outside(index * 2), |
| 132 | 134 | ||
| 133 | (Cursor::Outside(index), Direction::Left) => { | 135 | (Cursor::Outside(index), Direction::Left) => Cursor::Outside(index.saturating_sub(1)), |
| 134 | Cursor::Outside(index.saturating_sub(1)) | ||
| 135 | } | ||
| 136 | (Cursor::Outside(index), Direction::Right) => { | 136 | (Cursor::Outside(index), Direction::Right) => { |
| 137 | Cursor::Outside((index + 1).min(OUTSIDE_ORDER.len() - 1)) | 137 | Cursor::Outside((index + 1).min(OUTSIDE_ORDER.len() - 1)) |
| 138 | } | 138 | } |
| @@ -249,7 +249,11 @@ impl RouletteGame { | |||
| 249 | .iter() | 249 | .iter() |
| 250 | .enumerate() | 250 | .enumerate() |
| 251 | .map(|(index, kind)| { | 251 | .map(|(index, kind)| { |
| 252 | Self::wide_cell(kind.label(), OUTSIDE_WIDTH, cursor == Cursor::Outside(index)) | 252 | Self::wide_cell( |
| 253 | kind.label(), | ||
| 254 | OUTSIDE_WIDTH, | ||
| 255 | cursor == Cursor::Outside(index), | ||
| 256 | ) | ||
| 253 | }) | 257 | }) |
| 254 | .collect() | 258 | .collect() |
| 255 | } | 259 | } |
| @@ -268,7 +272,7 @@ impl RouletteGame { | |||
| 268 | if list.chars().count() > 40 { | 272 | if list.chars().count() > 40 { |
| 269 | list = list.chars().take(39).collect::<String>() + "…"; | 273 | list = list.chars().take(39).collect::<String>() + "…"; |
| 270 | } | 274 | } |
| 271 | format!("Bets: {list} Total: {} PLN", bets.total()) | 275 | format!("Bets: {list} Total: {} Kr", bets.total()) |
| 272 | } | 276 | } |
| 273 | 277 | ||
| 274 | fn status_line(state: &GameState) -> String { | 278 | fn status_line(state: &GameState) -> String { |
| @@ -309,7 +313,7 @@ impl RouletteGame { | |||
| 309 | format!("No win at {number} {color}{parity}") | 313 | format!("No win at {number} {color}{parity}") |
| 310 | } else { | 314 | } else { |
| 311 | let profit = winnings.parse::<u64>().unwrap_or(0).saturating_sub(stake); | 315 | let profit = winnings.parse::<u64>().unwrap_or(0).saturating_sub(stake); |
| 312 | format!("Won {winnings} PLN at {number} {color}{parity} (+{profit})") | 316 | format!("Won {winnings} Kr at {number} {color}{parity} (+{profit})") |
| 313 | } | 317 | } |
| 314 | } | 318 | } |
| 315 | Some("spinning") => "Spinning...".to_string(), | 319 | Some("spinning") => "Spinning...".to_string(), |
| @@ -332,7 +336,7 @@ impl Game for RouletteGame { | |||
| 332 | 336 | ||
| 333 | let lines = [ | 337 | let lines = [ |
| 334 | "=== ROULETTE ===".to_string(), | 338 | "=== ROULETTE ===".to_string(), |
| 335 | format!("Balance: {balance} PLN Chip: {chip} PLN"), | 339 | format!("Balance: {balance} Kr Chip: {chip} Kr"), |
| 336 | Self::zero_cell(cursor), | 340 | Self::zero_cell(cursor), |
| 337 | Self::number_row(0, &covered), | 341 | Self::number_row(0, &covered), |
| 338 | Self::number_row(1, &covered), | 342 | Self::number_row(1, &covered), |
| @@ -342,8 +346,7 @@ impl Game for RouletteGame { | |||
| 342 | Self::outside_row(cursor), | 346 | Self::outside_row(cursor), |
| 343 | Self::bets_line(&bets), | 347 | Self::bets_line(&bets), |
| 344 | Self::status_line(state), | 348 | Self::status_line(state), |
| 345 | "[ARROWS] Move [ENTER] Bet [C]lear [+/-] Chip [SPACE] Spin [Q] Quit" | 349 | "[ARROWS] Move [ENTER] Bet [C]lear [+/-] Chip [SPACE] Spin [Q] Quit".to_string(), |
| 346 | .to_string(), | ||
| 347 | ]; | 350 | ]; |
| 348 | lines.join("\r\n") | 351 | lines.join("\r\n") |
| 349 | } | 352 | } |
diff --git a/src/client/games/slots.rs b/src/client/games/slots.rs index 2ec354e..5bffc4c 100644 --- a/src/client/games/slots.rs +++ b/src/client/games/slots.rs | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | use super::traits::{Game, GamePhase, GameState}; | 1 | use super::traits::{Game, GamePhase, GameState}; |
| 2 | use crate::slots::{paytable_lines, LINE_NAMES, REELS, ROWS}; | 2 | use crate::slots::{LINE_NAMES, REELS, ROWS, paytable_lines}; |
| 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. |
| 5 | pub const SLOT_BOARD_LINES: usize = 10; | 5 | pub const SLOT_BOARD_LINES: usize = 10; |
| @@ -65,9 +65,9 @@ impl SlotsGame { | |||
| 65 | .unwrap_or("0"); | 65 | .unwrap_or("0"); |
| 66 | if winnings != "0" { | 66 | if winnings != "0" { |
| 67 | return match state.data.get("win_type").map(String::as_str) { | 67 | return match state.data.get("win_type").map(String::as_str) { |
| 68 | Some("mega") => format!("MEGA WIN! {winnings} PLN"), | 68 | Some("mega") => format!("MEGA WIN! {winnings} Kr"), |
| 69 | Some("big") => format!("BIG WIN! {winnings} PLN"), | 69 | Some("big") => format!("BIG WIN! {winnings} Kr"), |
| 70 | _ => format!("Won {winnings} PLN"), | 70 | _ => format!("Won {winnings} Kr"), |
| 71 | }; | 71 | }; |
| 72 | } | 72 | } |
| 73 | return "No win.".to_string(); | 73 | return "No win.".to_string(); |
| @@ -115,7 +115,7 @@ impl Game for SlotsGame { | |||
| 115 | let paytable = paytable_lines(); | 115 | let paytable = paytable_lines(); |
| 116 | 116 | ||
| 117 | format!( | 117 | format!( |
| 118 | "=== 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", | 118 | "=== SLOTS ===\r\nBet: {} Kr Balance: {} Kr\r\n{}\r\n{}\r\n{}\r\n{}\r\n{}\r\n{}\r\n{}\r\n[+/-] Bet [SPACE] Spin [Q] Quit", |
| 119 | state.bet_amount, | 119 | state.bet_amount, |
| 120 | balance, | 120 | balance, |
| 121 | grid[0], | 121 | grid[0], |
diff --git a/src/client/ui.rs b/src/client/ui.rs index ca001c2..e7f510b 100644 --- a/src/client/ui.rs +++ b/src/client/ui.rs | |||
| @@ -145,7 +145,7 @@ pub fn parse_bet_arg(raw: &str) -> Result<u64, String> { | |||
| 145 | .parse::<u64>() | 145 | .parse::<u64>() |
| 146 | .map_err(|_| format!("Invalid bet: {raw}"))?; | 146 | .map_err(|_| format!("Invalid bet: {raw}"))?; |
| 147 | if bet < 10 { | 147 | if bet < 10 { |
| 148 | return Err("Bet must be at least 10 PLN".to_string()); | 148 | return Err("Bet must be at least 10 Kr".to_string()); |
| 149 | } | 149 | } |
| 150 | Ok(bet) | 150 | Ok(bet) |
| 151 | } | 151 | } |
| @@ -189,11 +189,7 @@ fn char_width(c: char) -> usize { | |||
| 189 | if is_zero_width(cp) || is_combining(cp) { | 189 | if is_zero_width(cp) || is_combining(cp) { |
| 190 | return 0; | 190 | return 0; |
| 191 | } | 191 | } |
| 192 | if is_wide(cp) { | 192 | if is_wide(cp) { 2 } else { 1 } |
| 193 | 2 | ||
| 194 | } else { | ||
| 195 | 1 | ||
| 196 | } | ||
| 197 | } | 193 | } |
| 198 | 194 | ||
| 199 | fn is_zero_width(cp: u32) -> bool { | 195 | fn is_zero_width(cp: u32) -> bool { |
diff --git a/src/crash.rs b/src/crash.rs index 2936b99..9d6e98c 100644 --- a/src/crash.rs +++ b/src/crash.rs | |||
| @@ -47,7 +47,7 @@ pub fn crash_point_from_uniform(u: f64) -> f64 { | |||
| 47 | ((1.0 - HOUSE_EDGE) / (1.0 - u)).clamp(1.0, MAX_MULTIPLIER) | 47 | ((1.0 - HOUSE_EDGE) / (1.0 - u)).clamp(1.0, MAX_MULTIPLIER) |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | /// Whole-PLN cashout value. Balances are integers, so the payout is floored. | 50 | /// Whole-Kr cashout value. Balances are integers, so the payout is floored. |
| 51 | pub fn payout(bet: u64, multiplier: f64) -> u64 { | 51 | pub fn payout(bet: u64, multiplier: f64) -> u64 { |
| 52 | if bet == 0 || !multiplier.is_finite() || multiplier <= 0.0 { | 52 | if bet == 0 || !multiplier.is_finite() || multiplier <= 0.0 { |
| 53 | return 0; | 53 | return 0; |
diff --git a/tests/client_core.rs b/tests/client_core.rs index a7c5716..bb8772e 100644 --- a/tests/client_core.rs +++ b/tests/client_core.rs | |||
| @@ -173,7 +173,7 @@ fn play_shows_a_ten_line_board_and_fetches_the_balance() { | |||
| 173 | h.line("play slots 50"); | 173 | h.line("play slots 50"); |
| 174 | 174 | ||
| 175 | assert_eq!(h.board().len(), 10); | 175 | assert_eq!(h.board().len(), 10); |
| 176 | assert!(h.board()[1].contains("Bet: 50 PLN")); | 176 | assert!(h.board()[1].contains("Bet: 50 Kr")); |
| 177 | assert!(!h.board()[1].contains("Lines:")); | 177 | assert!(!h.board()[1].contains("Lines:")); |
| 178 | assert!(h.board()[5].contains("Pays bet x symbol x shape")); | 178 | assert!(h.board()[5].contains("Pays bet x symbol x shape")); |
| 179 | assert!(h.board()[6].contains("Symbol C1 L2 O3 G4 D6 S10")); | 179 | assert!(h.board()[6].contains("Symbol C1 L2 O3 G4 D6 S10")); |
| @@ -182,7 +182,7 @@ fn play_shows_a_ten_line_board_and_fetches_the_balance() { | |||
| 182 | 182 | ||
| 183 | h.reply("INFO alice 1 1000"); | 183 | h.reply("INFO alice 1 1000"); |
| 184 | assert_eq!(h.board().len(), 10); | 184 | assert_eq!(h.board().len(), 10); |
| 185 | assert!(h.board()[1].contains("Balance: 1000 PLN")); | 185 | assert!(h.board()[1].contains("Balance: 1000 Kr")); |
| 186 | } | 186 | } |
| 187 | 187 | ||
| 188 | #[test] | 188 | #[test] |
| @@ -200,13 +200,14 @@ fn spin_replaces_the_board_and_keeps_the_controls_line() { | |||
| 200 | 200 | ||
| 201 | h.reply("GAME_RESULT session-1 50 50 1000 CCCCLLCLODGSDGS win 1 0,3,6"); | 201 | h.reply("GAME_RESULT session-1 50 50 1000 CCCCLLCLODGSDGS win 1 0,3,6"); |
| 202 | assert_eq!(h.board().len(), 10); | 202 | assert_eq!(h.board().len(), 10); |
| 203 | assert!(h | 203 | assert!( |
| 204 | .board() | 204 | h.board() |
| 205 | .get(2) | 205 | .get(2) |
| 206 | .is_some_and(|line| line.contains("{ C } { C } { C } [ D ] [ D ]"))); | 206 | .is_some_and(|line| line.contains("{ C } { C } { C } [ D ] [ D ]")) |
| 207 | assert!(h.board()[7].contains("Won 50 PLN")); | 207 | ); |
| 208 | assert!(h.board()[7].contains("Won 50 Kr")); | ||
| 208 | assert!(h.board()[8].contains("Winning lines: 1 (top)")); | 209 | assert!(h.board()[8].contains("Winning lines: 1 (top)")); |
| 209 | assert!(h.board()[1].contains("Balance: 1000 PLN")); | 210 | assert!(h.board()[1].contains("Balance: 1000 Kr")); |
| 210 | assert!(h.board()[9].contains("[Q] Quit")); | 211 | assert!(h.board()[9].contains("[Q] Quit")); |
| 211 | assert_eq!(h.screen.lines.len(), lines_before_spin); | 212 | assert_eq!(h.screen.lines.len(), lines_before_spin); |
| 212 | } | 213 | } |
| @@ -219,7 +220,7 @@ fn equals_increases_the_bet_like_plus() { | |||
| 219 | h.key(Key::Char('=')); | 220 | h.key(Key::Char('=')); |
| 220 | 221 | ||
| 221 | assert_eq!(h.board().len(), 10); | 222 | assert_eq!(h.board().len(), 10); |
| 222 | assert!(h.board()[1].contains("Bet: 20 PLN")); | 223 | assert!(h.board()[1].contains("Bet: 20 Kr")); |
| 223 | } | 224 | } |
| 224 | 225 | ||
| 225 | #[test] | 226 | #[test] |
| @@ -233,11 +234,12 @@ fn changing_the_bet_after_a_spin_does_not_leave_stale_lines() { | |||
| 233 | h.key(Key::Char('+')); | 234 | h.key(Key::Char('+')); |
| 234 | 235 | ||
| 235 | assert_eq!(h.board().len(), 10); | 236 | assert_eq!(h.board().len(), 10); |
| 236 | assert!(h.board()[1].contains("Bet: 60 PLN")); | 237 | assert!(h.board()[1].contains("Bet: 60 Kr")); |
| 237 | assert!(h | 238 | assert!( |
| 238 | .board() | 239 | h.board() |
| 239 | .get(2) | 240 | .get(2) |
| 240 | .is_some_and(|line| line.contains("{ C } { C } { C } [ D ] [ D ]"))); | 241 | .is_some_and(|line| line.contains("{ C } { C } { C } [ D ] [ D ]")) |
| 242 | ); | ||
| 241 | assert_eq!(h.screen.lines.len(), lines_before_bet); | 243 | assert_eq!(h.screen.lines.len(), lines_before_bet); |
| 242 | assert_eq!( | 244 | assert_eq!( |
| 243 | h.screen | 245 | h.screen |
| @@ -272,9 +274,11 @@ fn a_mega_win_shows_all_winning_lines() { | |||
| 272 | h.reply(&format!("GAME_RESULT session-1 700 10 1690 {ALL_C} mega 1,2,3,4,5 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14")); | 274 | h.reply(&format!("GAME_RESULT session-1 700 10 1690 {ALL_C} mega 1,2,3,4,5 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14")); |
| 273 | 275 | ||
| 274 | assert_eq!(h.board().len(), 10); | 276 | assert_eq!(h.board().len(), 10); |
| 275 | assert!(h.board()[7].contains("MEGA WIN! 700 PLN")); | 277 | assert!(h.board()[7].contains("MEGA WIN! 700 Kr")); |
| 276 | assert!(h.board()[8] | 278 | assert!( |
| 277 | .contains("Winning lines: 1 (top), 2 (middle), 3 (bottom), 4 (V), 5 (inverted V)")); | 279 | h.board()[8] |
| 280 | .contains("Winning lines: 1 (top), 2 (middle), 3 (bottom), 4 (V), 5 (inverted V)") | ||
| 281 | ); | ||
| 278 | assert_eq!(h.board()[2].matches("{ C }").count(), 5); | 282 | assert_eq!(h.board()[2].matches("{ C }").count(), 5); |
| 279 | } | 283 | } |
| 280 | 284 | ||
| @@ -371,11 +375,12 @@ fn tab_completion_list_wraps() { | |||
| 371 | 375 | ||
| 372 | h.key(Key::Tab); | 376 | h.key(Key::Tab); |
| 373 | 377 | ||
| 374 | assert!(h | 378 | assert!( |
| 375 | .screen | 379 | h.screen |
| 376 | .lines | 380 | .lines |
| 377 | .iter() | 381 | .iter() |
| 378 | .any(|line| line == "Possible completions:")); | 382 | .any(|line| line == "Possible completions:") |
| 383 | ); | ||
| 379 | for line in h | 384 | for line in h |
| 380 | .screen | 385 | .screen |
| 381 | .lines | 386 | .lines |
| @@ -398,7 +403,7 @@ fn play_without_a_bet_uses_the_default() { | |||
| 398 | h.line("play slots"); | 403 | h.line("play slots"); |
| 399 | 404 | ||
| 400 | assert_eq!(h.board().len(), 10); | 405 | assert_eq!(h.board().len(), 10); |
| 401 | assert!(h.board()[1].contains("Bet: 10 PLN")); | 406 | assert!(h.board()[1].contains("Bet: 10 Kr")); |
| 402 | } | 407 | } |
| 403 | 408 | ||
| 404 | #[test] | 409 | #[test] |
| @@ -407,11 +412,12 @@ fn an_invalid_bet_does_not_start_a_game() { | |||
| 407 | h.line("play slots 5"); | 412 | h.line("play slots 5"); |
| 408 | 413 | ||
| 409 | assert!(h.board().is_empty()); | 414 | assert!(h.board().is_empty()); |
| 410 | assert!(h | 415 | assert!( |
| 411 | .screen | 416 | h.screen |
| 412 | .lines | 417 | .lines |
| 413 | .iter() | 418 | .iter() |
| 414 | .any(|line| line.contains("Bet must be at least 10 PLN"))); | 419 | .any(|line| line.contains("Bet must be at least 10 Kr")) |
| 420 | ); | ||
| 415 | } | 421 | } |
| 416 | 422 | ||
| 417 | #[test] | 423 | #[test] |
| @@ -438,20 +444,22 @@ fn play_requires_a_connection_and_a_login() { | |||
| 438 | let mut h = Harness::new(); | 444 | let mut h = Harness::new(); |
| 439 | h.core.set_connected(false); | 445 | h.core.set_connected(false); |
| 440 | h.line("play slots"); | 446 | h.line("play slots"); |
| 441 | assert!(h | 447 | assert!( |
| 442 | .screen | 448 | h.screen |
| 443 | .lines | 449 | .lines |
| 444 | .iter() | 450 | .iter() |
| 445 | .any(|line| line.contains("Not connected"))); | 451 | .any(|line| line.contains("Not connected")) |
| 452 | ); | ||
| 446 | 453 | ||
| 447 | let mut h = Harness::new(); | 454 | let mut h = Harness::new(); |
| 448 | h.core.set_logged_in(None); | 455 | h.core.set_logged_in(None); |
| 449 | h.line("play slots"); | 456 | h.line("play slots"); |
| 450 | assert!(h | 457 | assert!( |
| 451 | .screen | 458 | h.screen |
| 452 | .lines | 459 | .lines |
| 453 | .iter() | 460 | .iter() |
| 454 | .any(|line| line.contains("Not logged in"))); | 461 | .any(|line| line.contains("Not logged in")) |
| 462 | ); | ||
| 455 | } | 463 | } |
| 456 | 464 | ||
| 457 | #[test] | 465 | #[test] |
| @@ -489,26 +497,24 @@ fn info_shows_the_host_and_the_server_fields() { | |||
| 489 | ); | 497 | ); |
| 490 | 498 | ||
| 491 | h.line("info"); | 499 | h.line("info"); |
| 492 | assert!(h | 500 | assert!( |
| 493 | .screen | 501 | h.screen |
| 494 | .lines | 502 | .lines |
| 495 | .iter() | 503 | .iter() |
| 496 | .any(|line| line.contains("Connected to: 127.0.0.1:11525"))); | 504 | .any(|line| line.contains("Connected to: 127.0.0.1:11525")) |
| 497 | assert!(h | 505 | ); |
| 498 | .screen | 506 | assert!( |
| 499 | .lines | 507 | h.screen |
| 500 | .iter() | 508 | .lines |
| 501 | .any(|line| line.contains("Fingerprint: deadbeef"))); | 509 | .iter() |
| 510 | .any(|line| line.contains("Fingerprint: deadbeef")) | ||
| 511 | ); | ||
| 502 | 512 | ||
| 503 | h.reply("INFO alice 42 1234"); | 513 | h.reply("INFO alice 42 1234"); |
| 504 | 514 | ||
| 505 | assert!(h.screen.lines.iter().any(|line| line == "User: alice")); | 515 | assert!(h.screen.lines.iter().any(|line| line == "User: alice")); |
| 506 | assert!(h.screen.lines.iter().any(|line| line == "Account: 42")); | 516 | assert!(h.screen.lines.iter().any(|line| line == "Account: 42")); |
| 507 | assert!(h | 517 | assert!(h.screen.lines.iter().any(|line| line == "Balance: 1234 Kr")); |
| 508 | .screen | ||
| 509 | .lines | ||
| 510 | .iter() | ||
| 511 | .any(|line| line == "Balance: 1234 PLN")); | ||
| 512 | } | 518 | } |
| 513 | 519 | ||
| 514 | #[test] | 520 | #[test] |
| @@ -518,11 +524,12 @@ fn info_without_a_connection_does_not_send() { | |||
| 518 | 524 | ||
| 519 | h.line("info"); | 525 | h.line("info"); |
| 520 | 526 | ||
| 521 | assert!(h | 527 | assert!( |
| 522 | .screen | 528 | h.screen |
| 523 | .lines | 529 | .lines |
| 524 | .iter() | 530 | .iter() |
| 525 | .any(|line| line.contains("Not connected to any host"))); | 531 | .any(|line| line.contains("Not connected to any host")) |
| 532 | ); | ||
| 526 | assert!(h.sent.is_empty()); | 533 | assert!(h.sent.is_empty()); |
| 527 | } | 534 | } |
| 528 | 535 | ||
| @@ -554,11 +561,12 @@ fn disconnect_clears_connection_state() { | |||
| 554 | assert!(h.screen.lines.iter().any(|line| line == "Disconnected")); | 561 | assert!(h.screen.lines.iter().any(|line| line == "Disconnected")); |
| 555 | 562 | ||
| 556 | h.line("play slots"); | 563 | h.line("play slots"); |
| 557 | assert!(h | 564 | assert!( |
| 558 | .screen | 565 | h.screen |
| 559 | .lines | 566 | .lines |
| 560 | .iter() | 567 | .iter() |
| 561 | .any(|line| line.contains("Not connected"))); | 568 | .any(|line| line.contains("Not connected")) |
| 569 | ); | ||
| 562 | } | 570 | } |
| 563 | 571 | ||
| 564 | #[test] | 572 | #[test] |
| @@ -597,8 +605,8 @@ fn roulette_cursor_places_bets_and_spins() { | |||
| 597 | 605 | ||
| 598 | assert_eq!(h.board().len(), 12); | 606 | assert_eq!(h.board().len(), 12); |
| 599 | assert_eq!(h.board()[0], "=== ROULETTE ==="); | 607 | assert_eq!(h.board()[0], "=== ROULETTE ==="); |
| 600 | assert!(h.board()[1].contains("Balance: 1000 PLN")); | 608 | assert!(h.board()[1].contains("Balance: 1000 Kr")); |
| 601 | assert!(h.board()[1].contains("Chip: 10 PLN")); | 609 | assert!(h.board()[1].contains("Chip: 10 Kr")); |
| 602 | assert!(h.board()[9].contains("Bets: (none)")); | 610 | assert!(h.board()[9].contains("Bets: (none)")); |
| 603 | // The cursor starts on number 1, the bottom-left corner. | 611 | // The cursor starts on number 1, the bottom-left corner. |
| 604 | assert!(h.board()[5].contains("[ 1 ]")); | 612 | assert!(h.board()[5].contains("[ 1 ]")); |
| @@ -611,14 +619,14 @@ fn roulette_cursor_places_bets_and_spins() { | |||
| 611 | 619 | ||
| 612 | h.key(Key::Enter); | 620 | h.key(Key::Enter); |
| 613 | assert!(h.board()[9].contains("2/3 10")); | 621 | assert!(h.board()[9].contains("2/3 10")); |
| 614 | assert!(h.board()[9].contains("Total: 10 PLN")); | 622 | assert!(h.board()[9].contains("Total: 10 Kr")); |
| 615 | 623 | ||
| 616 | // Back down to number 1 and place a straight. | 624 | // Back down to number 1 and place a straight. |
| 617 | h.key(Key::Down); | 625 | h.key(Key::Down); |
| 618 | h.key(Key::Down); | 626 | h.key(Key::Down); |
| 619 | h.key(Key::Enter); | 627 | h.key(Key::Enter); |
| 620 | assert!(h.board()[9].contains("1 10")); | 628 | assert!(h.board()[9].contains("1 10")); |
| 621 | assert!(h.board()[9].contains("Total: 20 PLN")); | 629 | assert!(h.board()[9].contains("Total: 20 Kr")); |
| 622 | 630 | ||
| 623 | h.key(Key::Char(' ')); | 631 | h.key(Key::Char(' ')); |
| 624 | let request = h | 632 | let request = h |
| @@ -637,8 +645,8 @@ fn roulette_cursor_places_bets_and_spins() { | |||
| 637 | )); | 645 | )); |
| 638 | 646 | ||
| 639 | assert_eq!(h.board().len(), 12); | 647 | assert_eq!(h.board().len(), 12); |
| 640 | assert!(h.board()[1].contains("Balance: 1160 PLN")); | 648 | assert!(h.board()[1].contains("Balance: 1160 Kr")); |
| 641 | assert!(h.board()[10].contains("Won 180 PLN at 3 RED ODD")); | 649 | assert!(h.board()[10].contains("Won 180 Kr at 3 RED ODD")); |
| 642 | assert!(h.board()[10].contains("+160")); | 650 | assert!(h.board()[10].contains("+160")); |
| 643 | } | 651 | } |
| 644 | 652 | ||
| @@ -672,16 +680,17 @@ fn roulette_rejects_an_empty_spin_and_accepts_the_equals_chip_key() { | |||
| 672 | 680 | ||
| 673 | h.key(Key::Char(' ')); | 681 | h.key(Key::Char(' ')); |
| 674 | assert!(h.board()[10].contains("ERROR: Place at least one bet")); | 682 | assert!(h.board()[10].contains("ERROR: Place at least one bet")); |
| 675 | assert!(!h | 683 | assert!( |
| 676 | .sent | 684 | !h.sent |
| 677 | .iter() | 685 | .iter() |
| 678 | .any(|line| line.starts_with("PLAY_START roulette"))); | 686 | .any(|line| line.starts_with("PLAY_START roulette")) |
| 687 | ); | ||
| 679 | 688 | ||
| 680 | h.key(Key::Char('=')); | 689 | h.key(Key::Char('=')); |
| 681 | assert!(h.board()[1].contains("Chip: 20 PLN")); | 690 | assert!(h.board()[1].contains("Chip: 20 Kr")); |
| 682 | h.key(Key::Enter); | 691 | h.key(Key::Enter); |
| 683 | assert!(h.board()[9].contains("1 20")); | 692 | assert!(h.board()[9].contains("1 20")); |
| 684 | assert!(h.board()[9].contains("Total: 20 PLN")); | 693 | assert!(h.board()[9].contains("Total: 20 Kr")); |
| 685 | } | 694 | } |
| 686 | 695 | ||
| 687 | fn bj_state( | 696 | fn bj_state( |
| @@ -776,7 +785,7 @@ fn blackjack_deals_hits_and_settles() { | |||
| 776 | serde_json::to_string(&done).unwrap() | 785 | serde_json::to_string(&done).unwrap() |
| 777 | )); | 786 | )); |
| 778 | 787 | ||
| 779 | assert!(h.board()[4].contains("Win! Won 20 PLN")); | 788 | assert!(h.board()[4].contains("Win! Won 20 Kr")); |
| 780 | assert!(h.board()[5].contains("[SPACE] New hand")); | 789 | assert!(h.board()[5].contains("[SPACE] New hand")); |
| 781 | 790 | ||
| 782 | h.key(Key::Char('q')); | 791 | h.key(Key::Char('q')); |
| @@ -835,11 +844,12 @@ fn blackjack_forfeit_is_sent_when_leaving_an_active_hand() { | |||
| 835 | )); | 844 | )); |
| 836 | 845 | ||
| 837 | assert!(h.board().is_empty()); | 846 | assert!(h.board().is_empty()); |
| 838 | assert!(h | 847 | assert!( |
| 839 | .screen | 848 | h.screen |
| 840 | .lines | 849 | .lines |
| 841 | .iter() | 850 | .iter() |
| 842 | .any(|line| line.contains("forfeited (10 PLN lost)"))); | 851 | .any(|line| line.contains("forfeited (10 Kr lost)")) |
| 852 | ); | ||
| 843 | } | 853 | } |
| 844 | 854 | ||
| 845 | #[test] | 855 | #[test] |
| @@ -848,7 +858,7 @@ fn blackjack_ready_board_can_change_the_next_bet() { | |||
| 848 | h.enter_blackjack(); | 858 | h.enter_blackjack(); |
| 849 | 859 | ||
| 850 | h.key(Key::Char('=')); | 860 | h.key(Key::Char('=')); |
| 851 | assert!(h.board()[1].contains("Bet: 20 PLN")); | 861 | assert!(h.board()[1].contains("Bet: 20 Kr")); |
| 852 | 862 | ||
| 853 | h.key(Key::Char(' ')); | 863 | h.key(Key::Char(' ')); |
| 854 | assert!(h.sent.iter().any(|line| line == "PLAY_START blackjack 20")); | 864 | assert!(h.sent.iter().any(|line| line == "PLAY_START blackjack 20")); |
| @@ -917,14 +927,14 @@ fn crash_starts_a_round_and_draws_the_multiplier_locally() { | |||
| 917 | 927 | ||
| 918 | assert_eq!(h.board().len(), 6); | 928 | assert_eq!(h.board().len(), 6); |
| 919 | assert!(h.board()[2].contains("Multiplier: 1.00x")); | 929 | assert!(h.board()[2].contains("Multiplier: 1.00x")); |
| 920 | assert!(h.board()[3].contains("Potential: 10 PLN")); | 930 | assert!(h.board()[3].contains("Potential: 10 Kr")); |
| 921 | assert!(h.board()[4].contains("Rising")); | 931 | assert!(h.board()[4].contains("Rising")); |
| 922 | assert!(h.board()[5].contains("[C] Cash out")); | 932 | assert!(h.board()[5].contains("[C] Cash out")); |
| 923 | 933 | ||
| 924 | // The multiplier is drawn from the local clock, with no server round trip. | 934 | // The multiplier is drawn from the local clock, with no server round trip. |
| 925 | h.tick(Duration::from_secs(5)); | 935 | h.tick(Duration::from_secs(5)); |
| 926 | assert!(h.board()[2].contains("Multiplier: 2.00x")); | 936 | assert!(h.board()[2].contains("Multiplier: 2.00x")); |
| 927 | assert!(h.board()[3].contains("Potential: 20 PLN")); | 937 | assert!(h.board()[3].contains("Potential: 20 Kr")); |
| 928 | 938 | ||
| 929 | h.key(Key::Char('c')); | 939 | h.key(Key::Char('c')); |
| 930 | assert_eq!(h.sent.last().map(String::as_str), Some("CRASH_CASHOUT cr1")); | 940 | assert_eq!(h.sent.last().map(String::as_str), Some("CRASH_CASHOUT cr1")); |
| @@ -932,9 +942,9 @@ fn crash_starts_a_round_and_draws_the_multiplier_locally() { | |||
| 932 | let cashed = crash_state("cashed", 2.0, Some(3.0), 20, 1010); | 942 | let cashed = crash_state("cashed", 2.0, Some(3.0), 20, 1010); |
| 933 | h.reply(&crash_line("CRASH_STATE", &cashed)); | 943 | h.reply(&crash_line("CRASH_STATE", &cashed)); |
| 934 | 944 | ||
| 935 | assert!(h.board()[4].contains("Cashed out at 2.00x Won 20 PLN")); | 945 | assert!(h.board()[4].contains("Cashed out at 2.00x Won 20 Kr")); |
| 936 | assert!(h.board()[5].contains("[SPACE] Play")); | 946 | assert!(h.board()[5].contains("[SPACE] Play")); |
| 937 | assert_eq!(h.board()[1], "Balance: 1010 PLN Bet: 10 PLN"); | 947 | assert_eq!(h.board()[1], "Balance: 1010 Kr Bet: 10 Kr"); |
| 938 | } | 948 | } |
| 939 | 949 | ||
| 940 | #[test] | 950 | #[test] |
| @@ -977,11 +987,12 @@ fn crash_q_forfeits_an_active_round() { | |||
| 977 | h.reply(&crash_line("CRASH_STATE", &forfeited)); | 987 | h.reply(&crash_line("CRASH_STATE", &forfeited)); |
| 978 | 988 | ||
| 979 | assert!(h.board().is_empty()); | 989 | assert!(h.board().is_empty()); |
| 980 | assert!(h | 990 | assert!( |
| 981 | .screen | 991 | h.screen |
| 982 | .lines | 992 | .lines |
| 983 | .iter() | 993 | .iter() |
| 984 | .any(|line| line.contains("Round forfeited (10 PLN lost)"))); | 994 | .any(|line| line.contains("Round forfeited (10 Kr lost)")) |
| 995 | ); | ||
| 985 | } | 996 | } |
| 986 | 997 | ||
| 987 | #[test] | 998 | #[test] |
| @@ -990,8 +1001,8 @@ fn crash_ready_board_changes_the_next_bet() { | |||
| 990 | h.enter_crash(); | 1001 | h.enter_crash(); |
| 991 | 1002 | ||
| 992 | h.key(Key::Char('=')); | 1003 | h.key(Key::Char('=')); |
| 993 | assert!(h.board()[1].contains("Bet: 20 PLN")); | 1004 | assert!(h.board()[1].contains("Bet: 20 Kr")); |
| 994 | assert!(h.board()[3].contains("Potential: 20 PLN")); | 1005 | assert!(h.board()[3].contains("Potential: 20 Kr")); |
| 995 | 1006 | ||
| 996 | h.key(Key::Char(' ')); | 1007 | h.key(Key::Char(' ')); |
| 997 | assert!(h.sent.iter().any(|line| line == "PLAY_START crash 20")); | 1008 | assert!(h.sent.iter().any(|line| line == "PLAY_START crash 20")); |
| @@ -1058,7 +1069,7 @@ fn a_forfeit_that_arrives_after_the_bust_still_leaves_cleanly() { | |||
| 1058 | 1069 | ||
| 1059 | assert!(h.board().is_empty()); | 1070 | assert!(h.board().is_empty()); |
| 1060 | assert!(h.screen.lines.iter().any(|line| line == "Game exited.")); | 1071 | assert!(h.screen.lines.iter().any(|line| line == "Game exited.")); |
| 1061 | assert!(!h.screen.lines.iter().any(|line| line.contains("PLN lost"))); | 1072 | assert!(!h.screen.lines.iter().any(|line| line.contains("Kr lost"))); |
| 1062 | } | 1073 | } |
| 1063 | 1074 | ||
| 1064 | #[test] | 1075 | #[test] |
diff --git a/tests/crash.rs b/tests/crash.rs index 49eaf89..c985ebe 100644 --- a/tests/crash.rs +++ b/tests/crash.rs | |||
| @@ -2,8 +2,8 @@ | |||
| 2 | 2 | ||
| 3 | use std::time::Duration; | 3 | use std::time::Duration; |
| 4 | use zagan_bet::crash::{ | 4 | use zagan_bet::crash::{ |
| 5 | crash_point_from_uniform, multiplier_at, multiplier_at_secs, payout, time_for_multiplier, | 5 | HOUSE_EDGE, MAX_MULTIPLIER, crash_point_from_uniform, multiplier_at, multiplier_at_secs, |
| 6 | HOUSE_EDGE, MAX_MULTIPLIER, | 6 | payout, time_for_multiplier, |
| 7 | }; | 7 | }; |
| 8 | 8 | ||
| 9 | fn close(a: f64, b: f64, tolerance: f64) { | 9 | fn close(a: f64, b: f64, tolerance: f64) { |
| @@ -38,9 +38,9 @@ fn the_time_for_a_multiplier_is_the_inverse_of_the_curve() { | |||
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | #[test] | 40 | #[test] |
| 41 | fn cashout_value_is_floored_to_whole_pln() { | 41 | fn cashout_value_is_floored_to_whole_Kr() { |
| 42 | assert_eq!(payout(10, 1.0), 10); | 42 | assert_eq!(payout(10, 1.0), 10); |
| 43 | assert_eq!(payout(10, 1.05), 10, "half a PLN is not paid out"); | 43 | assert_eq!(payout(10, 1.05), 10, "half a Kr is not paid out"); |
| 44 | assert_eq!(payout(10, 1.10), 11); | 44 | assert_eq!(payout(10, 1.10), 11); |
| 45 | assert_eq!(payout(10, 2.47), 24); | 45 | assert_eq!(payout(10, 2.47), 24); |
| 46 | assert_eq!(payout(7, 3.0), 21); | 46 | assert_eq!(payout(7, 3.0), 21); |
