mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 23:26:40 -08:00
Use common library in 86_Target
This commit is contained in:
@@ -7,8 +7,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Games.Common", "Games.Commo
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Games.Common.Test", "Games.Common.Test\Games.Common.Test.csproj", "{8369DA66-0414-4A14-B5BE-73B0159498A2}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Games.Common.Test", "Games.Common.Test\Games.Common.Test.csproj", "{8369DA66-0414-4A14-B5BE-73B0159498A2}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Games.Common.Sample", "Games.Common.Sample\Games.Common.Sample.csproj", "{395FBF0D-404E-495B-9760-8BEE3A6F5B62}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -26,9 +24,5 @@ Global
|
|||||||
{8369DA66-0414-4A14-B5BE-73B0159498A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{8369DA66-0414-4A14-B5BE-73B0159498A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{8369DA66-0414-4A14-B5BE-73B0159498A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{8369DA66-0414-4A14-B5BE-73B0159498A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{8369DA66-0414-4A14-B5BE-73B0159498A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
{8369DA66-0414-4A14-B5BE-73B0159498A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{395FBF0D-404E-495B-9760-8BEE3A6F5B62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{395FBF0D-404E-495B-9760-8BEE3A6F5B62}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{395FBF0D-404E-495B-9760-8BEE3A6F5B62}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{395FBF0D-404E-495B-9760-8BEE3A6F5B62}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Games.Common.IO;
|
namespace Games.Common.IO;
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ public interface IReadWrite
|
|||||||
/// Writes a <see cref="string" /> to output, followed by a new-line.
|
/// Writes a <see cref="string" /> to output, followed by a new-line.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">The <see cref="string" /> to be written.</param>
|
/// <param name="message">The <see cref="string" /> to be written.</param>
|
||||||
void WriteLine(string message);
|
void WriteLine(string message = "");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a <see cref="float" /> to output, formatted per the BASIC interpreter, with leading and trailing spaces.
|
/// Writes a <see cref="float" /> to output, formatted per the BASIC interpreter, with leading and trailing spaces.
|
||||||
@@ -80,4 +81,10 @@ public interface IReadWrite
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="value">The <see cref="float" /> to be written.</param>
|
/// <param name="value">The <see cref="float" /> to be written.</param>
|
||||||
void WriteLine(float value);
|
void WriteLine(float value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the contents of a <see cref="Stream" /> to output.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream" /> to be written.</param>
|
||||||
|
void Write(Stream stream);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,11 +89,20 @@ public class TextIO : IReadWrite
|
|||||||
|
|
||||||
public void Write(string value) => _output.Write(value);
|
public void Write(string value) => _output.Write(value);
|
||||||
|
|
||||||
public void WriteLine(string value) => _output.WriteLine(value);
|
public void WriteLine(string value = "") => _output.WriteLine(value);
|
||||||
|
|
||||||
public void Write(float value) => _output.Write(GetString(value));
|
public void Write(float value) => _output.Write(GetString(value));
|
||||||
|
|
||||||
public void WriteLine(float value) => _output.WriteLine(GetString(value));
|
public void WriteLine(float value) => _output.WriteLine(GetString(value));
|
||||||
|
|
||||||
|
public void Write(Stream stream)
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(stream);
|
||||||
|
while (!reader.EndOfStream)
|
||||||
|
{
|
||||||
|
_output.WriteLine(reader.ReadLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private string GetString(float value) => value < 0 ? $"{value} " : $" {value} ";
|
private string GetString(float value) => value < 0 ? $"{value} " : $" {value} ";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
using System;
|
using Games.Common.Randomness;
|
||||||
|
|
||||||
namespace Target
|
namespace Target
|
||||||
{
|
{
|
||||||
internal class FiringRange
|
internal class FiringRange
|
||||||
{
|
{
|
||||||
private readonly Random random;
|
private readonly IRandom _random;
|
||||||
|
private Point _targetPosition;
|
||||||
|
|
||||||
public FiringRange()
|
public FiringRange(IRandom random)
|
||||||
{
|
{
|
||||||
random = new Random();
|
_random = random;
|
||||||
NextTarget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Point TargetPosition { get; private set; }
|
public Point NextTarget() => _targetPosition = _random.NextPosition();
|
||||||
|
|
||||||
public void NextTarget() => TargetPosition = random.NextPosition();
|
|
||||||
|
|
||||||
public Explosion Fire(Angle angleFromX, Angle angleFromZ, float distance)
|
public Explosion Fire(Angle angleFromX, Angle angleFromZ, float distance)
|
||||||
{
|
{
|
||||||
var explosionPosition = new Point(angleFromX, angleFromZ, distance);
|
var explosionPosition = new Point(angleFromX, angleFromZ, distance);
|
||||||
var targetOffset = explosionPosition - TargetPosition;
|
var targetOffset = explosionPosition - _targetPosition;
|
||||||
return new (explosionPosition, targetOffset);
|
return new (explosionPosition, targetOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,41 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Games.Common.IO;
|
||||||
|
|
||||||
namespace Target
|
namespace Target
|
||||||
{
|
{
|
||||||
internal class Game
|
internal class Game
|
||||||
{
|
{
|
||||||
|
private readonly IReadWrite _io;
|
||||||
private readonly FiringRange _firingRange;
|
private readonly FiringRange _firingRange;
|
||||||
private int _shotCount;
|
private int _shotCount;
|
||||||
|
|
||||||
private Game(FiringRange firingRange)
|
public Game(IReadWrite io, FiringRange firingRange)
|
||||||
{
|
{
|
||||||
|
_io = io;
|
||||||
_firingRange = firingRange;
|
_firingRange = firingRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Play(FiringRange firingRange) => new Game(firingRange).Play();
|
public void Play()
|
||||||
|
|
||||||
private void Play()
|
|
||||||
{
|
{
|
||||||
var target = _firingRange.TargetPosition;
|
_shotCount = 0;
|
||||||
Console.WriteLine(target.GetBearing());
|
var target = _firingRange.NextTarget();
|
||||||
Console.WriteLine($"Target sighted: approximate coordinates: {target}");
|
_io.WriteLine(target.GetBearing());
|
||||||
|
_io.WriteLine($"Target sighted: approximate coordinates: {target}");
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Console.WriteLine($" Estimated distance: {target.EstimateDistance()}");
|
_io.WriteLine($" Estimated distance: {target.EstimateDistance()}");
|
||||||
Console.WriteLine();
|
_io.WriteLine();
|
||||||
|
|
||||||
var explosion = Shoot();
|
var explosion = Shoot();
|
||||||
|
|
||||||
if (explosion.IsTooClose)
|
if (explosion.IsTooClose)
|
||||||
{
|
{
|
||||||
Console.WriteLine("You blew yourself up!!");
|
_io.WriteLine("You blew yourself up!!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(explosion.GetBearing());
|
_io.WriteLine(explosion.GetBearing());
|
||||||
|
|
||||||
if (explosion.IsHit)
|
if (explosion.IsHit)
|
||||||
{
|
{
|
||||||
@@ -47,31 +49,32 @@ namespace Target
|
|||||||
|
|
||||||
private Explosion Shoot()
|
private Explosion Shoot()
|
||||||
{
|
{
|
||||||
var input = Input.ReadNumbers("Input angle deviation from X, angle deviation from Z, distance", 3);
|
var (xDeviation, zDeviation, distance) = _io.Read3Numbers(
|
||||||
|
"Input angle deviation from X, angle deviation from Z, distance");
|
||||||
_shotCount++;
|
_shotCount++;
|
||||||
Console.WriteLine();
|
_io.WriteLine();
|
||||||
|
|
||||||
return _firingRange.Fire(Angle.InDegrees(input[0]), Angle.InDegrees(input[1]), input[2]);
|
return _firingRange.Fire(Angle.InDegrees(xDeviation), Angle.InDegrees(zDeviation), distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReportHit(float distance)
|
private void ReportHit(float distance)
|
||||||
{
|
{
|
||||||
Console.WriteLine();
|
_io.WriteLine();
|
||||||
Console.WriteLine($" * * * HIT * * * Target is non-functional");
|
_io.WriteLine($" * * * HIT * * * Target is non-functional");
|
||||||
Console.WriteLine();
|
_io.WriteLine();
|
||||||
Console.WriteLine($"Distance of explosion from target was {distance} kilometers.");
|
_io.WriteLine($"Distance of explosion from target was {distance} kilometers.");
|
||||||
Console.WriteLine();
|
_io.WriteLine();
|
||||||
Console.WriteLine($"Mission accomplished in {_shotCount} shots.");
|
_io.WriteLine($"Mission accomplished in {_shotCount} shots.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReportMiss(Explosion explosion)
|
private void ReportMiss(Explosion explosion)
|
||||||
{
|
{
|
||||||
ReportMiss(explosion.FromTarget);
|
ReportMiss(explosion.FromTarget);
|
||||||
Console.WriteLine($"Approx position of explosion: {explosion.Position}");
|
_io.WriteLine($"Approx position of explosion: {explosion.Position}");
|
||||||
Console.WriteLine($" Distance from target = {explosion.DistanceToTarget}");
|
_io.WriteLine($" Distance from target = {explosion.DistanceToTarget}");
|
||||||
Console.WriteLine();
|
_io.WriteLine();
|
||||||
Console.WriteLine();
|
_io.WriteLine();
|
||||||
Console.WriteLine();
|
_io.WriteLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReportMiss(Offset targetOffset)
|
private void ReportMiss(Offset targetOffset)
|
||||||
@@ -82,7 +85,7 @@ namespace Target
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void ReportMiss(float delta, string positiveText, string negativeText) =>
|
private void ReportMiss(float delta, string positiveText, string negativeText) =>
|
||||||
Console.WriteLine(delta >= 0 ? GetOffsetText(positiveText, delta) : GetOffsetText(negativeText, -delta));
|
_io.WriteLine(delta >= 0 ? GetOffsetText(positiveText, delta) : GetOffsetText(negativeText, -delta));
|
||||||
|
|
||||||
private static string GetOffsetText(string text, float distance) => $"Shot {text} target {distance} kilometers.";
|
private static string GetOffsetText(string text, float distance) => $"Shot {text} target {distance} kilometers.";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +1,43 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Games.Common.IO;
|
||||||
|
using Games.Common.Randomness;
|
||||||
|
|
||||||
namespace Target
|
namespace Target
|
||||||
{
|
{
|
||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main()
|
||||||
{
|
{
|
||||||
DisplayTitleAndInstructions();
|
var io = new ConsoleIO();
|
||||||
|
var game = new Game(io, new FiringRange(new RandomNumberGenerator()));
|
||||||
|
|
||||||
var firingRange = new FiringRange();
|
Play(game, io, () => true);
|
||||||
|
}
|
||||||
|
|
||||||
while (true)
|
public static void Play(Game game, TextIO io, Func<bool> playAgain)
|
||||||
{
|
{
|
||||||
Game.Play(firingRange);
|
DisplayTitleAndInstructions(io);
|
||||||
|
|
||||||
Console.WriteLine();
|
while (playAgain())
|
||||||
Console.WriteLine();
|
{
|
||||||
Console.WriteLine();
|
game.Play();
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine("Next target...");
|
|
||||||
Console.WriteLine();
|
|
||||||
|
|
||||||
firingRange.NextTarget();
|
io.WriteLine();
|
||||||
|
io.WriteLine();
|
||||||
|
io.WriteLine();
|
||||||
|
io.WriteLine();
|
||||||
|
io.WriteLine();
|
||||||
|
io.WriteLine("Next target...");
|
||||||
|
io.WriteLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DisplayTitleAndInstructions()
|
private static void DisplayTitleAndInstructions(TextIO io)
|
||||||
{
|
{
|
||||||
using var stream = Assembly.GetExecutingAssembly()
|
using var stream = Assembly.GetExecutingAssembly()
|
||||||
.GetManifestResourceStream("Target.Strings.TitleAndInstructions.txt");
|
.GetManifestResourceStream("Target.Strings.TitleAndInstructions.txt");
|
||||||
using var stdout = Console.OpenStandardOutput();
|
io.Write(stream);
|
||||||
|
|
||||||
stream.CopyTo(stdout);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
using System;
|
using Games.Common.Randomness;
|
||||||
|
|
||||||
namespace Target
|
namespace Target
|
||||||
{
|
{
|
||||||
internal static class RandomExtensions
|
internal static class RandomExtensions
|
||||||
{
|
{
|
||||||
public static float NextFloat(this Random rnd) => (float)rnd.NextDouble();
|
public static Point NextPosition(this IRandom rnd) => new (
|
||||||
|
|
||||||
public static Point NextPosition(this Random rnd) => new (
|
|
||||||
Angle.InRotations(rnd.NextFloat()),
|
Angle.InRotations(rnd.NextFloat()),
|
||||||
Angle.InRotations(rnd.NextFloat()),
|
Angle.InRotations(rnd.NextFloat()),
|
||||||
100000 * rnd.NextFloat() + rnd.NextFloat());
|
100000 * rnd.NextFloat() + rnd.NextFloat());
|
||||||
|
|||||||
@@ -9,4 +9,8 @@
|
|||||||
<EmbeddedResource Include="Strings\TitleAndInstructions.txt" />
|
<EmbeddedResource Include="Strings\TitleAndInstructions.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user