mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 15:16:33 -08:00
Split out play resolution
This commit is contained in:
32
07_Basketball/csharp/Clock.cs
Normal file
32
07_Basketball/csharp/Clock.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Basketball.Resources;
|
||||
using Games.Common.IO;
|
||||
|
||||
namespace Basketball;
|
||||
|
||||
internal class Clock
|
||||
{
|
||||
private readonly IReadWrite _io;
|
||||
private int time;
|
||||
|
||||
public Clock(IReadWrite io) => _io = io;
|
||||
|
||||
public bool IsHalfTime => time == 50;
|
||||
public bool IsFullTime => time >= 100;
|
||||
public bool TwoMinutesLeft => time == 92;
|
||||
|
||||
public void Increment(Scoreboard scoreboard)
|
||||
{
|
||||
time += 1;
|
||||
if (IsHalfTime)
|
||||
{
|
||||
scoreboard.Display(Resource.Formats.EndOfFirstHalf);
|
||||
// Loop back to center jump;
|
||||
}
|
||||
if (TwoMinutesLeft)
|
||||
{
|
||||
_io.Write(Resource.Streams.TwoMinutesLeft);
|
||||
}
|
||||
}
|
||||
|
||||
public void StartOvertime() => time = 93;
|
||||
}
|
||||
12
07_Basketball/csharp/Defense.cs
Normal file
12
07_Basketball/csharp/Defense.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Basketball;
|
||||
|
||||
internal class Defense
|
||||
{
|
||||
private float _value;
|
||||
|
||||
public Defense(float value) => Set(value);
|
||||
|
||||
public void Set(float value) => _value = value;
|
||||
|
||||
public static implicit operator float(Defense defense) => defense._value;
|
||||
}
|
||||
@@ -19,163 +19,109 @@ internal class Game
|
||||
{
|
||||
_io.Write(Resource.Streams.Introduction);
|
||||
|
||||
var defense = _io.ReadDefense("Your starting defense will be");
|
||||
|
||||
var homeTeam = new Team("Dartmouth");
|
||||
|
||||
_io.WriteLine();
|
||||
var visitingTeam = new Team(_io.ReadString("Choose your opponent"));
|
||||
|
||||
var time = 0;
|
||||
var scoreboard = new Scoreboard(homeTeam, visitingTeam, _io);
|
||||
|
||||
_io.WriteLine("Center jump");
|
||||
scoreboard.Offense = ContestBall(0.6f, visitingTeam, homeTeam, "{0} controls the tap");
|
||||
var defense = new Defense(_io.ReadDefense("Your starting defense will be"));
|
||||
var clock = new Clock(_io);
|
||||
|
||||
_io.WriteLine();
|
||||
|
||||
if (scoreboard.Offense == homeTeam)
|
||||
var scoreboard = new Scoreboard(
|
||||
new Team("Dartmouth", HomeTeamPlay(clock, defense)),
|
||||
new Team(_io.ReadString("Choose your opponent"), VisitingTeamPlay(clock, defense)),
|
||||
_io);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var shot = _io.ReadShot("Your shot");
|
||||
_io.WriteLine("Center jump");
|
||||
scoreboard.Offense = ContestBall(0.4f, scoreboard, "{0} controls the tap");
|
||||
|
||||
if (_random.NextFloat() >= 0.5f && time >= 100)
|
||||
_io.WriteLine();
|
||||
|
||||
while (true)
|
||||
{
|
||||
_io.WriteLine();
|
||||
if (scoreboard.ScoresAreEqual)
|
||||
{
|
||||
scoreboard.Display(Resource.Formats.EndOfSecondHalf);
|
||||
time = 93;
|
||||
// Loop back to center jump
|
||||
}
|
||||
else
|
||||
{
|
||||
scoreboard.Display(Resource.Formats.EndOfGame);
|
||||
return;
|
||||
}
|
||||
scoreboard.Offense.ResolvePlay(scoreboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Action<Scoreboard> HomeTeamPlay(Clock clock, Defense defense) => scoreboard =>
|
||||
{
|
||||
var shot = _io.ReadShot("Your shot");
|
||||
|
||||
if (_random.NextFloat() >= 0.5f && clock.IsFullTime)
|
||||
{
|
||||
_io.WriteLine();
|
||||
if (scoreboard.ScoresAreEqual)
|
||||
{
|
||||
scoreboard.Display(Resource.Formats.EndOfSecondHalf);
|
||||
clock.StartOvertime();
|
||||
// Loop back to center jump
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shot == 0)
|
||||
{
|
||||
defense = _io.ReadDefense("Your new defensive alignment is");
|
||||
// go to next shot
|
||||
}
|
||||
scoreboard.Display(Resource.Formats.EndOfGame);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shot == 0)
|
||||
{
|
||||
defense.Set(_io.ReadDefense("Your new defensive alignment is"));
|
||||
// go to next shot
|
||||
}
|
||||
|
||||
if (shot == 1 || shot == 2)
|
||||
if (shot == 1 || shot == 2)
|
||||
{
|
||||
clock.Increment(scoreboard);
|
||||
if (clock.IsHalfTime)
|
||||
{
|
||||
time++;
|
||||
if (time == 50)
|
||||
{
|
||||
scoreboard.Display(Resource.Formats.EndOfFirstHalf);
|
||||
// Loop back to center jump;
|
||||
}
|
||||
if (time == 92)
|
||||
{
|
||||
_io.Write(Resource.Streams.TwoMinutesLeft);
|
||||
}
|
||||
_io.WriteLine("Jump shot");
|
||||
if (_random.NextFloat() <= 0.341f * defense / 8)
|
||||
{
|
||||
scoreboard.AddBasket("Shot is good");
|
||||
// over to opponent
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.682f * defense / 8)
|
||||
{
|
||||
_io.WriteLine("Shot is off target");
|
||||
if (defense / 6 * _random.NextFloat() > 0.45f)
|
||||
{
|
||||
scoreboard.Turnover($"Rebound to {visitingTeam}");
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Dartmouth controls the rebound.");
|
||||
if (_random.NextFloat() <= 0.4f)
|
||||
{
|
||||
// fall through to 1300
|
||||
}
|
||||
else
|
||||
{
|
||||
if (defense == 6)
|
||||
{
|
||||
if (_random.NextFloat() > 0.6f)
|
||||
{
|
||||
scoreboard.Turnover();
|
||||
scoreboard.AddBasket($"Pass stolen by {visitingTeam} easy layup.");
|
||||
_io.WriteLine();
|
||||
}
|
||||
}
|
||||
_io.Write("Ball passed back to you. ");
|
||||
// next shot without writeline
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.782f * defense / 8)
|
||||
{
|
||||
scoreboard.Offense =
|
||||
ContestBall(0.5f, homeTeam, visitingTeam, "Shot is blocked. Ball controlled by {0}.");
|
||||
// go to next shot
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.843f * defense / 8)
|
||||
{
|
||||
FreeThrows("Shooter is fouled. Two shots.");
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
{
|
||||
scoreboard.Turnover("Charging foul. Dartmouth loses ball.");
|
||||
// over to opponent
|
||||
}
|
||||
}
|
||||
// 1300
|
||||
time++;
|
||||
if (time == 50)
|
||||
{
|
||||
scoreboard.Display(Resource.Formats.EndOfFirstHalf);
|
||||
// Loop back to center jump;
|
||||
}
|
||||
if (time == 92)
|
||||
_io.WriteLine("Jump shot");
|
||||
if (_random.NextFloat() <= 0.341f * defense / 8)
|
||||
{
|
||||
_io.Write(Resource.Streams.TwoMinutesLeft);
|
||||
}
|
||||
|
||||
_io.WriteLine(shot == 3 ? "Lay up." : "Set shot.");
|
||||
|
||||
if (_random.NextFloat() <= 0.4f * defense / 7)
|
||||
{
|
||||
scoreboard.AddBasket("Shot is good. Two points.");
|
||||
scoreboard.AddBasket("Shot is good");
|
||||
// over to opponent
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.7f * defense / 7)
|
||||
else if (_random.NextFloat() <= 0.682f * defense / 8)
|
||||
{
|
||||
_io.WriteLine("Shot is off the rim.");
|
||||
if (_random.NextFloat() <= 2 / 3f)
|
||||
_io.WriteLine("Shot is off target");
|
||||
if (defense / 6 * _random.NextFloat() > 0.45f)
|
||||
{
|
||||
scoreboard.Turnover($"{visitingTeam} controls the rebound.");
|
||||
scoreboard.Turnover($"Rebound to {scoreboard.Visitors}");
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Dartmouth controls the rebound");
|
||||
_io.WriteLine("Dartmouth controls the rebound.");
|
||||
if (_random.NextFloat() <= 0.4f)
|
||||
{
|
||||
// goto 1300
|
||||
// fall through to 1300
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Ball passed back to you.");
|
||||
// go to next shot
|
||||
if (defense == 6)
|
||||
{
|
||||
if (_random.NextFloat() > 0.6f)
|
||||
{
|
||||
scoreboard.Turnover();
|
||||
scoreboard.AddBasket($"Pass stolen by {scoreboard.Visitors} easy layup.");
|
||||
_io.WriteLine();
|
||||
}
|
||||
}
|
||||
_io.Write("Ball passed back to you. ");
|
||||
// next shot without writeline
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.875f * defense / 7)
|
||||
else if (_random.NextFloat() <= 0.782f * defense / 8)
|
||||
{
|
||||
FreeThrows("Shooter fouled. Two shots.");
|
||||
// over to opponent
|
||||
scoreboard.Offense = ContestBall(0.5f, scoreboard, "Shot is blocked. Ball controlled by {0}.");
|
||||
// go to next shot
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.925f * defense / 7)
|
||||
else if (_random.NextFloat() <= 0.843f * defense / 8)
|
||||
{
|
||||
scoreboard.Turnover($"Shot blocked. {visitingTeam}'s ball.");
|
||||
FreeThrows(scoreboard, "Shooter is fouled. Two shots.");
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
@@ -184,91 +130,90 @@ internal class Game
|
||||
// over to opponent
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
time++;
|
||||
if (time == 50)
|
||||
// 1300
|
||||
clock.Increment(scoreboard);
|
||||
if (clock.IsHalfTime)
|
||||
{
|
||||
scoreboard.Display(Resource.Formats.EndOfFirstHalf);
|
||||
// Loop back to center jump;
|
||||
}
|
||||
if (time == 92)
|
||||
|
||||
_io.WriteLine(shot == 3 ? "Lay up." : "Set shot.");
|
||||
|
||||
if (_random.NextFloat() <= 0.4f * defense / 7)
|
||||
{
|
||||
_io.Write(Resource.Streams.TwoMinutesLeft);
|
||||
scoreboard.AddBasket("Shot is good. Two points.");
|
||||
// over to opponent
|
||||
}
|
||||
|
||||
_io.WriteLine();
|
||||
var shot = _random.NextFloat(1, 3.5f);
|
||||
if (shot <= 2)
|
||||
else if (_random.NextFloat() <= 0.7f * defense / 7)
|
||||
{
|
||||
_io.WriteLine("Jump shot.");
|
||||
|
||||
if (_random.NextFloat() <= 0.35f * defense / 8)
|
||||
_io.WriteLine("Shot is off the rim.");
|
||||
if (_random.NextFloat() <= 2 / 3f)
|
||||
{
|
||||
scoreboard.AddBasket("Shot is good.");
|
||||
// over to Dartmouth
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.75f * defense / 8)
|
||||
{
|
||||
_io.WriteLine("Shot is off the rim.");
|
||||
if (_random.NextFloat() <= 0.5f / defense * 6)
|
||||
{
|
||||
scoreboard.Turnover("Dartmouth controls the rebound.");
|
||||
// over to Dartmouth
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine($"{visitingTeam} controls the rebound.");
|
||||
if (defense == 6)
|
||||
{
|
||||
if (_random.NextFloat() <= 0.25f)
|
||||
{
|
||||
scoreboard.Turnover();
|
||||
scoreboard.AddBasket("Ball stolen. Easy lay up for Dartmouth.");
|
||||
_io.WriteLine();
|
||||
// next opponent shot
|
||||
}
|
||||
}
|
||||
if (_random.NextFloat() <= 0.5f)
|
||||
{
|
||||
_io.WriteLine($"Pass back to {visitingTeam} guard.");
|
||||
// next opponent shot
|
||||
}
|
||||
// goto 3500
|
||||
}
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.9f * defense / 8)
|
||||
{
|
||||
FreeThrows("Player fouled. Two shots.");
|
||||
// next Dartmouth shot
|
||||
scoreboard.Turnover($"{scoreboard.Visitors} controls the rebound.");
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Offensive foul. Dartmouth's ball.");
|
||||
// next Dartmouth shot
|
||||
_io.WriteLine("Dartmouth controls the rebound");
|
||||
if (_random.NextFloat() <= 0.4f)
|
||||
{
|
||||
// goto 1300
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Ball passed back to you.");
|
||||
// go to next shot
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.875f * defense / 7)
|
||||
{
|
||||
FreeThrows(scoreboard, "Shooter fouled. Two shots.");
|
||||
// over to opponent
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.925f * defense / 7)
|
||||
{
|
||||
scoreboard.Turnover($"Shot blocked. {scoreboard.Visitors}'s ball.");
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
{
|
||||
scoreboard.Turnover("Charging foul. Dartmouth loses ball.");
|
||||
// over to opponent
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 3500
|
||||
_io.WriteLine(shot > 3 ? "Set shot." : "Lay up.");
|
||||
private Action<Scoreboard> VisitingTeamPlay(Clock clock, Defense defense) => scoreboard =>
|
||||
{
|
||||
clock.Increment(scoreboard);
|
||||
if (clock.IsHalfTime)
|
||||
{
|
||||
// Loop back to center jump;
|
||||
}
|
||||
|
||||
if (_random.NextFloat() <= 0.413f * defense / 7)
|
||||
_io.WriteLine();
|
||||
var shot = _random.NextFloat(1, 3.5f);
|
||||
if (shot <= 2)
|
||||
{
|
||||
_io.WriteLine("Jump shot.");
|
||||
|
||||
if (_random.NextFloat() <= 0.35f * defense / 8)
|
||||
{
|
||||
scoreboard.AddBasket("Shot is good.");
|
||||
// over to Dartmouth
|
||||
}
|
||||
else
|
||||
else if (_random.NextFloat() <= 0.75f * defense / 8)
|
||||
{
|
||||
_io.WriteLine("Shot is missed.");
|
||||
if (_random.NextFloat() <= 0.5f * 6 / defense)
|
||||
_io.WriteLine("Shot is off the rim.");
|
||||
if (_random.NextFloat() <= 0.5f / defense * 6)
|
||||
{
|
||||
scoreboard.Turnover("Dartmouth controls the rebound.");
|
||||
// over to Dartmouth
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine($"{visitingTeam} controls the rebound.");
|
||||
_io.WriteLine($"{scoreboard.Visitors} controls the rebound.");
|
||||
if (defense == 6)
|
||||
{
|
||||
if (_random.NextFloat() <= 0.25f)
|
||||
@@ -281,37 +226,85 @@ internal class Game
|
||||
}
|
||||
if (_random.NextFloat() <= 0.5f)
|
||||
{
|
||||
_io.WriteLine($"Pass back to {visitingTeam} guard.");
|
||||
_io.WriteLine($"Pass back to {scoreboard.Visitors} guard.");
|
||||
// next opponent shot
|
||||
}
|
||||
// goto 3500
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Team ContestBall(float probability, Team a, Team b, string messageFormat)
|
||||
{
|
||||
var winner = _random.NextFloat() <= probability ? a : b;
|
||||
_io.WriteLine(messageFormat, winner);
|
||||
return winner;
|
||||
}
|
||||
|
||||
void FreeThrows(string message)
|
||||
{
|
||||
_io.WriteLine(message);
|
||||
|
||||
if (_random.NextFloat() <= 0.49)
|
||||
else if (_random.NextFloat() <= 0.9f * defense / 8)
|
||||
{
|
||||
scoreboard.AddFreeThrows(2, "Shooter makes both shots.");
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.75)
|
||||
{
|
||||
scoreboard.AddFreeThrows(1, "Shooter makes one shot and misses one.");
|
||||
FreeThrows(scoreboard, "Player fouled. Two shots.");
|
||||
// next Dartmouth shot
|
||||
}
|
||||
else
|
||||
{
|
||||
scoreboard.AddFreeThrows(0, "Both shots missed.");
|
||||
_io.WriteLine("Offensive foul. Dartmouth's ball.");
|
||||
// next Dartmouth shot
|
||||
}
|
||||
}
|
||||
|
||||
// 3500
|
||||
_io.WriteLine(shot > 3 ? "Set shot." : "Lay up.");
|
||||
|
||||
if (_random.NextFloat() <= 0.413f * defense / 7)
|
||||
{
|
||||
scoreboard.AddBasket("Shot is good.");
|
||||
// over to Dartmouth
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Shot is missed.");
|
||||
if (_random.NextFloat() <= 0.5f * 6 / defense)
|
||||
{
|
||||
scoreboard.Turnover("Dartmouth controls the rebound.");
|
||||
// over to Dartmouth
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine($"{scoreboard.Visitors} controls the rebound.");
|
||||
if (defense == 6)
|
||||
{
|
||||
if (_random.NextFloat() <= 0.25f)
|
||||
{
|
||||
scoreboard.Turnover();
|
||||
scoreboard.AddBasket("Ball stolen. Easy lay up for Dartmouth.");
|
||||
_io.WriteLine();
|
||||
// next opponent shot
|
||||
}
|
||||
}
|
||||
if (_random.NextFloat() <= 0.5f)
|
||||
{
|
||||
_io.WriteLine($"Pass back to {scoreboard.Visitors} guard.");
|
||||
// next opponent shot
|
||||
}
|
||||
// goto 3500
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Team ContestBall(float probability, Scoreboard scoreboard, string messageFormat)
|
||||
{
|
||||
var winner = _random.NextFloat() <= probability ? scoreboard.Home : scoreboard.Visitors;
|
||||
_io.WriteLine(messageFormat, winner);
|
||||
return winner;
|
||||
}
|
||||
|
||||
void FreeThrows(Scoreboard scoreboard, string message)
|
||||
{
|
||||
_io.WriteLine(message);
|
||||
|
||||
if (_random.NextFloat() <= 0.49)
|
||||
{
|
||||
scoreboard.AddFreeThrows(2, "Shooter makes both shots.");
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.75)
|
||||
{
|
||||
scoreboard.AddFreeThrows(1, "Shooter makes one shot and misses one.");
|
||||
}
|
||||
else
|
||||
{
|
||||
scoreboard.AddFreeThrows(0, "Both shots missed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,20 +8,19 @@ internal class Scoreboard
|
||||
private readonly Dictionary<Team, uint> _scores;
|
||||
private readonly IReadWrite _io;
|
||||
|
||||
private Team _home;
|
||||
private Team _visitors;
|
||||
|
||||
public Scoreboard(Team home, Team visitors, IReadWrite io)
|
||||
{
|
||||
_scores = new() { [home] = 0, [visitors] = 0 };
|
||||
_home = home;
|
||||
_visitors = visitors;
|
||||
Home = home;
|
||||
Visitors = visitors;
|
||||
Offense = home;
|
||||
_io = io;
|
||||
}
|
||||
|
||||
public bool ScoresAreEqual => _scores[_home] == _scores[_visitors];
|
||||
public bool ScoresAreEqual => _scores[Home] == _scores[Visitors];
|
||||
public Team Offense { get; set; }
|
||||
public Team Home { get; }
|
||||
public Team Visitors { get; }
|
||||
|
||||
public void AddBasket(string message) => AddScore(2, message);
|
||||
|
||||
@@ -39,9 +38,9 @@ internal class Scoreboard
|
||||
{
|
||||
if (message is not null) { _io.WriteLine(message); }
|
||||
|
||||
Offense = Offense == _home ? _visitors : _home;
|
||||
Offense = Offense == Home ? Visitors : Home;
|
||||
}
|
||||
|
||||
public void Display(string? format = null) =>
|
||||
_io.WriteLine(format ?? Resource.Formats.Score, _home, _scores[_home], _visitors, _scores[_visitors]);
|
||||
_io.WriteLine(format ?? Resource.Formats.Score, Home, _scores[Home], Visitors, _scores[Visitors]);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
namespace Basketball;
|
||||
|
||||
internal record Team(string Name)
|
||||
internal record Team(string Name, Action<Scoreboard> PlayResolution)
|
||||
{
|
||||
public override string ToString() => Name;
|
||||
|
||||
public void ResolvePlay(Scoreboard scoreboard) => PlayResolution.Invoke(scoreboard);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user