Split out play resolution

This commit is contained in:
Andrew Cooper
2022-03-30 16:50:48 +11:00
parent 741b135d99
commit 1ba6dd48aa
5 changed files with 252 additions and 214 deletions

View 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;
}

View 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;
}

View File

@@ -19,32 +19,41 @@ internal class Game
{
_io.Write(Resource.Streams.Introduction);
var defense = _io.ReadDefense("Your starting defense will be");
var homeTeam = new Team("Dartmouth");
var defense = new Defense(_io.ReadDefense("Your starting defense will be"));
var clock = new Clock(_io);
_io.WriteLine();
var visitingTeam = new Team(_io.ReadString("Choose your opponent"));
var time = 0;
var scoreboard = new Scoreboard(homeTeam, visitingTeam, _io);
var scoreboard = new Scoreboard(
new Team("Dartmouth", HomeTeamPlay(clock, defense)),
new Team(_io.ReadString("Choose your opponent"), VisitingTeamPlay(clock, defense)),
_io);
while (true)
{
_io.WriteLine("Center jump");
scoreboard.Offense = ContestBall(0.6f, visitingTeam, homeTeam, "{0} controls the tap");
scoreboard.Offense = ContestBall(0.4f, scoreboard, "{0} controls the tap");
_io.WriteLine();
if (scoreboard.Offense == homeTeam)
while (true)
{
scoreboard.Offense.ResolvePlay(scoreboard);
}
}
}
private Action<Scoreboard> HomeTeamPlay(Clock clock, Defense defense) => scoreboard =>
{
var shot = _io.ReadShot("Your shot");
if (_random.NextFloat() >= 0.5f && time >= 100)
if (_random.NextFloat() >= 0.5f && clock.IsFullTime)
{
_io.WriteLine();
if (scoreboard.ScoresAreEqual)
{
scoreboard.Display(Resource.Formats.EndOfSecondHalf);
time = 93;
clock.StartOvertime();
// Loop back to center jump
}
else
@@ -57,22 +66,17 @@ internal class Game
{
if (shot == 0)
{
defense = _io.ReadDefense("Your new defensive alignment is");
defense.Set(_io.ReadDefense("Your new defensive alignment is"));
// go to next shot
}
if (shot == 1 || shot == 2)
{
time++;
if (time == 50)
clock.Increment(scoreboard);
if (clock.IsHalfTime)
{
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)
{
@@ -84,7 +88,7 @@ internal class Game
_io.WriteLine("Shot is off target");
if (defense / 6 * _random.NextFloat() > 0.45f)
{
scoreboard.Turnover($"Rebound to {visitingTeam}");
scoreboard.Turnover($"Rebound to {scoreboard.Visitors}");
// over to opponent
}
else
@@ -101,7 +105,7 @@ internal class Game
if (_random.NextFloat() > 0.6f)
{
scoreboard.Turnover();
scoreboard.AddBasket($"Pass stolen by {visitingTeam} easy layup.");
scoreboard.AddBasket($"Pass stolen by {scoreboard.Visitors} easy layup.");
_io.WriteLine();
}
}
@@ -112,13 +116,12 @@ internal class Game
}
else if (_random.NextFloat() <= 0.782f * defense / 8)
{
scoreboard.Offense =
ContestBall(0.5f, homeTeam, visitingTeam, "Shot is blocked. Ball controlled by {0}.");
scoreboard.Offense = ContestBall(0.5f, scoreboard, "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.");
FreeThrows(scoreboard, "Shooter is fouled. Two shots.");
// over to opponent
}
else
@@ -128,16 +131,11 @@ internal class Game
}
}
// 1300
time++;
if (time == 50)
clock.Increment(scoreboard);
if (clock.IsHalfTime)
{
scoreboard.Display(Resource.Formats.EndOfFirstHalf);
// Loop back to center jump;
}
if (time == 92)
{
_io.Write(Resource.Streams.TwoMinutesLeft);
}
_io.WriteLine(shot == 3 ? "Lay up." : "Set shot.");
@@ -151,7 +149,7 @@ internal class Game
_io.WriteLine("Shot is off the rim.");
if (_random.NextFloat() <= 2 / 3f)
{
scoreboard.Turnover($"{visitingTeam} controls the rebound.");
scoreboard.Turnover($"{scoreboard.Visitors} controls the rebound.");
// over to opponent
}
else
@@ -170,12 +168,12 @@ internal class Game
}
else if (_random.NextFloat() <= 0.875f * defense / 7)
{
FreeThrows("Shooter fouled. Two shots.");
FreeThrows(scoreboard, "Shooter fouled. Two shots.");
// over to opponent
}
else if (_random.NextFloat() <= 0.925f * defense / 7)
{
scoreboard.Turnover($"Shot blocked. {visitingTeam}'s ball.");
scoreboard.Turnover($"Shot blocked. {scoreboard.Visitors}'s ball.");
// over to opponent
}
else
@@ -184,19 +182,15 @@ internal class Game
// over to opponent
}
}
}
else
};
private Action<Scoreboard> VisitingTeamPlay(Clock clock, Defense defense) => scoreboard =>
{
time++;
if (time == 50)
clock.Increment(scoreboard);
if (clock.IsHalfTime)
{
scoreboard.Display(Resource.Formats.EndOfFirstHalf);
// Loop back to center jump;
}
if (time == 92)
{
_io.Write(Resource.Streams.TwoMinutesLeft);
}
_io.WriteLine();
var shot = _random.NextFloat(1, 3.5f);
@@ -219,7 +213,7 @@ internal class Game
}
else
{
_io.WriteLine($"{visitingTeam} controls the rebound.");
_io.WriteLine($"{scoreboard.Visitors} controls the rebound.");
if (defense == 6)
{
if (_random.NextFloat() <= 0.25f)
@@ -232,7 +226,7 @@ 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
@@ -240,7 +234,7 @@ internal class Game
}
else if (_random.NextFloat() <= 0.9f * defense / 8)
{
FreeThrows("Player fouled. Two shots.");
FreeThrows(scoreboard, "Player fouled. Two shots.");
// next Dartmouth shot
}
else
@@ -268,7 +262,7 @@ internal class Game
}
else
{
_io.WriteLine($"{visitingTeam} controls the rebound.");
_io.WriteLine($"{scoreboard.Visitors} controls the rebound.");
if (defense == 6)
{
if (_random.NextFloat() <= 0.25f)
@@ -281,22 +275,22 @@ 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)
Team ContestBall(float probability, Scoreboard scoreboard, string messageFormat)
{
var winner = _random.NextFloat() <= probability ? a : b;
var winner = _random.NextFloat() <= probability ? scoreboard.Home : scoreboard.Visitors;
_io.WriteLine(messageFormat, winner);
return winner;
}
void FreeThrows(string message)
void FreeThrows(Scoreboard scoreboard, string message)
{
_io.WriteLine(message);
@@ -314,4 +308,3 @@ internal class Game
}
}
}
}

View File

@@ -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]);
}

View File

@@ -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);
}