From 2d9d890269b79e6177c3370fd2d431a0cf520cea Mon Sep 17 00:00:00 2001 From: drewjcooper Date: Wed, 15 Feb 2023 17:49:22 +1100 Subject: [PATCH] Finish game logic --- 75_Roulette/csharp/Game.cs | 243 ++++++++++++++----- 75_Roulette/csharp/Resources/AgainPrompt.txt | 1 + 75_Roulette/csharp/Resources/Check.txt | 4 +- 75_Roulette/csharp/Resources/Outcome.txt | 2 +- 75_Roulette/csharp/Resources/Resource.cs | 8 +- 75_Roulette/csharp/Resources/Slot.txt | 2 +- 75_Roulette/csharp/Resources/Spinning.txt | 2 +- 75_Roulette/csharp/Resources/Totals.txt | 1 + 8 files changed, 193 insertions(+), 70 deletions(-) diff --git a/75_Roulette/csharp/Game.cs b/75_Roulette/csharp/Game.cs index c9570ca1..4543737f 100644 --- a/75_Roulette/csharp/Game.cs +++ b/75_Roulette/csharp/Game.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; namespace Roulette; @@ -7,12 +8,14 @@ internal class Game private readonly IReadWrite _io; private readonly IRandom _random; private readonly Table _table; + private readonly House _house; public Game(IReadWrite io, IRandom random) { _io = io; _random = random; - _table = new Table(io, random); + _house = new(); + _table = new(_house, io, random); } public void Play() @@ -25,10 +28,9 @@ internal class Game while (_table.Play()); - if (_table.Balance > 0) + if (!_house.PlayerIsBroke) { - var name = _io.ReadString(Prompts.Check); - _io.Write(Strings.Check(_random, name, _table.Balance)); + _house.CutCheck(_io, _random); } else { @@ -40,44 +42,44 @@ internal class Game internal class Wheel { private static readonly ImmutableArray _slots = ImmutableArray.Create( - new Slot(Strings.Red(1)), - new Slot(Strings.Black(2)), - new Slot(Strings.Red(3)), - new Slot(Strings.Black(4)), - new Slot(Strings.Red(5)), - new Slot(Strings.Black(6)), - new Slot(Strings.Red(7)), - new Slot(Strings.Black(8)), - new Slot(Strings.Red(9)), - new Slot(Strings.Black(10)), - new Slot(Strings.Black(11)), - new Slot(Strings.Red(12)), - new Slot(Strings.Black(13)), - new Slot(Strings.Red(14)), - new Slot(Strings.Black(15)), - new Slot(Strings.Red(16)), - new Slot(Strings.Black(17)), - new Slot(Strings.Red(18)), - new Slot(Strings.Red(19)), - new Slot(Strings.Black(20)), - new Slot(Strings.Red(21)), - new Slot(Strings.Black(22)), - new Slot(Strings.Red(23)), - new Slot(Strings.Black(24)), - new Slot(Strings.Red(25)), - new Slot(Strings.Black(26)), - new Slot(Strings.Red(27)), - new Slot(Strings.Black(28)), - new Slot(Strings.Black(29)), - new Slot(Strings.Red(30)), - new Slot(Strings.Black(31)), - new Slot(Strings.Red(32)), - new Slot(Strings.Black(33)), - new Slot(Strings.Red(34)), - new Slot(Strings.Black(35)), - new Slot(Strings.Red(36)), - new Slot("0"), - new Slot("00")); + new Slot(Strings.Red(1), 1, 37, 40, 43, 46, 47), + new Slot(Strings.Black(2), 2, 37, 41, 43, 45, 48), + new Slot(Strings.Red(3), 3, 37, 42, 43, 46, 47), + new Slot(Strings.Black(4), 4, 37, 40, 43, 45, 48), + new Slot(Strings.Red(5), 5, 37, 41, 43, 46, 47), + new Slot(Strings.Black(6), 6, 37, 42, 43, 45, 48), + new Slot(Strings.Red(7), 7, 37, 40, 43, 46, 47), + new Slot(Strings.Black(8), 8, 37, 41, 43, 45, 48), + new Slot(Strings.Red(9), 9, 37, 42, 43, 46, 47), + new Slot(Strings.Black(10), 10, 37, 40, 43, 45, 48), + new Slot(Strings.Black(11), 11, 37, 41, 43, 46, 48), + new Slot(Strings.Red(12), 12, 37, 42, 43, 45, 47), + new Slot(Strings.Black(13), 13, 38, 40, 43, 46, 48), + new Slot(Strings.Red(14), 14, 38, 41, 43, 45, 47), + new Slot(Strings.Black(15), 15, 38, 42, 43, 46, 48), + new Slot(Strings.Red(16), 16, 38, 40, 43, 45, 47), + new Slot(Strings.Black(17), 17, 38, 41, 43, 46, 48), + new Slot(Strings.Red(18), 18, 38, 42, 43, 45, 47), + new Slot(Strings.Red(19), 19, 38, 40, 44, 46, 47), + new Slot(Strings.Black(20), 20, 38, 41, 44, 45, 48), + new Slot(Strings.Red(21), 21, 38, 42, 44, 46, 47), + new Slot(Strings.Black(22), 22, 38, 40, 44, 45, 48), + new Slot(Strings.Red(23), 23, 38, 41, 44, 46, 47), + new Slot(Strings.Black(24), 24, 38, 42, 44, 45, 48), + new Slot(Strings.Red(25), 25, 39, 40, 44, 46, 47), + new Slot(Strings.Black(26), 26, 39, 41, 44, 45, 48), + new Slot(Strings.Red(27), 27, 39, 42, 44, 46, 47), + new Slot(Strings.Black(28), 28, 39, 40, 44, 45, 48), + new Slot(Strings.Black(29), 29, 39, 41, 44, 46, 48), + new Slot(Strings.Red(30), 30, 39, 42, 44, 45, 47), + new Slot(Strings.Black(31), 31, 39, 40, 44, 46, 48), + new Slot(Strings.Red(32), 32, 39, 41, 44, 45, 47), + new Slot(Strings.Black(33), 33, 39, 42, 44, 46, 48), + new Slot(Strings.Red(34), 34, 39, 40, 44, 45, 47), + new Slot(Strings.Black(35), 35, 39, 41, 44, 46, 48), + new Slot(Strings.Red(36), 36, 39, 42, 44, 45, 47), + new Slot("0", 49), + new Slot("00", 50)); private readonly IRandom _random; @@ -86,44 +88,156 @@ internal class Wheel public Slot Spin() => _slots[_random.Next(_slots.Length)]; } -internal record struct Slot(string Name); - -internal record struct Bet(int Number, int Amount) +internal class Slot { - public Bet(int number) : this(number, 0) { } + private readonly ImmutableHashSet _coveringBets; - public bool Equals(Bet? other) => Number == other?.Number; + public Slot (string name, params BetType[] coveringBets) + { + Name = name; + _coveringBets = coveringBets.ToImmutableHashSet(); + } + + public string Name { get; } + + public bool IsCoveredBy(Bet bet) => _coveringBets.Contains(bet.Type); +} + +internal record struct BetType(int Value) +{ + public static implicit operator BetType(int value) => new(value); + + public int Payout => Value switch + { + <= 36 or >= 49 => 35, + <= 42 => 2, + <= 48 => 1 + }; +} + +internal record struct Bet(BetType Type, int Number, int Wager) +{ + public int Payout => Wager * Type.Payout; } public class Table { private readonly IReadWrite _io; private readonly Wheel _wheel; + private readonly House _house; - private int _houseBalance = 100_000; - private int _playerBalance = 1_000; - - public Table(IReadWrite io, IRandom random) + public Table(House house, IReadWrite io, IRandom random) { + _house = house; _io = io; _wheel = new(random); } - public int Balance => _playerBalance; - public bool Play() { - var betCount = _io.ReadBetCount(); - var bets = new HashSet(); - for (int i = 0; i < betCount; i++) + var bets = AcceptBets(); + var slot = SpinWheel(); + SettleBets(bets, slot); + + _io.Write(_house.Totals); + + if (_house.PlayerIsBroke) { - while (!bets.Add(_io.ReadBet(i))) + _io.Write(Streams.LastDollar); + _io.Write(Streams.Thanks); + return false; + } + + if (_house.HouseIsBroke) + { + _io.Write(Streams.BrokeHouse); + return false; + } + + return _io.ReadString(Prompts.Again).ToLowerInvariant().StartsWith('y'); + } + + private Slot SpinWheel() + { + _io.Write(Streams.Spinning); + var slot = _wheel.Spin(); + _io.Write(slot.Name); + return slot; + } + + private IReadOnlyList AcceptBets() + { + var betCount = _io.ReadBetCount(); + var betTypes = new HashSet(); + var bets = new List(); + for (int i = 1; i <= betCount; i++) + { + while (!TryAdd(_io.ReadBet(i))) { _io.Write(Streams.BetAlready); } } - - return _io.ReadString(Prompts.Again).ToLowerInvariant().StartsWith('y'); + + return bets.AsReadOnly(); + + bool TryAdd(Bet bet) + { + if (betTypes.Add(bet.Type)) + { + bets.Add(bet); + return true; + } + + return false; + } + } + + private void SettleBets(IReadOnlyList bets, Slot slot) + { + foreach (var bet in bets) + { + _io.Write(slot.IsCoveredBy(bet) ? _house.Pay(bet) : _house.Take(bet)); + } + } +} + +public class House +{ + private const int _initialHouse = 100_000; + private const int _initialPlayer = 1_000; + + private int _balance = _initialHouse; + private int _player = _initialPlayer; + + public string Totals => Strings.Totals(_balance, _player); + public bool PlayerIsBroke => _player <= 0; + public bool HouseIsBroke => _balance <= 0; + + internal string Pay(Bet bet) + { + _balance -= bet.Payout; + _player += bet.Payout; + + if (_balance <= 0) + { + _player = _initialHouse + _initialPlayer; + } + + return Strings.Win(bet); + } + + internal string Take(Bet bet) + { + _balance += bet.Wager; + _player -= bet.Wager; + + return Strings.Lose(bet); + } + + public void CutCheck(IReadWrite io, IRandom random) + { + var name = io.ReadString(Prompts.Check); + io.Write(Strings.Check(random, name, _player)); } } @@ -142,11 +256,16 @@ internal static class IOExtensions { while (true) { - var (bet, amount) = io.Read2Numbers(Prompts.Bet(number)); + var (type, amount) = io.Read2Numbers(Prompts.Bet(number)); - if (bet.IsValidInt(1, 50) && amount.IsValidInt(5, 500)) + if (type.IsValidInt(1, 50) && amount.IsValidInt(5, 500)) { - return new((int)bet, (int)amount); + return new() + { + Type = (int)type, + Number = number, + Wager = (int)amount + }; } } } diff --git a/75_Roulette/csharp/Resources/AgainPrompt.txt b/75_Roulette/csharp/Resources/AgainPrompt.txt index e69de29b..bd1d18b6 100644 --- a/75_Roulette/csharp/Resources/AgainPrompt.txt +++ b/75_Roulette/csharp/Resources/AgainPrompt.txt @@ -0,0 +1 @@ +Again \ No newline at end of file diff --git a/75_Roulette/csharp/Resources/Check.txt b/75_Roulette/csharp/Resources/Check.txt index f2be95cd..ffdd640a 100644 --- a/75_Roulette/csharp/Resources/Check.txt +++ b/75_Roulette/csharp/Resources/Check.txt @@ -1,6 +1,7 @@ + ------------------------------------------------------------------------Check No. {0} - {1:mmmm d',' yyyy} + {1:MMMM d',' yyyy} Pay to the order of-----{2}-----$ {3} @@ -12,3 +13,4 @@ Pay to the order of-----{2}-----$ {3} ----------X----- --------------------------------------------------------------Come back soon! + diff --git a/75_Roulette/csharp/Resources/Outcome.txt b/75_Roulette/csharp/Resources/Outcome.txt index 7926cb87..30e227f7 100644 --- a/75_Roulette/csharp/Resources/Outcome.txt +++ b/75_Roulette/csharp/Resources/Outcome.txt @@ -1 +1 @@ -You {0} {1} dollars on bet {2} \ No newline at end of file +You {0} {1} dollars on bet {2} diff --git a/75_Roulette/csharp/Resources/Resource.cs b/75_Roulette/csharp/Resources/Resource.cs index aca0aaf5..dd9c8672 100644 --- a/75_Roulette/csharp/Resources/Resource.cs +++ b/75_Roulette/csharp/Resources/Resource.cs @@ -24,10 +24,10 @@ internal static class Resource private static string Slot(int number, [CallerMemberName] string? colour = null) => string.Format(GetString(), number, colour); - public static string Lose(int amount, int bet) => Outcome(amount, bet); - public static string Win(int amount, int bet) => Outcome(amount, bet); - private static string Outcome(int amount, int bet, [CallerMemberName] string? winlose = null) - => string.Format(GetString(), winlose, amount, bet); + public static string Lose(Bet bet) => Outcome(bet.Wager, bet.Number); + public static string Win(Bet bet) => Outcome(bet.Payout, bet.Number); + private static string Outcome(int amount, int number, [CallerMemberName] string? winlose = null) + => string.Format(GetString(), winlose, amount, number); public static string Totals(int me, int you) => string.Format(GetString(), me, you); diff --git a/75_Roulette/csharp/Resources/Slot.txt b/75_Roulette/csharp/Resources/Slot.txt index d45e103b..de02695d 100644 --- a/75_Roulette/csharp/Resources/Slot.txt +++ b/75_Roulette/csharp/Resources/Slot.txt @@ -1,2 +1,2 @@ {0} {1} - \ No newline at end of file + diff --git a/75_Roulette/csharp/Resources/Spinning.txt b/75_Roulette/csharp/Resources/Spinning.txt index 13514a0a..0d87fe39 100644 --- a/75_Roulette/csharp/Resources/Spinning.txt +++ b/75_Roulette/csharp/Resources/Spinning.txt @@ -1,3 +1,3 @@ -SPINNING +Spinning diff --git a/75_Roulette/csharp/Resources/Totals.txt b/75_Roulette/csharp/Resources/Totals.txt index 26f35724..4cecae3f 100644 --- a/75_Roulette/csharp/Resources/Totals.txt +++ b/75_Roulette/csharp/Resources/Totals.txt @@ -1,2 +1,3 @@ + Totals Me You {0,-14}{1}