mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-23 07:29:02 -08:00
Reorganise code
This commit is contained in:
6
75_Roulette/csharp/Bet.cs
Normal file
6
75_Roulette/csharp/Bet.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Roulette;
|
||||
|
||||
internal record struct Bet(BetType Type, int Number, int Wager)
|
||||
{
|
||||
public int Payout => Wager * Type.Payout;
|
||||
}
|
||||
13
75_Roulette/csharp/BetType.cs
Normal file
13
75_Roulette/csharp/BetType.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Roulette;
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
41
75_Roulette/csharp/Croupier.cs
Normal file
41
75_Roulette/csharp/Croupier.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
namespace Roulette;
|
||||
|
||||
internal class Croupier
|
||||
{
|
||||
private const int _initialHouse = 100_000;
|
||||
private const int _initialPlayer = 1_000;
|
||||
|
||||
private int _house = _initialHouse;
|
||||
private int _player = _initialPlayer;
|
||||
|
||||
public string Totals => Strings.Totals(_house, _player);
|
||||
public bool PlayerIsBroke => _player <= 0;
|
||||
public bool HouseIsBroke => _house <= 0;
|
||||
|
||||
internal string Pay(Bet bet)
|
||||
{
|
||||
_house -= bet.Payout;
|
||||
_player += bet.Payout;
|
||||
|
||||
if (_house <= 0)
|
||||
{
|
||||
_player = _initialHouse + _initialPlayer;
|
||||
}
|
||||
|
||||
return Strings.Win(bet);
|
||||
}
|
||||
|
||||
internal string Take(Bet bet)
|
||||
{
|
||||
_house += 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));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Roulette;
|
||||
@@ -8,7 +7,7 @@ internal class Game
|
||||
private readonly IReadWrite _io;
|
||||
private readonly IRandom _random;
|
||||
private readonly Table _table;
|
||||
private readonly House _house;
|
||||
private readonly Croupier _house;
|
||||
|
||||
public Game(IReadWrite io, IRandom random)
|
||||
{
|
||||
@@ -38,238 +37,3 @@ internal class Game
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class Wheel
|
||||
{
|
||||
private static readonly ImmutableArray<Slot> _slots = ImmutableArray.Create(
|
||||
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;
|
||||
|
||||
public Wheel(IRandom random) => _random = random;
|
||||
|
||||
public Slot Spin() => _slots[_random.Next(_slots.Length)];
|
||||
}
|
||||
|
||||
internal class Slot
|
||||
{
|
||||
private readonly ImmutableHashSet<BetType> _coveringBets;
|
||||
|
||||
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;
|
||||
|
||||
public Table(House house, IReadWrite io, IRandom random)
|
||||
{
|
||||
_house = house;
|
||||
_io = io;
|
||||
_wheel = new(random);
|
||||
}
|
||||
|
||||
public bool Play()
|
||||
{
|
||||
var bets = AcceptBets();
|
||||
var slot = SpinWheel();
|
||||
SettleBets(bets, slot);
|
||||
|
||||
_io.Write(_house.Totals);
|
||||
|
||||
if (_house.PlayerIsBroke)
|
||||
{
|
||||
_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<Bet> AcceptBets()
|
||||
{
|
||||
var betCount = _io.ReadBetCount();
|
||||
var betTypes = new HashSet<BetType>();
|
||||
var bets = new List<Bet>();
|
||||
for (int i = 1; i <= betCount; i++)
|
||||
{
|
||||
while (!TryAdd(_io.ReadBet(i)))
|
||||
{
|
||||
_io.Write(Streams.BetAlready);
|
||||
}
|
||||
}
|
||||
|
||||
return bets.AsReadOnly();
|
||||
|
||||
bool TryAdd(Bet bet)
|
||||
{
|
||||
if (betTypes.Add(bet.Type))
|
||||
{
|
||||
bets.Add(bet);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SettleBets(IReadOnlyList<Bet> 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));
|
||||
}
|
||||
}
|
||||
|
||||
internal static class IOExtensions
|
||||
{
|
||||
internal static int ReadBetCount(this IReadWrite io)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var betCount = io.ReadNumber(Prompts.HowManyBets);
|
||||
if (betCount.IsValidInt(1)) { return (int)betCount; }
|
||||
}
|
||||
}
|
||||
|
||||
internal static Bet ReadBet(this IReadWrite io, int number)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var (type, amount) = io.Read2Numbers(Prompts.Bet(number));
|
||||
|
||||
if (type.IsValidInt(1, 50) && amount.IsValidInt(5, 500))
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Type = (int)type,
|
||||
Number = number,
|
||||
Wager = (int)amount
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsValidInt(this float value, int minValue, int maxValue = int.MaxValue)
|
||||
=> value == (int)value && value >= minValue && value <= maxValue;
|
||||
}
|
||||
34
75_Roulette/csharp/IOExtensions.cs
Normal file
34
75_Roulette/csharp/IOExtensions.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
namespace Roulette;
|
||||
|
||||
internal static class IOExtensions
|
||||
{
|
||||
internal static int ReadBetCount(this IReadWrite io)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var betCount = io.ReadNumber(Prompts.HowManyBets);
|
||||
if (betCount.IsValidInt(1)) { return (int)betCount; }
|
||||
}
|
||||
}
|
||||
|
||||
internal static Bet ReadBet(this IReadWrite io, int number)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var (type, amount) = io.Read2Numbers(Prompts.Bet(number));
|
||||
|
||||
if (type.IsValidInt(1, 50) && amount.IsValidInt(5, 500))
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Type = (int)type,
|
||||
Number = number,
|
||||
Wager = (int)amount
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsValidInt(this float value, int minValue, int maxValue = int.MaxValue)
|
||||
=> value == (int)value && value >= minValue && value <= maxValue;
|
||||
}
|
||||
18
75_Roulette/csharp/Slot.cs
Normal file
18
75_Roulette/csharp/Slot.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Roulette;
|
||||
|
||||
internal class Slot
|
||||
{
|
||||
private readonly ImmutableHashSet<BetType> _coveringBets;
|
||||
|
||||
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);
|
||||
}
|
||||
82
75_Roulette/csharp/Table.cs
Normal file
82
75_Roulette/csharp/Table.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
namespace Roulette;
|
||||
|
||||
internal class Table
|
||||
{
|
||||
private readonly IReadWrite _io;
|
||||
private readonly Wheel _wheel;
|
||||
private readonly Croupier _house;
|
||||
|
||||
public Table(Croupier house, IReadWrite io, IRandom random)
|
||||
{
|
||||
_house = house;
|
||||
_io = io;
|
||||
_wheel = new(random);
|
||||
}
|
||||
|
||||
public bool Play()
|
||||
{
|
||||
var bets = AcceptBets();
|
||||
var slot = SpinWheel();
|
||||
SettleBets(bets, slot);
|
||||
|
||||
_io.Write(_house.Totals);
|
||||
|
||||
if (_house.PlayerIsBroke)
|
||||
{
|
||||
_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<Bet> AcceptBets()
|
||||
{
|
||||
var betCount = _io.ReadBetCount();
|
||||
var betTypes = new HashSet<BetType>();
|
||||
var bets = new List<Bet>();
|
||||
for (int i = 1; i <= betCount; i++)
|
||||
{
|
||||
while (!TryAdd(_io.ReadBet(i)))
|
||||
{
|
||||
_io.Write(Streams.BetAlready);
|
||||
}
|
||||
}
|
||||
|
||||
return bets.AsReadOnly();
|
||||
|
||||
bool TryAdd(Bet bet)
|
||||
{
|
||||
if (betTypes.Add(bet.Type))
|
||||
{
|
||||
bets.Add(bet);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void SettleBets(IReadOnlyList<Bet> bets, Slot slot)
|
||||
{
|
||||
foreach (var bet in bets)
|
||||
{
|
||||
_io.Write(slot.IsCoveredBy(bet) ? _house.Pay(bet) : _house.Take(bet));
|
||||
}
|
||||
}
|
||||
}
|
||||
52
75_Roulette/csharp/Wheel.cs
Normal file
52
75_Roulette/csharp/Wheel.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Roulette;
|
||||
|
||||
internal class Wheel
|
||||
{
|
||||
private static readonly ImmutableArray<Slot> _slots = ImmutableArray.Create(
|
||||
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;
|
||||
|
||||
public Wheel(IRandom random) => _random = random;
|
||||
|
||||
public Slot Spin() => _slots[_random.Next(_slots.Length)];
|
||||
}
|
||||
Reference in New Issue
Block a user