From 9951e2d5afbcabb4bd8016b6d160653c1ec5ed8e Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 28 Oct 2026 23:45:43 +0100 Subject: v1.3.0: simplify payouts to bet x symbol x shape --- tests/client_core.rs | 23 ++++++++++++----------- tests/server_core.rs | 4 ++-- tests/slots.rs | 40 +++++++++++++++++++++++++++------------- 3 files changed, 41 insertions(+), 26 deletions(-) (limited to 'tests') diff --git a/tests/client_core.rs b/tests/client_core.rs index e75a611..fd88566 100644 --- a/tests/client_core.rs +++ b/tests/client_core.rs @@ -135,8 +135,9 @@ fn play_shows_a_ten_line_board_and_fetches_the_balance() { assert_eq!(h.board().len(), 10); assert!(h.board()[1].contains("Bet: 50 PLN")); assert!(!h.board()[1].contains("Lines:")); - assert!(h.board()[5].contains("Pays 3/4/5 x bet:")); - assert!(h.board()[6].contains("G 4/12/40 D 6/18/70 S 9/30/150")); + assert!(h.board()[5].contains("Pays bet x symbol x shape")); + assert!(h.board()[6].contains("Symbol C1 L2 O3 G4 D6 S10")); + assert!(h.board()[6].contains("Shape 3:1 4:3 5:14")); assert!(h.sent.contains(&"INFO".to_string())); h.reply("INFO alice 1 1000"); @@ -157,15 +158,15 @@ fn spin_replaces_the_board_and_keeps_the_controls_line() { assert!(h.sent.iter().any(|line| line == "PLAY_START slots 50")); assert_eq!(h.screen.lines.len(), lines_before_spin); - h.reply("GAME_RESULT session-1 100 50 1050 CCCCLLCLODGSDGS win 1 0,3,6"); + h.reply("GAME_RESULT session-1 50 50 1000 CCCCLLCLODGSDGS win 1 0,3,6"); assert_eq!(h.board().len(), 10); assert!(h .board() .get(2) .is_some_and(|line| line.contains("{ C } { C } { C } [ D ] [ D ]"))); - assert!(h.board()[7].contains("Won 100 PLN")); - assert!(h.board()[8].contains("Winning lines: 1")); - assert!(h.board()[1].contains("Balance: 1050 PLN")); + assert!(h.board()[7].contains("Won 50 PLN")); + assert!(h.board()[8].contains("Winning lines: 1 (top)")); + assert!(h.board()[1].contains("Balance: 1000 PLN")); assert!(h.board()[9].contains("[Q] Quit")); assert_eq!(h.screen.lines.len(), lines_before_spin); } @@ -175,7 +176,7 @@ fn changing_the_bet_after_a_spin_does_not_leave_stale_lines() { let mut h = Harness::new(); h.enter_slots("50"); h.key(Key::Char(' ')); - h.reply("GAME_RESULT session-1 100 50 1050 CCCCLLCLODGSDGS win 1 0,3,6"); + h.reply("GAME_RESULT session-1 50 50 1000 CCCCLLCLODGSDGS win 1 0,3,6"); let lines_before_bet = h.screen.lines.len(); h.key(Key::Char('+')); @@ -217,11 +218,11 @@ fn a_mega_win_shows_all_winning_lines() { h.enter_slots("10"); h.key(Key::Char(' ')); - h.reply(&format!("GAME_RESULT session-1 750 10 1740 {ALL_C} mega 1,2,3,4,5 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14")); + 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")); assert_eq!(h.board().len(), 10); - assert!(h.board()[7].contains("MEGA WIN! 750 PLN")); - assert!(h.board()[8].contains("Winning lines: 1,2,3,4,5")); + assert!(h.board()[7].contains("MEGA WIN! 700 PLN")); + assert!(h.board()[8].contains("Winning lines: 1 (top), 2 (middle), 3 (bottom), 4 (V), 5 (inverted V)")); assert_eq!(h.board()[2].matches("{ C }").count(), 5); } @@ -521,7 +522,7 @@ fn disconnect_clears_connection_state() { #[test] fn parses_a_five_by_three_result_with_winning_lines() { - let response = format!("GAME_RESULT session-1 100 50 1050 {WIN3} win 1 0,3,6"); + let response = format!("GAME_RESULT session-1 50 50 1000 {WIN3} win 1 0,3,6"); let result = parse_spin_response(&response).expect("valid 5x3 result"); assert_eq!(result.reels.chars().count(), 15); diff --git a/tests/server_core.rs b/tests/server_core.rs index 5c3c316..3f5dc9a 100644 --- a/tests/server_core.rs +++ b/tests/server_core.rs @@ -188,10 +188,10 @@ fn a_winning_spin_updates_the_balance_and_the_audit_row() { let response = core.handle_message(&mut state, "PLAY_START slots 10"); assert!(response.starts_with("GAME_RESULT"), "spin failed: {response}"); - assert!(response.contains(" 20 10 1010 "), "response: {response}"); + assert!(response.contains(" 10 10 1000 "), "response: {response}"); assert!(response.ends_with(" win 1 0,3,6"), "response: {response}"); - assert_eq!(core.balance("alice"), Some(1010)); + assert_eq!(core.balance("alice"), Some(1000)); assert_eq!(core.play_count("alice"), 1); assert_eq!(core.session_count(), 0); } diff --git a/tests/slots.rs b/tests/slots.rs index bdf8f10..8bdd9af 100644 --- a/tests/slots.rs +++ b/tests/slots.rs @@ -1,6 +1,6 @@ //! Regression tests for the server-side 5x3 slots game. -use zagan_bet::slots::{paytable_lines, PAYTABLE}; +use zagan_bet::slots::{paytable_lines, LINE_NAMES, SHAPE_MULTIPLIERS, SYMBOL_MULTIPLIERS}; use zagan_bet::server::games::slots::{LineWin, SlotsGame, PAYLINES, REELS, ROWS}; use zagan_bet::server::games::traits::{Game, GamePhase}; @@ -44,7 +44,7 @@ fn the_wire_grid_is_five_reels_by_three_rows() { fn three_of_a_kind_on_the_top_line_pays() { let (winnings, wins) = payout(["CCCDD", "CLLGG", "CLOSS"], 10); - assert_eq!(winnings, 20, "CCC on line 1 pays 2x total bet"); + assert_eq!(winnings, 10, "CCC: 10 * C(1) * shape3(1)"); assert_eq!(lines(&wins), vec![1]); assert_eq!(wins[0].cells, vec![0, 3, 6]); } @@ -53,7 +53,7 @@ fn three_of_a_kind_on_the_top_line_pays() { fn four_of_a_kind_pays_more_than_three() { let (winnings, wins) = payout(["CCCCD", "LOGDS", "OGDSL"], 10); - assert_eq!(winnings, 50, "CCCC pays 5x total bet"); + assert_eq!(winnings, 30, "CCCC: 10 * C(1) * shape4(3)"); assert_eq!(lines(&wins), vec![1]); assert_eq!(wins[0].cells, vec![0, 3, 6, 9]); } @@ -62,7 +62,7 @@ fn four_of_a_kind_pays_more_than_three() { fn five_of_a_kind_pays_all_lines() { let (winnings, wins) = payout(["SSSSS", "SSSSS", "SSSSS"], 10); - assert_eq!(winnings, 7500, "five SSSSS lines at 150x each"); + assert_eq!(winnings, 7000, "five SSSSS lines: 5 * 10 * S(10) * shape5(14)"); assert_eq!(lines(&wins), vec![1, 2, 3, 4, 5]); assert_eq!(wins[0].cells, vec![0, 3, 6, 9, 12]); assert_eq!(wins[3].cells, vec![0, 4, 8, 10, 12]); @@ -73,7 +73,7 @@ fn five_of_a_kind_pays_all_lines() { fn the_v_payline_is_evaluated() { let (winnings, wins) = payout(["COGDC", "LCLCG", "OGCSD"], 10); - assert_eq!(winnings, 150, "V line with five C symbols pays 15x"); + assert_eq!(winnings, 140, "V line: 10 * C(1) * shape5(14)"); assert_eq!(lines(&wins), vec![4]); assert_eq!(wins[0].cells, vec![0, 4, 8, 10, 12]); } @@ -90,7 +90,7 @@ fn no_matching_line_pays_nothing() { fn winning_lines_are_added_together() { let (winnings, wins) = payout(["SSSSS", "LLLLL", "OOOOO"], 10); - assert_eq!(winnings, (150 + 20 + 25) * 10); + assert_eq!(winnings, (10 + 2 + 3) * 14 * 10); assert_eq!(lines(&wins), vec![1, 2, 3]); let mut cells: Vec = wins.iter().flat_map(|win| win.cells.clone()).collect(); @@ -102,21 +102,35 @@ fn winning_lines_are_added_together() { fn theoretical_rtp_is_just_under_one() { let rtp = SlotsGame::new().theoretical_rtp(); - assert!((rtp - 425.0 / 432.0).abs() < 1e-12); + assert!((rtp - 3835.0 / 3888.0).abs() < 1e-12); assert!(rtp < 1.0, "house edge must be positive"); assert!(rtp > 0.98, "house edge should be tiny"); } #[test] -fn the_paytable_lines_list_every_symbol_and_fit_80_columns() { +fn payline_names_match_the_paylines() { + assert_eq!(LINE_NAMES.len(), PAYLINES.len()); + assert_eq!(LINE_NAMES[3], "V"); + assert_eq!(LINE_NAMES[4], "inverted V"); +} + +#[test] +fn the_paytable_lines_show_the_product_formula_and_fit_80_columns() { let lines = paytable_lines(); let joined = lines.join(" "); - assert_eq!(lines.len(), 2, "paytable should be split into two lines"); - for (symbol, three, four, five) in PAYTABLE { + assert_eq!(lines.len(), 2, "paytable should be two lines"); + assert!(joined.contains("Pays bet x symbol x shape")); + for (symbol, multiplier) in SYMBOL_MULTIPLIERS { + assert!( + joined.contains(&format!("{symbol}{multiplier}")), + "paytable is missing symbol {symbol}" + ); + } + for (length, multiplier) in SHAPE_MULTIPLIERS { assert!( - joined.contains(&format!("{symbol} {three}/{four}/{five}")), - "paytable is missing {symbol}" + joined.contains(&format!("{length}:{multiplier}")), + "paytable is missing shape {length}" ); } for line in lines { @@ -140,7 +154,7 @@ fn a_spin_produces_a_finished_state_and_a_result_with_win_lines() { assert_eq!(state.phase, GamePhase::Finished); let result = game.calculate_result(&state).unwrap(); - assert_eq!(result.winnings, 20); + assert_eq!(result.winnings, 10); assert_eq!(result.result_data.get("win_type").unwrap(), "win"); assert_eq!(result.result_data.get("win_lines").unwrap(), "1"); assert_eq!(result.result_data.get("win_cells").unwrap(), "0,3,6"); -- cgit v1.2.3