Encapsulate shot number and name

This commit is contained in:
Andrew Cooper
2022-04-10 13:10:46 +10:00
parent 1ce5f7b3a7
commit 11ee731832
7 changed files with 89 additions and 36 deletions

View File

@@ -0,0 +1,8 @@
using Games.Common.Randomness;
namespace Basketball;
internal static class IRandomExtensions
{
internal static Shot NextShot(this IRandom random) => Shot.Get(random.NextFloat(1, 3.5f));
}

View File

@@ -13,16 +13,22 @@ internal static class IReadWriteExtensions
}
}
public static int ReadShot(this IReadWrite io, string prompt)
private static bool TryReadInteger(this IReadWrite io, string prompt, out int value)
{
var floatValue = io.ReadNumber(prompt);
value = (int)floatValue;
return value == floatValue;
}
public static Shot? ReadShot(this IReadWrite io, string prompt)
{
while (true)
{
var shot = io.ReadNumber(prompt);
if ((int)shot == shot && shot >= 0 && shot <= 4) { return (int)shot; }
if (io.TryReadInteger(prompt, out var value) && Shot.TryGet(value, out var shot))
{
return shot;
}
io.Write("Incorrect answer. Retype it. ");
}
}
public static void WriteScore(this IReadWrite io, string format, string opponent, Dictionary<string ,int> score) =>
io.WriteLine(format, "Dartmouth", score["Dartmouth"], opponent, score[opponent]);
}

View File

@@ -0,0 +1,9 @@
namespace Basketball;
public class JumpShot : Shot
{
public JumpShot()
: base("Jump shot")
{
}
}

View File

@@ -3,7 +3,7 @@ using Games.Common.Randomness;
namespace Basketball.Plays;
internal class BallContest : Play
internal class BallContest
{
private readonly float _probability;
private readonly string _messageFormat;
@@ -11,7 +11,6 @@ internal class BallContest : Play
private readonly IRandom _random;
internal BallContest(float probability, string messageFormat, IReadWrite io, IRandom random)
: base(io, random)
{
_io = io;
_probability = probability;
@@ -19,7 +18,7 @@ internal class BallContest : Play
_random = random;
}
internal override bool Resolve(Scoreboard scoreboard)
internal bool Resolve(Scoreboard scoreboard)
{
var winner = _random.NextFloat() <= _probability ? scoreboard.Home : scoreboard.Visitors;
scoreboard.Offense = winner;

View File

@@ -26,35 +26,35 @@ internal class HomeTeamPlay : Play
if (_random.NextFloat() >= 0.5f && _clock.IsFullTime) { return true; }
if (shot == 0)
if (shot is null)
{
_defense.Set(_io.ReadDefense("Your new defensive alignment is"));
_io.WriteLine();
return false;
}
if (shot == 1 || shot == 2)
if (shot is JumpShot jumpShot)
{
if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
ResolveJumpShot(scoreboard);
Resolve(jumpShot, scoreboard);
}
_playContinues |= shot >= 3;
_playContinues |= shot is not JumpShot;
while (_playContinues)
{
if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
ResolveLayupOrSetShot(scoreboard, shot);
Resolve(shot, scoreboard);
}
return false;
}
private void ResolveJumpShot(Scoreboard scoreboard)
private void Resolve(JumpShot shot, Scoreboard scoreboard)
{
var ballContest = new BallContest(0.5f, "Shot is blocked. Ball controlled by {0}.", _io, _random);
Resolve("Jump shot", _defense / 8)
Resolve(shot.ToString(), _defense / 8)
.Do(0.341f, () => scoreboard.AddBasket("Shot is good"))
.Or(0.682f, () => ResolveShotOffTarget(scoreboard))
.Or(0.782f, () => ballContest.Resolve(scoreboard))
@@ -62,11 +62,11 @@ internal class HomeTeamPlay : Play
.Or(() => scoreboard.Turnover($"Charging foul. {scoreboard.Home} loses ball."));
}
private void ResolveLayupOrSetShot(Scoreboard scoreboard, int shot)
private void Resolve(Shot shot, Scoreboard scoreboard)
{
_playContinues = false;
Resolve(shot == 3 ? "Lay up." : "Set shot.", _defense / 7)
Resolve(shot.ToString(), _defense / 7)
.Do(0.4f, () => scoreboard.AddBasket("Shot is good. Two points."))
.Or(0.7f, () => ResolveShotOffTheRim(scoreboard))
.Or(0.875f, () => ResolveFreeThrows(scoreboard, "Shooter fouled. Two shots."))

View File

@@ -23,42 +23,37 @@ internal class VisitingTeamPlay : Play
if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
_io.WriteLine();
var shot = _random.NextFloat(1, 3.5f);
var shot = _random.NextShot();
if (shot <= 2)
if (shot is JumpShot jumpShot)
{
ResolveJumpShot(scoreboard);
Resolve(jumpShot, scoreboard);
_io.WriteLine();
}
_playContinues |= shot > 2;
_playContinues |= shot is not JumpShot;
while (_playContinues)
{
ResolveLayupOrSetShot(scoreboard, shot);
_playContinues = false;
Resolve(shot, scoreboard);
_io.WriteLine();
}
return false;
}
private void ResolveJumpShot(Scoreboard scoreboard)
{
Resolve("Jump shot.", _defense / 8)
private void Resolve(JumpShot shot, Scoreboard scoreboard) =>
Resolve(shot.ToString(), _defense / 8)
.Do(0.35f, () => scoreboard.AddBasket("Shot is good."))
.Or(0.75f, () => ResolveBadShot(scoreboard, "Shot is off the rim.", _defense * 6))
.Or(0.9f, () => ResolveFreeThrows(scoreboard, "Player fouled. Two shots."))
.Or(() => _io.WriteLine("Offensive foul. Dartmouth's ball."));
_io.WriteLine();
}
private void ResolveLayupOrSetShot(Scoreboard scoreboard, float shot)
{
_playContinues = false;
Resolve(shot > 3 ? "Set shot." : "Lay up.", _defense / 7)
private void Resolve(Shot shot, Scoreboard scoreboard) =>
Resolve(shot.ToString(), _defense / 7)
.Do(0.413f, () => scoreboard.AddBasket("Shot is good."))
.Or(() => ResolveBadShot(scoreboard, "Shot is missed.", 6 / _defense));
_io.WriteLine();
}
void ResolveBadShot(Scoreboard scoreboard, string message, float defenseFactor) =>
Resolve(message, defenseFactor)

View File

@@ -0,0 +1,36 @@
namespace Basketball;
public class Shot
{
private readonly string _name;
public Shot(string name)
{
_name = name;
}
public static bool TryGet(int shotNumber, out Shot? shot)
{
shot = shotNumber switch
{
// Although the game instructions reference two different jump shots,
// the original game code treats them both the same and just prints "Jump shot"
<= 2 => new JumpShot(),
3 => new Shot("Lay up"),
4 => new Shot("Set shot"),
_ => null
};
return shot is not null;
}
public static Shot Get(float shotNumber) =>
shotNumber switch
{
<= 2 => new JumpShot(),
> 3 => new Shot("Set shot"),
> 2 => new Shot("Lay up"),
_ => throw new Exception("Unexpected value")
};
public override string ToString() => _name;
}