mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-25 20:34:32 -08:00
Add C# implementaion of 86 Target
This commit is contained in:
34
86 Target/csharp/Target.sln
Normal file
34
86 Target/csharp/Target.sln
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26124.0
|
||||
MinimumVisualStudioVersion = 15.0.26124.0
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Target", "Target\Target.csproj", "{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{8B0B5114-1D05-4F8D-B328-EA2FB89992E7}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
18
86 Target/csharp/Target/Angle.cs
Normal file
18
86 Target/csharp/Target/Angle.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Target
|
||||
{
|
||||
internal class Angle
|
||||
{
|
||||
// Use same precision for constants as original code
|
||||
private const float PI = 3.14159f;
|
||||
private const float DegreesPerRadian = 57.296f;
|
||||
|
||||
private readonly float _radians;
|
||||
|
||||
private Angle(float radians) => _radians = radians;
|
||||
|
||||
public static Angle InDegrees(float degrees) => new (degrees / DegreesPerRadian);
|
||||
public static Angle InRotations(float rotations) => new (2 * PI * rotations);
|
||||
|
||||
public static implicit operator float(Angle angle) => angle._radians;
|
||||
}
|
||||
}
|
||||
22
86 Target/csharp/Target/Explosion.cs
Normal file
22
86 Target/csharp/Target/Explosion.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace Target
|
||||
{
|
||||
internal class Explosion
|
||||
{
|
||||
private readonly Point _position;
|
||||
|
||||
public Explosion(Point position, Offset targetOffset)
|
||||
{
|
||||
_position = position;
|
||||
FromTarget = targetOffset;
|
||||
DistanceToTarget = targetOffset.Distance;
|
||||
}
|
||||
|
||||
public Point Position => _position;
|
||||
public Offset FromTarget { get; }
|
||||
public float DistanceToTarget { get; }
|
||||
public string GetBearing() => _position.GetBearing();
|
||||
|
||||
public bool IsHit => DistanceToTarget <= 20;
|
||||
public bool IsTooClose => _position.Distance < 20;
|
||||
}
|
||||
}
|
||||
26
86 Target/csharp/Target/FiringRange.cs
Normal file
26
86 Target/csharp/Target/FiringRange.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Target
|
||||
{
|
||||
internal class FiringRange
|
||||
{
|
||||
private readonly Random random;
|
||||
|
||||
public FiringRange()
|
||||
{
|
||||
random = new Random();
|
||||
NextTarget();
|
||||
}
|
||||
|
||||
public Point TargetPosition { get; private set; }
|
||||
|
||||
public void NextTarget() => TargetPosition = random.NextPosition();
|
||||
|
||||
public Explosion Fire(Angle angleFromX, Angle angleFromZ, float distance)
|
||||
{
|
||||
var explosionPosition = new Point(angleFromX, angleFromZ, distance);
|
||||
var targetOffset = explosionPosition - TargetPosition;
|
||||
return new (explosionPosition, targetOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
89
86 Target/csharp/Target/Game.cs
Normal file
89
86 Target/csharp/Target/Game.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
|
||||
namespace Target
|
||||
{
|
||||
internal class Game
|
||||
{
|
||||
private readonly FiringRange _firingRange;
|
||||
private int _shotCount;
|
||||
|
||||
private Game(FiringRange firingRange)
|
||||
{
|
||||
_firingRange = firingRange;
|
||||
}
|
||||
|
||||
public static void Play(FiringRange firingRange) => new Game(firingRange).Play();
|
||||
|
||||
private void Play()
|
||||
{
|
||||
var target = _firingRange.TargetPosition;
|
||||
Console.WriteLine(target.GetBearing());
|
||||
Console.WriteLine($"Target sighted: approximate coordinates: {target}");
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.WriteLine($" Estimated distance: {target.EstimateDistance()}");
|
||||
Console.WriteLine();
|
||||
|
||||
var explosion = Shoot();
|
||||
|
||||
if (explosion.IsTooClose)
|
||||
{
|
||||
Console.WriteLine("You blew yourself up!!");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine(explosion.GetBearing());
|
||||
|
||||
if (explosion.IsHit)
|
||||
{
|
||||
ReportHit(explosion.DistanceToTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
ReportMiss(explosion);
|
||||
}
|
||||
}
|
||||
|
||||
private Explosion Shoot()
|
||||
{
|
||||
var input = Input.ReadNumbers("Input angle deviation from X, angle deviation from Z, distance", 3);
|
||||
_shotCount++;
|
||||
Console.WriteLine();
|
||||
|
||||
return _firingRange.Fire(Angle.InDegrees(input[0]), Angle.InDegrees(input[1]), input[2]);
|
||||
}
|
||||
|
||||
private void ReportHit(float distance)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($" * * * HIT * * * Target is non-functional");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Distance of explosion from target was {distance} kilometers.");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Mission accomplished in {_shotCount} shots.");
|
||||
}
|
||||
|
||||
private void ReportMiss(Explosion explosion)
|
||||
{
|
||||
ReportMiss(explosion.FromTarget);
|
||||
Console.WriteLine($"Approx position of explosion: {explosion.Position}");
|
||||
Console.WriteLine($" Distance from target = {explosion.DistanceToTarget}");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
private void ReportMiss(Offset targetOffset)
|
||||
{
|
||||
ReportMiss(targetOffset.DeltaX, "in front of", "behind");
|
||||
ReportMiss(targetOffset.DeltaY, "to left of", "to right of");
|
||||
ReportMiss(targetOffset.DeltaZ, "above", "below");
|
||||
}
|
||||
|
||||
private void ReportMiss(float delta, string positiveText, string negativeText) =>
|
||||
Console.WriteLine(delta >= 0 ? GetOffsetText(positiveText, delta) : GetOffsetText(negativeText, -delta));
|
||||
|
||||
private static string GetOffsetText(string text, float distance) => $"Shot {text} target {distance} kilometers.";
|
||||
}
|
||||
}
|
||||
59
86 Target/csharp/Target/Input.cs
Normal file
59
86 Target/csharp/Target/Input.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Target
|
||||
{
|
||||
// Provides input methods which emulate the BASIC interpreter's keyboard input routines
|
||||
internal static class Input
|
||||
{
|
||||
internal static void Prompt(string text = "") => Console.Write($"{text}? ");
|
||||
|
||||
internal static List<float> ReadNumbers(string prompt, int requiredCount)
|
||||
{
|
||||
var numbers = new List<float>();
|
||||
|
||||
while (!TryReadNumbers(prompt, requiredCount, numbers))
|
||||
{
|
||||
numbers.Clear();
|
||||
prompt = "";
|
||||
}
|
||||
|
||||
return numbers;
|
||||
}
|
||||
|
||||
private static bool TryReadNumbers(string prompt, int requiredCount, List<float> numbers)
|
||||
{
|
||||
Prompt(prompt);
|
||||
var inputValues = ReadStrings();
|
||||
|
||||
foreach (var value in inputValues)
|
||||
{
|
||||
if (numbers.Count == requiredCount)
|
||||
{
|
||||
Console.WriteLine("!Extra input ingored");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!TryParseNumber(value, out var number))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
numbers.Add(number);
|
||||
}
|
||||
|
||||
return numbers.Count == requiredCount || TryReadNumbers("?", requiredCount, numbers);
|
||||
}
|
||||
|
||||
private static string[] ReadStrings() => Console.ReadLine().Split(',', StringSplitOptions.TrimEntries);
|
||||
|
||||
private static bool TryParseNumber(string text, out float number)
|
||||
{
|
||||
if (float.TryParse(text, out number)) { return true; }
|
||||
|
||||
Console.WriteLine("!Number expected - retry input line");
|
||||
number = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
86 Target/csharp/Target/Offset.cs
Normal file
21
86 Target/csharp/Target/Offset.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace Target
|
||||
{
|
||||
internal class Offset
|
||||
{
|
||||
public Offset(float deltaX, float deltaY, float deltaZ)
|
||||
{
|
||||
DeltaX = deltaX;
|
||||
DeltaY = deltaY;
|
||||
DeltaZ = deltaZ;
|
||||
|
||||
Distance = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ + deltaZ);
|
||||
}
|
||||
|
||||
public float DeltaX { get; }
|
||||
public float DeltaY { get; }
|
||||
public float DeltaZ { get; }
|
||||
public float Distance { get; }
|
||||
}
|
||||
}
|
||||
47
86 Target/csharp/Target/Point.cs
Normal file
47
86 Target/csharp/Target/Point.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace Target
|
||||
{
|
||||
internal class Point
|
||||
{
|
||||
private readonly float _angleFromX;
|
||||
private readonly float _angleFromZ;
|
||||
|
||||
private readonly float _x;
|
||||
private readonly float _y;
|
||||
private readonly float _z;
|
||||
|
||||
private int _estimateCount;
|
||||
|
||||
public Point(Angle angleFromX, Angle angleFromZ, float distance)
|
||||
{
|
||||
_angleFromX = angleFromX;
|
||||
_angleFromZ = angleFromZ;
|
||||
Distance = distance;
|
||||
|
||||
_x = distance * (float)Math.Sin(_angleFromZ) * (float)Math.Cos(_angleFromX);
|
||||
_y = distance * (float)Math.Sin(_angleFromZ) * (float)Math.Sin(_angleFromX);
|
||||
_z = distance * (float)Math.Cos(_angleFromZ);
|
||||
}
|
||||
|
||||
public float Distance { get; }
|
||||
|
||||
public float EstimateDistance() =>
|
||||
++_estimateCount switch
|
||||
{
|
||||
1 => EstimateDistance(20),
|
||||
2 => EstimateDistance(10),
|
||||
3 => EstimateDistance(5),
|
||||
4 => EstimateDistance(1),
|
||||
_ => Distance
|
||||
};
|
||||
|
||||
public float EstimateDistance(int precision) => (float)Math.Floor(Distance / precision) * precision;
|
||||
|
||||
public string GetBearing() => $"Radians from X axis = {_angleFromX} from Z axis = {_angleFromZ}";
|
||||
|
||||
public override string ToString() => $"X= {_x} Y = {_y} Z= {_z}";
|
||||
|
||||
public static Offset operator -(Point p1, Point p2) => new (p1._x - p2._x, p1._y - p2._y, p1._z - p2._z);
|
||||
}
|
||||
}
|
||||
39
86 Target/csharp/Target/Program.cs
Normal file
39
86 Target/csharp/Target/Program.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Target
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
DisplayTitleAndInstructions();
|
||||
|
||||
var firingRange = new FiringRange();
|
||||
|
||||
while (true)
|
||||
{
|
||||
Game.Play(firingRange);
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Next target...");
|
||||
Console.WriteLine();
|
||||
|
||||
firingRange.NextTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisplayTitleAndInstructions()
|
||||
{
|
||||
using var stream = Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream("Target.Strings.TitleAndInstructions.txt");
|
||||
using var stdout = Console.OpenStandardOutput();
|
||||
|
||||
stream.CopyTo(stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
86 Target/csharp/Target/RandomExtensions.cs
Normal file
14
86 Target/csharp/Target/RandomExtensions.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Target
|
||||
{
|
||||
internal static class RandomExtensions
|
||||
{
|
||||
public static float NextFloat(this Random rnd) => (float)rnd.NextDouble();
|
||||
|
||||
public static Point NextPosition(this Random rnd) => new (
|
||||
Angle.InRotations(rnd.NextFloat()),
|
||||
Angle.InRotations(rnd.NextFloat()),
|
||||
100000 * rnd.NextFloat() + rnd.NextFloat());
|
||||
}
|
||||
}
|
||||
18
86 Target/csharp/Target/Strings/TitleAndInstructions.txt
Normal file
18
86 Target/csharp/Target/Strings/TitleAndInstructions.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
Target
|
||||
Creative Computing Morristown, New Jersey
|
||||
|
||||
|
||||
|
||||
You are the weapons officer on the Starship Enterprise
|
||||
and this is a test to see how accurate a shot you
|
||||
are in a three-dimensional range. You will be told
|
||||
the radian offset for the X and Z axes, the location
|
||||
of the target in three dimensional rectangular coordinates,
|
||||
the approximate number of degrees from the X and Z
|
||||
axes, and the approximate distance to the target.
|
||||
You will then proceed to shoot at the target until it is
|
||||
destroyed!
|
||||
|
||||
Good luck!!
|
||||
|
||||
|
||||
12
86 Target/csharp/Target/Target.csproj
Normal file
12
86 Target/csharp/Target/Target.csproj
Normal file
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Strings\TitleAndInstructions.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user