diff options
| author | salmonik <salmonik@tuta.io> | 2026-08-24 18:30:25 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-08-24 18:30:25 +0200 |
| commit | fdc8024a61b21314fb1ca1ab6d8adf45c9c7b063 (patch) | |
| tree | 83039ea7d216234360b8f9953c4dbbc0beafb705 | |
| parent | Client: read PLAY_START response before animating; show error immediately on ... (diff) | |
| download | zagan-bet-fdc8024a61b21314fb1ca1ab6d8adf45c9c7b063.tar.gz zagan-bet-fdc8024a61b21314fb1ca1ab6d8adf45c9c7b063.zip | |
Balance slots payouts: EV=0.972 (2.8% house edge). Three-of-a-kind: 2-28x, two-of-a-kind: 1-3x.
| -rw-r--r-- | src/bin/client/games/slots.rs | 4 | ||||
| -rw-r--r-- | src/bin/server/games/slots.rs | 27 | ||||
| -rw-r--r-- | tests/slots.rs | 16 |
3 files changed, 24 insertions, 23 deletions
diff --git a/src/bin/client/games/slots.rs b/src/bin/client/games/slots.rs index d664dbc..bc0a9b4 100644 --- a/src/bin/client/games/slots.rs +++ b/src/bin/client/games/slots.rs | |||
| @@ -50,8 +50,8 @@ impl Game for SlotsGame { | |||
| 50 | Match symbols to win!\n\ | 50 | Match symbols to win!\n\ |
| 51 | \n\ | 51 | \n\ |
| 52 | Payouts:\n\ | 52 | Payouts:\n\ |
| 53 | Three of a kind: 5x-100x bet\n\ | 53 | Three of a kind: 2x-28x bet\n\ |
| 54 | Two of a kind: 2x-20x bet\n\ | 54 | Two of a kind: 1x-3x bet\n\ |
| 55 | \n\ | 55 | \n\ |
| 56 | Symbols: C=Cherry, L=Lemon, O=Orange, G=Grapes, D=Diamond, S=Seven\n\ | 56 | Symbols: C=Cherry, L=Lemon, O=Orange, G=Grapes, D=Diamond, S=Seven\n\ |
| 57 | \n\ | 57 | \n\ |
diff --git a/src/bin/server/games/slots.rs b/src/bin/server/games/slots.rs index 3d285ef..04ac175 100644 --- a/src/bin/server/games/slots.rs +++ b/src/bin/server/games/slots.rs | |||
| @@ -12,19 +12,20 @@ impl SlotsGame { | |||
| 12 | let symbols = vec!['C', 'L', 'O', 'G', 'D', 'S']; | 12 | let symbols = vec!['C', 'L', 'O', 'G', 'D', 'S']; |
| 13 | let mut payouts = HashMap::new(); | 13 | let mut payouts = HashMap::new(); |
| 14 | 14 | ||
| 15 | payouts.insert("CCC".to_string(), 5); | 15 | // Balanced payouts: EV = 210/216 ≈ 0.972 (2.8% house edge) |
| 16 | payouts.insert("LLL".to_string(), 10); | 16 | payouts.insert("CCC".to_string(), 2); |
| 17 | payouts.insert("OOO".to_string(), 15); | 17 | payouts.insert("LLL".to_string(), 3); |
| 18 | payouts.insert("GGG".to_string(), 20); | 18 | payouts.insert("OOO".to_string(), 5); |
| 19 | payouts.insert("DDD".to_string(), 50); | 19 | payouts.insert("GGG".to_string(), 8); |
| 20 | payouts.insert("SSS".to_string(), 100); | 20 | payouts.insert("DDD".to_string(), 14); |
| 21 | 21 | payouts.insert("SSS".to_string(), 28); | |
| 22 | payouts.insert("CC".to_string(), 2); | 22 | |
| 23 | payouts.insert("LL".to_string(), 3); | 23 | payouts.insert("CC".to_string(), 1); |
| 24 | payouts.insert("OO".to_string(), 4); | 24 | payouts.insert("LL".to_string(), 1); |
| 25 | payouts.insert("GG".to_string(), 5); | 25 | payouts.insert("OO".to_string(), 1); |
| 26 | payouts.insert("DD".to_string(), 10); | 26 | payouts.insert("GG".to_string(), 2); |
| 27 | payouts.insert("SS".to_string(), 20); | 27 | payouts.insert("DD".to_string(), 2); |
| 28 | payouts.insert("SS".to_string(), 3); | ||
| 28 | 29 | ||
| 29 | Self { symbols, payouts } | 30 | Self { symbols, payouts } |
| 30 | } | 31 | } |
diff --git a/tests/slots.rs b/tests/slots.rs index c50f425..6babd98 100644 --- a/tests/slots.rs +++ b/tests/slots.rs | |||
| @@ -12,9 +12,9 @@ fn make_game() -> SlotsGame { | |||
| 12 | fn three_of_a_kind_pays() { | 12 | fn three_of_a_kind_pays() { |
| 13 | let game = make_game(); | 13 | let game = make_game(); |
| 14 | // CCC = 5x | 14 | // CCC = 5x |
| 15 | assert_eq!(game.calculate_payout(&['C', 'C', 'C'], 100), 500); | 15 | assert_eq!(game.calculate_payout(&['C', 'C', 'C'], 100), 200); |
| 16 | // SSS = 100x | 16 | // SSS = 100x |
| 17 | assert_eq!(game.calculate_payout(&['S', 'S', 'S'], 10), 1000); | 17 | assert_eq!(game.calculate_payout(&['S', 'S', 'S'], 10), 280); |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | /// Two adjacent matching symbols pays the two-of-a-kind multiplier. | 20 | /// Two adjacent matching symbols pays the two-of-a-kind multiplier. |
| @@ -22,9 +22,9 @@ fn three_of_a_kind_pays() { | |||
| 22 | fn two_adjacent_pays() { | 22 | fn two_adjacent_pays() { |
| 23 | let game = make_game(); | 23 | let game = make_game(); |
| 24 | // CC? = 2x | 24 | // CC? = 2x |
| 25 | assert_eq!(game.calculate_payout(&['C', 'C', 'D'], 100), 200); | 25 | assert_eq!(game.calculate_payout(&['C', 'C', 'D'], 100), 100); |
| 26 | // ?LL = 3x | 26 | // ?LL = 3x |
| 27 | assert_eq!(game.calculate_payout(&['D', 'L', 'L'], 50), 150); | 27 | assert_eq!(game.calculate_payout(&['D', 'L', 'L'], 50), 50); |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | /// Non-adjacent matching symbols (e.g. [C,D,C]) must also pay. | 30 | /// Non-adjacent matching symbols (e.g. [C,D,C]) must also pay. |
| @@ -33,11 +33,11 @@ fn two_adjacent_pays() { | |||
| 33 | fn two_non_adjacent_pays() { | 33 | fn two_non_adjacent_pays() { |
| 34 | let game = make_game(); | 34 | let game = make_game(); |
| 35 | // C,D,C — two cherries at positions 0 and 2 | 35 | // C,D,C — two cherries at positions 0 and 2 |
| 36 | assert_eq!(game.calculate_payout(&['C', 'D', 'C'], 100), 200); | 36 | assert_eq!(game.calculate_payout(&['C', 'D', 'C'], 100), 100); |
| 37 | // S,X,S — two sevens at positions 0 and 2 | 37 | // S,X,S — two sevens at positions 0 and 2 |
| 38 | assert_eq!(game.calculate_payout(&['S', 'L', 'S'], 10), 200); | 38 | assert_eq!(game.calculate_payout(&['S', 'L', 'S'], 10), 30); |
| 39 | // L,O,L | 39 | // L,O,L |
| 40 | assert_eq!(game.calculate_payout(&['L', 'O', 'L'], 50), 150); | 40 | assert_eq!(game.calculate_payout(&['L', 'O', 'L'], 50), 50); |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | /// No matching symbols returns 0. | 43 | /// No matching symbols returns 0. |
| @@ -80,7 +80,7 @@ fn calculate_result_on_finished_state() { | |||
| 80 | 80 | ||
| 81 | let result = game.calculate_result(&state).unwrap(); | 81 | let result = game.calculate_result(&state).unwrap(); |
| 82 | assert_eq!(result.bet_amount, 100); | 82 | assert_eq!(result.bet_amount, 100); |
| 83 | assert_eq!(result.winnings, 200); // CC = 2x | 83 | assert_eq!(result.winnings, 100); // CC = 1x |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | /// calculate_result on a non-finished state returns an error. | 86 | /// calculate_result on a non-finished state returns an error. |
