diff --git a/00_Common/dotnet/Games.Common/IO/ConsoleIO.cs b/00_Common/dotnet/Games.Common/IO/ConsoleIO.cs index a24238bf..a1f77083 100644 --- a/00_Common/dotnet/Games.Common/IO/ConsoleIO.cs +++ b/00_Common/dotnet/Games.Common/IO/ConsoleIO.cs @@ -12,4 +12,6 @@ public sealed class ConsoleIO : TextIO : base(Console.In, Console.Out) { } + + public override char ReadCharacter() => Console.ReadKey(intercept: true).KeyChar; } diff --git a/00_Common/dotnet/Games.Common/IO/IReadWrite.cs b/00_Common/dotnet/Games.Common/IO/IReadWrite.cs index 02ab573e..3d2cfc7e 100644 --- a/00_Common/dotnet/Games.Common/IO/IReadWrite.cs +++ b/00_Common/dotnet/Games.Common/IO/IReadWrite.cs @@ -8,6 +8,12 @@ namespace Games.Common.IO; /// public interface IReadWrite { + /// + /// Reads a character from input. + /// + /// The character read. + char ReadCharacter(); + /// /// Reads a value from input. /// @@ -99,6 +105,13 @@ public interface IReadWrite /// /// The format to be written. /// The values to be inserted into the format. + void Write(string format, params object[] values); + + /// + /// Writes a formatted string to output followed by a new-line. + /// + /// The format to be written. + /// The values to be inserted into the format. void WriteLine(string format, params object[] values); /// diff --git a/00_Common/dotnet/Games.Common/IO/TextIO.cs b/00_Common/dotnet/Games.Common/IO/TextIO.cs index c2584e69..5a31e37c 100644 --- a/00_Common/dotnet/Games.Common/IO/TextIO.cs +++ b/00_Common/dotnet/Games.Common/IO/TextIO.cs @@ -29,6 +29,15 @@ public class TextIO : IReadWrite _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, float) Read2Numbers(string prompt) @@ -99,6 +108,8 @@ public class TextIO : IReadWrite 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 Write(Stream stream, bool keepOpen = false) diff --git a/84_Super_Star_Trek/csharp/Commands/Command.cs b/84_Super_Star_Trek/csharp/Commands/Command.cs index f0d569f8..e6b65db9 100644 --- a/84_Super_Star_Trek/csharp/Commands/Command.cs +++ b/84_Super_Star_Trek/csharp/Commands/Command.cs @@ -1,34 +1,33 @@ using System.ComponentModel; -namespace SuperStarTrek.Commands +namespace SuperStarTrek.Commands; + +internal enum Command { - internal enum Command - { - [Description("To set course")] - NAV, + [Description("To set course")] + NAV, - [Description("For short range sensor scan")] - SRS, + [Description("For short range sensor scan")] + SRS, - [Description("For long range sensor scan")] - LRS, + [Description("For long range sensor scan")] + LRS, - [Description("To fire phasers")] - PHA, + [Description("To fire phasers")] + PHA, - [Description("To fire photon torpedoes")] - TOR, + [Description("To fire photon torpedoes")] + TOR, - [Description("To raise or lower shields")] - SHE, + [Description("To raise or lower shields")] + SHE, - [Description("For damage control reports")] - DAM, + [Description("For damage control reports")] + DAM, - [Description("To call on library-computer")] - COM, + [Description("To call on library-computer")] + COM, - [Description("To resign your command")] - XXX - } + [Description("To resign your command")] + XXX } diff --git a/84_Super_Star_Trek/csharp/Commands/CommandExtensions.cs b/84_Super_Star_Trek/csharp/Commands/CommandExtensions.cs index f8f7e9da..e3e2f7aa 100644 --- a/84_Super_Star_Trek/csharp/Commands/CommandExtensions.cs +++ b/84_Super_Star_Trek/csharp/Commands/CommandExtensions.cs @@ -1,14 +1,13 @@ using System.Reflection; using System.ComponentModel; -namespace SuperStarTrek.Commands +namespace SuperStarTrek.Commands; + +internal static class CommandExtensions { - internal static class CommandExtensions - { - internal static string GetDescription(this Command command) => - typeof(Command) - .GetField(command.ToString()) - .GetCustomAttribute() - .Description; - } + internal static string GetDescription(this Command command) => + typeof(Command) + .GetField(command.ToString()) + .GetCustomAttribute() + .Description; } diff --git a/84_Super_Star_Trek/csharp/Commands/CommandResult.cs b/84_Super_Star_Trek/csharp/Commands/CommandResult.cs index 1b02780c..be95526f 100644 --- a/84_Super_Star_Trek/csharp/Commands/CommandResult.cs +++ b/84_Super_Star_Trek/csharp/Commands/CommandResult.cs @@ -1,23 +1,22 @@ -namespace SuperStarTrek.Commands +namespace SuperStarTrek.Commands; + +internal class CommandResult { - internal class CommandResult + public static readonly CommandResult Ok = new(false); + public static readonly CommandResult GameOver = new(true); + + private CommandResult(bool isGameOver) { - public static readonly CommandResult Ok = new(false); - public static readonly CommandResult GameOver = new(true); - - private CommandResult(bool isGameOver) - { - IsGameOver = isGameOver; - } - - private CommandResult(float timeElapsed) - { - TimeElapsed = timeElapsed; - } - - public bool IsGameOver { get; } - public float TimeElapsed { get; } - - public static CommandResult Elapsed(float timeElapsed) => new(timeElapsed); + IsGameOver = isGameOver; } + + private CommandResult(float timeElapsed) + { + TimeElapsed = timeElapsed; + } + + public bool IsGameOver { get; } + public float TimeElapsed { get; } + + public static CommandResult Elapsed(float timeElapsed) => new(timeElapsed); } diff --git a/84_Super_Star_Trek/csharp/Game.cs b/84_Super_Star_Trek/csharp/Game.cs index 274e2948..918b9f65 100644 --- a/84_Super_Star_Trek/csharp/Game.cs +++ b/84_Super_Star_Trek/csharp/Game.cs @@ -1,131 +1,128 @@ using System; +using Games.Common.IO; +using Games.Common.Randomness; using SuperStarTrek.Objects; using SuperStarTrek.Resources; using SuperStarTrek.Space; using SuperStarTrek.Systems; using SuperStarTrek.Systems.ComputerFunctions; -namespace SuperStarTrek +namespace SuperStarTrek; + +internal class Game { - internal class Game + private readonly TextIO _io; + private readonly IRandom _random; + + private int _initialStardate; + private int _finalStarDate; + private float _currentStardate; + private Coordinates _currentQuadrant; + private Galaxy _galaxy; + private int _initialKlingonCount; + private Enterprise _enterprise; + + internal Game(TextIO io, IRandom random) { - private readonly Output _output; - private readonly Input _input; - private readonly Random _random; - - private int _initialStardate; - private int _finalStarDate; - private float _currentStardate; - private Coordinates _currentQuadrant; - private Galaxy _galaxy; - private int _initialKlingonCount; - private Enterprise _enterprise; - - internal Game(Output output, Input input, Random random) - { - _output = output; - _input = input; - _random = random; - } - - internal float Stardate => _currentStardate; - - internal float StardatesRemaining => _finalStarDate - _currentStardate; - - internal void DoIntroduction() - { - _output.Write(Strings.Title); - - if (_input.GetYesNo("Do you need instructions", Input.YesNoMode.FalseOnN)) - { - _output.Write(Strings.Instructions); - - _input.WaitForAnyKeyButEnter("to continue"); - } - } - - internal void Play() - { - Initialise(); - var gameOver = false; - - while (!gameOver) - { - var command = _input.GetCommand(); - - var result = _enterprise.Execute(command); - - gameOver = result.IsGameOver || CheckIfStranded(); - _currentStardate += result.TimeElapsed; - gameOver |= _currentStardate > _finalStarDate; - } - - if (_galaxy.KlingonCount > 0) - { - _output.Write(Strings.EndOfMission, _currentStardate, _galaxy.KlingonCount); - } - else - { - _output.Write(Strings.Congratulations, GetEfficiency()); - } - } - - private void Initialise() - { - _currentStardate = _initialStardate = _random.GetInt(20, 40) * 100; - _finalStarDate = _initialStardate + _random.GetInt(25, 35); - - _currentQuadrant = _random.GetCoordinate(); - - _galaxy = new Galaxy(_random); - _initialKlingonCount = _galaxy.KlingonCount; - - _enterprise = new Enterprise(3000, _random.GetCoordinate(), _output, _random, _input); - _enterprise - .Add(new WarpEngines(_enterprise, _output, _input)) - .Add(new ShortRangeSensors(_enterprise, _galaxy, this, _output)) - .Add(new LongRangeSensors(_galaxy, _output)) - .Add(new PhaserControl(_enterprise, _output, _input, _random)) - .Add(new PhotonTubes(10, _enterprise, _output, _input)) - .Add(new ShieldControl(_enterprise, _output, _input)) - .Add(new DamageControl(_enterprise, _output)) - .Add(new LibraryComputer( - _output, - _input, - new CumulativeGalacticRecord(_output, _galaxy), - new StatusReport(this, _galaxy, _enterprise, _output), - new TorpedoDataCalculator(_enterprise, _output), - new StarbaseDataCalculator(_enterprise, _output), - new DirectionDistanceCalculator(_enterprise, _output, _input), - new GalaxyRegionMap(_output, _galaxy))); - - _output.Write(Strings.Enterprise); - _output.Write( - Strings.Orders, - _galaxy.KlingonCount, - _finalStarDate, - _finalStarDate - _initialStardate, - _galaxy.StarbaseCount > 1 ? "are" : "is", - _galaxy.StarbaseCount, - _galaxy.StarbaseCount > 1 ? "s" : ""); - - _input.WaitForAnyKeyButEnter("when ready to accept command"); - - _enterprise.StartIn(BuildCurrentQuadrant()); - } - - private Quadrant BuildCurrentQuadrant() => - new Quadrant(_galaxy[_currentQuadrant], _enterprise, _random, _galaxy, _input, _output); - - internal bool Replay() => _galaxy.StarbaseCount > 0 && _input.GetString(Strings.ReplayPrompt, "Aye"); - - private bool CheckIfStranded() - { - if (_enterprise.IsStranded) { _output.Write(Strings.Stranded); } - return _enterprise.IsStranded; - } - - private float GetEfficiency() => - 1000 * (float)Math.Pow(_initialKlingonCount / (_currentStardate - _initialStardate), 2); + _io = io; + _random = random; } + + internal float Stardate => _currentStardate; + + internal float StardatesRemaining => _finalStarDate - _currentStardate; + + internal void DoIntroduction() + { + _io.Write(Strings.Title); + + if (_io.GetYesNo("Do you need instructions", IReadWriteExtensions.YesNoMode.FalseOnN)) + { + _io.Write(Strings.Instructions); + + _io.WaitForAnyKeyButEnter("to continue"); + } + } + + internal void Play() + { + Initialise(); + var gameOver = false; + + while (!gameOver) + { + var command = _io.ReadCommand(); + + var result = _enterprise.Execute(command); + + gameOver = result.IsGameOver || CheckIfStranded(); + _currentStardate += result.TimeElapsed; + gameOver |= _currentStardate > _finalStarDate; + } + + if (_galaxy.KlingonCount > 0) + { + _io.Write(Strings.EndOfMission, _currentStardate, _galaxy.KlingonCount); + } + else + { + _io.Write(Strings.Congratulations, CalculateEfficiency()); + } + } + + private void Initialise() + { + _currentStardate = _initialStardate = _random.Next(20, 40) * 100; + _finalStarDate = _initialStardate + _random.Next(25, 35); + + _currentQuadrant = _random.NextCoordinate(); + + _galaxy = new Galaxy(_random); + _initialKlingonCount = _galaxy.KlingonCount; + + _enterprise = new Enterprise(3000, _random.NextCoordinate(), _io, _random); + _enterprise + .Add(new WarpEngines(_enterprise, _io)) + .Add(new ShortRangeSensors(_enterprise, _galaxy, this, _io)) + .Add(new LongRangeSensors(_galaxy, _io)) + .Add(new PhaserControl(_enterprise, _io, _random)) + .Add(new PhotonTubes(10, _enterprise, _io)) + .Add(new ShieldControl(_enterprise, _io)) + .Add(new DamageControl(_enterprise, _io)) + .Add(new LibraryComputer( + _io, + new CumulativeGalacticRecord(_io, _galaxy), + new StatusReport(this, _galaxy, _enterprise, _io), + new TorpedoDataCalculator(_enterprise, _io), + new StarbaseDataCalculator(_enterprise, _io), + new DirectionDistanceCalculator(_enterprise, _io), + new GalaxyRegionMap(_io, _galaxy))); + + _io.Write(Strings.Enterprise); + _io.Write( + Strings.Orders, + _galaxy.KlingonCount, + _finalStarDate, + _finalStarDate - _initialStardate, + _galaxy.StarbaseCount > 1 ? "are" : "is", + _galaxy.StarbaseCount, + _galaxy.StarbaseCount > 1 ? "s" : ""); + + _io.WaitForAnyKeyButEnter("when ready to accept command"); + + _enterprise.StartIn(BuildCurrentQuadrant()); + } + + private Quadrant BuildCurrentQuadrant() => new(_galaxy[_currentQuadrant], _enterprise, _random, _galaxy, _io); + + internal bool Replay() => _galaxy.StarbaseCount > 0 && _io.ReadExpectedString(Strings.ReplayPrompt, "Aye"); + + private bool CheckIfStranded() + { + if (_enterprise.IsStranded) { _io.Write(Strings.Stranded); } + return _enterprise.IsStranded; + } + + private float CalculateEfficiency() => + 1000 * (float)Math.Pow(_initialKlingonCount / (_currentStardate - _initialStardate), 2); } diff --git a/84_Super_Star_Trek/csharp/IRandomExtensions.cs b/84_Super_Star_Trek/csharp/IRandomExtensions.cs new file mode 100644 index 00000000..f115aeed --- /dev/null +++ b/84_Super_Star_Trek/csharp/IRandomExtensions.cs @@ -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); +} diff --git a/84_Super_Star_Trek/csharp/IReadWriteExtensions.cs b/84_Super_Star_Trek/csharp/IReadWriteExtensions.cs new file mode 100644 index 00000000..f46e74f7 --- /dev/null +++ b/84_Super_Star_Trek/csharp/IReadWriteExtensions.cs @@ -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()) + { + 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 + } +} diff --git a/84_Super_Star_Trek/csharp/Input.cs b/84_Super_Star_Trek/csharp/Input.cs deleted file mode 100644 index 2b37b2ba..00000000 --- a/84_Super_Star_Trek/csharp/Input.cs +++ /dev/null @@ -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()) - { - _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 - } - } -} diff --git a/84_Super_Star_Trek/csharp/Objects/Enterprise.cs b/84_Super_Star_Trek/csharp/Objects/Enterprise.cs index ac9e8112..f288e920 100644 --- a/84_Super_Star_Trek/csharp/Objects/Enterprise.cs +++ b/84_Super_Star_Trek/csharp/Objects/Enterprise.cs @@ -1,231 +1,230 @@ using System; using System.Collections.Generic; using System.Linq; +using Games.Common.IO; +using Games.Common.Randomness; using SuperStarTrek.Commands; using SuperStarTrek.Resources; using SuperStarTrek.Space; using SuperStarTrek.Systems; -namespace SuperStarTrek.Objects +namespace SuperStarTrek.Objects; + +internal class Enterprise { - internal class Enterprise + private readonly int _maxEnergy; + private readonly IReadWrite _io; + private readonly List _systems; + private readonly Dictionary _commandExecutors; + private readonly IRandom _random; + private Quadrant _quadrant; + + public Enterprise(int maxEnergy, Coordinates sector, IReadWrite io, IRandom random) { - private readonly int _maxEnergy; - private readonly Output _output; - private readonly List _systems; - private readonly Dictionary _commandExecutors; - private readonly Random _random; - private readonly Input _input; - private Quadrant _quadrant; + SectorCoordinates = sector; + TotalEnergy = _maxEnergy = maxEnergy; - public Enterprise(int maxEnergy, Coordinates sector, Output output, Random random, Input input) - { - SectorCoordinates = sector; - TotalEnergy = _maxEnergy = maxEnergy; - - _systems = new List(); - _commandExecutors = new Dictionary(); - _output = output; - _random = random; - _input = input; - } - - internal Quadrant Quadrant => _quadrant; - - internal Coordinates QuadrantCoordinates => _quadrant.Coordinates; - - internal Coordinates SectorCoordinates { get; private set; } - - internal string Condition => GetCondition(); - - internal LibraryComputer Computer => (LibraryComputer)_commandExecutors[Command.COM]; - - internal ShieldControl ShieldControl => (ShieldControl)_commandExecutors[Command.SHE]; - - internal float Energy => TotalEnergy - ShieldControl.ShieldEnergy; - - internal float TotalEnergy { get; private set; } - - internal int DamagedSystemCount => _systems.Count(s => s.IsDamaged); - - internal IEnumerable Systems => _systems; - - internal PhotonTubes PhotonTubes => (PhotonTubes)_commandExecutors[Command.TOR]; - - internal bool IsDocked => _quadrant.EnterpriseIsNextToStarbase; - - internal bool IsStranded => TotalEnergy < 10 || Energy < 10 && ShieldControl.IsDamaged; - - internal Enterprise Add(Subsystem system) - { - _systems.Add(system); - _commandExecutors[system.Command] = system; - - return this; - } - - internal void StartIn(Quadrant quadrant) - { - _quadrant = quadrant; - quadrant.Display(Strings.StartText); - } - - private string GetCondition() => - IsDocked switch - { - true => "Docked", - false when _quadrant.HasKlingons => "*Red*", - false when Energy / _maxEnergy < 0.1f => "Yellow", - false => "Green" - }; - - internal CommandResult Execute(Command command) - { - if (command == Command.XXX) { return CommandResult.GameOver; } - - return _commandExecutors[command].ExecuteCommand(_quadrant); - } - - internal void Refuel() => TotalEnergy = _maxEnergy; - - public override string ToString() => "<*>"; - - internal void UseEnergy(float amountUsed) - { - TotalEnergy -= amountUsed; - } - - internal CommandResult TakeHit(Coordinates sector, int hitStrength) - { - _output.WriteLine($"{hitStrength} unit hit on Enterprise from sector {sector}"); - ShieldControl.AbsorbHit(hitStrength); - - if (ShieldControl.ShieldEnergy <= 0) - { - _output.WriteLine(Strings.Destroyed); - return CommandResult.GameOver; - } - - _output.WriteLine($" "); - - if (hitStrength >= 20) - { - TakeDamage(hitStrength); - } - - return CommandResult.Ok; - } - - private void TakeDamage(float hitStrength) - { - var hitShieldRatio = hitStrength / ShieldControl.ShieldEnergy; - if (_random.GetFloat() > 0.6 || hitShieldRatio <= 0.02f) - { - return; - } - - var system = _systems[_random.Get1To8Inclusive() - 1]; - system.TakeDamage(hitShieldRatio + 0.5f * _random.GetFloat()); - _output.WriteLine($"Damage Control reports, '{system.Name} damaged by the hit.'"); - } - - internal void RepairSystems(float repairWorkDone) - { - var repairedSystems = new List(); - - foreach (var system in _systems.Where(s => s.IsDamaged)) - { - if (system.Repair(repairWorkDone)) - { - repairedSystems.Add(system.Name); - } - } - - if (repairedSystems.Any()) - { - _output.WriteLine("Damage Control report:"); - foreach (var systemName in repairedSystems) - { - _output.WriteLine($" {systemName} repair completed."); - } - } - } - - internal void VaryConditionOfRandomSystem() - { - if (_random.GetFloat() > 0.2f) { return; } - - var system = _systems[_random.Get1To8Inclusive() - 1]; - _output.Write($"Damage Control report: {system.Name} "); - if (_random.GetFloat() >= 0.6) - { - system.Repair(_random.GetFloat() * 3 + 1); - _output.WriteLine("state of repair improved"); - } - else - { - system.TakeDamage(_random.GetFloat() * 5 + 1); - _output.WriteLine("damaged"); - } - } - - internal float Move(Course course, float warpFactor, int distance) - { - var (quadrant, sector) = MoveWithinQuadrant(course, distance) ?? MoveBeyondQuadrant(course, distance); - - if (quadrant != _quadrant.Coordinates) - { - _quadrant = new Quadrant(_quadrant.Galaxy[quadrant], this, _random, _quadrant.Galaxy, _input, _output); - } - _quadrant.SetEnterpriseSector(sector); - SectorCoordinates = sector; - - TotalEnergy -= distance + 10; - if (Energy < 0) - { - _output.WriteLine("Shield Control supplies energy to complete the maneuver."); - ShieldControl.ShieldEnergy = Math.Max(0, TotalEnergy); - } - - return GetTimeElapsed(quadrant, warpFactor); - } - - private (Coordinates, Coordinates)? MoveWithinQuadrant(Course course, int distance) - { - var currentSector = SectorCoordinates; - foreach (var (sector, index) in course.GetSectorsFrom(SectorCoordinates).Select((s, i) => (s, i))) - { - if (distance == 0) { break; } - - if (_quadrant.HasObjectAt(sector)) - { - _output.WriteLine($"Warp engines shut down at sector {currentSector} dues to bad navigation"); - distance = 0; - break; - } - - currentSector = sector; - distance -= 1; - } - - return distance == 0 ? (_quadrant.Coordinates, currentSector) : null; - } - - private (Coordinates, Coordinates) MoveBeyondQuadrant(Course course, int distance) - { - var (complete, quadrant, sector) = course.GetDestination(QuadrantCoordinates, SectorCoordinates, distance); - - if (!complete) - { - _output.Write(Strings.PermissionDenied, sector, quadrant); - } - - return (quadrant, sector); - } - - private float GetTimeElapsed(Coordinates finalQuadrant, float warpFactor) => - finalQuadrant == _quadrant.Coordinates - ? Math.Min(1, (float)Math.Round(warpFactor, 1, MidpointRounding.ToZero)) - : 1; + _systems = new List(); + _commandExecutors = new Dictionary(); + _io = io; + _random = random; } + + internal Quadrant Quadrant => _quadrant; + + internal Coordinates QuadrantCoordinates => _quadrant.Coordinates; + + internal Coordinates SectorCoordinates { get; private set; } + + internal string Condition => GetCondition(); + + internal LibraryComputer Computer => (LibraryComputer)_commandExecutors[Command.COM]; + + internal ShieldControl ShieldControl => (ShieldControl)_commandExecutors[Command.SHE]; + + internal float Energy => TotalEnergy - ShieldControl.ShieldEnergy; + + internal float TotalEnergy { get; private set; } + + internal int DamagedSystemCount => _systems.Count(s => s.IsDamaged); + + internal IEnumerable Systems => _systems; + + internal PhotonTubes PhotonTubes => (PhotonTubes)_commandExecutors[Command.TOR]; + + internal bool IsDocked => _quadrant.EnterpriseIsNextToStarbase; + + internal bool IsStranded => TotalEnergy < 10 || Energy < 10 && ShieldControl.IsDamaged; + + internal Enterprise Add(Subsystem system) + { + _systems.Add(system); + _commandExecutors[system.Command] = system; + + return this; + } + + internal void StartIn(Quadrant quadrant) + { + _quadrant = quadrant; + quadrant.Display(Strings.StartText); + } + + private string GetCondition() => + IsDocked switch + { + true => "Docked", + false when _quadrant.HasKlingons => "*Red*", + false when Energy / _maxEnergy < 0.1f => "Yellow", + false => "Green" + }; + + internal CommandResult Execute(Command command) + { + if (command == Command.XXX) { return CommandResult.GameOver; } + + return _commandExecutors[command].ExecuteCommand(_quadrant); + } + + internal void Refuel() => TotalEnergy = _maxEnergy; + + public override string ToString() => "<*>"; + + internal void UseEnergy(float amountUsed) + { + TotalEnergy -= amountUsed; + } + + internal CommandResult TakeHit(Coordinates sector, int hitStrength) + { + _io.WriteLine($"{hitStrength} unit hit on Enterprise from sector {sector}"); + ShieldControl.AbsorbHit(hitStrength); + + if (ShieldControl.ShieldEnergy <= 0) + { + _io.WriteLine(Strings.Destroyed); + return CommandResult.GameOver; + } + + _io.WriteLine($" "); + + if (hitStrength >= 20) + { + TakeDamage(hitStrength); + } + + return CommandResult.Ok; + } + + private void TakeDamage(float hitStrength) + { + var hitShieldRatio = hitStrength / ShieldControl.ShieldEnergy; + if (_random.NextFloat() > 0.6 || hitShieldRatio <= 0.02f) + { + return; + } + + var system = _systems[_random.Next1To8Inclusive() - 1]; + system.TakeDamage(hitShieldRatio + 0.5f * _random.NextFloat()); + _io.WriteLine($"Damage Control reports, '{system.Name} damaged by the hit.'"); + } + + internal void RepairSystems(float repairWorkDone) + { + var repairedSystems = new List(); + + foreach (var system in _systems.Where(s => s.IsDamaged)) + { + if (system.Repair(repairWorkDone)) + { + repairedSystems.Add(system.Name); + } + } + + if (repairedSystems.Any()) + { + _io.WriteLine("Damage Control report:"); + foreach (var systemName in repairedSystems) + { + _io.WriteLine($" {systemName} repair completed."); + } + } + } + + internal void VaryConditionOfRandomSystem() + { + if (_random.NextFloat() > 0.2f) { return; } + + var system = _systems[_random.Next1To8Inclusive() - 1]; + _io.Write($"Damage Control report: {system.Name} "); + if (_random.NextFloat() >= 0.6) + { + system.Repair(_random.NextFloat() * 3 + 1); + _io.WriteLine("state of repair improved"); + } + else + { + system.TakeDamage(_random.NextFloat() * 5 + 1); + _io.WriteLine("damaged"); + } + } + + internal float Move(Course course, float warpFactor, int distance) + { + var (quadrant, sector) = MoveWithinQuadrant(course, distance) ?? MoveBeyondQuadrant(course, distance); + + if (quadrant != _quadrant.Coordinates) + { + _quadrant = new Quadrant(_quadrant.Galaxy[quadrant], this, _random, _quadrant.Galaxy, _io); + } + _quadrant.SetEnterpriseSector(sector); + SectorCoordinates = sector; + + TotalEnergy -= distance + 10; + if (Energy < 0) + { + _io.WriteLine("Shield Control supplies energy to complete the maneuver."); + ShieldControl.ShieldEnergy = Math.Max(0, TotalEnergy); + } + + return GetTimeElapsed(quadrant, warpFactor); + } + + private (Coordinates, Coordinates)? MoveWithinQuadrant(Course course, int distance) + { + var currentSector = SectorCoordinates; + foreach (var (sector, index) in course.GetSectorsFrom(SectorCoordinates).Select((s, i) => (s, i))) + { + if (distance == 0) { break; } + + if (_quadrant.HasObjectAt(sector)) + { + _io.WriteLine($"Warp engines shut down at sector {currentSector} dues to bad navigation"); + distance = 0; + break; + } + + currentSector = sector; + distance -= 1; + } + + return distance == 0 ? (_quadrant.Coordinates, currentSector) : null; + } + + private (Coordinates, Coordinates) MoveBeyondQuadrant(Course course, int distance) + { + var (complete, quadrant, sector) = course.GetDestination(QuadrantCoordinates, SectorCoordinates, distance); + + if (!complete) + { + _io.Write(Strings.PermissionDenied, sector, quadrant); + } + + return (quadrant, sector); + } + + private float GetTimeElapsed(Coordinates finalQuadrant, float warpFactor) => + finalQuadrant == _quadrant.Coordinates + ? Math.Min(1, (float)Math.Round(warpFactor, 1, MidpointRounding.ToZero)) + : 1; } diff --git a/84_Super_Star_Trek/csharp/Objects/Klingon.cs b/84_Super_Star_Trek/csharp/Objects/Klingon.cs index 1b55940a..7e84ae56 100644 --- a/84_Super_Star_Trek/csharp/Objects/Klingon.cs +++ b/84_Super_Star_Trek/csharp/Objects/Klingon.cs @@ -1,43 +1,43 @@ +using Games.Common.Randomness; using SuperStarTrek.Commands; using SuperStarTrek.Space; -namespace SuperStarTrek.Objects +namespace SuperStarTrek.Objects; + +internal class Klingon { - internal class Klingon + private readonly IRandom _random; + + internal Klingon(Coordinates sector, IRandom random) { - private readonly Random _random; - - internal Klingon(Coordinates sector, Random random) - { - Sector = sector; - _random = random; - Energy = _random.GetFloat(100, 300); - } - - internal float Energy { get; private set; } - - internal Coordinates Sector { get; private set; } - - public override string ToString() => "+K+"; - - internal CommandResult FireOn(Enterprise enterprise) - { - var attackStrength = _random.GetFloat(); - var distanceToEnterprise = Sector.GetDistanceTo(enterprise.SectorCoordinates); - var hitStrength = (int)(Energy * (2 + attackStrength) / distanceToEnterprise); - Energy /= 3 + attackStrength; - - return enterprise.TakeHit(Sector, hitStrength); - } - - internal bool TakeHit(int hitStrength) - { - if (hitStrength < 0.15 * Energy) { return false; } - - Energy -= hitStrength; - return true; - } - - internal void MoveTo(Coordinates newSector) => Sector = newSector; + Sector = sector; + _random = random; + Energy = _random.NextFloat(100, 300); } + + internal float Energy { get; private set; } + + internal Coordinates Sector { get; private set; } + + public override string ToString() => "+K+"; + + internal CommandResult FireOn(Enterprise enterprise) + { + var attackStrength = _random.NextFloat(); + var distanceToEnterprise = Sector.GetDistanceTo(enterprise.SectorCoordinates); + var hitStrength = (int)(Energy * (2 + attackStrength) / distanceToEnterprise); + Energy /= 3 + attackStrength; + + return enterprise.TakeHit(Sector, hitStrength); + } + + internal bool TakeHit(int hitStrength) + { + if (hitStrength < 0.15 * Energy) { return false; } + + Energy -= hitStrength; + return true; + } + + internal void MoveTo(Coordinates newSector) => Sector = newSector; } diff --git a/84_Super_Star_Trek/csharp/Objects/Star.cs b/84_Super_Star_Trek/csharp/Objects/Star.cs index 1d9eef6f..8676d3c3 100644 --- a/84_Super_Star_Trek/csharp/Objects/Star.cs +++ b/84_Super_Star_Trek/csharp/Objects/Star.cs @@ -1,7 +1,6 @@ -namespace SuperStarTrek.Objects +namespace SuperStarTrek.Objects; + +internal class Star { - internal class Star - { - public override string ToString() => " * "; - } + public override string ToString() => " * "; } diff --git a/84_Super_Star_Trek/csharp/Objects/Starbase.cs b/84_Super_Star_Trek/csharp/Objects/Starbase.cs index c9abb870..f9e5eaec 100644 --- a/84_Super_Star_Trek/csharp/Objects/Starbase.cs +++ b/84_Super_Star_Trek/csharp/Objects/Starbase.cs @@ -1,45 +1,44 @@ +using Games.Common.IO; +using Games.Common.Randomness; using SuperStarTrek.Resources; using SuperStarTrek.Space; -namespace SuperStarTrek.Objects +namespace SuperStarTrek.Objects; + +internal class Starbase { - internal class Starbase + private readonly IReadWrite _io; + private readonly float _repairDelay; + + internal Starbase(Coordinates sector, IRandom random, IReadWrite io) { - private readonly Input _input; - private readonly Output _output; - private readonly float _repairDelay; - - internal Starbase(Coordinates sector, Random random, Input input, Output output) - { - Sector = sector; - _repairDelay = random.GetFloat() * 0.5f; - _input = input; - _output = output; - } - - internal Coordinates Sector { get; } - - public override string ToString() => ">!<"; - - internal bool TryRepair(Enterprise enterprise, out float repairTime) - { - repairTime = enterprise.DamagedSystemCount * 0.1f + _repairDelay; - if (repairTime >= 1) { repairTime = 0.9f; } - - _output.Write(Strings.RepairEstimate, repairTime); - if (_input.GetYesNo(Strings.RepairPrompt, Input.YesNoMode.TrueOnY)) - { - foreach (var system in enterprise.Systems) - { - system.Repair(); - } - return true; - } - - repairTime = 0; - return false; - } - - internal void ProtectEnterprise() => _output.WriteLine(Strings.Protected); + Sector = sector; + _repairDelay = random.NextFloat(0.5f); + _io = io; } + + internal Coordinates Sector { get; } + + public override string ToString() => ">!<"; + + internal bool TryRepair(Enterprise enterprise, out float repairTime) + { + repairTime = enterprise.DamagedSystemCount * 0.1f + _repairDelay; + if (repairTime >= 1) { repairTime = 0.9f; } + + _io.Write(Strings.RepairEstimate, repairTime); + if (_io.GetYesNo(Strings.RepairPrompt, IReadWriteExtensions.YesNoMode.TrueOnY)) + { + foreach (var system in enterprise.Systems) + { + system.Repair(); + } + return true; + } + + repairTime = 0; + return false; + } + + internal void ProtectEnterprise() => _io.WriteLine(Strings.Protected); } diff --git a/84_Super_Star_Trek/csharp/Output.cs b/84_Super_Star_Trek/csharp/Output.cs deleted file mode 100644 index 6a377bce..00000000 --- a/84_Super_Star_Trek/csharp/Output.cs +++ /dev/null @@ -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; - } - - } -} diff --git a/84_Super_Star_Trek/csharp/Program.cs b/84_Super_Star_Trek/csharp/Program.cs index 7080df29..730d56d4 100644 --- a/84_Super_Star_Trek/csharp/Program.cs +++ b/84_Super_Star_Trek/csharp/Program.cs @@ -23,24 +23,18 @@ // **** 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(); - } while (game.Replay()); - } - } -} + game.Play(); +} while (game.Replay()); diff --git a/84_Super_Star_Trek/csharp/Random.cs b/84_Super_Star_Trek/csharp/Random.cs deleted file mode 100644 index cd0ef230..00000000 --- a/84_Super_Star_Trek/csharp/Random.cs +++ /dev/null @@ -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; - } -} diff --git a/84_Super_Star_Trek/csharp/Resources/CourtMartial.txt b/84_Super_Star_Trek/csharp/Resources/CourtMartial.txt index a6c5285e..7d05a5b8 100644 --- a/84_Super_Star_Trek/csharp/Resources/CourtMartial.txt +++ b/84_Super_Star_Trek/csharp/Resources/CourtMartial.txt @@ -1,3 +1,3 @@ Starfleet Command reviewing your record to consider -court martial! +court martial! \ No newline at end of file diff --git a/84_Super_Star_Trek/csharp/Resources/Enterprise.txt b/84_Super_Star_Trek/csharp/Resources/Enterprise.txt index a47156a5..4310ca91 100644 --- a/84_Super_Star_Trek/csharp/Resources/Enterprise.txt +++ b/84_Super_Star_Trek/csharp/Resources/Enterprise.txt @@ -16,3 +16,9 @@ '----------------' THE USS ENTERPRISE --- NCC-1701 + + + + + + diff --git a/84_Super_Star_Trek/csharp/Resources/Instructions.txt b/84_Super_Star_Trek/csharp/Resources/Instructions.txt index 72ded5ce..34b4169c 100644 --- a/84_Super_Star_Trek/csharp/Resources/Instructions.txt +++ b/84_Super_Star_Trek/csharp/Resources/Instructions.txt @@ -104,3 +104,4 @@ COM command = Library-Computer Option 5 = Galactic Region Name Map This option prints the names of the sixteen major galactic regions referred to in the game. + diff --git a/84_Super_Star_Trek/csharp/Resources/NoEnemyShips.txt b/84_Super_Star_Trek/csharp/Resources/NoEnemyShips.txt index d4b2efb2..394f1057 100644 --- a/84_Super_Star_Trek/csharp/Resources/NoEnemyShips.txt +++ b/84_Super_Star_Trek/csharp/Resources/NoEnemyShips.txt @@ -1,2 +1,2 @@ Science Officer Spock reports, 'Sensors show no enemy ships - in this quadrant' + in this quadrant' \ No newline at end of file diff --git a/84_Super_Star_Trek/csharp/Resources/NoStarbase.txt b/84_Super_Star_Trek/csharp/Resources/NoStarbase.txt index 59c991ec..5bb4e5fb 100644 --- a/84_Super_Star_Trek/csharp/Resources/NoStarbase.txt +++ b/84_Super_Star_Trek/csharp/Resources/NoStarbase.txt @@ -1 +1 @@ -Mr. Spock reports, 'Sensors show no starbases in this quadrant.' +Mr. Spock reports, 'Sensors show no starbases in this quadrant.' \ No newline at end of file diff --git a/84_Super_Star_Trek/csharp/Resources/NowEntering.txt b/84_Super_Star_Trek/csharp/Resources/NowEntering.txt index 6e770a71..6545f9c4 100644 --- a/84_Super_Star_Trek/csharp/Resources/NowEntering.txt +++ b/84_Super_Star_Trek/csharp/Resources/NowEntering.txt @@ -1,2 +1,3 @@ Now entering {0} quadrant . . . + diff --git a/84_Super_Star_Trek/csharp/Resources/Orders.txt b/84_Super_Star_Trek/csharp/Resources/Orders.txt index 7dd142e5..7dc14b24 100644 --- a/84_Super_Star_Trek/csharp/Resources/Orders.txt +++ b/84_Super_Star_Trek/csharp/Resources/Orders.txt @@ -3,3 +3,4 @@ Your orders are as follows: the galaxy before they can attack federation headquarters on stardate {1}. This gives you {2} days. There {3} {4} starbase{5} in the galaxy for resupplying your ship. + diff --git a/84_Super_Star_Trek/csharp/Resources/Protected.txt b/84_Super_Star_Trek/csharp/Resources/Protected.txt index fe23b63b..27c4a5f8 100644 --- a/84_Super_Star_Trek/csharp/Resources/Protected.txt +++ b/84_Super_Star_Trek/csharp/Resources/Protected.txt @@ -1 +1 @@ -Starbase shields protect the Enterprise +Starbase shields protect the Enterprise \ No newline at end of file diff --git a/84_Super_Star_Trek/csharp/Resources/RegionNames.txt b/84_Super_Star_Trek/csharp/Resources/RegionNames.txt index 7556a9f8..f84fe43b 100644 --- a/84_Super_Star_Trek/csharp/Resources/RegionNames.txt +++ b/84_Super_Star_Trek/csharp/Resources/RegionNames.txt @@ -5,4 +5,4 @@ Canopus Aldebaran Altair Regulus Sagittarius Arcturus - Pollux Spica + Pollux Spica \ No newline at end of file diff --git a/84_Super_Star_Trek/csharp/Resources/RelievedOfCommand.txt b/84_Super_Star_Trek/csharp/Resources/RelievedOfCommand.txt index 31f398ba..8086e3ca 100644 --- a/84_Super_Star_Trek/csharp/Resources/RelievedOfCommand.txt +++ b/84_Super_Star_Trek/csharp/Resources/RelievedOfCommand.txt @@ -1,3 +1,3 @@ That does it, Captain!! You are hereby relieved of command -and sentenced to 99 stardates at hard labor on Cygnus 12!! +and sentenced to 99 stardates at hard labor on Cygnus 12!! \ No newline at end of file diff --git a/84_Super_Star_Trek/csharp/Resources/RepairPrompt.txt b/84_Super_Star_Trek/csharp/Resources/RepairPrompt.txt index 36428e30..feffdb27 100644 --- a/84_Super_Star_Trek/csharp/Resources/RepairPrompt.txt +++ b/84_Super_Star_Trek/csharp/Resources/RepairPrompt.txt @@ -1 +1 @@ -Will you authorize the repair order (Y/N) +Will you authorize the repair order (Y/N) \ No newline at end of file diff --git a/84_Super_Star_Trek/csharp/Resources/ReplayPrompt.txt b/84_Super_Star_Trek/csharp/Resources/ReplayPrompt.txt index 8f9c2d54..3ca3a102 100644 --- a/84_Super_Star_Trek/csharp/Resources/ReplayPrompt.txt +++ b/84_Super_Star_Trek/csharp/Resources/ReplayPrompt.txt @@ -2,4 +2,4 @@ The Federation is in need of a new starship commander for a similar mission -- if there is a volunteer -let him step forward and enter 'Aye' +let him step forward and enter 'Aye' \ No newline at end of file diff --git a/84_Super_Star_Trek/csharp/Resources/ShieldsDropped.txt b/84_Super_Star_Trek/csharp/Resources/ShieldsDropped.txt index acc87f59..9135e8b4 100644 --- a/84_Super_Star_Trek/csharp/Resources/ShieldsDropped.txt +++ b/84_Super_Star_Trek/csharp/Resources/ShieldsDropped.txt @@ -1 +1 @@ -Shields dropped for docking purposes +Shields dropped for docking purposes \ No newline at end of file diff --git a/84_Super_Star_Trek/csharp/Resources/StartText.txt b/84_Super_Star_Trek/csharp/Resources/StartText.txt index c599a2e2..3c6028a5 100644 --- a/84_Super_Star_Trek/csharp/Resources/StartText.txt +++ b/84_Super_Star_Trek/csharp/Resources/StartText.txt @@ -2,3 +2,4 @@ Your mission begins with your starship located in the galactic quadrant, '{0}'. + diff --git a/84_Super_Star_Trek/csharp/Resources/Title.txt b/84_Super_Star_Trek/csharp/Resources/Title.txt index edee3578..ccd86774 100644 --- a/84_Super_Star_Trek/csharp/Resources/Title.txt +++ b/84_Super_Star_Trek/csharp/Resources/Title.txt @@ -17,3 +17,11 @@ * * * * ************************************* + + + + + + + + diff --git a/84_Super_Star_Trek/csharp/Space/Coordinates.cs b/84_Super_Star_Trek/csharp/Space/Coordinates.cs index 4768d2c2..adfda6de 100644 --- a/84_Super_Star_Trek/csharp/Space/Coordinates.cs +++ b/84_Super_Star_Trek/csharp/Space/Coordinates.cs @@ -1,70 +1,69 @@ using System; 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)); - Y = Validated(y, nameof(y)); + X = Validated(x, nameof(x)); + Y = Validated(y, nameof(y)); - RegionIndex = (X << 1) + (Y >> 2); - SubRegionIndex = Y % 4; + RegionIndex = (X << 1) + (Y >> 2); + SubRegionIndex = Y % 4; + } + + internal int X { get; } + + internal int Y { get; } + + internal int RegionIndex { get; } + + internal int SubRegionIndex { get; } + + private static int Validated(int value, string argumentName) + { + if (value >= 0 && value <= 7) { return value; } + + throw new ArgumentOutOfRangeException(argumentName, value, "Must be 0 to 7 inclusive"); + } + + private static bool IsValid(int value) => value >= 0 && value <= 7; + + public override string ToString() => $"{X+1} , {Y+1}"; + + internal void Deconstruct(out int x, out int y) + { + x = X; + y = Y; + } + + internal static bool TryCreate(float x, float y, out Coordinates coordinates) + { + var roundedX = Round(x); + var roundedY = Round(y); + + if (IsValid(roundedX) && IsValid(roundedY)) + { + coordinates = new Coordinates(roundedX, roundedY); + return true; } - internal int X { get; } + coordinates = default; + return false; - internal int Y { get; } + static int Round(float value) => (int)Math.Round(value, MidpointRounding.AwayFromZero); + } - internal int RegionIndex { get; } + internal (float Direction, float Distance) GetDirectionAndDistanceTo(Coordinates destination) => + DirectionAndDistance.From(this).To(destination); - internal int SubRegionIndex { get; } - - private static int Validated(int value, string argumentName) - { - if (value >= 0 && value <= 7) { return value; } - - throw new ArgumentOutOfRangeException(argumentName, value, "Must be 0 to 7 inclusive"); - } - - private static bool IsValid(int value) => value >= 0 && value <= 7; - - public override string ToString() => $"{X+1} , {Y+1}"; - - internal void Deconstruct(out int x, out int y) - { - x = X; - y = Y; - } - - internal static bool TryCreate(float x, float y, out Coordinates coordinates) - { - var roundedX = Round(x); - var roundedY = Round(y); - - if (IsValid(roundedX) && IsValid(roundedY)) - { - coordinates = new Coordinates(roundedX, roundedY); - return true; - } - - coordinates = default; - return false; - - static int Round(float value) => (int)Math.Round(value, MidpointRounding.AwayFromZero); - } - - internal (float Direction, float Distance) GetDirectionAndDistanceTo(Coordinates destination) => - DirectionAndDistance.From(this).To(destination); - - internal float GetDistanceTo(Coordinates destination) - { - var (_, distance) = GetDirectionAndDistanceTo(destination); - return distance; - } + internal float GetDistanceTo(Coordinates destination) + { + var (_, distance) = GetDirectionAndDistanceTo(destination); + return distance; } } diff --git a/84_Super_Star_Trek/csharp/Space/Course.cs b/84_Super_Star_Trek/csharp/Space/Course.cs index 6b9dfc31..f411976a 100644 --- a/84_Super_Star_Trek/csharp/Space/Course.cs +++ b/84_Super_Star_Trek/csharp/Space/Course.cs @@ -1,98 +1,97 @@ using System; 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), + (-1, 1), + (-1, 0), + (-1, -1), + (0, -1), + (1, -1), + (1, 0), + (1, 1), + (0, 1) + }; + + internal Course(float direction) + { + if (direction < 1 || direction > 9) { - (0, 1), - (-1, 1), - (-1, 0), - (-1, -1), - (0, -1), - (1, -1), - (1, 0), - (1, 1), - (0, 1) - }; - - internal Course(float direction) - { - if (direction < 1 || direction > 9) - { - throw new ArgumentOutOfRangeException( - nameof(direction), - direction, - "Must be between 1 and 9, inclusive."); - } - - var cardinalDirection = (int)(direction - 1) % 8; - var fractionalDirection = direction - (int)direction; - - var baseCardinal = cardinals[cardinalDirection]; - var nextCardinal = cardinals[cardinalDirection + 1]; - - DeltaX = baseCardinal.DeltaX + (nextCardinal.DeltaX - baseCardinal.DeltaX) * fractionalDirection; - DeltaY = baseCardinal.DeltaY + (nextCardinal.DeltaY - baseCardinal.DeltaY) * fractionalDirection; + throw new ArgumentOutOfRangeException( + nameof(direction), + direction, + "Must be between 1 and 9, inclusive."); } - internal float DeltaX { get; } + var cardinalDirection = (int)(direction - 1) % 8; + var fractionalDirection = direction - (int)direction; - internal float DeltaY { get; } + var baseCardinal = cardinals[cardinalDirection]; + var nextCardinal = cardinals[cardinalDirection + 1]; - internal IEnumerable GetSectorsFrom(Coordinates start) + DeltaX = baseCardinal.DeltaX + (nextCardinal.DeltaX - baseCardinal.DeltaX) * fractionalDirection; + DeltaY = baseCardinal.DeltaY + (nextCardinal.DeltaY - baseCardinal.DeltaY) * fractionalDirection; + } + + internal float DeltaX { get; } + + internal float DeltaY { get; } + + internal IEnumerable GetSectorsFrom(Coordinates start) + { + (float x, float y) = start; + + while(true) { - (float x, float y) = start; + x += DeltaX; + y += DeltaY; - while(true) + if (!Coordinates.TryCreate(x, y, out var coordinates)) { - x += DeltaX; - y += DeltaY; - - if (!Coordinates.TryCreate(x, y, out var coordinates)) - { - yield break; - } - - yield return coordinates; - } - } - - internal (bool, Coordinates, Coordinates) GetDestination(Coordinates quadrant, Coordinates sector, int distance) - { - var (xComplete, quadrantX, sectorX) = GetNewCoordinate(quadrant.X, sector.X, DeltaX * distance); - var (yComplete, quadrantY, sectorY) = GetNewCoordinate(quadrant.Y, sector.Y, DeltaY * distance); - - return (xComplete && yComplete, new Coordinates(quadrantX, quadrantY), new Coordinates(sectorX, sectorY)); - } - - private static (bool, int, int) GetNewCoordinate(int quadrant, int sector, float sectorsTravelled) - { - var galacticCoordinate = quadrant * 8 + sector + sectorsTravelled; - var newQuadrant = (int)(galacticCoordinate / 8); - var newSector = (int)(galacticCoordinate - newQuadrant * 8); - - if (newSector < 0) - { - newQuadrant -= 1; - newSector += 8; + yield break; } - return newQuadrant switch - { - < 0 => (false, 0, 0), - > 7 => (false, 7, 7), - _ => (true, newQuadrant, newSector) - }; + yield return coordinates; } } + + internal (bool, Coordinates, Coordinates) GetDestination(Coordinates quadrant, Coordinates sector, int distance) + { + var (xComplete, quadrantX, sectorX) = GetNewCoordinate(quadrant.X, sector.X, DeltaX * distance); + var (yComplete, quadrantY, sectorY) = GetNewCoordinate(quadrant.Y, sector.Y, DeltaY * distance); + + return (xComplete && yComplete, new Coordinates(quadrantX, quadrantY), new Coordinates(sectorX, sectorY)); + } + + private static (bool, int, int) GetNewCoordinate(int quadrant, int sector, float sectorsTravelled) + { + var galacticCoordinate = quadrant * 8 + sector + sectorsTravelled; + var newQuadrant = (int)(galacticCoordinate / 8); + var newSector = (int)(galacticCoordinate - newQuadrant * 8); + + if (newSector < 0) + { + newQuadrant -= 1; + newSector += 8; + } + + return newQuadrant switch + { + < 0 => (false, 0, 0), + > 7 => (false, 7, 7), + _ => (true, newQuadrant, newSector) + }; + } } diff --git a/84_Super_Star_Trek/csharp/Space/Galaxy.cs b/84_Super_Star_Trek/csharp/Space/Galaxy.cs index 0b348b06..d8e990b4 100644 --- a/84_Super_Star_Trek/csharp/Space/Galaxy.cs +++ b/84_Super_Star_Trek/csharp/Space/Galaxy.cs @@ -1,64 +1,64 @@ using System.Collections.Generic; using System.Linq; +using Games.Common.Randomness; using SuperStarTrek.Resources; 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[] _subRegionIdentifiers; + private readonly QuadrantInfo[][] _quadrants; + + static Galaxy() { - private static readonly string[] _regionNames; - private static readonly string[] _subRegionIdentifiers; - private readonly QuadrantInfo[][] _quadrants; + _regionNames = Strings.RegionNames.Split(new[] { ' ', '\n' }, RemoveEmptyEntries | TrimEntries); + _subRegionIdentifiers = new[] { "I", "II", "III", "IV" }; + } - static Galaxy() - { - _regionNames = Strings.RegionNames.Split(new[] { ' ', '\n' }, RemoveEmptyEntries | TrimEntries); - _subRegionIdentifiers = new[] { "I", "II", "III", "IV" }; - } - - internal Galaxy(Random random) - { - _quadrants = Enumerable + internal Galaxy(IRandom random) + { + _quadrants = Enumerable + .Range(0, 8) + .Select(x => Enumerable .Range(0, 8) - .Select(x => Enumerable - .Range(0, 8) - .Select(y => new Coordinates(x, y)) - .Select(c => QuadrantInfo.Create(c, GetQuadrantName(c), random)) - .ToArray()) - .ToArray(); + .Select(y => new Coordinates(x, y)) + .Select(c => QuadrantInfo.Create(c, GetQuadrantName(c), random)) + .ToArray()) + .ToArray(); - if (StarbaseCount == 0) + if (StarbaseCount == 0) + { + var randomQuadrant = this[random.NextCoordinate()]; + randomQuadrant.AddStarbase(); + + if (randomQuadrant.KlingonCount < 2) { - var randomQuadrant = this[random.GetCoordinate()]; - randomQuadrant.AddStarbase(); - - if (randomQuadrant.KlingonCount < 2) - { - randomQuadrant.AddKlingon(); - } + randomQuadrant.AddKlingon(); } } - - internal QuadrantInfo this[Coordinates coordinate] => _quadrants[coordinate.X][coordinate.Y]; - - internal int KlingonCount => _quadrants.SelectMany(q => q).Sum(q => q.KlingonCount); - - internal int StarbaseCount => _quadrants.SelectMany(q => q).Count(q => q.HasStarbase); - - internal IEnumerable> Quadrants => _quadrants; - - private static string GetQuadrantName(Coordinates coordinates) => - $"{_regionNames[coordinates.RegionIndex]} {_subRegionIdentifiers[coordinates.SubRegionIndex]}"; - - internal IEnumerable> GetNeighborhood(Quadrant quadrant) => - Enumerable.Range(-1, 3) - .Select(dx => dx + quadrant.Coordinates.X) - .Select(x => GetNeighborhoodRow(quadrant, x)); - private IEnumerable GetNeighborhoodRow(Quadrant quadrant, int x) => - Enumerable.Range(-1, 3) - .Select(dy => dy + quadrant.Coordinates.Y) - .Select(y => y < 0 || y > 7 || x < 0 || x > 7 ? null : _quadrants[x][y]); } + + internal QuadrantInfo this[Coordinates coordinate] => _quadrants[coordinate.X][coordinate.Y]; + + internal int KlingonCount => _quadrants.SelectMany(q => q).Sum(q => q.KlingonCount); + + internal int StarbaseCount => _quadrants.SelectMany(q => q).Count(q => q.HasStarbase); + + internal IEnumerable> Quadrants => _quadrants; + + private static string GetQuadrantName(Coordinates coordinates) => + $"{_regionNames[coordinates.RegionIndex]} {_subRegionIdentifiers[coordinates.SubRegionIndex]}"; + + internal IEnumerable> GetNeighborhood(Quadrant quadrant) => + Enumerable.Range(-1, 3) + .Select(dx => dx + quadrant.Coordinates.X) + .Select(x => GetNeighborhoodRow(quadrant, x)); + private IEnumerable GetNeighborhoodRow(Quadrant quadrant, int x) => + Enumerable.Range(-1, 3) + .Select(dy => dy + quadrant.Coordinates.Y) + .Select(y => y < 0 || y > 7 || x < 0 || x > 7 ? null : _quadrants[x][y]); } diff --git a/84_Super_Star_Trek/csharp/Space/Quadrant.cs b/84_Super_Star_Trek/csharp/Space/Quadrant.cs index 694f7fc8..ba8f9a99 100644 --- a/84_Super_Star_Trek/csharp/Space/Quadrant.cs +++ b/84_Super_Star_Trek/csharp/Space/Quadrant.cs @@ -1,192 +1,192 @@ using System; using System.Collections.Generic; using System.Linq; +using Games.Common.IO; +using Games.Common.Randomness; using SuperStarTrek.Commands; using SuperStarTrek.Objects; using SuperStarTrek.Resources; -namespace SuperStarTrek.Space +namespace SuperStarTrek.Space; + +internal class Quadrant { - internal class Quadrant + private readonly QuadrantInfo _info; + private readonly IRandom _random; + private readonly Dictionary _sectors; + private readonly Enterprise _enterprise; + private readonly IReadWrite _io; + private bool _entered = false; + + internal Quadrant( + QuadrantInfo info, + Enterprise enterprise, + IRandom random, + Galaxy galaxy, + IReadWrite io) { - private readonly QuadrantInfo _info; - private readonly Random _random; - private readonly Dictionary _sectors; - private readonly Enterprise _enterprise; - private readonly Output _output; - private bool _entered = false; + _info = info; + _random = random; + _io = io; + Galaxy = galaxy; - internal Quadrant( - QuadrantInfo info, - Enterprise enterprise, - Random random, - Galaxy galaxy, - Input input, - Output output) + info.MarkAsKnown(); + _sectors = new() { [enterprise.SectorCoordinates] = _enterprise = enterprise }; + PositionObject(sector => new Klingon(sector, _random), _info.KlingonCount); + if (_info.HasStarbase) { - _info = info; - _random = random; - _output = output; - Galaxy = galaxy; + Starbase = PositionObject(sector => new Starbase(sector, _random, io)); + } + PositionObject(_ => new Star(), _info.StarCount); + } - info.MarkAsKnown(); - _sectors = new() { [enterprise.SectorCoordinates] = _enterprise = enterprise }; - PositionObject(sector => new Klingon(sector, _random), _info.KlingonCount); - if (_info.HasStarbase) - { - Starbase = PositionObject(sector => new Starbase(sector, _random, input, output)); - } - PositionObject(_ => new Star(), _info.StarCount); + internal Coordinates Coordinates => _info.Coordinates; + + internal bool HasKlingons => _info.KlingonCount > 0; + + internal int KlingonCount => _info.KlingonCount; + + internal bool HasStarbase => _info.HasStarbase; + + internal Starbase Starbase { get; } + + internal Galaxy Galaxy { get; } + + internal bool EnterpriseIsNextToStarbase => + _info.HasStarbase && + Math.Abs(_enterprise.SectorCoordinates.X - Starbase.Sector.X) <= 1 && + Math.Abs(_enterprise.SectorCoordinates.Y - Starbase.Sector.Y) <= 1; + + internal IEnumerable Klingons => _sectors.Values.OfType(); + + public override string ToString() => _info.Name; + + private T PositionObject(Func objectFactory) + { + var sector = GetRandomEmptySector(); + _sectors[sector] = objectFactory.Invoke(sector); + return (T)_sectors[sector]; + } + + private void PositionObject(Func objectFactory, int count) + { + for (int i = 0; i < count; i++) + { + PositionObject(objectFactory); + } + } + + internal void Display(string textFormat) + { + if (!_entered) + { + _io.Write(textFormat, this); + _entered = true; } - internal Coordinates Coordinates => _info.Coordinates; - - internal bool HasKlingons => _info.KlingonCount > 0; - - internal int KlingonCount => _info.KlingonCount; - - internal bool HasStarbase => _info.HasStarbase; - - internal Starbase Starbase { get; } - - internal Galaxy Galaxy { get; } - - internal bool EnterpriseIsNextToStarbase => - _info.HasStarbase && - Math.Abs(_enterprise.SectorCoordinates.X - Starbase.Sector.X) <= 1 && - Math.Abs(_enterprise.SectorCoordinates.Y - Starbase.Sector.Y) <= 1; - - internal IEnumerable Klingons => _sectors.Values.OfType(); - - public override string ToString() => _info.Name; - - private T PositionObject(Func objectFactory) + if (_info.KlingonCount > 0) { - var sector = GetRandomEmptySector(); - _sectors[sector] = objectFactory.Invoke(sector); - return (T)_sectors[sector]; + _io.Write(Strings.CombatArea); + if (_enterprise.ShieldControl.ShieldEnergy <= 200) { _io.Write(Strings.LowShields); } } - private void PositionObject(Func objectFactory, int count) + _enterprise.Execute(Command.SRS); + } + + internal bool HasObjectAt(Coordinates coordinates) => _sectors.ContainsKey(coordinates); + + internal bool TorpedoCollisionAt(Coordinates coordinates, out string message, out bool gameOver) + { + gameOver = false; + message = default; + + switch (_sectors.GetValueOrDefault(coordinates)) { - for (int i = 0; i < count; i++) - { - PositionObject(objectFactory); - } + case Klingon klingon: + message = Remove(klingon); + gameOver = Galaxy.KlingonCount == 0; + return true; + + case Star _: + message = $"Star at {coordinates} absorbed torpedo energy."; + return true; + + case Starbase _: + _sectors.Remove(coordinates); + _info.RemoveStarbase(); + message = "*** Starbase destroyed ***" + + (Galaxy.StarbaseCount > 0 ? Strings.CourtMartial : Strings.RelievedOfCommand); + gameOver = Galaxy.StarbaseCount == 0; + return true; + + default: + return false; } + } - internal void Display(string textFormat) - { - if (!_entered) - { - _output.Write(textFormat, this); - _entered = true; - } - - if (_info.KlingonCount > 0) - { - _output.Write(Strings.CombatArea); - if (_enterprise.ShieldControl.ShieldEnergy <= 200) { _output.Write(Strings.LowShields); } - } - - _enterprise.Execute(Command.SRS); - } - - internal bool HasObjectAt(Coordinates coordinates) => _sectors.ContainsKey(coordinates); - - internal bool TorpedoCollisionAt(Coordinates coordinates, out string message, out bool gameOver) - { - gameOver = false; - message = default; - - switch (_sectors.GetValueOrDefault(coordinates)) - { - case Klingon klingon: - message = Remove(klingon); - gameOver = Galaxy.KlingonCount == 0; - return true; - - case Star _: - message = $"Star at {coordinates} absorbed torpedo energy."; - return true; - - case Starbase _: - _sectors.Remove(coordinates); - _info.RemoveStarbase(); - message = "*** Starbase destroyed ***" + - (Galaxy.StarbaseCount > 0 ? Strings.CourtMartial : Strings.RelievedOfCommand); - gameOver = Galaxy.StarbaseCount == 0; - return true; - - default: - return false; - } - } - - internal string Remove(Klingon klingon) + internal string Remove(Klingon klingon) + { + _sectors.Remove(klingon.Sector); + _info.RemoveKlingon(); + return "*** Klingon destroyed ***"; + } + + internal CommandResult KlingonsMoveAndFire() + { + foreach (var klingon in Klingons.ToList()) { + var newSector = GetRandomEmptySector(); _sectors.Remove(klingon.Sector); - _info.RemoveKlingon(); - return "*** Klingon destroyed ***"; + _sectors[newSector] = klingon; + klingon.MoveTo(newSector); } - internal CommandResult KlingonsMoveAndFire() + return KlingonsFireOnEnterprise(); + } + + internal CommandResult KlingonsFireOnEnterprise() + { + if (EnterpriseIsNextToStarbase && Klingons.Any()) { - foreach (var klingon in Klingons.ToList()) - { - var newSector = GetRandomEmptySector(); - _sectors.Remove(klingon.Sector); - _sectors[newSector] = klingon; - klingon.MoveTo(newSector); - } - - return KlingonsFireOnEnterprise(); - } - - internal CommandResult KlingonsFireOnEnterprise() - { - if (EnterpriseIsNextToStarbase && Klingons.Any()) - { - Starbase.ProtectEnterprise(); - return CommandResult.Ok; - } - - foreach (var klingon in Klingons) - { - var result = klingon.FireOn(_enterprise); - if (result.IsGameOver) { return result; } - } - + Starbase.ProtectEnterprise(); return CommandResult.Ok; } - private Coordinates GetRandomEmptySector() + foreach (var klingon in Klingons) { - while (true) + var result = klingon.FireOn(_enterprise); + if (result.IsGameOver) { return result; } + } + + return CommandResult.Ok; + } + + private Coordinates GetRandomEmptySector() + { + while (true) + { + var sector = _random.NextCoordinate(); + if (!_sectors.ContainsKey(sector)) { - var sector = _random.GetCoordinate(); - if (!_sectors.ContainsKey(sector)) - { - return sector; - } + return sector; } } + } - internal IEnumerable GetDisplayLines() => Enumerable.Range(0, 8).Select(x => GetDisplayLine(x)); + internal IEnumerable GetDisplayLines() => Enumerable.Range(0, 8).Select(x => GetDisplayLine(x)); - private string GetDisplayLine(int x) => - string.Join( - " ", - Enumerable - .Range(0, 8) - .Select(y => new Coordinates(x, y)) - .Select(c => _sectors.GetValueOrDefault(c)) - .Select(o => o?.ToString() ?? " ")); + private string GetDisplayLine(int x) => + string.Join( + " ", + Enumerable + .Range(0, 8) + .Select(y => new Coordinates(x, y)) + .Select(c => _sectors.GetValueOrDefault(c)) + .Select(o => o?.ToString() ?? " ")); - internal void SetEnterpriseSector(Coordinates sector) - { - _sectors.Remove(_enterprise.SectorCoordinates); - _sectors[sector] = _enterprise; - } + internal void SetEnterpriseSector(Coordinates sector) + { + _sectors.Remove(_enterprise.SectorCoordinates); + _sectors[sector] = _enterprise; } } diff --git a/84_Super_Star_Trek/csharp/Space/QuadrantInfo.cs b/84_Super_Star_Trek/csharp/Space/QuadrantInfo.cs index 6a403c2e..0c0b65a0 100644 --- a/84_Super_Star_Trek/csharp/Space/QuadrantInfo.cs +++ b/84_Super_Star_Trek/csharp/Space/QuadrantInfo.cs @@ -1,65 +1,66 @@ -namespace SuperStarTrek.Space +using Games.Common.Randomness; + +namespace SuperStarTrek.Space; + +internal class QuadrantInfo { - internal class QuadrantInfo + private bool _isKnown; + + private QuadrantInfo(Coordinates coordinates, string name, int klingonCount, int starCount, bool hasStarbase) { - private bool _isKnown; - - private QuadrantInfo(Coordinates coordinates, string name, int klingonCount, int starCount, bool hasStarbase) - { - Coordinates = coordinates; - Name = name; - KlingonCount = klingonCount; - StarCount = starCount; - HasStarbase = hasStarbase; - } - - internal Coordinates Coordinates { get; } - - internal string Name { get; } - - internal int KlingonCount { get; private set; } - - internal bool HasStarbase { get; private set; } - - internal int StarCount { get; } - - internal static QuadrantInfo Create(Coordinates coordinates, string name, Random random) - { - var klingonCount = random.GetFloat() switch - { - > 0.98f => 3, - > 0.95f => 2, - > 0.80f => 1, - _ => 0 - }; - var hasStarbase = random.GetFloat() > 0.96f; - var starCount = random.Get1To8Inclusive(); - - return new QuadrantInfo(coordinates, name, klingonCount, starCount, hasStarbase); - } - - internal void AddKlingon() => KlingonCount += 1; - - internal void AddStarbase() => HasStarbase = true; - - internal void MarkAsKnown() => _isKnown = true; - - internal string Scan() - { - _isKnown = true; - return ToString(); - } - - public override string ToString() => _isKnown ? $"{KlingonCount}{(HasStarbase ? 1 : 0)}{StarCount}" : "***"; - - internal void RemoveKlingon() - { - if (KlingonCount > 0) - { - KlingonCount -= 1; - } - } - - internal void RemoveStarbase() => HasStarbase = false; + Coordinates = coordinates; + Name = name; + KlingonCount = klingonCount; + StarCount = starCount; + HasStarbase = hasStarbase; } + + internal Coordinates Coordinates { get; } + + internal string Name { get; } + + internal int KlingonCount { get; private set; } + + internal bool HasStarbase { get; private set; } + + internal int StarCount { get; } + + internal static QuadrantInfo Create(Coordinates coordinates, string name, IRandom random) + { + var klingonCount = random.NextFloat() switch + { + > 0.98f => 3, + > 0.95f => 2, + > 0.80f => 1, + _ => 0 + }; + var hasStarbase = random.NextFloat() > 0.96f; + var starCount = random.Next1To8Inclusive(); + + return new QuadrantInfo(coordinates, name, klingonCount, starCount, hasStarbase); + } + + internal void AddKlingon() => KlingonCount += 1; + + internal void AddStarbase() => HasStarbase = true; + + internal void MarkAsKnown() => _isKnown = true; + + internal string Scan() + { + _isKnown = true; + return ToString(); + } + + public override string ToString() => _isKnown ? $"{KlingonCount}{(HasStarbase ? 1 : 0)}{StarCount}" : "***"; + + internal void RemoveKlingon() + { + if (KlingonCount > 0) + { + KlingonCount -= 1; + } + } + + internal void RemoveStarbase() => HasStarbase = false; } diff --git a/84_Super_Star_Trek/csharp/StringExtensions.cs b/84_Super_Star_Trek/csharp/StringExtensions.cs index 4e77291a..4706e9cb 100644 --- a/84_Super_Star_Trek/csharp/StringExtensions.cs +++ b/84_Super_Star_Trek/csharp/StringExtensions.cs @@ -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" : ""); } diff --git a/84_Super_Star_Trek/csharp/SuperStarTrek.csproj b/84_Super_Star_Trek/csharp/SuperStarTrek.csproj index c0de0594..de228ec6 100644 --- a/84_Super_Star_Trek/csharp/SuperStarTrek.csproj +++ b/84_Super_Star_Trek/csharp/SuperStarTrek.csproj @@ -2,11 +2,15 @@ Exe - net5.0 + net6.0 + + + + diff --git a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/ComputerFunction.cs b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/ComputerFunction.cs index 43553bd7..4356b859 100644 --- a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/ComputerFunction.cs +++ b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/ComputerFunction.cs @@ -1,19 +1,19 @@ +using Games.Common.IO; 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; - Output = output; - } - - internal string Description { get; } - - protected Output Output { get; } - - internal abstract void Execute(Quadrant quadrant); + Description = description; + IO = io; } + + internal string Description { get; } + + protected IReadWrite IO { get; } + + internal abstract void Execute(Quadrant quadrant); } diff --git a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/CumulativeGalacticRecord.cs b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/CumulativeGalacticRecord.cs index 920fbcca..f7c93f2b 100644 --- a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/CumulativeGalacticRecord.cs +++ b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/CumulativeGalacticRecord.cs @@ -1,21 +1,24 @@ -using System; using System.Collections.Generic; using System.Linq; +using Games.Common.IO; 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) => - Output.NextLine().WriteLine($"Computer record of galaxy for quadrant {quadrant.Coordinates}").NextLine(); - - protected override IEnumerable GetRowData() => - Galaxy.Quadrants.Select(row => " " + string.Join(" ", row)); } + + protected override void WriteHeader(Quadrant quadrant) + { + IO.WriteLine(); + IO.WriteLine($"Computer record of galaxy for quadrant {quadrant.Coordinates}"); + IO.WriteLine(); + } + + protected override IEnumerable GetRowData() => + Galaxy.Quadrants.Select(row => " " + string.Join(" ", row)); } diff --git a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/DirectionDistanceCalculator.cs b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/DirectionDistanceCalculator.cs index 3e9965db..e3402ab2 100644 --- a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/DirectionDistanceCalculator.cs +++ b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/DirectionDistanceCalculator.cs @@ -1,30 +1,30 @@ +using Games.Common.IO; using SuperStarTrek.Objects; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems.ComputerFunctions +namespace SuperStarTrek.Systems.ComputerFunctions; + +internal class DirectionDistanceCalculator : NavigationCalculator { - internal class DirectionDistanceCalculator : NavigationCalculator + private readonly Enterprise _enterprise; + private readonly IReadWrite _io; + + internal DirectionDistanceCalculator(Enterprise enterprise, IReadWrite io) + : base("Direction/distance calculator", io) { - private readonly Enterprise _enterprise; - private readonly Input _input; + _enterprise = enterprise; + _io = io; + } - internal DirectionDistanceCalculator(Enterprise enterprise, Output output, Input input) - : base("Direction/distance calculator", output) - { - _enterprise = enterprise; - _input = input; - } + internal override void Execute(Quadrant quadrant) + { + IO.WriteLine("Direction/distance calculator:"); + IO.Write($"You are at quadrant {_enterprise.QuadrantCoordinates}"); + IO.WriteLine($" sector {_enterprise.SectorCoordinates}"); + IO.WriteLine("Please enter"); - internal override void Execute(Quadrant quadrant) - { - Output.WriteLine("Direction/distance calculator:") - .Write($"You are at quadrant {_enterprise.QuadrantCoordinates}") - .WriteLine($" sector {_enterprise.SectorCoordinates}") - .WriteLine("Please enter"); - - WriteDirectionAndDistance( - _input.GetCoordinates(" Initial coordinates"), - _input.GetCoordinates(" Final coordinates")); - } + WriteDirectionAndDistance( + _io.GetCoordinates(" Initial coordinates"), + _io.GetCoordinates(" Final coordinates")); } } diff --git a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalacticReport.cs b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalacticReport.cs index 1e8d5f23..49f7363d 100644 --- a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalacticReport.cs +++ b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalacticReport.cs @@ -1,34 +1,34 @@ using System.Collections.Generic; using System.Linq; +using Games.Common.IO; 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; + } + + protected Galaxy Galaxy { get; } + + protected abstract void WriteHeader(Quadrant quadrant); + + protected abstract IEnumerable GetRowData(); + + internal sealed override void Execute(Quadrant quadrant) + { + WriteHeader(quadrant); + IO.WriteLine(" 1 2 3 4 5 6 7 8"); + IO.WriteLine(" ----- ----- ----- ----- ----- ----- ----- -----"); + + foreach (var (row, index) in GetRowData().Select((r, i) => (r, i))) { - Galaxy = galaxy; - } - - protected Galaxy Galaxy { get; } - - protected abstract void WriteHeader(Quadrant quadrant); - - protected abstract IEnumerable GetRowData(); - - internal sealed override void Execute(Quadrant quadrant) - { - WriteHeader(quadrant); - Output.WriteLine(" 1 2 3 4 5 6 7 8") - .WriteLine(" ----- ----- ----- ----- ----- ----- ----- -----"); - - foreach (var (row, index) in GetRowData().Select((r, i) => (r, i))) - { - Output.WriteLine($" {index+1} {row}") - .WriteLine(" ----- ----- ----- ----- ----- ----- ----- -----"); - } + IO.WriteLine($" {index+1} {row}"); + IO.WriteLine(" ----- ----- ----- ----- ----- ----- ----- -----"); } } } diff --git a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalaxyRegionMap.cs b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalaxyRegionMap.cs index 12ff204b..c4850e1c 100644 --- a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalaxyRegionMap.cs +++ b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalaxyRegionMap.cs @@ -1,21 +1,21 @@ using System.Collections.Generic; using System.Linq; +using Games.Common.IO; using SuperStarTrek.Resources; 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) => - Output.WriteLine(" The Galaxy"); - - protected override IEnumerable GetRowData() => - Strings.RegionNames.Split('\n').Select(n => n.TrimEnd('\r')); } + + protected override void WriteHeader(Quadrant quadrant) => + IO.WriteLine(" The Galaxy"); + + protected override IEnumerable GetRowData() => + Strings.RegionNames.Split('\n').Select(n => n.TrimEnd('\r')); } diff --git a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/NavigationCalculator.cs b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/NavigationCalculator.cs index ef35f8c3..bddb00d2 100644 --- a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/NavigationCalculator.cs +++ b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/NavigationCalculator.cs @@ -1,29 +1,31 @@ +using Games.Common.IO; using SuperStarTrek.Space; 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) - { - } + } - protected void WriteDirectionAndDistance(Coordinates from, Coordinates to) - { - var (direction, distance) = from.GetDirectionAndDistanceTo(to); - Write(direction, distance); - } + protected void WriteDirectionAndDistance(Coordinates from, Coordinates to) + { + var (direction, distance) = from.GetDirectionAndDistanceTo(to); + Write(direction, distance); + } - protected void WriteDirectionAndDistance((float X, float Y) from, (float X, float Y) to) - { - var (direction, distance) = DirectionAndDistance.From(from.X, from.Y).To(to.X, to.Y); - Write(direction, distance); - } + protected void WriteDirectionAndDistance((float X, float Y) from, (float X, float Y) to) + { + var (direction, distance) = DirectionAndDistance.From(from.X, from.Y).To(to.X, to.Y); + Write(direction, distance); + } - private void Write(float direction, float distance) => - Output.WriteLine($"Direction = {direction}") - .WriteLine($"Distance = {distance}"); + private void Write(float direction, float distance) + { + IO.WriteLine($"Direction = {direction}"); + IO.WriteLine($"Distance = {distance}"); } } diff --git a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StarbaseDataCalculator.cs b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StarbaseDataCalculator.cs index 07f2119b..08e7eda3 100644 --- a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StarbaseDataCalculator.cs +++ b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StarbaseDataCalculator.cs @@ -1,30 +1,30 @@ +using Games.Common.IO; using SuperStarTrek.Objects; using SuperStarTrek.Resources; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems.ComputerFunctions +namespace SuperStarTrek.Systems.ComputerFunctions; + +internal class StarbaseDataCalculator : NavigationCalculator { - internal class StarbaseDataCalculator : NavigationCalculator + private readonly Enterprise _enterprise; + + internal StarbaseDataCalculator(Enterprise enterprise, IReadWrite io) + : base("Starbase nav data", io) { - private readonly Enterprise _enterprise; + _enterprise = enterprise; + } - internal StarbaseDataCalculator(Enterprise enterprise, Output output) - : base("Starbase nav data", output) + internal override void Execute(Quadrant quadrant) + { + if (!quadrant.HasStarbase) { - _enterprise = enterprise; + IO.WriteLine(Strings.NoStarbase); + return; } - internal override void Execute(Quadrant quadrant) - { - if (!quadrant.HasStarbase) - { - Output.WriteLine(Strings.NoStarbase); - return; - } + IO.WriteLine("From Enterprise to Starbase:"); - Output.WriteLine("From Enterprise to Starbase:"); - - WriteDirectionAndDistance(_enterprise.SectorCoordinates, quadrant.Starbase.Sector); - } + WriteDirectionAndDistance(_enterprise.SectorCoordinates, quadrant.Starbase.Sector); } } diff --git a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StatusReport.cs b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StatusReport.cs index 6e6bf6f1..b0d6bc1d 100644 --- a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StatusReport.cs +++ b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StatusReport.cs @@ -1,41 +1,43 @@ +using Games.Common.IO; using SuperStarTrek.Commands; using SuperStarTrek.Objects; 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 Galaxy _galaxy; + private readonly Enterprise _enterprise; + + internal StatusReport(Game game, Galaxy galaxy, Enterprise enterprise, IReadWrite io) + : base("Status report", io) { - private readonly Game _game; - private readonly Galaxy _galaxy; - private readonly Enterprise _enterprise; + _game = game; + _galaxy = galaxy; + _enterprise = enterprise; + } - internal StatusReport(Game game, Galaxy galaxy, Enterprise enterprise, Output output) - : base("Status report", output) + internal override void Execute(Quadrant quadrant) + { + IO.WriteLine(" Status report:"); + IO.Write("Klingon".Pluralize(_galaxy.KlingonCount)); + IO.WriteLine($" left: {_galaxy.KlingonCount}"); + IO.WriteLine($"Mission must be completed in {_game.StardatesRemaining:0.#} stardates."); + + if (_galaxy.StarbaseCount > 0) { - _game = game; - _galaxy = galaxy; - _enterprise = enterprise; + IO.Write($"The Federation is maintaining {_galaxy.StarbaseCount} "); + IO.Write("starbase".Pluralize(_galaxy.StarbaseCount)); + IO.WriteLine(" in the galaxy."); + } + else + { + IO.WriteLine("Your stupidity has left you on your own in"); + IO.WriteLine(" the galaxy -- you have no starbases left!"); } - internal override void Execute(Quadrant quadrant) - { - Output.WriteLine(" Status report:") - .Write("Klingon".Pluralize(_galaxy.KlingonCount)).WriteLine($" left: {_galaxy.KlingonCount}") - .WriteLine($"Mission must be completed in {_game.StardatesRemaining:0.#} stardates."); - - if (_galaxy.StarbaseCount > 0) - { - Output.Write($"The Federation is maintaining {_galaxy.StarbaseCount} ") - .Write("starbase".Pluralize(_galaxy.StarbaseCount)).WriteLine(" in the galaxy."); - } - else - { - Output.WriteLine("Your stupidity has left you on your own in") - .WriteLine(" the galaxy -- you have no starbases left!"); - } - - _enterprise.Execute(Command.DAM); - } + _enterprise.Execute(Command.DAM); } } diff --git a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/TorpedoDataCalculator.cs b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/TorpedoDataCalculator.cs index 94f8ae9a..d1de3eb7 100644 --- a/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/TorpedoDataCalculator.cs +++ b/84_Super_Star_Trek/csharp/Systems/ComputerFunctions/TorpedoDataCalculator.cs @@ -1,33 +1,33 @@ +using Games.Common.IO; using SuperStarTrek.Objects; using SuperStarTrek.Resources; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems.ComputerFunctions -{ - internal class TorpedoDataCalculator : NavigationCalculator - { - private readonly Enterprise _enterprise; +namespace SuperStarTrek.Systems.ComputerFunctions; - internal TorpedoDataCalculator(Enterprise enterprise, Output output) - : base("Photon torpedo data", output) +internal class TorpedoDataCalculator : NavigationCalculator +{ + private readonly Enterprise _enterprise; + + internal TorpedoDataCalculator(Enterprise enterprise, IReadWrite io) + : base("Photon torpedo data", io) + { + _enterprise = enterprise; + } + + internal override void Execute(Quadrant quadrant) + { + if (!quadrant.HasKlingons) { - _enterprise = enterprise; + IO.WriteLine(Strings.NoEnemyShips); + return; } - internal override void Execute(Quadrant quadrant) + IO.WriteLine("From Enterprise to Klingon battle cruiser".Pluralize(quadrant.KlingonCount)); + + foreach (var klingon in quadrant.Klingons) { - if (!quadrant.HasKlingons) - { - Output.WriteLine(Strings.NoEnemyShips); - return; - } - - Output.WriteLine("From Enterprise to Klingon battle cruiser".Pluralize(quadrant.KlingonCount)); - - foreach (var klingon in quadrant.Klingons) - { - WriteDirectionAndDistance(_enterprise.SectorCoordinates, klingon.Sector); - } + WriteDirectionAndDistance(_enterprise.SectorCoordinates, klingon.Sector); } } } diff --git a/84_Super_Star_Trek/csharp/Systems/DamageControl.cs b/84_Super_Star_Trek/csharp/Systems/DamageControl.cs index 8fbf21d3..53dc586e 100644 --- a/84_Super_Star_Trek/csharp/Systems/DamageControl.cs +++ b/84_Super_Star_Trek/csharp/Systems/DamageControl.cs @@ -1,54 +1,55 @@ +using Games.Common.IO; using SuperStarTrek.Commands; using SuperStarTrek.Objects; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems +namespace SuperStarTrek.Systems; + +internal class DamageControl : Subsystem { - internal class DamageControl : Subsystem + private readonly Enterprise _enterprise; + private readonly IReadWrite _io; + + internal DamageControl(Enterprise enterprise, IReadWrite io) + : base("Damage Control", Command.DAM, io) { - private readonly Enterprise _enterprise; - private readonly Output _output; + _enterprise = enterprise; + _io = io; + } - internal DamageControl(Enterprise enterprise, Output output) - : base("Damage Control", Command.DAM, output) + protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + { + if (IsDamaged) { - _enterprise = enterprise; - _output = output; + _io.WriteLine("Damage Control report not available"); + } + else + { + _io.WriteLine(); + WriteDamageReport(); } - protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + if (_enterprise.DamagedSystemCount > 0 && _enterprise.IsDocked) { - if (IsDamaged) + if (quadrant.Starbase.TryRepair(_enterprise, out var repairTime)) { - _output.WriteLine("Damage Control report not available"); - } - else - { - _output.NextLine(); WriteDamageReport(); + return CommandResult.Elapsed(repairTime); } - - if (_enterprise.DamagedSystemCount > 0 && _enterprise.IsDocked) - { - if (quadrant.Starbase.TryRepair(_enterprise, out var repairTime)) - { - WriteDamageReport(); - return CommandResult.Elapsed(repairTime); - } - } - - return CommandResult.Ok; } - internal void WriteDamageReport() + return CommandResult.Ok; + } + + internal void WriteDamageReport() + { + _io.WriteLine(); + _io.WriteLine("Device State of Repair"); + foreach (var system in _enterprise.Systems) { - _output.NextLine().WriteLine("Device State of Repair"); - foreach (var system in _enterprise.Systems) - { - _output.Write(system.Name.PadRight(25)) - .WriteLine(((int)(system.Condition * 100) * 0.01).ToString(" 0.##;-0.##")); - } - _output.NextLine(); + _io.Write(system.Name.PadRight(25)); + _io.WriteLine((int)(system.Condition * 100) * 0.01F); } + _io.WriteLine(); } } diff --git a/84_Super_Star_Trek/csharp/Systems/LibraryComputer.cs b/84_Super_Star_Trek/csharp/Systems/LibraryComputer.cs index df5c6868..4a56909e 100644 --- a/84_Super_Star_Trek/csharp/Systems/LibraryComputer.cs +++ b/84_Super_Star_Trek/csharp/Systems/LibraryComputer.cs @@ -1,46 +1,44 @@ +using Games.Common.IO; using SuperStarTrek.Commands; using SuperStarTrek.Space; using SuperStarTrek.Systems.ComputerFunctions; -namespace SuperStarTrek.Systems +namespace SuperStarTrek.Systems; + +internal class LibraryComputer : Subsystem { - internal class LibraryComputer : Subsystem + private readonly IReadWrite _io; + private readonly ComputerFunction[] _functions; + + internal LibraryComputer(IReadWrite io, params ComputerFunction[] functions) + : base("Library-Computer", Command.COM, io) { - private readonly Output _output; - private readonly Input _input; - private readonly ComputerFunction[] _functions; + _io = io; + _functions = functions; + } - internal LibraryComputer(Output output, Input input, params ComputerFunction[] functions) - : base("Library-Computer", Command.COM, output) + protected override bool CanExecuteCommand() => IsOperational("Computer disabled"); + + protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + { + var index = GetFunctionIndex(); + _io.WriteLine(); + + _functions[index].Execute(quadrant); + + return CommandResult.Ok; + } + + private int GetFunctionIndex() + { + while (true) { - _output = output; - _input = input; - _functions = functions; - } + var index = (int)_io.ReadNumber("Computer active and waiting command"); + if (index >= 0 && index <= 5) { return index; } - protected override bool CanExecuteCommand() => IsOperational("Computer disabled"); - - protected override CommandResult ExecuteCommandCore(Quadrant quadrant) - { - var index = GetFunctionIndex(); - _output.NextLine(); - - _functions[index].Execute(quadrant); - - return CommandResult.Ok; - } - - private int GetFunctionIndex() - { - while (true) + for (int i = 0; i < _functions.Length; i++) { - var index = (int)_input.GetNumber("Computer active and waiting command"); - if (index >= 0 && index <= 5) { return index; } - - for (int i = 0; i < _functions.Length; i++) - { - _output.WriteLine($" {i} = {_functions[i].Description}"); - } + _io.WriteLine($" {i} = {_functions[i].Description}"); } } } diff --git a/84_Super_Star_Trek/csharp/Systems/LongRangeSensors.cs b/84_Super_Star_Trek/csharp/Systems/LongRangeSensors.cs index a873eeba..2e21b63c 100644 --- a/84_Super_Star_Trek/csharp/Systems/LongRangeSensors.cs +++ b/84_Super_Star_Trek/csharp/Systems/LongRangeSensors.cs @@ -1,34 +1,35 @@ using System.Linq; +using Games.Common.IO; using SuperStarTrek.Commands; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems +namespace SuperStarTrek.Systems; + +internal class LongRangeSensors : Subsystem { - internal class LongRangeSensors : Subsystem + private readonly Galaxy _galaxy; + private readonly IReadWrite _io; + + internal LongRangeSensors(Galaxy galaxy, IReadWrite io) + : base("Long Range Sensors", Command.LRS, io) { - private readonly Galaxy _galaxy; - private readonly Output _output; + _galaxy = galaxy; + _io = io; + } - internal LongRangeSensors(Galaxy galaxy, Output output) - : base("Long Range Sensors", Command.LRS, output) + protected override bool CanExecuteCommand() => IsOperational("{name} are inoperable"); + + protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + { + _io.WriteLine($"Long range scan for quadrant {quadrant.Coordinates}"); + _io.WriteLine("-------------------"); + foreach (var quadrants in _galaxy.GetNeighborhood(quadrant)) { - _galaxy = galaxy; - _output = output; + _io.WriteLine(": " + string.Join(" : ", quadrants.Select(q => q?.Scan() ?? "***")) + " :"); + _io.WriteLine("-------------------"); } - protected override bool CanExecuteCommand() => IsOperational("{name} are inoperable"); - - protected override CommandResult ExecuteCommandCore(Quadrant quadrant) - { - _output.WriteLine($"Long range scan for quadrant {quadrant.Coordinates}"); - _output.WriteLine("-------------------"); - foreach (var quadrants in _galaxy.GetNeighborhood(quadrant)) - { - _output.WriteLine(": " + string.Join(" : ", quadrants.Select(q => q?.Scan() ?? "***")) + " :"); - _output.WriteLine("-------------------"); - } - - return CommandResult.Ok; - } + return CommandResult.Ok; } } + diff --git a/84_Super_Star_Trek/csharp/Systems/PhaserControl.cs b/84_Super_Star_Trek/csharp/Systems/PhaserControl.cs index 9207ad94..61249d51 100644 --- a/84_Super_Star_Trek/csharp/Systems/PhaserControl.cs +++ b/84_Super_Star_Trek/csharp/Systems/PhaserControl.cs @@ -1,97 +1,96 @@ using System.Linq; +using Games.Common.IO; +using Games.Common.Randomness; using SuperStarTrek.Commands; using SuperStarTrek.Objects; using SuperStarTrek.Resources; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems +namespace SuperStarTrek.Systems; + +internal class PhaserControl : Subsystem { - internal class PhaserControl : Subsystem + 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) { - private readonly Enterprise _enterprise; - private readonly Output _output; - private readonly Input _input; - private readonly Random _random; + _enterprise = enterprise; + _io = io; + _random = random; + } - internal PhaserControl(Enterprise enterprise, Output output, Input input, Random random) - : base("Phaser Control", Command.PHA, output) + protected override bool CanExecuteCommand() => IsOperational("Phasers inoperative"); + + protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + { + if (!quadrant.HasKlingons) { - _enterprise = enterprise; - _output = output; - _input = input; - _random = random; + _io.WriteLine(Strings.NoEnemyShips); + return CommandResult.Ok; } - protected override bool CanExecuteCommand() => IsOperational("Phasers inoperative"); - - protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + if (_enterprise.Computer.IsDamaged) { - if (!quadrant.HasKlingons) - { - _output.WriteLine(Strings.NoEnemyShips); - return CommandResult.Ok; - } - - if (_enterprise.Computer.IsDamaged) - { - _output.WriteLine("Computer failure hampers accuracy"); - } - - _output.Write($"Phasers locked on target; "); - - var phaserStrength = GetPhaserStrength(); - if (phaserStrength < 0) { return CommandResult.Ok; } - - _enterprise.UseEnergy(phaserStrength); - - var perEnemyStrength = GetPerTargetPhaserStrength(phaserStrength, quadrant.KlingonCount); - - foreach (var klingon in quadrant.Klingons.ToList()) - { - ResolveHitOn(klingon, perEnemyStrength, quadrant); - } - - return quadrant.KlingonsFireOnEnterprise(); + _io.WriteLine("Computer failure hampers accuracy"); } - private float GetPhaserStrength() - { - while (true) - { - _output.WriteLine($"Energy available = {_enterprise.Energy} units"); - var phaserStrength = _input.GetNumber("Number of units to fire"); + _io.Write($"Phasers locked on target; "); - if (phaserStrength <= _enterprise.Energy) { return phaserStrength; } - } + var phaserStrength = GetPhaserStrength(); + if (phaserStrength < 0) { return CommandResult.Ok; } + + _enterprise.UseEnergy(phaserStrength); + + var perEnemyStrength = GetPerTargetPhaserStrength(phaserStrength, quadrant.KlingonCount); + + foreach (var klingon in quadrant.Klingons.ToList()) + { + ResolveHitOn(klingon, perEnemyStrength, quadrant); } - private float GetPerTargetPhaserStrength(float phaserStrength, int targetCount) - { - if (_enterprise.Computer.IsDamaged) - { - phaserStrength *= _random.GetFloat(); - } + return quadrant.KlingonsFireOnEnterprise(); + } - return phaserStrength / targetCount; + private float GetPhaserStrength() + { + while (true) + { + _io.WriteLine($"Energy available = {_enterprise.Energy} units"); + var phaserStrength = _io.ReadNumber("Number of units to fire"); + + if (phaserStrength <= _enterprise.Energy) { return phaserStrength; } + } + } + + private float GetPerTargetPhaserStrength(float phaserStrength, int targetCount) + { + if (_enterprise.Computer.IsDamaged) + { + phaserStrength *= _random.NextFloat(); } - private void ResolveHitOn(Klingon klingon, float perEnemyStrength, Quadrant quadrant) - { - var distance = _enterprise.SectorCoordinates.GetDistanceTo(klingon.Sector); - var hitStrength = (int)(perEnemyStrength / distance * (2 + _random.GetFloat())); + return phaserStrength / targetCount; + } - if (klingon.TakeHit(hitStrength)) - { - _output.WriteLine($"{hitStrength} unit hit on Klingon at sector {klingon.Sector}"); - _output.WriteLine( - klingon.Energy <= 0 - ? quadrant.Remove(klingon) - : $" (sensors show {klingon.Energy} units remaining)"); - } - else - { - _output.WriteLine($"Sensors show no damage to enemy at {klingon.Sector}"); - } + private void ResolveHitOn(Klingon klingon, float perEnemyStrength, Quadrant quadrant) + { + var distance = _enterprise.SectorCoordinates.GetDistanceTo(klingon.Sector); + var hitStrength = (int)(perEnemyStrength / distance * (2 + _random.NextFloat())); + + if (klingon.TakeHit(hitStrength)) + { + _io.WriteLine($"{hitStrength} unit hit on Klingon at sector {klingon.Sector}"); + _io.WriteLine( + klingon.Energy <= 0 + ? quadrant.Remove(klingon) + : $" (sensors show {klingon.Energy} units remaining)"); + } + else + { + _io.WriteLine($"Sensors show no damage to enemy at {klingon.Sector}"); } } } diff --git a/84_Super_Star_Trek/csharp/Systems/PhotonTubes.cs b/84_Super_Star_Trek/csharp/Systems/PhotonTubes.cs index 37613037..597ae3a0 100644 --- a/84_Super_Star_Trek/csharp/Systems/PhotonTubes.cs +++ b/84_Super_Star_Trek/csharp/Systems/PhotonTubes.cs @@ -1,66 +1,64 @@ +using Games.Common.IO; using SuperStarTrek.Commands; using SuperStarTrek.Objects; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems +namespace SuperStarTrek.Systems; + +internal class PhotonTubes : Subsystem { - internal class PhotonTubes : Subsystem + private readonly int _tubeCount; + private readonly Enterprise _enterprise; + private readonly IReadWrite _io; + + internal PhotonTubes(int tubeCount, Enterprise enterprise, IReadWrite io) + : base("Photon Tubes", Command.TOR, io) { - private readonly int _tubeCount; - private readonly Enterprise _enterprise; - private readonly Output _output; - private readonly Input _input; - - internal PhotonTubes(int tubeCount, Enterprise enterprise, Output output, Input input) - : base("Photon Tubes", Command.TOR, output) - { - TorpedoCount = _tubeCount = tubeCount; - _enterprise = enterprise; - _output = output; - _input = input; - } - - internal int TorpedoCount { get; private set; } - - protected override bool CanExecuteCommand() => HasTorpedoes() && IsOperational("{name} are not operational"); - - private bool HasTorpedoes() - { - if (TorpedoCount > 0) { return true; } - - _output.WriteLine("All photon torpedoes expended"); - return false; - } - - protected override CommandResult ExecuteCommandCore(Quadrant quadrant) - { - if (!_input.TryGetCourse("Photon torpedo course", "Ensign Chekov", out var course)) - { - return CommandResult.Ok; - } - - TorpedoCount -= 1; - - var isHit = false; - _output.WriteLine("Torpedo track:"); - foreach (var sector in course.GetSectorsFrom(_enterprise.SectorCoordinates)) - { - _output.WriteLine($" {sector}"); - - if (quadrant.TorpedoCollisionAt(sector, out var message, out var gameOver)) - { - _output.WriteLine(message); - isHit = true; - if (gameOver) { return CommandResult.GameOver; } - break; - } - } - - if (!isHit) { _output.WriteLine("Torpedo missed!"); } - - return quadrant.KlingonsFireOnEnterprise(); - } - - internal void ReplenishTorpedoes() => TorpedoCount = _tubeCount; + TorpedoCount = _tubeCount = tubeCount; + _enterprise = enterprise; + _io = io; } + + internal int TorpedoCount { get; private set; } + + protected override bool CanExecuteCommand() => HasTorpedoes() && IsOperational("{name} are not operational"); + + private bool HasTorpedoes() + { + if (TorpedoCount > 0) { return true; } + + _io.WriteLine("All photon torpedoes expended"); + return false; + } + + protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + { + if (!_io.TryReadCourse("Photon torpedo course", "Ensign Chekov", out var course)) + { + return CommandResult.Ok; + } + + TorpedoCount -= 1; + + var isHit = false; + _io.WriteLine("Torpedo track:"); + foreach (var sector in course.GetSectorsFrom(_enterprise.SectorCoordinates)) + { + _io.WriteLine($" {sector}"); + + if (quadrant.TorpedoCollisionAt(sector, out var message, out var gameOver)) + { + _io.WriteLine(message); + isHit = true; + if (gameOver) { return CommandResult.GameOver; } + break; + } + } + + if (!isHit) { _io.WriteLine("Torpedo missed!"); } + + return quadrant.KlingonsFireOnEnterprise(); + } + + internal void ReplenishTorpedoes() => TorpedoCount = _tubeCount; } diff --git a/84_Super_Star_Trek/csharp/Systems/ShieldControl.cs b/84_Super_Star_Trek/csharp/Systems/ShieldControl.cs index de4f44d9..9d781b23 100644 --- a/84_Super_Star_Trek/csharp/Systems/ShieldControl.cs +++ b/84_Super_Star_Trek/csharp/Systems/ShieldControl.cs @@ -1,59 +1,57 @@ +using Games.Common.IO; using SuperStarTrek.Commands; using SuperStarTrek.Objects; using SuperStarTrek.Resources; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems +namespace SuperStarTrek.Systems; + +internal class ShieldControl : Subsystem { - internal class ShieldControl : Subsystem + private readonly Enterprise _enterprise; + private readonly IReadWrite _io; + + internal ShieldControl(Enterprise enterprise, IReadWrite io) + : base("Shield Control", Command.SHE, io) { - private readonly Enterprise _enterprise; - private readonly Output _output; - private readonly Input _input; - - internal ShieldControl(Enterprise enterprise, Output output, Input input) - : base("Shield Control", Command.SHE, output) - { - _enterprise = enterprise; - _output = output; - _input = input; - } - - internal float ShieldEnergy { get; set; } - - protected override bool CanExecuteCommand() => IsOperational("{name} inoperable"); - - protected override CommandResult ExecuteCommandCore(Quadrant quadrant) - { - _output.WriteLine($"Energy available = {_enterprise.TotalEnergy}"); - var requested = _input.GetNumber($"Number of units to shields"); - - if (Validate(requested)) - { - ShieldEnergy = requested; - _output.Write(Strings.ShieldsSet, requested); - } - else - { - _output.WriteLine(""); - } - - return CommandResult.Ok; - } - - private bool Validate(float requested) - { - if (requested > _enterprise.TotalEnergy) - { - _output.WriteLine("Shield Control reports, 'This is not the Federation Treasury.'"); - return false; - } - - return requested >= 0 && requested != ShieldEnergy; - } - - internal void AbsorbHit(int hitStrength) => ShieldEnergy -= hitStrength; - - internal void DropShields() => ShieldEnergy = 0; + _enterprise = enterprise; + _io = io; } + + internal float ShieldEnergy { get; set; } + + protected override bool CanExecuteCommand() => IsOperational("{name} inoperable"); + + protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + { + _io.WriteLine($"Energy available = {_enterprise.TotalEnergy}"); + var requested = _io.ReadNumber($"Number of units to shields"); + + if (Validate(requested)) + { + ShieldEnergy = requested; + _io.Write(Strings.ShieldsSet, requested); + } + else + { + _io.WriteLine(""); + } + + return CommandResult.Ok; + } + + private bool Validate(float requested) + { + if (requested > _enterprise.TotalEnergy) + { + _io.WriteLine("Shield Control reports, 'This is not the Federation Treasury.'"); + return false; + } + + return requested >= 0 && requested != ShieldEnergy; + } + + internal void AbsorbHit(int hitStrength) => ShieldEnergy -= hitStrength; + + internal void DropShields() => ShieldEnergy = 0; } diff --git a/84_Super_Star_Trek/csharp/Systems/ShortRangeSensors.cs b/84_Super_Star_Trek/csharp/Systems/ShortRangeSensors.cs index c37028cb..406af6b8 100644 --- a/84_Super_Star_Trek/csharp/Systems/ShortRangeSensors.cs +++ b/84_Super_Star_Trek/csharp/Systems/ShortRangeSensors.cs @@ -2,61 +2,61 @@ using System; using System.Collections.Generic; using System.Linq; +using Games.Common.IO; using SuperStarTrek.Commands; using SuperStarTrek.Objects; using SuperStarTrek.Resources; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems +namespace SuperStarTrek.Systems; + +internal class ShortRangeSensors : Subsystem { - internal class ShortRangeSensors : Subsystem + private readonly Enterprise _enterprise; + private readonly Galaxy _galaxy; + private readonly Game _game; + private readonly IReadWrite _io; + + internal ShortRangeSensors(Enterprise enterprise, Galaxy galaxy, Game game, IReadWrite io) + : base("Short Range Sensors", Command.SRS, io) { - private readonly Enterprise _enterprise; - private readonly Galaxy _galaxy; - private readonly Game _game; - private readonly Output _output; + _enterprise = enterprise; + _galaxy = galaxy; + _game = game; + _io = io; + } - internal ShortRangeSensors(Enterprise enterprise, Galaxy galaxy, Game game, Output output) - : base("Short Range Sensors", Command.SRS, output) + protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + { + if (_enterprise.IsDocked) { - _enterprise = enterprise; - _galaxy = galaxy; - _game = game; - _output = output; + _io.WriteLine(Strings.ShieldsDropped); } - protected override CommandResult ExecuteCommandCore(Quadrant quadrant) + if (Condition < 0) { - if (_enterprise.IsDocked) - { - _output.WriteLine(Strings.ShieldsDropped); - } - - if (Condition < 0) - { - _output.WriteLine(Strings.ShortRangeSensorsOut); - } - - _output.WriteLine("---------------------------------"); - quadrant.GetDisplayLines() - .Zip(GetStatusLines(), (sectors, status) => $" {sectors} {status}") - .ToList() - .ForEach(l => _output.WriteLine(l)); - _output.WriteLine("---------------------------------"); - - return CommandResult.Ok; + _io.WriteLine(Strings.ShortRangeSensorsOut); } - internal IEnumerable GetStatusLines() - { - yield return $"Stardate {_game.Stardate}"; - yield return $"Condition {_enterprise.Condition}"; - yield return $"Quadrant {_enterprise.QuadrantCoordinates}"; - yield return $"Sector {_enterprise.SectorCoordinates}"; - yield return $"Photon torpedoes {_enterprise.PhotonTubes.TorpedoCount}"; - yield return $"Total energy {Math.Ceiling(_enterprise.TotalEnergy)}"; - yield return $"Shields {(int)_enterprise.ShieldControl.ShieldEnergy}"; - yield return $"Klingons remaining {_galaxy.KlingonCount}"; - } + _io.WriteLine("---------------------------------"); + quadrant.GetDisplayLines() + .Zip(GetStatusLines(), (sectors, status) => $" {sectors} {status}") + .ToList() + .ForEach(l => _io.WriteLine(l)); + _io.WriteLine("---------------------------------"); + + return CommandResult.Ok; + } + + internal IEnumerable GetStatusLines() + { + yield return $"Stardate {_game.Stardate}"; + yield return $"Condition {_enterprise.Condition}"; + yield return $"Quadrant {_enterprise.QuadrantCoordinates}"; + yield return $"Sector {_enterprise.SectorCoordinates}"; + yield return $"Photon torpedoes {_enterprise.PhotonTubes.TorpedoCount}"; + yield return $"Total energy {Math.Ceiling(_enterprise.TotalEnergy)}"; + yield return $"Shields {(int)_enterprise.ShieldControl.ShieldEnergy}"; + yield return $"Klingons remaining {_galaxy.KlingonCount}"; } } diff --git a/84_Super_Star_Trek/csharp/Systems/Subsystem.cs b/84_Super_Star_Trek/csharp/Systems/Subsystem.cs index 5c11e656..5a371103 100644 --- a/84_Super_Star_Trek/csharp/Systems/Subsystem.cs +++ b/84_Super_Star_Trek/csharp/Systems/Subsystem.cs @@ -1,68 +1,68 @@ +using Games.Common.IO; using SuperStarTrek.Commands; using SuperStarTrek.Space; -namespace SuperStarTrek.Systems +namespace SuperStarTrek.Systems; + +internal abstract class Subsystem { - internal abstract class Subsystem + private readonly IReadWrite _io; + + protected Subsystem(string name, Command command, IReadWrite io) { - private readonly Output _output; - - protected Subsystem(string name, Command command, Output output) - { - Name = name; - Command = command; - Condition = 0; - _output = output; - } - - internal string Name { get; } - - internal float Condition { get; private set; } - - internal bool IsDamaged => Condition < 0; - - internal Command Command { get; } - - protected virtual bool CanExecuteCommand() => true; - - protected bool IsOperational(string notOperationalMessage) - { - if (IsDamaged) - { - _output.WriteLine(notOperationalMessage.Replace("{name}", Name)); - return false; - } - - return true; - } - - internal CommandResult ExecuteCommand(Quadrant quadrant) - => CanExecuteCommand() ? ExecuteCommandCore(quadrant) : CommandResult.Ok; - - protected abstract CommandResult ExecuteCommandCore(Quadrant quadrant); - - internal virtual void Repair() - { - if (IsDamaged) - { - Condition = 0; - } - } - - internal virtual bool Repair(float repairWorkDone) - { - if (IsDamaged) - { - Condition += repairWorkDone; - if (Condition > -0.1f && Condition < 0) - { - Condition = -0.1f; - } - } - - return !IsDamaged; - } - - internal void TakeDamage(float damage) => Condition -= damage; + Name = name; + Command = command; + Condition = 0; + _io = io; } + + internal string Name { get; } + + internal float Condition { get; private set; } + + internal bool IsDamaged => Condition < 0; + + internal Command Command { get; } + + protected virtual bool CanExecuteCommand() => true; + + protected bool IsOperational(string notOperationalMessage) + { + if (IsDamaged) + { + _io.WriteLine(notOperationalMessage.Replace("{name}", Name)); + return false; + } + + return true; + } + + internal CommandResult ExecuteCommand(Quadrant quadrant) + => CanExecuteCommand() ? ExecuteCommandCore(quadrant) : CommandResult.Ok; + + protected abstract CommandResult ExecuteCommandCore(Quadrant quadrant); + + internal virtual void Repair() + { + if (IsDamaged) + { + Condition = 0; + } + } + + internal virtual bool Repair(float repairWorkDone) + { + if (IsDamaged) + { + Condition += repairWorkDone; + if (Condition > -0.1f && Condition < 0) + { + Condition = -0.1f; + } + } + + return !IsDamaged; + } + + internal void TakeDamage(float damage) => Condition -= damage; } diff --git a/84_Super_Star_Trek/csharp/Systems/WarpEngines.cs b/84_Super_Star_Trek/csharp/Systems/WarpEngines.cs index 096d0e43..a9651abf 100644 --- a/84_Super_Star_Trek/csharp/Systems/WarpEngines.cs +++ b/84_Super_Star_Trek/csharp/Systems/WarpEngines.cs @@ -1,4 +1,5 @@ using System; +using Games.Common.IO; using SuperStarTrek.Commands; using SuperStarTrek.Objects; using SuperStarTrek.Resources; @@ -9,20 +10,18 @@ namespace SuperStarTrek.Systems internal class WarpEngines : Subsystem { private readonly Enterprise _enterprise; - private readonly Output _output; - private readonly Input _input; + private readonly IReadWrite _io; - internal WarpEngines(Enterprise enterprise, Output output, Input input) - : base("Warp Engines", Command.NAV, output) + internal WarpEngines(Enterprise enterprise, IReadWrite io) + : base("Warp Engines", Command.NAV, io) { _enterprise = enterprise; - _output = output; - _input = input; + _io = io; } 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) && TryGetDistanceToMove(warpFactor, out var distanceToMove)) { @@ -51,12 +50,12 @@ namespace SuperStarTrek.Systems private bool TryGetWarpFactor(out float warpFactor) { 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; } - _output.WriteLine( + _io.WriteLine( IsDamaged && warpFactor > maximumWarp ? "Warp engines are damaged. Maximum speed = warp 0.2" : $" 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); if (distanceToTravel <= _enterprise.Energy) { return true; } - _output.WriteLine("Engineering reports, 'Insufficient energy available") - .WriteLine($" for maneuvering at warp {warpFactor} !'"); + _io.WriteLine("Engineering reports, 'Insufficient energy available"); + _io.WriteLine($" for maneuvering at warp {warpFactor} !'"); if (distanceToTravel <= _enterprise.TotalEnergy && !_enterprise.ShieldControl.IsDamaged) { - _output.Write($"Deflector control room acknowledges {_enterprise.ShieldControl.ShieldEnergy} ") - .WriteLine("units of energy") - .WriteLine(" presently deployed to shields."); + _io.Write($"Deflector control room acknowledges {_enterprise.ShieldControl.ShieldEnergy} "); + _io.WriteLine("units of energy"); + _io.WriteLine(" presently deployed to shields."); } return false;