Use common library in SuperStarTrek

This commit is contained in:
Andrew Cooper
2022-03-22 17:55:44 +11:00
parent 4fb62ce30c
commit 42c21bbc62
57 changed files with 1595 additions and 1682 deletions

View File

@@ -12,4 +12,6 @@ public sealed class ConsoleIO : TextIO
: base(Console.In, Console.Out) : base(Console.In, Console.Out)
{ {
} }
public override char ReadCharacter() => Console.ReadKey(intercept: true).KeyChar;
} }

View File

@@ -8,6 +8,12 @@ namespace Games.Common.IO;
/// </summary> /// </summary>
public interface IReadWrite public interface IReadWrite
{ {
/// <summary>
/// Reads a character from input.
/// </summary>
/// <returns>The character read.</returns>
char ReadCharacter();
/// <summary> /// <summary>
/// Reads a <see cref="float" /> value from input. /// Reads a <see cref="float" /> value from input.
/// </summary> /// </summary>
@@ -99,6 +105,13 @@ public interface IReadWrite
/// </summary> /// </summary>
/// <param name="format">The format <see cref="string" /> to be written.</param> /// <param name="format">The format <see cref="string" /> to be written.</param>
/// <param name="value">The values to be inserted into the format.</param> /// <param name="value">The values to be inserted into the format.</param>
void Write(string format, params object[] values);
/// <summary>
/// Writes a formatted string to output followed by a new-line.
/// </summary>
/// <param name="format">The format <see cref="string" /> to be written.</param>
/// <param name="value">The values to be inserted into the format.</param>
void WriteLine(string format, params object[] values); void WriteLine(string format, params object[] values);
/// <summary> /// <summary>

View File

@@ -29,6 +29,15 @@ public class TextIO : IReadWrite
_numberTokenReader = TokenReader.ForNumbers(this); _numberTokenReader = TokenReader.ForNumbers(this);
} }
public virtual char ReadCharacter()
{
while(true)
{
var ch = _input.Read();
if (ch != -1) { return (char)ch; }
}
}
public float ReadNumber(string prompt) => ReadNumbers(prompt, 1)[0]; public float ReadNumber(string prompt) => ReadNumbers(prompt, 1)[0];
public (float, float) Read2Numbers(string prompt) public (float, float) Read2Numbers(string prompt)
@@ -99,6 +108,8 @@ public class TextIO : IReadWrite
public void WriteLine(object value) => _output.WriteLine(value.ToString()); public void WriteLine(object value) => _output.WriteLine(value.ToString());
public void Write(string format, params object[] values) => _output.Write(format, values);
public void WriteLine(string format, params object[] values) => _output.WriteLine(format, values); public void WriteLine(string format, params object[] values) => _output.WriteLine(format, values);
public void Write(Stream stream, bool keepOpen = false) public void Write(Stream stream, bool keepOpen = false)

View File

@@ -1,9 +1,9 @@
using System.ComponentModel; using System.ComponentModel;
namespace SuperStarTrek.Commands namespace SuperStarTrek.Commands;
internal enum Command
{ {
internal enum Command
{
[Description("To set course")] [Description("To set course")]
NAV, NAV,
@@ -30,5 +30,4 @@ namespace SuperStarTrek.Commands
[Description("To resign your command")] [Description("To resign your command")]
XXX XXX
}
} }

View File

@@ -1,14 +1,13 @@
using System.Reflection; using System.Reflection;
using System.ComponentModel; using System.ComponentModel;
namespace SuperStarTrek.Commands namespace SuperStarTrek.Commands;
internal static class CommandExtensions
{ {
internal static class CommandExtensions
{
internal static string GetDescription(this Command command) => internal static string GetDescription(this Command command) =>
typeof(Command) typeof(Command)
.GetField(command.ToString()) .GetField(command.ToString())
.GetCustomAttribute<DescriptionAttribute>() .GetCustomAttribute<DescriptionAttribute>()
.Description; .Description;
}
} }

View File

@@ -1,7 +1,7 @@
namespace SuperStarTrek.Commands namespace SuperStarTrek.Commands;
internal class CommandResult
{ {
internal class CommandResult
{
public static readonly CommandResult Ok = new(false); public static readonly CommandResult Ok = new(false);
public static readonly CommandResult GameOver = new(true); public static readonly CommandResult GameOver = new(true);
@@ -19,5 +19,4 @@ namespace SuperStarTrek.Commands
public float TimeElapsed { get; } public float TimeElapsed { get; }
public static CommandResult Elapsed(float timeElapsed) => new(timeElapsed); public static CommandResult Elapsed(float timeElapsed) => new(timeElapsed);
}
} }

View File

@@ -1,17 +1,18 @@
using System; using System;
using Games.Common.IO;
using Games.Common.Randomness;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using SuperStarTrek.Space; using SuperStarTrek.Space;
using SuperStarTrek.Systems; using SuperStarTrek.Systems;
using SuperStarTrek.Systems.ComputerFunctions; using SuperStarTrek.Systems.ComputerFunctions;
namespace SuperStarTrek namespace SuperStarTrek;
internal class Game
{ {
internal class Game private readonly TextIO _io;
{ private readonly IRandom _random;
private readonly Output _output;
private readonly Input _input;
private readonly Random _random;
private int _initialStardate; private int _initialStardate;
private int _finalStarDate; private int _finalStarDate;
@@ -21,10 +22,9 @@ namespace SuperStarTrek
private int _initialKlingonCount; private int _initialKlingonCount;
private Enterprise _enterprise; private Enterprise _enterprise;
internal Game(Output output, Input input, Random random) internal Game(TextIO io, IRandom random)
{ {
_output = output; _io = io;
_input = input;
_random = random; _random = random;
} }
@@ -34,13 +34,13 @@ namespace SuperStarTrek
internal void DoIntroduction() internal void DoIntroduction()
{ {
_output.Write(Strings.Title); _io.Write(Strings.Title);
if (_input.GetYesNo("Do you need instructions", Input.YesNoMode.FalseOnN)) if (_io.GetYesNo("Do you need instructions", IReadWriteExtensions.YesNoMode.FalseOnN))
{ {
_output.Write(Strings.Instructions); _io.Write(Strings.Instructions);
_input.WaitForAnyKeyButEnter("to continue"); _io.WaitForAnyKeyButEnter("to continue");
} }
} }
@@ -51,7 +51,7 @@ namespace SuperStarTrek
while (!gameOver) while (!gameOver)
{ {
var command = _input.GetCommand(); var command = _io.ReadCommand();
var result = _enterprise.Execute(command); var result = _enterprise.Execute(command);
@@ -62,45 +62,44 @@ namespace SuperStarTrek
if (_galaxy.KlingonCount > 0) if (_galaxy.KlingonCount > 0)
{ {
_output.Write(Strings.EndOfMission, _currentStardate, _galaxy.KlingonCount); _io.Write(Strings.EndOfMission, _currentStardate, _galaxy.KlingonCount);
} }
else else
{ {
_output.Write(Strings.Congratulations, GetEfficiency()); _io.Write(Strings.Congratulations, CalculateEfficiency());
} }
} }
private void Initialise() private void Initialise()
{ {
_currentStardate = _initialStardate = _random.GetInt(20, 40) * 100; _currentStardate = _initialStardate = _random.Next(20, 40) * 100;
_finalStarDate = _initialStardate + _random.GetInt(25, 35); _finalStarDate = _initialStardate + _random.Next(25, 35);
_currentQuadrant = _random.GetCoordinate(); _currentQuadrant = _random.NextCoordinate();
_galaxy = new Galaxy(_random); _galaxy = new Galaxy(_random);
_initialKlingonCount = _galaxy.KlingonCount; _initialKlingonCount = _galaxy.KlingonCount;
_enterprise = new Enterprise(3000, _random.GetCoordinate(), _output, _random, _input); _enterprise = new Enterprise(3000, _random.NextCoordinate(), _io, _random);
_enterprise _enterprise
.Add(new WarpEngines(_enterprise, _output, _input)) .Add(new WarpEngines(_enterprise, _io))
.Add(new ShortRangeSensors(_enterprise, _galaxy, this, _output)) .Add(new ShortRangeSensors(_enterprise, _galaxy, this, _io))
.Add(new LongRangeSensors(_galaxy, _output)) .Add(new LongRangeSensors(_galaxy, _io))
.Add(new PhaserControl(_enterprise, _output, _input, _random)) .Add(new PhaserControl(_enterprise, _io, _random))
.Add(new PhotonTubes(10, _enterprise, _output, _input)) .Add(new PhotonTubes(10, _enterprise, _io))
.Add(new ShieldControl(_enterprise, _output, _input)) .Add(new ShieldControl(_enterprise, _io))
.Add(new DamageControl(_enterprise, _output)) .Add(new DamageControl(_enterprise, _io))
.Add(new LibraryComputer( .Add(new LibraryComputer(
_output, _io,
_input, new CumulativeGalacticRecord(_io, _galaxy),
new CumulativeGalacticRecord(_output, _galaxy), new StatusReport(this, _galaxy, _enterprise, _io),
new StatusReport(this, _galaxy, _enterprise, _output), new TorpedoDataCalculator(_enterprise, _io),
new TorpedoDataCalculator(_enterprise, _output), new StarbaseDataCalculator(_enterprise, _io),
new StarbaseDataCalculator(_enterprise, _output), new DirectionDistanceCalculator(_enterprise, _io),
new DirectionDistanceCalculator(_enterprise, _output, _input), new GalaxyRegionMap(_io, _galaxy)));
new GalaxyRegionMap(_output, _galaxy)));
_output.Write(Strings.Enterprise); _io.Write(Strings.Enterprise);
_output.Write( _io.Write(
Strings.Orders, Strings.Orders,
_galaxy.KlingonCount, _galaxy.KlingonCount,
_finalStarDate, _finalStarDate,
@@ -109,23 +108,21 @@ namespace SuperStarTrek
_galaxy.StarbaseCount, _galaxy.StarbaseCount,
_galaxy.StarbaseCount > 1 ? "s" : ""); _galaxy.StarbaseCount > 1 ? "s" : "");
_input.WaitForAnyKeyButEnter("when ready to accept command"); _io.WaitForAnyKeyButEnter("when ready to accept command");
_enterprise.StartIn(BuildCurrentQuadrant()); _enterprise.StartIn(BuildCurrentQuadrant());
} }
private Quadrant BuildCurrentQuadrant() => private Quadrant BuildCurrentQuadrant() => new(_galaxy[_currentQuadrant], _enterprise, _random, _galaxy, _io);
new Quadrant(_galaxy[_currentQuadrant], _enterprise, _random, _galaxy, _input, _output);
internal bool Replay() => _galaxy.StarbaseCount > 0 && _input.GetString(Strings.ReplayPrompt, "Aye"); internal bool Replay() => _galaxy.StarbaseCount > 0 && _io.ReadExpectedString(Strings.ReplayPrompt, "Aye");
private bool CheckIfStranded() private bool CheckIfStranded()
{ {
if (_enterprise.IsStranded) { _output.Write(Strings.Stranded); } if (_enterprise.IsStranded) { _io.Write(Strings.Stranded); }
return _enterprise.IsStranded; return _enterprise.IsStranded;
} }
private float GetEfficiency() => private float CalculateEfficiency() =>
1000 * (float)Math.Pow(_initialKlingonCount / (_currentStardate - _initialStardate), 2); 1000 * (float)Math.Pow(_initialKlingonCount / (_currentStardate - _initialStardate), 2);
}
} }

View File

@@ -0,0 +1,16 @@
using Games.Common.Randomness;
using SuperStarTrek.Space;
namespace SuperStarTrek;
internal static class IRandomExtensions
{
internal static Coordinates NextCoordinate(this IRandom random) =>
new Coordinates(random.Next1To8Inclusive() - 1, random.Next1To8Inclusive() - 1);
// Duplicates the algorithm used in the original code to get an integer value from 1 to 8, inclusive:
// 475 DEF FNR(R)=INT(RND(R)*7.98+1.01)
// Returns a value from 1 to 8, inclusive.
// Note there's a slight bias away from the extreme values, 1 and 8.
internal static int Next1To8Inclusive(this IRandom random) => (int)(random.NextFloat() * 7.98 + 1.01);
}

View File

@@ -0,0 +1,89 @@
using System;
using System.Linq;
using Games.Common.IO;
using SuperStarTrek.Commands;
using SuperStarTrek.Space;
using static System.StringComparison;
namespace SuperStarTrek;
internal static class IReadWriteExtensions
{
internal static void WaitForAnyKeyButEnter(this IReadWrite io, string prompt)
{
io.Write($"Hit any key but Enter {prompt} ");
while (io.ReadCharacter() == '\r');
}
internal static (float X, float Y) GetCoordinates(this IReadWrite io, string prompt) =>
io.Read2Numbers($"{prompt} (X,Y)");
internal static bool TryReadNumberInRange(
this IReadWrite io,
string prompt,
float minValue,
float maxValue,
out float value)
{
value = io.ReadNumber($"{prompt} ({minValue}-{maxValue})");
return value >= minValue && value <= maxValue;
}
internal static bool ReadExpectedString(this IReadWrite io, string prompt, string trueValue) =>
io.ReadString(prompt).Equals(trueValue, InvariantCultureIgnoreCase);
internal static Command ReadCommand(this IReadWrite io)
{
while(true)
{
var response = io.ReadString("Command");
if (response.Length >= 3 &&
Enum.TryParse(response.Substring(0, 3), ignoreCase: true, out Command parsedCommand))
{
return parsedCommand;
}
io.WriteLine("Enter one of the following:");
foreach (var command in Enum.GetValues(typeof(Command)).OfType<Command>())
{
io.WriteLine($" {command} ({command.GetDescription()})");
}
io.WriteLine();
}
}
internal static bool TryReadCourse(this IReadWrite io, string prompt, string officer, out Course course)
{
if (!io.TryReadNumberInRange(prompt, 1, 9, out var direction))
{
io.WriteLine($"{officer} reports, 'Incorrect course data, sir!'");
course = default;
return false;
}
course = new Course(direction);
return true;
}
internal static bool GetYesNo(this IReadWrite io, string prompt, YesNoMode mode)
{
var response = io.ReadString($"{prompt} (Y/N)").ToUpperInvariant();
return (mode, response) switch
{
(YesNoMode.FalseOnN, "N") => false,
(YesNoMode.FalseOnN, _) => true,
(YesNoMode.TrueOnY, "Y") => true,
(YesNoMode.TrueOnY, _) => false,
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Invalid value")
};
}
internal enum YesNoMode
{
TrueOnY,
FalseOnN
}
}

View File

@@ -1,159 +0,0 @@
using System;
using System.Linq;
using SuperStarTrek.Commands;
using SuperStarTrek.Space;
using static System.StringComparison;
namespace SuperStarTrek
{
internal class Input
{
private readonly Output _output;
internal Input(Output output)
{
_output = output;
}
internal void WaitForAnyKeyButEnter(string prompt)
{
_output.Write($"Hit any key but Enter {prompt} ");
while (Console.ReadKey(intercept: true).Key == ConsoleKey.Enter);
}
internal string GetString(string prompt)
{
_output.Prompt(prompt);
return Console.ReadLine();
}
internal float GetNumber(string prompt)
{
_output.Prompt(prompt);
while (true)
{
var response = Console.ReadLine();
if (float.TryParse(response, out var value))
{
return value;
}
_output.WriteLine("!Number expected - retry input line");
_output.Prompt();
}
}
internal (float X, float Y) GetCoordinates(string prompt)
{
_output.Prompt($"{prompt} (X,Y)");
var responses = ReadNumbers(2);
return (responses[0], responses[1]);
}
internal bool TryGetNumber(string prompt, float minValue, float maxValue, out float value)
{
value = GetNumber($"{prompt} ({minValue}-{maxValue})");
return value >= minValue && value <= maxValue;
}
internal bool GetString(string replayPrompt, string trueValue) =>
GetString(replayPrompt).Equals(trueValue, InvariantCultureIgnoreCase);
internal Command GetCommand()
{
while(true)
{
var response = GetString("Command");
if (response.Length >= 3 &&
Enum.TryParse(response.Substring(0, 3), ignoreCase: true, out Command parsedCommand))
{
return parsedCommand;
}
_output.WriteLine("Enter one of the following:");
foreach (var command in Enum.GetValues(typeof(Command)).OfType<Command>())
{
_output.WriteLine($" {command} ({command.GetDescription()})");
}
_output.WriteLine();
}
}
internal bool TryGetCourse(string prompt, string officer, out Course course)
{
if (!TryGetNumber(prompt, 1, 9, out var direction))
{
_output.WriteLine($"{officer} reports, 'Incorrect course data, sir!'");
course = default;
return false;
}
course = new Course(direction);
return true;
}
internal bool GetYesNo(string prompt, YesNoMode mode)
{
_output.Prompt($"{prompt} (Y/N)");
var response = Console.ReadLine().ToUpperInvariant();
return (mode, response) switch
{
(YesNoMode.FalseOnN, "N") => false,
(YesNoMode.FalseOnN, _) => true,
(YesNoMode.TrueOnY, "Y") => true,
(YesNoMode.TrueOnY, _) => false,
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Invalid value")
};
}
private float[] ReadNumbers(int quantity)
{
var numbers = new float[quantity];
var index = 0;
bool tryAgain;
do
{
tryAgain = false;
var responses = Console.ReadLine().Split(',');
if (responses.Length > quantity)
{
_output.WriteLine("!Extra input ingored");
}
for (; index < responses.Length; index++)
{
if (!float.TryParse(responses[index], out numbers[index]))
{
_output.WriteLine("!Number expected - retry input line");
_output.Prompt();
tryAgain = true;
break;
}
}
} while (tryAgain);
if (index < quantity)
{
_output.Prompt("?");
var responses = ReadNumbers(quantity - index);
for (int i = 0; i < responses.Length; i++, index++)
{
numbers[index] = responses[i];
}
}
return numbers;
}
internal enum YesNoMode
{
TrueOnY,
FalseOnN
}
}
}

View File

@@ -1,33 +1,33 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Games.Common.IO;
using Games.Common.Randomness;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using SuperStarTrek.Space; using SuperStarTrek.Space;
using SuperStarTrek.Systems; using SuperStarTrek.Systems;
namespace SuperStarTrek.Objects namespace SuperStarTrek.Objects;
internal class Enterprise
{ {
internal class Enterprise
{
private readonly int _maxEnergy; private readonly int _maxEnergy;
private readonly Output _output; private readonly IReadWrite _io;
private readonly List<Subsystem> _systems; private readonly List<Subsystem> _systems;
private readonly Dictionary<Command, Subsystem> _commandExecutors; private readonly Dictionary<Command, Subsystem> _commandExecutors;
private readonly Random _random; private readonly IRandom _random;
private readonly Input _input;
private Quadrant _quadrant; private Quadrant _quadrant;
public Enterprise(int maxEnergy, Coordinates sector, Output output, Random random, Input input) public Enterprise(int maxEnergy, Coordinates sector, IReadWrite io, IRandom random)
{ {
SectorCoordinates = sector; SectorCoordinates = sector;
TotalEnergy = _maxEnergy = maxEnergy; TotalEnergy = _maxEnergy = maxEnergy;
_systems = new List<Subsystem>(); _systems = new List<Subsystem>();
_commandExecutors = new Dictionary<Command, Subsystem>(); _commandExecutors = new Dictionary<Command, Subsystem>();
_output = output; _io = io;
_random = random; _random = random;
_input = input;
} }
internal Quadrant Quadrant => _quadrant; internal Quadrant Quadrant => _quadrant;
@@ -97,16 +97,16 @@ namespace SuperStarTrek.Objects
internal CommandResult TakeHit(Coordinates sector, int hitStrength) internal CommandResult TakeHit(Coordinates sector, int hitStrength)
{ {
_output.WriteLine($"{hitStrength} unit hit on Enterprise from sector {sector}"); _io.WriteLine($"{hitStrength} unit hit on Enterprise from sector {sector}");
ShieldControl.AbsorbHit(hitStrength); ShieldControl.AbsorbHit(hitStrength);
if (ShieldControl.ShieldEnergy <= 0) if (ShieldControl.ShieldEnergy <= 0)
{ {
_output.WriteLine(Strings.Destroyed); _io.WriteLine(Strings.Destroyed);
return CommandResult.GameOver; return CommandResult.GameOver;
} }
_output.WriteLine($" <Shields down to {ShieldControl.ShieldEnergy} units>"); _io.WriteLine($" <Shields down to {ShieldControl.ShieldEnergy} units>");
if (hitStrength >= 20) if (hitStrength >= 20)
{ {
@@ -119,14 +119,14 @@ namespace SuperStarTrek.Objects
private void TakeDamage(float hitStrength) private void TakeDamage(float hitStrength)
{ {
var hitShieldRatio = hitStrength / ShieldControl.ShieldEnergy; var hitShieldRatio = hitStrength / ShieldControl.ShieldEnergy;
if (_random.GetFloat() > 0.6 || hitShieldRatio <= 0.02f) if (_random.NextFloat() > 0.6 || hitShieldRatio <= 0.02f)
{ {
return; return;
} }
var system = _systems[_random.Get1To8Inclusive() - 1]; var system = _systems[_random.Next1To8Inclusive() - 1];
system.TakeDamage(hitShieldRatio + 0.5f * _random.GetFloat()); system.TakeDamage(hitShieldRatio + 0.5f * _random.NextFloat());
_output.WriteLine($"Damage Control reports, '{system.Name} damaged by the hit.'"); _io.WriteLine($"Damage Control reports, '{system.Name} damaged by the hit.'");
} }
internal void RepairSystems(float repairWorkDone) internal void RepairSystems(float repairWorkDone)
@@ -143,29 +143,29 @@ namespace SuperStarTrek.Objects
if (repairedSystems.Any()) if (repairedSystems.Any())
{ {
_output.WriteLine("Damage Control report:"); _io.WriteLine("Damage Control report:");
foreach (var systemName in repairedSystems) foreach (var systemName in repairedSystems)
{ {
_output.WriteLine($" {systemName} repair completed."); _io.WriteLine($" {systemName} repair completed.");
} }
} }
} }
internal void VaryConditionOfRandomSystem() internal void VaryConditionOfRandomSystem()
{ {
if (_random.GetFloat() > 0.2f) { return; } if (_random.NextFloat() > 0.2f) { return; }
var system = _systems[_random.Get1To8Inclusive() - 1]; var system = _systems[_random.Next1To8Inclusive() - 1];
_output.Write($"Damage Control report: {system.Name} "); _io.Write($"Damage Control report: {system.Name} ");
if (_random.GetFloat() >= 0.6) if (_random.NextFloat() >= 0.6)
{ {
system.Repair(_random.GetFloat() * 3 + 1); system.Repair(_random.NextFloat() * 3 + 1);
_output.WriteLine("state of repair improved"); _io.WriteLine("state of repair improved");
} }
else else
{ {
system.TakeDamage(_random.GetFloat() * 5 + 1); system.TakeDamage(_random.NextFloat() * 5 + 1);
_output.WriteLine("damaged"); _io.WriteLine("damaged");
} }
} }
@@ -175,7 +175,7 @@ namespace SuperStarTrek.Objects
if (quadrant != _quadrant.Coordinates) if (quadrant != _quadrant.Coordinates)
{ {
_quadrant = new Quadrant(_quadrant.Galaxy[quadrant], this, _random, _quadrant.Galaxy, _input, _output); _quadrant = new Quadrant(_quadrant.Galaxy[quadrant], this, _random, _quadrant.Galaxy, _io);
} }
_quadrant.SetEnterpriseSector(sector); _quadrant.SetEnterpriseSector(sector);
SectorCoordinates = sector; SectorCoordinates = sector;
@@ -183,7 +183,7 @@ namespace SuperStarTrek.Objects
TotalEnergy -= distance + 10; TotalEnergy -= distance + 10;
if (Energy < 0) if (Energy < 0)
{ {
_output.WriteLine("Shield Control supplies energy to complete the maneuver."); _io.WriteLine("Shield Control supplies energy to complete the maneuver.");
ShieldControl.ShieldEnergy = Math.Max(0, TotalEnergy); ShieldControl.ShieldEnergy = Math.Max(0, TotalEnergy);
} }
@@ -199,7 +199,7 @@ namespace SuperStarTrek.Objects
if (_quadrant.HasObjectAt(sector)) if (_quadrant.HasObjectAt(sector))
{ {
_output.WriteLine($"Warp engines shut down at sector {currentSector} dues to bad navigation"); _io.WriteLine($"Warp engines shut down at sector {currentSector} dues to bad navigation");
distance = 0; distance = 0;
break; break;
} }
@@ -217,7 +217,7 @@ namespace SuperStarTrek.Objects
if (!complete) if (!complete)
{ {
_output.Write(Strings.PermissionDenied, sector, quadrant); _io.Write(Strings.PermissionDenied, sector, quadrant);
} }
return (quadrant, sector); return (quadrant, sector);
@@ -227,5 +227,4 @@ namespace SuperStarTrek.Objects
finalQuadrant == _quadrant.Coordinates finalQuadrant == _quadrant.Coordinates
? Math.Min(1, (float)Math.Round(warpFactor, 1, MidpointRounding.ToZero)) ? Math.Min(1, (float)Math.Round(warpFactor, 1, MidpointRounding.ToZero))
: 1; : 1;
}
} }

View File

@@ -1,17 +1,18 @@
using Games.Common.Randomness;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Objects namespace SuperStarTrek.Objects;
{
internal class Klingon
{
private readonly Random _random;
internal Klingon(Coordinates sector, Random random) internal class Klingon
{
private readonly IRandom _random;
internal Klingon(Coordinates sector, IRandom random)
{ {
Sector = sector; Sector = sector;
_random = random; _random = random;
Energy = _random.GetFloat(100, 300); Energy = _random.NextFloat(100, 300);
} }
internal float Energy { get; private set; } internal float Energy { get; private set; }
@@ -22,7 +23,7 @@ namespace SuperStarTrek.Objects
internal CommandResult FireOn(Enterprise enterprise) internal CommandResult FireOn(Enterprise enterprise)
{ {
var attackStrength = _random.GetFloat(); var attackStrength = _random.NextFloat();
var distanceToEnterprise = Sector.GetDistanceTo(enterprise.SectorCoordinates); var distanceToEnterprise = Sector.GetDistanceTo(enterprise.SectorCoordinates);
var hitStrength = (int)(Energy * (2 + attackStrength) / distanceToEnterprise); var hitStrength = (int)(Energy * (2 + attackStrength) / distanceToEnterprise);
Energy /= 3 + attackStrength; Energy /= 3 + attackStrength;
@@ -39,5 +40,4 @@ namespace SuperStarTrek.Objects
} }
internal void MoveTo(Coordinates newSector) => Sector = newSector; internal void MoveTo(Coordinates newSector) => Sector = newSector;
}
} }

View File

@@ -1,7 +1,6 @@
namespace SuperStarTrek.Objects namespace SuperStarTrek.Objects;
internal class Star
{ {
internal class Star
{
public override string ToString() => " * "; public override string ToString() => " * ";
}
} }

View File

@@ -1,20 +1,20 @@
using Games.Common.IO;
using Games.Common.Randomness;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Objects namespace SuperStarTrek.Objects;
internal class Starbase
{ {
internal class Starbase private readonly IReadWrite _io;
{
private readonly Input _input;
private readonly Output _output;
private readonly float _repairDelay; private readonly float _repairDelay;
internal Starbase(Coordinates sector, Random random, Input input, Output output) internal Starbase(Coordinates sector, IRandom random, IReadWrite io)
{ {
Sector = sector; Sector = sector;
_repairDelay = random.GetFloat() * 0.5f; _repairDelay = random.NextFloat(0.5f);
_input = input; _io = io;
_output = output;
} }
internal Coordinates Sector { get; } internal Coordinates Sector { get; }
@@ -26,8 +26,8 @@ namespace SuperStarTrek.Objects
repairTime = enterprise.DamagedSystemCount * 0.1f + _repairDelay; repairTime = enterprise.DamagedSystemCount * 0.1f + _repairDelay;
if (repairTime >= 1) { repairTime = 0.9f; } if (repairTime >= 1) { repairTime = 0.9f; }
_output.Write(Strings.RepairEstimate, repairTime); _io.Write(Strings.RepairEstimate, repairTime);
if (_input.GetYesNo(Strings.RepairPrompt, Input.YesNoMode.TrueOnY)) if (_io.GetYesNo(Strings.RepairPrompt, IReadWriteExtensions.YesNoMode.TrueOnY))
{ {
foreach (var system in enterprise.Systems) foreach (var system in enterprise.Systems)
{ {
@@ -40,6 +40,5 @@ namespace SuperStarTrek.Objects
return false; return false;
} }
internal void ProtectEnterprise() => _output.WriteLine(Strings.Protected); internal void ProtectEnterprise() => _io.WriteLine(Strings.Protected);
}
} }

View File

@@ -1,40 +0,0 @@
using System;
namespace SuperStarTrek
{
internal class Output
{
internal Output Write(string text)
{
Console.Write(text);
return this;
}
internal Output Write(string format, params object[] args)
{
Console.Write(format, args);
return this;
}
internal Output WriteLine(string text = "")
{
Console.WriteLine(text);
return this;
}
internal Output NextLine()
{
Console.WriteLine();
return this;
}
internal Output Prompt(string text = "")
{
Console.Write($"{text}? ");
return this;
}
}
}

View File

@@ -23,24 +23,18 @@
// **** CONVERTED TO MICROSOFT C# 2/20/21 BY ANDREW COOPER // **** CONVERTED TO MICROSOFT C# 2/20/21 BY ANDREW COOPER
// **** // ****
namespace SuperStarTrek using Games.Common.IO;
using Games.Common.Randomness;
using SuperStarTrek;
var io = new ConsoleIO();
var random = new RandomNumberGenerator();
var game = new Game(io, random);
game.DoIntroduction();
do
{ {
internal class Program
{
static void Main()
{
var output = new Output();
var input = new Input(output);
var random = new Random();
var game = new Game(output, input, random);
game.DoIntroduction();
do
{
game.Play(); game.Play();
} while (game.Replay()); } while (game.Replay());
}
}
}

View File

@@ -1,25 +0,0 @@
using SuperStarTrek.Space;
namespace SuperStarTrek
{
internal class Random
{
private readonly System.Random _random = new();
internal Coordinates GetCoordinate() => new Coordinates(Get1To8Inclusive() - 1, Get1To8Inclusive() - 1);
// Duplicates the algorithm used in the original code to get an integer value from 1 to 8, inclusive:
// 475 DEF FNR(R)=INT(RND(R)*7.98+1.01)
// Returns a value from 1 to 8, inclusive.
// Note there's a slight bias away from the extreme values, 1 and 8.
internal int Get1To8Inclusive() => (int)(GetFloat() * 7.98 + 1.01);
internal int GetInt(int inclusiveMinValue, int exclusiveMaxValue) =>
_random.Next(inclusiveMinValue, exclusiveMaxValue);
internal float GetFloat() => (float)_random.NextDouble();
internal float GetFloat(float inclusiveMinValue, float exclusiveMaxValue)
=> GetFloat() * (exclusiveMaxValue - inclusiveMinValue) + inclusiveMinValue;
}
}

View File

@@ -16,3 +16,9 @@
'----------------' '----------------'
THE USS ENTERPRISE --- NCC-1701 THE USS ENTERPRISE --- NCC-1701

View File

@@ -104,3 +104,4 @@ COM command = Library-Computer
Option 5 = Galactic Region Name Map Option 5 = Galactic Region Name Map
This option prints the names of the sixteen major This option prints the names of the sixteen major
galactic regions referred to in the game. galactic regions referred to in the game.

View File

@@ -1,2 +1,3 @@
Now entering {0} quadrant . . . Now entering {0} quadrant . . .

View File

@@ -3,3 +3,4 @@ Your orders are as follows:
the galaxy before they can attack federation headquarters the galaxy before they can attack federation headquarters
on stardate {1}. This gives you {2} days. There {3} on stardate {1}. This gives you {2} days. There {3}
{4} starbase{5} in the galaxy for resupplying your ship. {4} starbase{5} in the galaxy for resupplying your ship.

View File

@@ -2,3 +2,4 @@
Your mission begins with your starship located Your mission begins with your starship located
in the galactic quadrant, '{0}'. in the galactic quadrant, '{0}'.

View File

@@ -17,3 +17,11 @@
* * * *
* * * *
************************************* *************************************

View File

@@ -1,12 +1,12 @@
using System; using System;
using SuperStarTrek.Utils; using SuperStarTrek.Utils;
namespace SuperStarTrek.Space namespace SuperStarTrek.Space;
// Represents the corrdintate of a quadrant in the galaxy, or a sector in a quadrant.
// Note that the origin is top-left, x increase downwards, and y increases to the right.
internal record Coordinates
{ {
// Represents the corrdintate of a quadrant in the galaxy, or a sector in a quadrant.
// Note that the origin is top-left, x increase downwards, and y increases to the right.
internal record Coordinates
{
internal Coordinates(int x, int y) internal Coordinates(int x, int y)
{ {
X = Validated(x, nameof(x)); X = Validated(x, nameof(x));
@@ -66,5 +66,4 @@ namespace SuperStarTrek.Space
var (_, distance) = GetDirectionAndDistanceTo(destination); var (_, distance) = GetDirectionAndDistanceTo(destination);
return distance; return distance;
} }
}
} }

View File

@@ -1,17 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace SuperStarTrek.Space namespace SuperStarTrek.Space;
// Implements the course calculations from the original code:
// 530 FORI=1TO9:C(I,1)=0:C(I,2)=0:NEXTI
// 540 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1
// 600 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1
//
// 3110 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1))
// 3140 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1))
internal class Course
{ {
// Implements the course calculations from the original code:
// 530 FORI=1TO9:C(I,1)=0:C(I,2)=0:NEXTI
// 540 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1
// 600 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1
//
// 3110 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1))
// 3140 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1))
internal class Course
{
private static readonly (int DeltaX, int DeltaY)[] cardinals = new[] private static readonly (int DeltaX, int DeltaY)[] cardinals = new[]
{ {
(0, 1), (0, 1),
@@ -94,5 +94,4 @@ namespace SuperStarTrek.Space
_ => (true, newQuadrant, newSector) _ => (true, newQuadrant, newSector)
}; };
} }
}
} }

View File

@@ -1,13 +1,14 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Games.Common.Randomness;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using static System.StringSplitOptions; using static System.StringSplitOptions;
namespace SuperStarTrek.Space namespace SuperStarTrek.Space;
internal class Galaxy
{ {
internal class Galaxy
{
private static readonly string[] _regionNames; private static readonly string[] _regionNames;
private static readonly string[] _subRegionIdentifiers; private static readonly string[] _subRegionIdentifiers;
private readonly QuadrantInfo[][] _quadrants; private readonly QuadrantInfo[][] _quadrants;
@@ -18,7 +19,7 @@ namespace SuperStarTrek.Space
_subRegionIdentifiers = new[] { "I", "II", "III", "IV" }; _subRegionIdentifiers = new[] { "I", "II", "III", "IV" };
} }
internal Galaxy(Random random) internal Galaxy(IRandom random)
{ {
_quadrants = Enumerable _quadrants = Enumerable
.Range(0, 8) .Range(0, 8)
@@ -31,7 +32,7 @@ namespace SuperStarTrek.Space
if (StarbaseCount == 0) if (StarbaseCount == 0)
{ {
var randomQuadrant = this[random.GetCoordinate()]; var randomQuadrant = this[random.NextCoordinate()];
randomQuadrant.AddStarbase(); randomQuadrant.AddStarbase();
if (randomQuadrant.KlingonCount < 2) if (randomQuadrant.KlingonCount < 2)
@@ -60,5 +61,4 @@ namespace SuperStarTrek.Space
Enumerable.Range(-1, 3) Enumerable.Range(-1, 3)
.Select(dy => dy + quadrant.Coordinates.Y) .Select(dy => dy + quadrant.Coordinates.Y)
.Select(y => y < 0 || y > 7 || x < 0 || x > 7 ? null : _quadrants[x][y]); .Select(y => y < 0 || y > 7 || x < 0 || x > 7 ? null : _quadrants[x][y]);
}
} }

View File

@@ -1,32 +1,33 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Games.Common.IO;
using Games.Common.Randomness;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
namespace SuperStarTrek.Space namespace SuperStarTrek.Space;
internal class Quadrant
{ {
internal class Quadrant
{
private readonly QuadrantInfo _info; private readonly QuadrantInfo _info;
private readonly Random _random; private readonly IRandom _random;
private readonly Dictionary<Coordinates, object> _sectors; private readonly Dictionary<Coordinates, object> _sectors;
private readonly Enterprise _enterprise; private readonly Enterprise _enterprise;
private readonly Output _output; private readonly IReadWrite _io;
private bool _entered = false; private bool _entered = false;
internal Quadrant( internal Quadrant(
QuadrantInfo info, QuadrantInfo info,
Enterprise enterprise, Enterprise enterprise,
Random random, IRandom random,
Galaxy galaxy, Galaxy galaxy,
Input input, IReadWrite io)
Output output)
{ {
_info = info; _info = info;
_random = random; _random = random;
_output = output; _io = io;
Galaxy = galaxy; Galaxy = galaxy;
info.MarkAsKnown(); info.MarkAsKnown();
@@ -34,7 +35,7 @@ namespace SuperStarTrek.Space
PositionObject(sector => new Klingon(sector, _random), _info.KlingonCount); PositionObject(sector => new Klingon(sector, _random), _info.KlingonCount);
if (_info.HasStarbase) if (_info.HasStarbase)
{ {
Starbase = PositionObject(sector => new Starbase(sector, _random, input, output)); Starbase = PositionObject(sector => new Starbase(sector, _random, io));
} }
PositionObject(_ => new Star(), _info.StarCount); PositionObject(_ => new Star(), _info.StarCount);
} }
@@ -79,14 +80,14 @@ namespace SuperStarTrek.Space
{ {
if (!_entered) if (!_entered)
{ {
_output.Write(textFormat, this); _io.Write(textFormat, this);
_entered = true; _entered = true;
} }
if (_info.KlingonCount > 0) if (_info.KlingonCount > 0)
{ {
_output.Write(Strings.CombatArea); _io.Write(Strings.CombatArea);
if (_enterprise.ShieldControl.ShieldEnergy <= 200) { _output.Write(Strings.LowShields); } if (_enterprise.ShieldControl.ShieldEnergy <= 200) { _io.Write(Strings.LowShields); }
} }
_enterprise.Execute(Command.SRS); _enterprise.Execute(Command.SRS);
@@ -164,7 +165,7 @@ namespace SuperStarTrek.Space
{ {
while (true) while (true)
{ {
var sector = _random.GetCoordinate(); var sector = _random.NextCoordinate();
if (!_sectors.ContainsKey(sector)) if (!_sectors.ContainsKey(sector))
{ {
return sector; return sector;
@@ -188,5 +189,4 @@ namespace SuperStarTrek.Space
_sectors.Remove(_enterprise.SectorCoordinates); _sectors.Remove(_enterprise.SectorCoordinates);
_sectors[sector] = _enterprise; _sectors[sector] = _enterprise;
} }
}
} }

View File

@@ -1,7 +1,9 @@
namespace SuperStarTrek.Space using Games.Common.Randomness;
namespace SuperStarTrek.Space;
internal class QuadrantInfo
{ {
internal class QuadrantInfo
{
private bool _isKnown; private bool _isKnown;
private QuadrantInfo(Coordinates coordinates, string name, int klingonCount, int starCount, bool hasStarbase) private QuadrantInfo(Coordinates coordinates, string name, int klingonCount, int starCount, bool hasStarbase)
@@ -23,17 +25,17 @@ namespace SuperStarTrek.Space
internal int StarCount { get; } internal int StarCount { get; }
internal static QuadrantInfo Create(Coordinates coordinates, string name, Random random) internal static QuadrantInfo Create(Coordinates coordinates, string name, IRandom random)
{ {
var klingonCount = random.GetFloat() switch var klingonCount = random.NextFloat() switch
{ {
> 0.98f => 3, > 0.98f => 3,
> 0.95f => 2, > 0.95f => 2,
> 0.80f => 1, > 0.80f => 1,
_ => 0 _ => 0
}; };
var hasStarbase = random.GetFloat() > 0.96f; var hasStarbase = random.NextFloat() > 0.96f;
var starCount = random.Get1To8Inclusive(); var starCount = random.Next1To8Inclusive();
return new QuadrantInfo(coordinates, name, klingonCount, starCount, hasStarbase); return new QuadrantInfo(coordinates, name, klingonCount, starCount, hasStarbase);
} }
@@ -61,5 +63,4 @@ namespace SuperStarTrek.Space
} }
internal void RemoveStarbase() => HasStarbase = false; internal void RemoveStarbase() => HasStarbase = false;
}
} }

View File

@@ -1,7 +1,6 @@
namespace SuperStarTrek namespace SuperStarTrek;
internal static class StringExtensions
{ {
internal static class StringExtensions
{
internal static string Pluralize(this string singular, int quantity) => singular + (quantity > 1 ? "s" : ""); internal static string Pluralize(this string singular, int quantity) => singular + (quantity > 1 ? "s" : "");
}
} }

View File

@@ -2,11 +2,15 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Resources\*.txt" /> <EmbeddedResource Include="Resources\*.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
</ItemGroup>
</Project> </Project>

View File

@@ -1,19 +1,19 @@
using Games.Common.IO;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems.ComputerFunctions namespace SuperStarTrek.Systems.ComputerFunctions;
internal abstract class ComputerFunction
{ {
internal abstract class ComputerFunction protected ComputerFunction(string description, IReadWrite io)
{
protected ComputerFunction(string description, Output output)
{ {
Description = description; Description = description;
Output = output; IO = io;
} }
internal string Description { get; } internal string Description { get; }
protected Output Output { get; } protected IReadWrite IO { get; }
internal abstract void Execute(Quadrant quadrant); internal abstract void Execute(Quadrant quadrant);
}
} }

View File

@@ -1,21 +1,24 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Games.Common.IO;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems.ComputerFunctions namespace SuperStarTrek.Systems.ComputerFunctions;
internal class CumulativeGalacticRecord : GalacticReport
{ {
internal class CumulativeGalacticRecord : GalacticReport internal CumulativeGalacticRecord(IReadWrite io, Galaxy galaxy)
{ : base("Cumulative galactic record", io, galaxy)
internal CumulativeGalacticRecord(Output output, Galaxy galaxy)
: base("Cumulative galactic record", output, galaxy)
{ {
} }
protected override void WriteHeader(Quadrant quadrant) => protected override void WriteHeader(Quadrant quadrant)
Output.NextLine().WriteLine($"Computer record of galaxy for quadrant {quadrant.Coordinates}").NextLine(); {
IO.WriteLine();
IO.WriteLine($"Computer record of galaxy for quadrant {quadrant.Coordinates}");
IO.WriteLine();
}
protected override IEnumerable<string> GetRowData() => protected override IEnumerable<string> GetRowData() =>
Galaxy.Quadrants.Select(row => " " + string.Join(" ", row)); Galaxy.Quadrants.Select(row => " " + string.Join(" ", row));
}
} }

View File

@@ -1,30 +1,30 @@
using Games.Common.IO;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems.ComputerFunctions namespace SuperStarTrek.Systems.ComputerFunctions;
{
internal class DirectionDistanceCalculator : NavigationCalculator
{
private readonly Enterprise _enterprise;
private readonly Input _input;
internal DirectionDistanceCalculator(Enterprise enterprise, Output output, Input input) internal class DirectionDistanceCalculator : NavigationCalculator
: base("Direction/distance calculator", output) {
private readonly Enterprise _enterprise;
private readonly IReadWrite _io;
internal DirectionDistanceCalculator(Enterprise enterprise, IReadWrite io)
: base("Direction/distance calculator", io)
{ {
_enterprise = enterprise; _enterprise = enterprise;
_input = input; _io = io;
} }
internal override void Execute(Quadrant quadrant) internal override void Execute(Quadrant quadrant)
{ {
Output.WriteLine("Direction/distance calculator:") IO.WriteLine("Direction/distance calculator:");
.Write($"You are at quadrant {_enterprise.QuadrantCoordinates}") IO.Write($"You are at quadrant {_enterprise.QuadrantCoordinates}");
.WriteLine($" sector {_enterprise.SectorCoordinates}") IO.WriteLine($" sector {_enterprise.SectorCoordinates}");
.WriteLine("Please enter"); IO.WriteLine("Please enter");
WriteDirectionAndDistance( WriteDirectionAndDistance(
_input.GetCoordinates(" Initial coordinates"), _io.GetCoordinates(" Initial coordinates"),
_input.GetCoordinates(" Final coordinates")); _io.GetCoordinates(" Final coordinates"));
}
} }
} }

View File

@@ -1,13 +1,14 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Games.Common.IO;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems.ComputerFunctions namespace SuperStarTrek.Systems.ComputerFunctions;
internal abstract class GalacticReport : ComputerFunction
{ {
internal abstract class GalacticReport : ComputerFunction internal GalacticReport(string description, IReadWrite io, Galaxy galaxy)
{ : base(description, io)
internal GalacticReport(string description, Output output, Galaxy galaxy)
: base(description, output)
{ {
Galaxy = galaxy; Galaxy = galaxy;
} }
@@ -21,14 +22,13 @@ namespace SuperStarTrek.Systems.ComputerFunctions
internal sealed override void Execute(Quadrant quadrant) internal sealed override void Execute(Quadrant quadrant)
{ {
WriteHeader(quadrant); WriteHeader(quadrant);
Output.WriteLine(" 1 2 3 4 5 6 7 8") IO.WriteLine(" 1 2 3 4 5 6 7 8");
.WriteLine(" ----- ----- ----- ----- ----- ----- ----- -----"); IO.WriteLine(" ----- ----- ----- ----- ----- ----- ----- -----");
foreach (var (row, index) in GetRowData().Select((r, i) => (r, i))) foreach (var (row, index) in GetRowData().Select((r, i) => (r, i)))
{ {
Output.WriteLine($" {index+1} {row}") IO.WriteLine($" {index+1} {row}");
.WriteLine(" ----- ----- ----- ----- ----- ----- ----- -----"); IO.WriteLine(" ----- ----- ----- ----- ----- ----- ----- -----");
}
} }
} }
} }

View File

@@ -1,21 +1,21 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Games.Common.IO;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems.ComputerFunctions namespace SuperStarTrek.Systems.ComputerFunctions;
internal class GalaxyRegionMap : GalacticReport
{ {
internal class GalaxyRegionMap : GalacticReport internal GalaxyRegionMap(IReadWrite io, Galaxy galaxy)
{ : base("Galaxy 'region name' map", io, galaxy)
internal GalaxyRegionMap(Output output, Galaxy galaxy)
: base("Galaxy 'region name' map", output, galaxy)
{ {
} }
protected override void WriteHeader(Quadrant quadrant) => protected override void WriteHeader(Quadrant quadrant) =>
Output.WriteLine(" The Galaxy"); IO.WriteLine(" The Galaxy");
protected override IEnumerable<string> GetRowData() => protected override IEnumerable<string> GetRowData() =>
Strings.RegionNames.Split('\n').Select(n => n.TrimEnd('\r')); Strings.RegionNames.Split('\n').Select(n => n.TrimEnd('\r'));
}
} }

View File

@@ -1,12 +1,13 @@
using Games.Common.IO;
using SuperStarTrek.Space; using SuperStarTrek.Space;
using SuperStarTrek.Utils; using SuperStarTrek.Utils;
namespace SuperStarTrek.Systems.ComputerFunctions namespace SuperStarTrek.Systems.ComputerFunctions;
internal abstract class NavigationCalculator : ComputerFunction
{ {
internal abstract class NavigationCalculator : ComputerFunction protected NavigationCalculator(string description, IReadWrite io)
{ : base(description, io)
protected NavigationCalculator(string description, Output output)
: base(description, output)
{ {
} }
@@ -22,8 +23,9 @@ namespace SuperStarTrek.Systems.ComputerFunctions
Write(direction, distance); Write(direction, distance);
} }
private void Write(float direction, float distance) => private void Write(float direction, float distance)
Output.WriteLine($"Direction = {direction}") {
.WriteLine($"Distance = {distance}"); IO.WriteLine($"Direction = {direction}");
IO.WriteLine($"Distance = {distance}");
} }
} }

View File

@@ -1,15 +1,16 @@
using Games.Common.IO;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems.ComputerFunctions namespace SuperStarTrek.Systems.ComputerFunctions;
internal class StarbaseDataCalculator : NavigationCalculator
{ {
internal class StarbaseDataCalculator : NavigationCalculator
{
private readonly Enterprise _enterprise; private readonly Enterprise _enterprise;
internal StarbaseDataCalculator(Enterprise enterprise, Output output) internal StarbaseDataCalculator(Enterprise enterprise, IReadWrite io)
: base("Starbase nav data", output) : base("Starbase nav data", io)
{ {
_enterprise = enterprise; _enterprise = enterprise;
} }
@@ -18,13 +19,12 @@ namespace SuperStarTrek.Systems.ComputerFunctions
{ {
if (!quadrant.HasStarbase) if (!quadrant.HasStarbase)
{ {
Output.WriteLine(Strings.NoStarbase); IO.WriteLine(Strings.NoStarbase);
return; return;
} }
Output.WriteLine("From Enterprise to Starbase:"); IO.WriteLine("From Enterprise to Starbase:");
WriteDirectionAndDistance(_enterprise.SectorCoordinates, quadrant.Starbase.Sector); WriteDirectionAndDistance(_enterprise.SectorCoordinates, quadrant.Starbase.Sector);
} }
}
} }

View File

@@ -1,17 +1,18 @@
using Games.Common.IO;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems.ComputerFunctions namespace SuperStarTrek.Systems.ComputerFunctions;
internal class StatusReport : ComputerFunction
{ {
internal class StatusReport : ComputerFunction
{
private readonly Game _game; private readonly Game _game;
private readonly Galaxy _galaxy; private readonly Galaxy _galaxy;
private readonly Enterprise _enterprise; private readonly Enterprise _enterprise;
internal StatusReport(Game game, Galaxy galaxy, Enterprise enterprise, Output output) internal StatusReport(Game game, Galaxy galaxy, Enterprise enterprise, IReadWrite io)
: base("Status report", output) : base("Status report", io)
{ {
_game = game; _game = game;
_galaxy = galaxy; _galaxy = galaxy;
@@ -20,22 +21,23 @@ namespace SuperStarTrek.Systems.ComputerFunctions
internal override void Execute(Quadrant quadrant) internal override void Execute(Quadrant quadrant)
{ {
Output.WriteLine(" Status report:") IO.WriteLine(" Status report:");
.Write("Klingon".Pluralize(_galaxy.KlingonCount)).WriteLine($" left: {_galaxy.KlingonCount}") IO.Write("Klingon".Pluralize(_galaxy.KlingonCount));
.WriteLine($"Mission must be completed in {_game.StardatesRemaining:0.#} stardates."); IO.WriteLine($" left: {_galaxy.KlingonCount}");
IO.WriteLine($"Mission must be completed in {_game.StardatesRemaining:0.#} stardates.");
if (_galaxy.StarbaseCount > 0) if (_galaxy.StarbaseCount > 0)
{ {
Output.Write($"The Federation is maintaining {_galaxy.StarbaseCount} ") IO.Write($"The Federation is maintaining {_galaxy.StarbaseCount} ");
.Write("starbase".Pluralize(_galaxy.StarbaseCount)).WriteLine(" in the galaxy."); IO.Write("starbase".Pluralize(_galaxy.StarbaseCount));
IO.WriteLine(" in the galaxy.");
} }
else else
{ {
Output.WriteLine("Your stupidity has left you on your own in") IO.WriteLine("Your stupidity has left you on your own in");
.WriteLine(" the galaxy -- you have no starbases left!"); IO.WriteLine(" the galaxy -- you have no starbases left!");
} }
_enterprise.Execute(Command.DAM); _enterprise.Execute(Command.DAM);
} }
}
} }

View File

@@ -1,15 +1,16 @@
using Games.Common.IO;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems.ComputerFunctions namespace SuperStarTrek.Systems.ComputerFunctions;
internal class TorpedoDataCalculator : NavigationCalculator
{ {
internal class TorpedoDataCalculator : NavigationCalculator
{
private readonly Enterprise _enterprise; private readonly Enterprise _enterprise;
internal TorpedoDataCalculator(Enterprise enterprise, Output output) internal TorpedoDataCalculator(Enterprise enterprise, IReadWrite io)
: base("Photon torpedo data", output) : base("Photon torpedo data", io)
{ {
_enterprise = enterprise; _enterprise = enterprise;
} }
@@ -18,16 +19,15 @@ namespace SuperStarTrek.Systems.ComputerFunctions
{ {
if (!quadrant.HasKlingons) if (!quadrant.HasKlingons)
{ {
Output.WriteLine(Strings.NoEnemyShips); IO.WriteLine(Strings.NoEnemyShips);
return; return;
} }
Output.WriteLine("From Enterprise to Klingon battle cruiser".Pluralize(quadrant.KlingonCount)); IO.WriteLine("From Enterprise to Klingon battle cruiser".Pluralize(quadrant.KlingonCount));
foreach (var klingon in quadrant.Klingons) foreach (var klingon in quadrant.Klingons)
{ {
WriteDirectionAndDistance(_enterprise.SectorCoordinates, klingon.Sector); WriteDirectionAndDistance(_enterprise.SectorCoordinates, klingon.Sector);
} }
} }
}
} }

View File

@@ -1,30 +1,31 @@
using Games.Common.IO;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems namespace SuperStarTrek.Systems;
{
internal class DamageControl : Subsystem
{
private readonly Enterprise _enterprise;
private readonly Output _output;
internal DamageControl(Enterprise enterprise, Output output) internal class DamageControl : Subsystem
: base("Damage Control", Command.DAM, output) {
private readonly Enterprise _enterprise;
private readonly IReadWrite _io;
internal DamageControl(Enterprise enterprise, IReadWrite io)
: base("Damage Control", Command.DAM, io)
{ {
_enterprise = enterprise; _enterprise = enterprise;
_output = output; _io = io;
} }
protected override CommandResult ExecuteCommandCore(Quadrant quadrant) protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
{ {
if (IsDamaged) if (IsDamaged)
{ {
_output.WriteLine("Damage Control report not available"); _io.WriteLine("Damage Control report not available");
} }
else else
{ {
_output.NextLine(); _io.WriteLine();
WriteDamageReport(); WriteDamageReport();
} }
@@ -42,13 +43,13 @@ namespace SuperStarTrek.Systems
internal void WriteDamageReport() internal void WriteDamageReport()
{ {
_output.NextLine().WriteLine("Device State of Repair"); _io.WriteLine();
_io.WriteLine("Device State of Repair");
foreach (var system in _enterprise.Systems) foreach (var system in _enterprise.Systems)
{ {
_output.Write(system.Name.PadRight(25)) _io.Write(system.Name.PadRight(25));
.WriteLine(((int)(system.Condition * 100) * 0.01).ToString(" 0.##;-0.##")); _io.WriteLine((int)(system.Condition * 100) * 0.01F);
}
_output.NextLine();
} }
_io.WriteLine();
} }
} }

View File

@@ -1,20 +1,19 @@
using Games.Common.IO;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Space; using SuperStarTrek.Space;
using SuperStarTrek.Systems.ComputerFunctions; using SuperStarTrek.Systems.ComputerFunctions;
namespace SuperStarTrek.Systems namespace SuperStarTrek.Systems;
internal class LibraryComputer : Subsystem
{ {
internal class LibraryComputer : Subsystem private readonly IReadWrite _io;
{
private readonly Output _output;
private readonly Input _input;
private readonly ComputerFunction[] _functions; private readonly ComputerFunction[] _functions;
internal LibraryComputer(Output output, Input input, params ComputerFunction[] functions) internal LibraryComputer(IReadWrite io, params ComputerFunction[] functions)
: base("Library-Computer", Command.COM, output) : base("Library-Computer", Command.COM, io)
{ {
_output = output; _io = io;
_input = input;
_functions = functions; _functions = functions;
} }
@@ -23,7 +22,7 @@ namespace SuperStarTrek.Systems
protected override CommandResult ExecuteCommandCore(Quadrant quadrant) protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
{ {
var index = GetFunctionIndex(); var index = GetFunctionIndex();
_output.NextLine(); _io.WriteLine();
_functions[index].Execute(quadrant); _functions[index].Execute(quadrant);
@@ -34,13 +33,12 @@ namespace SuperStarTrek.Systems
{ {
while (true) while (true)
{ {
var index = (int)_input.GetNumber("Computer active and waiting command"); var index = (int)_io.ReadNumber("Computer active and waiting command");
if (index >= 0 && index <= 5) { return index; } if (index >= 0 && index <= 5) { return index; }
for (int i = 0; i < _functions.Length; i++) for (int i = 0; i < _functions.Length; i++)
{ {
_output.WriteLine($" {i} = {_functions[i].Description}"); _io.WriteLine($" {i} = {_functions[i].Description}");
}
} }
} }
} }

View File

@@ -1,34 +1,35 @@
using System.Linq; using System.Linq;
using Games.Common.IO;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems namespace SuperStarTrek.Systems;
{
internal class LongRangeSensors : Subsystem
{
private readonly Galaxy _galaxy;
private readonly Output _output;
internal LongRangeSensors(Galaxy galaxy, Output output) internal class LongRangeSensors : Subsystem
: base("Long Range Sensors", Command.LRS, output) {
private readonly Galaxy _galaxy;
private readonly IReadWrite _io;
internal LongRangeSensors(Galaxy galaxy, IReadWrite io)
: base("Long Range Sensors", Command.LRS, io)
{ {
_galaxy = galaxy; _galaxy = galaxy;
_output = output; _io = io;
} }
protected override bool CanExecuteCommand() => IsOperational("{name} are inoperable"); protected override bool CanExecuteCommand() => IsOperational("{name} are inoperable");
protected override CommandResult ExecuteCommandCore(Quadrant quadrant) protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
{ {
_output.WriteLine($"Long range scan for quadrant {quadrant.Coordinates}"); _io.WriteLine($"Long range scan for quadrant {quadrant.Coordinates}");
_output.WriteLine("-------------------"); _io.WriteLine("-------------------");
foreach (var quadrants in _galaxy.GetNeighborhood(quadrant)) foreach (var quadrants in _galaxy.GetNeighborhood(quadrant))
{ {
_output.WriteLine(": " + string.Join(" : ", quadrants.Select(q => q?.Scan() ?? "***")) + " :"); _io.WriteLine(": " + string.Join(" : ", quadrants.Select(q => q?.Scan() ?? "***")) + " :");
_output.WriteLine("-------------------"); _io.WriteLine("-------------------");
} }
return CommandResult.Ok; return CommandResult.Ok;
} }
}
} }

View File

@@ -1,24 +1,24 @@
using System.Linq; using System.Linq;
using Games.Common.IO;
using Games.Common.Randomness;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems namespace SuperStarTrek.Systems;
{
internal class PhaserControl : Subsystem
{
private readonly Enterprise _enterprise;
private readonly Output _output;
private readonly Input _input;
private readonly Random _random;
internal PhaserControl(Enterprise enterprise, Output output, Input input, Random random) internal class PhaserControl : Subsystem
: base("Phaser Control", Command.PHA, output) {
private readonly Enterprise _enterprise;
private readonly IReadWrite _io;
private readonly IRandom _random;
internal PhaserControl(Enterprise enterprise, IReadWrite io, IRandom random)
: base("Phaser Control", Command.PHA, io)
{ {
_enterprise = enterprise; _enterprise = enterprise;
_output = output; _io = io;
_input = input;
_random = random; _random = random;
} }
@@ -28,16 +28,16 @@ namespace SuperStarTrek.Systems
{ {
if (!quadrant.HasKlingons) if (!quadrant.HasKlingons)
{ {
_output.WriteLine(Strings.NoEnemyShips); _io.WriteLine(Strings.NoEnemyShips);
return CommandResult.Ok; return CommandResult.Ok;
} }
if (_enterprise.Computer.IsDamaged) if (_enterprise.Computer.IsDamaged)
{ {
_output.WriteLine("Computer failure hampers accuracy"); _io.WriteLine("Computer failure hampers accuracy");
} }
_output.Write($"Phasers locked on target; "); _io.Write($"Phasers locked on target; ");
var phaserStrength = GetPhaserStrength(); var phaserStrength = GetPhaserStrength();
if (phaserStrength < 0) { return CommandResult.Ok; } if (phaserStrength < 0) { return CommandResult.Ok; }
@@ -58,8 +58,8 @@ namespace SuperStarTrek.Systems
{ {
while (true) while (true)
{ {
_output.WriteLine($"Energy available = {_enterprise.Energy} units"); _io.WriteLine($"Energy available = {_enterprise.Energy} units");
var phaserStrength = _input.GetNumber("Number of units to fire"); var phaserStrength = _io.ReadNumber("Number of units to fire");
if (phaserStrength <= _enterprise.Energy) { return phaserStrength; } if (phaserStrength <= _enterprise.Energy) { return phaserStrength; }
} }
@@ -69,7 +69,7 @@ namespace SuperStarTrek.Systems
{ {
if (_enterprise.Computer.IsDamaged) if (_enterprise.Computer.IsDamaged)
{ {
phaserStrength *= _random.GetFloat(); phaserStrength *= _random.NextFloat();
} }
return phaserStrength / targetCount; return phaserStrength / targetCount;
@@ -78,20 +78,19 @@ namespace SuperStarTrek.Systems
private void ResolveHitOn(Klingon klingon, float perEnemyStrength, Quadrant quadrant) private void ResolveHitOn(Klingon klingon, float perEnemyStrength, Quadrant quadrant)
{ {
var distance = _enterprise.SectorCoordinates.GetDistanceTo(klingon.Sector); var distance = _enterprise.SectorCoordinates.GetDistanceTo(klingon.Sector);
var hitStrength = (int)(perEnemyStrength / distance * (2 + _random.GetFloat())); var hitStrength = (int)(perEnemyStrength / distance * (2 + _random.NextFloat()));
if (klingon.TakeHit(hitStrength)) if (klingon.TakeHit(hitStrength))
{ {
_output.WriteLine($"{hitStrength} unit hit on Klingon at sector {klingon.Sector}"); _io.WriteLine($"{hitStrength} unit hit on Klingon at sector {klingon.Sector}");
_output.WriteLine( _io.WriteLine(
klingon.Energy <= 0 klingon.Energy <= 0
? quadrant.Remove(klingon) ? quadrant.Remove(klingon)
: $" (sensors show {klingon.Energy} units remaining)"); : $" (sensors show {klingon.Energy} units remaining)");
} }
else else
{ {
_output.WriteLine($"Sensors show no damage to enemy at {klingon.Sector}"); _io.WriteLine($"Sensors show no damage to enemy at {klingon.Sector}");
}
} }
} }
} }

View File

@@ -1,23 +1,22 @@
using Games.Common.IO;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems namespace SuperStarTrek.Systems;
internal class PhotonTubes : Subsystem
{ {
internal class PhotonTubes : Subsystem
{
private readonly int _tubeCount; private readonly int _tubeCount;
private readonly Enterprise _enterprise; private readonly Enterprise _enterprise;
private readonly Output _output; private readonly IReadWrite _io;
private readonly Input _input;
internal PhotonTubes(int tubeCount, Enterprise enterprise, Output output, Input input) internal PhotonTubes(int tubeCount, Enterprise enterprise, IReadWrite io)
: base("Photon Tubes", Command.TOR, output) : base("Photon Tubes", Command.TOR, io)
{ {
TorpedoCount = _tubeCount = tubeCount; TorpedoCount = _tubeCount = tubeCount;
_enterprise = enterprise; _enterprise = enterprise;
_output = output; _io = io;
_input = input;
} }
internal int TorpedoCount { get; private set; } internal int TorpedoCount { get; private set; }
@@ -28,13 +27,13 @@ namespace SuperStarTrek.Systems
{ {
if (TorpedoCount > 0) { return true; } if (TorpedoCount > 0) { return true; }
_output.WriteLine("All photon torpedoes expended"); _io.WriteLine("All photon torpedoes expended");
return false; return false;
} }
protected override CommandResult ExecuteCommandCore(Quadrant quadrant) protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
{ {
if (!_input.TryGetCourse("Photon torpedo course", "Ensign Chekov", out var course)) if (!_io.TryReadCourse("Photon torpedo course", "Ensign Chekov", out var course))
{ {
return CommandResult.Ok; return CommandResult.Ok;
} }
@@ -42,25 +41,24 @@ namespace SuperStarTrek.Systems
TorpedoCount -= 1; TorpedoCount -= 1;
var isHit = false; var isHit = false;
_output.WriteLine("Torpedo track:"); _io.WriteLine("Torpedo track:");
foreach (var sector in course.GetSectorsFrom(_enterprise.SectorCoordinates)) foreach (var sector in course.GetSectorsFrom(_enterprise.SectorCoordinates))
{ {
_output.WriteLine($" {sector}"); _io.WriteLine($" {sector}");
if (quadrant.TorpedoCollisionAt(sector, out var message, out var gameOver)) if (quadrant.TorpedoCollisionAt(sector, out var message, out var gameOver))
{ {
_output.WriteLine(message); _io.WriteLine(message);
isHit = true; isHit = true;
if (gameOver) { return CommandResult.GameOver; } if (gameOver) { return CommandResult.GameOver; }
break; break;
} }
} }
if (!isHit) { _output.WriteLine("Torpedo missed!"); } if (!isHit) { _io.WriteLine("Torpedo missed!"); }
return quadrant.KlingonsFireOnEnterprise(); return quadrant.KlingonsFireOnEnterprise();
} }
internal void ReplenishTorpedoes() => TorpedoCount = _tubeCount; internal void ReplenishTorpedoes() => TorpedoCount = _tubeCount;
}
} }

View File

@@ -1,22 +1,21 @@
using Games.Common.IO;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems namespace SuperStarTrek.Systems;
{
internal class ShieldControl : Subsystem
{
private readonly Enterprise _enterprise;
private readonly Output _output;
private readonly Input _input;
internal ShieldControl(Enterprise enterprise, Output output, Input input) internal class ShieldControl : Subsystem
: base("Shield Control", Command.SHE, output) {
private readonly Enterprise _enterprise;
private readonly IReadWrite _io;
internal ShieldControl(Enterprise enterprise, IReadWrite io)
: base("Shield Control", Command.SHE, io)
{ {
_enterprise = enterprise; _enterprise = enterprise;
_output = output; _io = io;
_input = input;
} }
internal float ShieldEnergy { get; set; } internal float ShieldEnergy { get; set; }
@@ -25,17 +24,17 @@ namespace SuperStarTrek.Systems
protected override CommandResult ExecuteCommandCore(Quadrant quadrant) protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
{ {
_output.WriteLine($"Energy available = {_enterprise.TotalEnergy}"); _io.WriteLine($"Energy available = {_enterprise.TotalEnergy}");
var requested = _input.GetNumber($"Number of units to shields"); var requested = _io.ReadNumber($"Number of units to shields");
if (Validate(requested)) if (Validate(requested))
{ {
ShieldEnergy = requested; ShieldEnergy = requested;
_output.Write(Strings.ShieldsSet, requested); _io.Write(Strings.ShieldsSet, requested);
} }
else else
{ {
_output.WriteLine("<SHIELDS UNCHANGED>"); _io.WriteLine("<SHIELDS UNCHANGED>");
} }
return CommandResult.Ok; return CommandResult.Ok;
@@ -45,7 +44,7 @@ namespace SuperStarTrek.Systems
{ {
if (requested > _enterprise.TotalEnergy) if (requested > _enterprise.TotalEnergy)
{ {
_output.WriteLine("Shield Control reports, 'This is not the Federation Treasury.'"); _io.WriteLine("Shield Control reports, 'This is not the Federation Treasury.'");
return false; return false;
} }
@@ -55,5 +54,4 @@ namespace SuperStarTrek.Systems
internal void AbsorbHit(int hitStrength) => ShieldEnergy -= hitStrength; internal void AbsorbHit(int hitStrength) => ShieldEnergy -= hitStrength;
internal void DropShields() => ShieldEnergy = 0; internal void DropShields() => ShieldEnergy = 0;
}
} }

View File

@@ -2,47 +2,48 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Games.Common.IO;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems namespace SuperStarTrek.Systems;
internal class ShortRangeSensors : Subsystem
{ {
internal class ShortRangeSensors : Subsystem
{
private readonly Enterprise _enterprise; private readonly Enterprise _enterprise;
private readonly Galaxy _galaxy; private readonly Galaxy _galaxy;
private readonly Game _game; private readonly Game _game;
private readonly Output _output; private readonly IReadWrite _io;
internal ShortRangeSensors(Enterprise enterprise, Galaxy galaxy, Game game, Output output) internal ShortRangeSensors(Enterprise enterprise, Galaxy galaxy, Game game, IReadWrite io)
: base("Short Range Sensors", Command.SRS, output) : base("Short Range Sensors", Command.SRS, io)
{ {
_enterprise = enterprise; _enterprise = enterprise;
_galaxy = galaxy; _galaxy = galaxy;
_game = game; _game = game;
_output = output; _io = io;
} }
protected override CommandResult ExecuteCommandCore(Quadrant quadrant) protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
{ {
if (_enterprise.IsDocked) if (_enterprise.IsDocked)
{ {
_output.WriteLine(Strings.ShieldsDropped); _io.WriteLine(Strings.ShieldsDropped);
} }
if (Condition < 0) if (Condition < 0)
{ {
_output.WriteLine(Strings.ShortRangeSensorsOut); _io.WriteLine(Strings.ShortRangeSensorsOut);
} }
_output.WriteLine("---------------------------------"); _io.WriteLine("---------------------------------");
quadrant.GetDisplayLines() quadrant.GetDisplayLines()
.Zip(GetStatusLines(), (sectors, status) => $" {sectors} {status}") .Zip(GetStatusLines(), (sectors, status) => $" {sectors} {status}")
.ToList() .ToList()
.ForEach(l => _output.WriteLine(l)); .ForEach(l => _io.WriteLine(l));
_output.WriteLine("---------------------------------"); _io.WriteLine("---------------------------------");
return CommandResult.Ok; return CommandResult.Ok;
} }
@@ -58,5 +59,4 @@ namespace SuperStarTrek.Systems
yield return $"Shields {(int)_enterprise.ShieldControl.ShieldEnergy}"; yield return $"Shields {(int)_enterprise.ShieldControl.ShieldEnergy}";
yield return $"Klingons remaining {_galaxy.KlingonCount}"; yield return $"Klingons remaining {_galaxy.KlingonCount}";
} }
}
} }

View File

@@ -1,18 +1,19 @@
using Games.Common.IO;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Space; using SuperStarTrek.Space;
namespace SuperStarTrek.Systems namespace SuperStarTrek.Systems;
{
internal abstract class Subsystem
{
private readonly Output _output;
protected Subsystem(string name, Command command, Output output) internal abstract class Subsystem
{
private readonly IReadWrite _io;
protected Subsystem(string name, Command command, IReadWrite io)
{ {
Name = name; Name = name;
Command = command; Command = command;
Condition = 0; Condition = 0;
_output = output; _io = io;
} }
internal string Name { get; } internal string Name { get; }
@@ -29,7 +30,7 @@ namespace SuperStarTrek.Systems
{ {
if (IsDamaged) if (IsDamaged)
{ {
_output.WriteLine(notOperationalMessage.Replace("{name}", Name)); _io.WriteLine(notOperationalMessage.Replace("{name}", Name));
return false; return false;
} }
@@ -64,5 +65,4 @@ namespace SuperStarTrek.Systems
} }
internal void TakeDamage(float damage) => Condition -= damage; internal void TakeDamage(float damage) => Condition -= damage;
}
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using Games.Common.IO;
using SuperStarTrek.Commands; using SuperStarTrek.Commands;
using SuperStarTrek.Objects; using SuperStarTrek.Objects;
using SuperStarTrek.Resources; using SuperStarTrek.Resources;
@@ -9,20 +10,18 @@ namespace SuperStarTrek.Systems
internal class WarpEngines : Subsystem internal class WarpEngines : Subsystem
{ {
private readonly Enterprise _enterprise; private readonly Enterprise _enterprise;
private readonly Output _output; private readonly IReadWrite _io;
private readonly Input _input;
internal WarpEngines(Enterprise enterprise, Output output, Input input) internal WarpEngines(Enterprise enterprise, IReadWrite io)
: base("Warp Engines", Command.NAV, output) : base("Warp Engines", Command.NAV, io)
{ {
_enterprise = enterprise; _enterprise = enterprise;
_output = output; _io = io;
_input = input;
} }
protected override CommandResult ExecuteCommandCore(Quadrant quadrant) protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
{ {
if (_input.TryGetCourse("Course", " Lt. Sulu", out var course) && if (_io.TryReadCourse("Course", " Lt. Sulu", out var course) &&
TryGetWarpFactor(out var warpFactor) && TryGetWarpFactor(out var warpFactor) &&
TryGetDistanceToMove(warpFactor, out var distanceToMove)) TryGetDistanceToMove(warpFactor, out var distanceToMove))
{ {
@@ -51,12 +50,12 @@ namespace SuperStarTrek.Systems
private bool TryGetWarpFactor(out float warpFactor) private bool TryGetWarpFactor(out float warpFactor)
{ {
var maximumWarp = IsDamaged ? 0.2f : 8; var maximumWarp = IsDamaged ? 0.2f : 8;
if (_input.TryGetNumber("Warp Factor", 0, maximumWarp, out warpFactor)) if (_io.TryReadNumberInRange("Warp Factor", 0, maximumWarp, out warpFactor))
{ {
return warpFactor > 0; return warpFactor > 0;
} }
_output.WriteLine( _io.WriteLine(
IsDamaged && warpFactor > maximumWarp IsDamaged && warpFactor > maximumWarp
? "Warp engines are damaged. Maximum speed = warp 0.2" ? "Warp engines are damaged. Maximum speed = warp 0.2"
: $" Chief Engineer Scott reports, 'The engines won't take warp {warpFactor} !'"); : $" Chief Engineer Scott reports, 'The engines won't take warp {warpFactor} !'");
@@ -69,14 +68,14 @@ namespace SuperStarTrek.Systems
distanceToTravel = (int)Math.Round(warpFactor * 8, MidpointRounding.AwayFromZero); distanceToTravel = (int)Math.Round(warpFactor * 8, MidpointRounding.AwayFromZero);
if (distanceToTravel <= _enterprise.Energy) { return true; } if (distanceToTravel <= _enterprise.Energy) { return true; }
_output.WriteLine("Engineering reports, 'Insufficient energy available") _io.WriteLine("Engineering reports, 'Insufficient energy available");
.WriteLine($" for maneuvering at warp {warpFactor} !'"); _io.WriteLine($" for maneuvering at warp {warpFactor} !'");
if (distanceToTravel <= _enterprise.TotalEnergy && !_enterprise.ShieldControl.IsDamaged) if (distanceToTravel <= _enterprise.TotalEnergy && !_enterprise.ShieldControl.IsDamaged)
{ {
_output.Write($"Deflector control room acknowledges {_enterprise.ShieldControl.ShieldEnergy} ") _io.Write($"Deflector control room acknowledges {_enterprise.ShieldControl.ShieldEnergy} ");
.WriteLine("units of energy") _io.WriteLine("units of energy");
.WriteLine(" presently deployed to shields."); _io.WriteLine(" presently deployed to shields.");
} }
return false; return false;