mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-23 07:29:02 -08:00
Encapsulate shot number and name
This commit is contained in:
8
07_Basketball/csharp/IRandomExtensions.cs
Normal file
8
07_Basketball/csharp/IRandomExtensions.cs
Normal 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));
|
||||||
|
}
|
||||||
@@ -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)
|
while (true)
|
||||||
{
|
{
|
||||||
var shot = io.ReadNumber(prompt);
|
if (io.TryReadInteger(prompt, out var value) && Shot.TryGet(value, out var shot))
|
||||||
if ((int)shot == shot && shot >= 0 && shot <= 4) { return (int)shot; }
|
{
|
||||||
|
return shot;
|
||||||
|
}
|
||||||
io.Write("Incorrect answer. Retype it. ");
|
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]);
|
|
||||||
}
|
}
|
||||||
9
07_Basketball/csharp/JumpShot.cs
Normal file
9
07_Basketball/csharp/JumpShot.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Basketball;
|
||||||
|
|
||||||
|
public class JumpShot : Shot
|
||||||
|
{
|
||||||
|
public JumpShot()
|
||||||
|
: base("Jump shot")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ using Games.Common.Randomness;
|
|||||||
|
|
||||||
namespace Basketball.Plays;
|
namespace Basketball.Plays;
|
||||||
|
|
||||||
internal class BallContest : Play
|
internal class BallContest
|
||||||
{
|
{
|
||||||
private readonly float _probability;
|
private readonly float _probability;
|
||||||
private readonly string _messageFormat;
|
private readonly string _messageFormat;
|
||||||
@@ -11,7 +11,6 @@ internal class BallContest : Play
|
|||||||
private readonly IRandom _random;
|
private readonly IRandom _random;
|
||||||
|
|
||||||
internal BallContest(float probability, string messageFormat, IReadWrite io, IRandom random)
|
internal BallContest(float probability, string messageFormat, IReadWrite io, IRandom random)
|
||||||
: base(io, random)
|
|
||||||
{
|
{
|
||||||
_io = io;
|
_io = io;
|
||||||
_probability = probability;
|
_probability = probability;
|
||||||
@@ -19,7 +18,7 @@ internal class BallContest : Play
|
|||||||
_random = random;
|
_random = random;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override bool Resolve(Scoreboard scoreboard)
|
internal bool Resolve(Scoreboard scoreboard)
|
||||||
{
|
{
|
||||||
var winner = _random.NextFloat() <= _probability ? scoreboard.Home : scoreboard.Visitors;
|
var winner = _random.NextFloat() <= _probability ? scoreboard.Home : scoreboard.Visitors;
|
||||||
scoreboard.Offense = winner;
|
scoreboard.Offense = winner;
|
||||||
|
|||||||
@@ -26,35 +26,35 @@ internal class HomeTeamPlay : Play
|
|||||||
|
|
||||||
if (_random.NextFloat() >= 0.5f && _clock.IsFullTime) { return true; }
|
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"));
|
_defense.Set(_io.ReadDefense("Your new defensive alignment is"));
|
||||||
_io.WriteLine();
|
_io.WriteLine();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shot == 1 || shot == 2)
|
if (shot is JumpShot jumpShot)
|
||||||
{
|
{
|
||||||
if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
|
if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
|
||||||
ResolveJumpShot(scoreboard);
|
Resolve(jumpShot, scoreboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
_playContinues |= shot >= 3;
|
_playContinues |= shot is not JumpShot;
|
||||||
|
|
||||||
while (_playContinues)
|
while (_playContinues)
|
||||||
{
|
{
|
||||||
if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
|
if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
|
||||||
ResolveLayupOrSetShot(scoreboard, shot);
|
Resolve(shot, scoreboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
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);
|
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"))
|
.Do(0.341f, () => scoreboard.AddBasket("Shot is good"))
|
||||||
.Or(0.682f, () => ResolveShotOffTarget(scoreboard))
|
.Or(0.682f, () => ResolveShotOffTarget(scoreboard))
|
||||||
.Or(0.782f, () => ballContest.Resolve(scoreboard))
|
.Or(0.782f, () => ballContest.Resolve(scoreboard))
|
||||||
@@ -62,11 +62,11 @@ internal class HomeTeamPlay : Play
|
|||||||
.Or(() => scoreboard.Turnover($"Charging foul. {scoreboard.Home} loses ball."));
|
.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;
|
_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."))
|
.Do(0.4f, () => scoreboard.AddBasket("Shot is good. Two points."))
|
||||||
.Or(0.7f, () => ResolveShotOffTheRim(scoreboard))
|
.Or(0.7f, () => ResolveShotOffTheRim(scoreboard))
|
||||||
.Or(0.875f, () => ResolveFreeThrows(scoreboard, "Shooter fouled. Two shots."))
|
.Or(0.875f, () => ResolveFreeThrows(scoreboard, "Shooter fouled. Two shots."))
|
||||||
|
|||||||
@@ -23,42 +23,37 @@ internal class VisitingTeamPlay : Play
|
|||||||
if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
|
if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
|
||||||
|
|
||||||
_io.WriteLine();
|
_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)
|
while (_playContinues)
|
||||||
{
|
{
|
||||||
ResolveLayupOrSetShot(scoreboard, shot);
|
_playContinues = false;
|
||||||
|
Resolve(shot, scoreboard);
|
||||||
|
_io.WriteLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResolveJumpShot(Scoreboard scoreboard)
|
private void Resolve(JumpShot shot, Scoreboard scoreboard) =>
|
||||||
{
|
Resolve(shot.ToString(), _defense / 8)
|
||||||
Resolve("Jump shot.", _defense / 8)
|
|
||||||
.Do(0.35f, () => scoreboard.AddBasket("Shot is good."))
|
.Do(0.35f, () => scoreboard.AddBasket("Shot is good."))
|
||||||
.Or(0.75f, () => ResolveBadShot(scoreboard, "Shot is off the rim.", _defense * 6))
|
.Or(0.75f, () => ResolveBadShot(scoreboard, "Shot is off the rim.", _defense * 6))
|
||||||
.Or(0.9f, () => ResolveFreeThrows(scoreboard, "Player fouled. Two shots."))
|
.Or(0.9f, () => ResolveFreeThrows(scoreboard, "Player fouled. Two shots."))
|
||||||
.Or(() => _io.WriteLine("Offensive foul. Dartmouth's ball."));
|
.Or(() => _io.WriteLine("Offensive foul. Dartmouth's ball."));
|
||||||
_io.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResolveLayupOrSetShot(Scoreboard scoreboard, float shot)
|
private void Resolve(Shot shot, Scoreboard scoreboard) =>
|
||||||
{
|
Resolve(shot.ToString(), _defense / 7)
|
||||||
_playContinues = false;
|
|
||||||
|
|
||||||
Resolve(shot > 3 ? "Set shot." : "Lay up.", _defense / 7)
|
|
||||||
.Do(0.413f, () => scoreboard.AddBasket("Shot is good."))
|
.Do(0.413f, () => scoreboard.AddBasket("Shot is good."))
|
||||||
.Or(() => ResolveBadShot(scoreboard, "Shot is missed.", 6 / _defense));
|
.Or(() => ResolveBadShot(scoreboard, "Shot is missed.", 6 / _defense));
|
||||||
_io.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResolveBadShot(Scoreboard scoreboard, string message, float defenseFactor) =>
|
void ResolveBadShot(Scoreboard scoreboard, string message, float defenseFactor) =>
|
||||||
Resolve(message, defenseFactor)
|
Resolve(message, defenseFactor)
|
||||||
|
|||||||
36
07_Basketball/csharp/Shot.cs
Normal file
36
07_Basketball/csharp/Shot.cs
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user