mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-25 20:34:32 -08:00
Transliterate game logic
This commit is contained in:
@@ -14,7 +14,7 @@ Both teams use the same defense, but you may call it:
|
||||
- Enter (7): Zone
|
||||
- Enter (7.5): None
|
||||
|
||||
To change defense, type “0” as your next shot.
|
||||
To change defense, type "0" as your next shot.
|
||||
|
||||
Note: The game is biased slightly in favor of Dartmouth. The average probability of a Dartmouth shot being good is 62.95% compared to a probability of 61.85% for their opponent. (This makes the sample run slightly remarkable in that Cornell won by a score of 45 to 42 Hooray for the Big Red!)
|
||||
|
||||
@@ -33,3 +33,19 @@ http://www.vintage-basic.net/games.html
|
||||
|
||||
(please note any difficulties or challenges in porting here)
|
||||
|
||||
##### Original bugs
|
||||
|
||||
###### Initial defense selection
|
||||
|
||||
If a number <6 is entered for the starting defense then the original code prompts again until a value >=6 is entered,
|
||||
but then skips the opponent selection center jump.
|
||||
|
||||
The C# port does not reproduce this behavior. It does prompt for a correct value, but will then go to opponent selection
|
||||
followed by the center jump.
|
||||
|
||||
###### Unvalidated defense selection
|
||||
|
||||
The original code does not validate the value entered for the defense beyond checking that it is >=6. A large enough
|
||||
defense value will guarantee that all shots are good, and the game gets rather predictable.
|
||||
|
||||
This bug is preserved in the C# port.
|
||||
|
||||
@@ -6,4 +6,13 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources/*.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
352
07_Basketball/csharp/Game.cs
Normal file
352
07_Basketball/csharp/Game.cs
Normal file
@@ -0,0 +1,352 @@
|
||||
using Basketball.Resources;
|
||||
using Games.Common.IO;
|
||||
using Games.Common.Randomness;
|
||||
|
||||
namespace Basketball;
|
||||
|
||||
internal class Game
|
||||
{
|
||||
private readonly TextIO _io;
|
||||
private readonly IRandom _random;
|
||||
|
||||
public Game(TextIO io, IRandom random)
|
||||
{
|
||||
_io = io;
|
||||
_random = random;
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
_io.Write(Resource.Streams.Introduction);
|
||||
|
||||
var defense = _io.ReadDefense("Your starting defense will be");
|
||||
|
||||
_io.WriteLine();
|
||||
var opponent = _io.ReadString("Choose your opponent");
|
||||
|
||||
_io.WriteLine("Center jump");
|
||||
var offense = _random.NextFloat() > 0.6 ? "Dartmouth" : opponent;
|
||||
|
||||
_io.WriteLine($"{offense} controls the tap.");
|
||||
|
||||
var time = 0;
|
||||
var score = new Dictionary<string, int> { ["Dartmouth"] = 0, [opponent] = 0 };
|
||||
|
||||
_io.WriteLine();
|
||||
|
||||
if (offense == "Dartmouth")
|
||||
{
|
||||
var shot = _io.ReadShot("Your shot");
|
||||
|
||||
if (_random.NextFloat() >= 0.5 && time >= 100)
|
||||
{
|
||||
_io.WriteLine();
|
||||
if (score["Dartmouth"] == score[opponent])
|
||||
{
|
||||
_io.WriteScore(Resource.Formats.EndOfSecondHalf, opponent, score);
|
||||
time = 93;
|
||||
// Loop back to center jump
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteScore(Resource.Formats.EndOfGame, opponent, score);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shot == 0)
|
||||
{
|
||||
defense = _io.ReadDefense("Your new defensive alignment is");
|
||||
// go to next shot
|
||||
}
|
||||
|
||||
if (shot == 1 || shot == 2)
|
||||
{
|
||||
time++;
|
||||
if (time == 50)
|
||||
{
|
||||
_io.WriteScore(Resource.Formats.EndOfFirstHalf, opponent, score);
|
||||
// Loop back to center jump;
|
||||
}
|
||||
if (time == 92)
|
||||
{
|
||||
_io.Write(Resource.Streams.TwoMinutesLeft);
|
||||
}
|
||||
_io.WriteLine("Jump shot");
|
||||
if (_random.NextFloat() <= 0.341 * defense / 8)
|
||||
{
|
||||
_io.WriteLine("Shot is good");
|
||||
score["Dartmouth"] += 2;
|
||||
_io.WriteScore(Resource.Formats.Score, opponent, score);
|
||||
// over to opponent
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.682 * defense / 8)
|
||||
{
|
||||
_io.WriteLine("Shot is off target");
|
||||
if (defense / 6 * _random.NextFloat() > 0.45)
|
||||
{
|
||||
_io.WriteLine($"Rebound to {opponent}");
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Dartmouth controls the rebound.");
|
||||
if (_random.NextFloat() <= 0.4)
|
||||
{
|
||||
// fall through to 1300
|
||||
}
|
||||
else
|
||||
{
|
||||
if (defense == 6)
|
||||
{
|
||||
if (_random.NextFloat() > 0.6)
|
||||
{
|
||||
_io.WriteLine($"Pass stolen be {opponent} easy layup.");
|
||||
score[opponent] += 2;
|
||||
_io.WriteScore(Resource.Formats.Score, opponent, score);
|
||||
_io.WriteLine();
|
||||
}
|
||||
}
|
||||
_io.Write("Ball passed back to you. ");
|
||||
// next shot without writeline
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.782 * defense / 8)
|
||||
{
|
||||
offense = _random.NextFloat() <= 0.5 ? "Dartmouth" : opponent;
|
||||
_io.WriteLine($"Shot is blocked. Ball controlled by {offense}.");
|
||||
// go to next shot
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.843 * defense / 8)
|
||||
{
|
||||
_io.WriteLine("Shooter is fouled. Two shots.");
|
||||
FreeShots();
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Charging foul. Dartmouth loses ball.");
|
||||
// over to opponent
|
||||
}
|
||||
}
|
||||
// 1300
|
||||
time++;
|
||||
if (time == 50)
|
||||
{
|
||||
_io.WriteScore(Resource.Formats.EndOfFirstHalf, opponent, score);
|
||||
// Loop back to center jump;
|
||||
}
|
||||
if (time == 92)
|
||||
{
|
||||
_io.Write(Resource.Streams.TwoMinutesLeft);
|
||||
}
|
||||
|
||||
_io.WriteLine(shot == 3 ? "Lay up." : "Set shot.");
|
||||
|
||||
if (7 / defense * _random.NextFloat() <= 0.4)
|
||||
{
|
||||
_io.WriteLine("Shot is good. Two points.");
|
||||
score["Dartmouth"] += 2;
|
||||
_io.WriteScore(Resource.Formats.Score, opponent, score);
|
||||
// over to opponent
|
||||
}
|
||||
else if (7 / defense * _random.NextFloat() <= 0.7)
|
||||
{
|
||||
_io.WriteLine("Shot is off the rim.");
|
||||
if (_random.NextFloat() <= 2 / 3f)
|
||||
{
|
||||
_io.WriteLine($"{opponent} controls the rebound.");
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Dartmouth controls the rebound");
|
||||
if (_random.NextFloat() <= 0.4)
|
||||
{
|
||||
// goto 1300
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Ball passed back to you.");
|
||||
// go to next shot
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (7 / defense * _random.NextFloat() <= 0.875)
|
||||
{
|
||||
_io.WriteLine("Shooter fouled. Two shots.");
|
||||
FreeShots();
|
||||
// over to opponent
|
||||
}
|
||||
else if (7 / defense * _random.NextFloat() <= 0.925)
|
||||
{
|
||||
_io.WriteLine($"Shot blocked. {opponent}'s ball.");
|
||||
// over to opponent
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Charging foul. Dartmouth loses ball.");
|
||||
// over to opponent
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
time++;
|
||||
if (time == 50)
|
||||
{
|
||||
_io.WriteScore(Resource.Formats.EndOfFirstHalf, opponent, score);
|
||||
// Loop back to center jump;
|
||||
}
|
||||
if (time == 92)
|
||||
{
|
||||
_io.Write(Resource.Streams.TwoMinutesLeft);
|
||||
}
|
||||
|
||||
_io.WriteLine();
|
||||
var shot = _random.NextFloat(1, 3.5f);
|
||||
if (shot <= 2)
|
||||
{
|
||||
_io.WriteLine("Jump shot.");
|
||||
|
||||
if (8 / defense * _random.NextFloat() <= 0.35)
|
||||
{
|
||||
_io.WriteLine("Shot is good.");
|
||||
score[opponent] += 2;
|
||||
_io.WriteScore(Resource.Formats.Score, opponent, score);
|
||||
// over to Dartmouth
|
||||
}
|
||||
else if (8 / defense * _random.NextFloat() <= 0.75)
|
||||
{
|
||||
_io.WriteLine("Shot is off the rim.");
|
||||
if (defense / 6 * _random.NextFloat() <= 0.5)
|
||||
{
|
||||
_io.WriteLine("Dartmouth controls the rebound.");
|
||||
// over to Dartmouth
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine($"{opponent} controls the rebound.");
|
||||
if (defense == 6)
|
||||
{
|
||||
if (_random.NextFloat() > 0.75)
|
||||
{
|
||||
_io.WriteLine("Ball stolen. Easy lay up for Dartmouth.");
|
||||
score["Dartmouth"] += 2;
|
||||
_io.WriteScore(Resource.Formats.Score, opponent, score);
|
||||
_io.WriteLine();
|
||||
// next opponent shot
|
||||
}
|
||||
}
|
||||
if (_random.NextFloat() <= 0.5)
|
||||
{
|
||||
_io.WriteLine($"Pass back to {opponent} guard.");
|
||||
// next opponent shot
|
||||
}
|
||||
// goto 3500
|
||||
}
|
||||
}
|
||||
else if (8 / defense * _random.NextFloat() <= 0.9)
|
||||
{
|
||||
_io.WriteLine("Player fouled. Two shots.");
|
||||
FreeShots();
|
||||
// next Dartmouth shot
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Offensive foul. Dartmouth's ball.");
|
||||
// next Dartmouth shot
|
||||
}
|
||||
}
|
||||
|
||||
// 3500
|
||||
_io.WriteLine(shot > 3 ? "Set shot." : "Lay up.");
|
||||
|
||||
if (7 / defense * _random.NextFloat() <= 0.413)
|
||||
{
|
||||
_io.WriteLine("Shot is good.");
|
||||
score[opponent] += 2;
|
||||
_io.WriteScore(Resource.Formats.Score, opponent, score);
|
||||
// over to Dartmouth
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Shot is missed.");
|
||||
if (defense / 6 * _random.NextFloat() <= 0.5)
|
||||
{
|
||||
_io.WriteLine("Dartmouth controls the rebound.");
|
||||
// over to Dartmouth
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine($"{opponent} controls the rebound.");
|
||||
if (defense == 6)
|
||||
{
|
||||
if (_random.NextFloat() > 0.75)
|
||||
{
|
||||
_io.WriteLine("Ball stolen. Easy lay up for Dartmouth.");
|
||||
score["Dartmouth"] += 2;
|
||||
_io.WriteScore(Resource.Formats.Score, opponent, score);
|
||||
_io.WriteLine();
|
||||
// next opponent shot
|
||||
}
|
||||
}
|
||||
if (_random.NextFloat() <= 0.5)
|
||||
{
|
||||
_io.WriteLine($"Pass back to {opponent} guard.");
|
||||
// next opponent shot
|
||||
}
|
||||
// goto 3500
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreeShots()
|
||||
{
|
||||
if (_random.NextFloat() <= 0.49)
|
||||
{
|
||||
_io.WriteLine("Shooter makes both shots.");
|
||||
score[offense] += 2;
|
||||
}
|
||||
else if (_random.NextFloat() <= 0.75)
|
||||
{
|
||||
_io.WriteLine("Shooter makes one shot and misses one.");
|
||||
score[offense] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_io.WriteLine("Both shots missed.");
|
||||
}
|
||||
_io.WriteScore(Resource.Formats.Score, opponent, score);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal record Team(string Name);
|
||||
|
||||
internal static class IReadWriteExtensions
|
||||
{
|
||||
public static float ReadDefense(this IReadWrite io, string prompt)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var defense = io.ReadNumber(prompt);
|
||||
if (defense >= 6) { return defense; }
|
||||
}
|
||||
}
|
||||
|
||||
public static int ReadShot(this IReadWrite io, string prompt)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var shot = io.ReadNumber(prompt);
|
||||
if ((int)shot == shot && shot >= 0 && shot <= 4) { return (int)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]);
|
||||
}
|
||||
7
07_Basketball/csharp/Program.cs
Normal file
7
07_Basketball/csharp/Program.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Basketball;
|
||||
using Games.Common.IO;
|
||||
using Games.Common.Randomness;
|
||||
|
||||
var game = new Game(new ConsoleIO(), new RandomNumberGenerator());
|
||||
|
||||
game.Play();
|
||||
5
07_Basketball/csharp/Resources/EndOfFirstHalf.txt
Normal file
5
07_Basketball/csharp/Resources/EndOfFirstHalf.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
***** End of first half *****
|
||||
|
||||
Score: {0}: {1} {2}: {3}
|
||||
|
||||
2
07_Basketball/csharp/Resources/EndOfGame.txt
Normal file
2
07_Basketball/csharp/Resources/EndOfGame.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
***** End of game *****
|
||||
Final score: {0}: {1} {2}: {3}
|
||||
7
07_Basketball/csharp/Resources/EndOfSecondHalf.txt
Normal file
7
07_Basketball/csharp/Resources/EndOfSecondHalf.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
***** End of second half *****
|
||||
|
||||
Score at end of regulation time:
|
||||
{0}: {1} {2}: {3}
|
||||
|
||||
Begin two minute overtime period
|
||||
12
07_Basketball/csharp/Resources/Introduction.txt
Normal file
12
07_Basketball/csharp/Resources/Introduction.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Basketball
|
||||
Creative Computing Morristown, New Jersey
|
||||
|
||||
|
||||
|
||||
This is Dartmouth College basketball. You will be Dartmouth
|
||||
captain and playmaker. Call shots as follows: 1. Long
|
||||
(30 ft.) jump shot; 2. Short (15 ft.) jump shot; 3. Lay
|
||||
up; 4. Set shot.
|
||||
Both teams will use the same defense. Call defense as
|
||||
follows: 6. Press; 6.5 Man-to-man; 7. Zone; 7.5 None.
|
||||
To change defense, just type 0 as your next shot.
|
||||
32
07_Basketball/csharp/Resources/Resource.cs
Normal file
32
07_Basketball/csharp/Resources/Resource.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Basketball.Resources;
|
||||
|
||||
internal static class Resource
|
||||
{
|
||||
internal static class Streams
|
||||
{
|
||||
public static Stream Introduction => GetStream();
|
||||
public static Stream TwoMinutesLeft => GetStream();
|
||||
}
|
||||
|
||||
internal static class Formats
|
||||
{
|
||||
public static string EndOfFirstHalf => GetString();
|
||||
public static string EndOfGame => GetString();
|
||||
public static string EndOfSecondHalf => GetString();
|
||||
public static string Score => GetString();
|
||||
}
|
||||
|
||||
private static string GetString([CallerMemberName] string? name = null)
|
||||
{
|
||||
using var stream = GetStream(name);
|
||||
using var reader = new StreamReader(stream);
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
|
||||
private static Stream GetStream([CallerMemberName] string? name = null) =>
|
||||
Assembly.GetExecutingAssembly().GetManifestResourceStream($"Basketball.Resources.{name}.txt")
|
||||
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
|
||||
}
|
||||
1
07_Basketball/csharp/Resources/Score.txt
Normal file
1
07_Basketball/csharp/Resources/Score.txt
Normal file
@@ -0,0 +1 @@
|
||||
Score: {1} to {3}
|
||||
3
07_Basketball/csharp/Resources/TwoMinutesLeft.txt
Normal file
3
07_Basketball/csharp/Resources/TwoMinutesLeft.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
*** Two minutes left in the game ***
|
||||
|
||||
Reference in New Issue
Block a user