diff --git a/84 Super Star Trek/csharp/Game.cs b/84 Super Star Trek/csharp/Game.cs index 0d5539ba..e32f15c8 100644 --- a/84 Super Star Trek/csharp/Game.cs +++ b/84 Super Star Trek/csharp/Game.cs @@ -3,6 +3,7 @@ using SuperStarTrek.Objects; using SuperStarTrek.Resources; using SuperStarTrek.Space; using SuperStarTrek.Systems; +using SuperStarTrek.Systems.ComputerFunctions; using static System.StringComparison; namespace SuperStarTrek @@ -16,7 +17,6 @@ namespace SuperStarTrek private int _finalStarDate; private float _currentStardate; private Coordinates _currentQuadrant; - private Coordinates _currentSector; private Galaxy _galaxy; private int _initialKlingonCount; private Enterprise _enterprise; @@ -28,6 +28,7 @@ namespace SuperStarTrek } public float Stardate => _currentStardate; + public float StardatesRemaining => _finalStarDate - _currentStardate; public void DoIntroduction() { @@ -74,7 +75,6 @@ namespace SuperStarTrek _finalStarDate = _initialStardate + random.GetInt(25, 35); _currentQuadrant = random.GetCoordinate(); - _currentSector = random.GetCoordinate(); _galaxy = new Galaxy(); _initialKlingonCount = _galaxy.KlingonCount; @@ -85,7 +85,16 @@ namespace SuperStarTrek .Add(new LongRangeSensors(_galaxy, _output)) .Add(new PhotonTubes(10, _enterprise, _output, _input)) .Add(new ShieldControl(_enterprise, _output, _input)) - .Add(new DamageControl(_enterprise, _output)); + .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( @@ -99,7 +108,7 @@ namespace SuperStarTrek _input.WaitForAnyKeyButEnter("when ready to accept command"); - var quadrant = _galaxy[_currentQuadrant].BuildQuadrant(_enterprise, random, _galaxy); + var quadrant = _galaxy[_currentQuadrant].BuildQuadrant(_enterprise, random, _galaxy, _input, _output); _enterprise.Enter(quadrant, Strings.StartText); } diff --git a/84 Super Star Trek/csharp/Input.cs b/84 Super Star Trek/csharp/Input.cs index c632007f..af17f3c4 100644 --- a/84 Super Star Trek/csharp/Input.cs +++ b/84 Super Star Trek/csharp/Input.cs @@ -44,6 +44,13 @@ namespace SuperStarTrek } } + public (float X, float Y) GetCoordinates(string prompt) + { + _output.Prompt($"{prompt} (X,Y)"); + var responses = ReadNumbers(2); + return (responses[0], responses[1]); + } + public bool TryGetNumber(string prompt, float minValue, float maxValue, out float value) { value = GetNumber($"{prompt} ({minValue}-{maxValue})"); @@ -90,6 +97,46 @@ namespace SuperStarTrek }; } + 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; + } + public enum YesNoMode { TrueOnY, diff --git a/84 Super Star Trek/csharp/Objects/Klingon.cs b/84 Super Star Trek/csharp/Objects/Klingon.cs index 29584aec..f722fe06 100644 --- a/84 Super Star Trek/csharp/Objects/Klingon.cs +++ b/84 Super Star Trek/csharp/Objects/Klingon.cs @@ -6,25 +6,27 @@ namespace SuperStarTrek.Objects internal class Klingon { private float _energy; - private Coordinates _sector; private readonly Random _random; public Klingon(Coordinates sector, Random random) { - _sector = sector; + Sector = sector; _random = random; _energy = _random.GetFloat(100, 300); } + public Coordinates Sector { get; private set; } + public override string ToString() => "+K+"; public CommandResult FireOn(Enterprise enterprise) { var attackStrength = _random.GetFloat(); - var hitStrength = (int)(_energy * (2 + attackStrength) / _sector.GetDistanceTo(enterprise.Sector)); + var (_, distanceToEnterprise) = Sector.GetDirectionAndDistanceTo(enterprise.Sector); + var hitStrength = (int)(_energy * (2 + attackStrength) / distanceToEnterprise); _energy /= 3 + attackStrength; - return enterprise.TakeHit(_sector, hitStrength); + return enterprise.TakeHit(Sector, hitStrength); } } } diff --git a/84 Super Star Trek/csharp/Objects/Starbase.cs b/84 Super Star Trek/csharp/Objects/Starbase.cs index 4d854efd..4befa73a 100644 --- a/84 Super Star Trek/csharp/Objects/Starbase.cs +++ b/84 Super Star Trek/csharp/Objects/Starbase.cs @@ -1,4 +1,5 @@ using SuperStarTrek.Resources; +using SuperStarTrek.Space; namespace SuperStarTrek.Objects { @@ -8,13 +9,15 @@ namespace SuperStarTrek.Objects private readonly Output _output; private readonly float _repairDelay; - public Starbase(Random random, Input input, Output output) + public 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) diff --git a/84 Super Star Trek/csharp/Program.cs b/84 Super Star Trek/csharp/Program.cs index 30360ee0..7f8395a6 100644 --- a/84 Super Star Trek/csharp/Program.cs +++ b/84 Super Star Trek/csharp/Program.cs @@ -29,6 +29,7 @@ namespace SuperStarTrek { static void Main() { + var foo = Utils.DirectionAndDistance.From(1,1).To(4,5); var game = new Game(); game.DoIntroduction(); diff --git a/84 Super Star Trek/csharp/Resources/ComputerFunctions.txt b/84 Super Star Trek/csharp/Resources/ComputerFunctions.txt new file mode 100644 index 00000000..4827b0fe --- /dev/null +++ b/84 Super Star Trek/csharp/Resources/ComputerFunctions.txt @@ -0,0 +1,9 @@ + +Functions available from Library-Computer: + 0 = Cumulative galactic record + 1 = Status report + 2 = Photon torpedo data + 3 = Starbase nav data + 4 = Direction/distance calculator + 5 = Galaxy 'region name' map + diff --git a/84 Super Star Trek/csharp/Resources/NoEnemyShips.txt b/84 Super Star Trek/csharp/Resources/NoEnemyShips.txt new file mode 100644 index 00000000..394f1057 --- /dev/null +++ b/84 Super Star Trek/csharp/Resources/NoEnemyShips.txt @@ -0,0 +1,2 @@ +Science Officer Spock reports, 'Sensors show no enemy ships + 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 new file mode 100644 index 00000000..5bb4e5fb --- /dev/null +++ b/84 Super Star Trek/csharp/Resources/NoStarbase.txt @@ -0,0 +1 @@ +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/RegionNames.txt b/84 Super Star Trek/csharp/Resources/RegionNames.txt index e2f47d79..f84fe43b 100644 --- a/84 Super Star Trek/csharp/Resources/RegionNames.txt +++ b/84 Super Star Trek/csharp/Resources/RegionNames.txt @@ -1,8 +1,8 @@ -Antares Sirius -Rigel Deneb -Procyon Capella -Vega Betelgeuse -Canopus Aldebaran -Altair Regulus -Sagittarius Arcturus -Pollux Spica \ No newline at end of file + Antares Sirius + Rigel Deneb + Procyon Capella + Vega Betelgeuse + Canopus Aldebaran + Altair Regulus + Sagittarius Arcturus + Pollux Spica \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Resources/Strings.cs b/84 Super Star Trek/csharp/Resources/Strings.cs index c76fe691..7b315545 100644 --- a/84 Super Star Trek/csharp/Resources/Strings.cs +++ b/84 Super Star Trek/csharp/Resources/Strings.cs @@ -7,6 +7,7 @@ namespace SuperStarTrek.Resources internal static class Strings { public static string CombatArea => GetResource(); + public static string ComputerFunctions => GetResource(); public static string Congratulations => GetResource(); public static string CourtMartial => GetResource(); public static string Destroyed => GetResource(); @@ -14,6 +15,8 @@ namespace SuperStarTrek.Resources public static string Enterprise => GetResource(); public static string Instructions => GetResource(); public static string LowShields => GetResource(); + public static string NoEnemyShips => GetResource(); + public static string NoStarbase => GetResource(); public static string Orders => GetResource(); public static string Protected => GetResource(); public static string RegionNames => GetResource(); diff --git a/84 Super Star Trek/csharp/Space/Coordinate.cs b/84 Super Star Trek/csharp/Space/Coordinate.cs index b103a528..b8660d52 100644 --- a/84 Super Star Trek/csharp/Space/Coordinate.cs +++ b/84 Super Star Trek/csharp/Space/Coordinate.cs @@ -1,4 +1,5 @@ using System; +using SuperStarTrek.Utils; namespace SuperStarTrek.Space { @@ -10,12 +11,15 @@ namespace SuperStarTrek.Space { X = Validated(x, nameof(x)); Y = Validated(y, nameof(y)); + + RegionIndex = (X << 1) + (Y >> 2); + SubRegionIndex = Y % 4; } public int X { get; } public int Y { get; } - public int RegionIndex => (X << 1) + (Y >> 2); - public int SubRegionIndex => Y % 4; + public int RegionIndex { get; } + public int SubRegionIndex { get; } private int Validated(int value, string argumentName) { @@ -51,7 +55,7 @@ namespace SuperStarTrek.Space int Round(float value) => (int)Math.Round(value, MidpointRounding.AwayFromZero); } - internal float GetDistanceTo(Coordinates destination) => - (float)Math.Sqrt(Math.Pow(X - destination.X, 2) + Math.Pow(Y - destination.Y, 2)); + internal (float Direction, float Distance) GetDirectionAndDistanceTo(Coordinates destination) => + DirectionAndDistance.From(this).To(destination); } } diff --git a/84 Super Star Trek/csharp/Space/Galaxy.cs b/84 Super Star Trek/csharp/Space/Galaxy.cs index 0392b335..38382754 100644 --- a/84 Super Star Trek/csharp/Space/Galaxy.cs +++ b/84 Super Star Trek/csharp/Space/Galaxy.cs @@ -48,6 +48,7 @@ namespace SuperStarTrek.Space public int KlingonCount => _quadrants.SelectMany(q => q).Sum(q => q.KlingonCount); public int StarbaseCount => _quadrants.SelectMany(q => q).Count(q => q.HasStarbase); + public IEnumerable> Quadrants => _quadrants; private static string GetQuadrantName(Coordinates coordinates) => $"{_regionNames[coordinates.RegionIndex]} {_subRegionIdentifiers[coordinates.SubRegionIndex]}"; diff --git a/84 Super Star Trek/csharp/Space/Quadrant.cs b/84 Super Star Trek/csharp/Space/Quadrant.cs index d89d60e0..efcaadb2 100644 --- a/84 Super Star Trek/csharp/Space/Quadrant.cs +++ b/84 Super Star Trek/csharp/Space/Quadrant.cs @@ -13,10 +13,15 @@ namespace SuperStarTrek.Space private readonly Random _random; private readonly Dictionary _sectors; private readonly Enterprise _enterprise; - private readonly Coordinates _starbaseSector; private readonly Galaxy _galaxy; - public Quadrant(QuadrantInfo info, Enterprise enterprise, Random random, Galaxy galaxy) + public Quadrant( + QuadrantInfo info, + Enterprise enterprise, + Random random, + Galaxy galaxy, + Input input, + Output output) { _info = info; _random = random; @@ -26,30 +31,30 @@ namespace SuperStarTrek.Space PositionObject(sector => new Klingon(sector, _random), _info.KlingonCount); if (_info.HasStarbase) { - _starbaseSector = PositionObject(_ => new Starbase(_random, new Input(new Output()), new Output())); + Starbase = PositionObject(sector => new Starbase(sector, _random, input, output)); } PositionObject(_ => new Star(), _info.StarCount); } - public object this[Coordinates coordinates] => _sectors.GetValueOrDefault(coordinates); public Coordinates Coordinates => _info.Coordinates; public bool HasKlingons => _info.KlingonCount > 0; + public int KlingonCount => _info.KlingonCount; public bool HasStarbase => _info.HasStarbase; - public Starbase Starbase => HasStarbase ? (Starbase)_sectors[_starbaseSector] : null; + public Starbase Starbase { get; } public bool EnterpriseIsNextToStarbase => _info.HasStarbase && - Math.Abs(_enterprise.Sector.X - _starbaseSector.X) <= 1 && - Math.Abs(_enterprise.Sector.Y - _starbaseSector.Y) <= 1; + Math.Abs(_enterprise.Sector.X - Starbase.Sector.X) <= 1 && + Math.Abs(_enterprise.Sector.Y - Starbase.Sector.Y) <= 1; - private IEnumerable Klingons => _sectors.Values.OfType(); + internal IEnumerable Klingons => _sectors.Values.OfType(); public override string ToString() => _info.Name; - private Coordinates PositionObject(Func objectFactory) + private T PositionObject(Func objectFactory) { var sector = GetRandomEmptySector(); _sectors[sector] = objectFactory.Invoke(sector); - return sector; + return (T)_sectors[sector]; } private void PositionObject(Func objectFactory, int count) diff --git a/84 Super Star Trek/csharp/Space/QuadrantInfo.cs b/84 Super Star Trek/csharp/Space/QuadrantInfo.cs index 04780a27..8b4357db 100644 --- a/84 Super Star Trek/csharp/Space/QuadrantInfo.cs +++ b/84 Super Star Trek/csharp/Space/QuadrantInfo.cs @@ -41,8 +41,11 @@ namespace SuperStarTrek.Space internal void AddStarbase() => HasStarbase = true; - internal Quadrant BuildQuadrant(Enterprise enterprise, Random random, Galaxy galaxy) => - new(this, enterprise, random, galaxy); + internal Quadrant BuildQuadrant(Enterprise enterprise, Random random, Galaxy galaxy, Input input, Output output) + { + _isKnown = true; + return new(this, enterprise, random, galaxy, input, output); + } internal string Scan() { diff --git a/84 Super Star Trek/csharp/StringExtensions.cs b/84 Super Star Trek/csharp/StringExtensions.cs new file mode 100644 index 00000000..02e9794f --- /dev/null +++ b/84 Super Star Trek/csharp/StringExtensions.cs @@ -0,0 +1,7 @@ +namespace SuperStarTrek +{ + internal static class StringExtensions + { + internal static string Pluralize(this string singular, int quantity) => singular + (quantity > 1 ? "s" : ""); + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/ComputerFunctions/ComputerFunction.cs b/84 Super Star Trek/csharp/Systems/ComputerFunctions/ComputerFunction.cs new file mode 100644 index 00000000..400c1f62 --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/ComputerFunctions/ComputerFunction.cs @@ -0,0 +1,18 @@ +using SuperStarTrek.Space; + +namespace SuperStarTrek.Systems.ComputerFunctions +{ + internal abstract class ComputerFunction + { + protected ComputerFunction(string description, Output output) + { + Description = description; + Output = output; + } + + internal string Description { get; } + protected Output Output { get; } + + internal abstract void Execute(Quadrant quadrant); + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/ComputerFunctions/CumulativeGalacticRecord.cs b/84 Super Star Trek/csharp/Systems/ComputerFunctions/CumulativeGalacticRecord.cs new file mode 100644 index 00000000..f02677ca --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/ComputerFunctions/CumulativeGalacticRecord.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SuperStarTrek.Space; + +namespace SuperStarTrek.Systems.ComputerFunctions +{ + internal class CumulativeGalacticRecord : GalacticReport + { + 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)); + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/ComputerFunctions/DirectionDistanceCalculator.cs b/84 Super Star Trek/csharp/Systems/ComputerFunctions/DirectionDistanceCalculator.cs new file mode 100644 index 00000000..c8b195f4 --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/ComputerFunctions/DirectionDistanceCalculator.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SuperStarTrek.Objects; +using SuperStarTrek.Space; + +namespace SuperStarTrek.Systems.ComputerFunctions +{ + internal class DirectionDistanceCalculator : NavigationCalculator + { + private readonly Enterprise _enterprise; + private readonly Input _input; + + public DirectionDistanceCalculator(Enterprise enterprise, Output output, Input input) + : base("Starbase nav data", output) + { + _enterprise = enterprise; + _input = input; + } + + internal override void Execute(Quadrant quadrant) + { + Output.WriteLine("Direction/distance calculator:") + .WriteLine($"You are at quadrant {_enterprise.Quadrant} sector {_enterprise.Sector}") + .WriteLine("Please enter"); + + WriteDirectionAndDistance( + _input.GetCoordinates(" Initial coordinates"), + _input.GetCoordinates(" Final coordinates")); + } + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/ComputerFunctions/GalacticReport.cs b/84 Super Star Trek/csharp/Systems/ComputerFunctions/GalacticReport.cs new file mode 100644 index 00000000..8b26cc2e --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/ComputerFunctions/GalacticReport.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using SuperStarTrek.Space; + +namespace SuperStarTrek.Systems.ComputerFunctions +{ + internal abstract class GalacticReport : ComputerFunction + { + public 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); + 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(" ----- ----- ----- ----- ----- ----- ----- -----"); + } + } + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/ComputerFunctions/GalaxyRegionMap.cs b/84 Super Star Trek/csharp/Systems/ComputerFunctions/GalaxyRegionMap.cs new file mode 100644 index 00000000..f6fe0305 --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/ComputerFunctions/GalaxyRegionMap.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Linq; +using SuperStarTrek.Resources; +using SuperStarTrek.Space; + +namespace SuperStarTrek.Systems.ComputerFunctions +{ + internal class GalaxyRegionMap : GalacticReport + { + 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')); + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/ComputerFunctions/NavigationCalculator.cs b/84 Super Star Trek/csharp/Systems/ComputerFunctions/NavigationCalculator.cs new file mode 100644 index 00000000..680f3d18 --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/ComputerFunctions/NavigationCalculator.cs @@ -0,0 +1,29 @@ +using SuperStarTrek.Space; +using SuperStarTrek.Utils; + +namespace SuperStarTrek.Systems.ComputerFunctions +{ + internal abstract class NavigationCalculator : ComputerFunction + { + 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((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}"); + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/ComputerFunctions/StarbaseDataCalculator.cs b/84 Super Star Trek/csharp/Systems/ComputerFunctions/StarbaseDataCalculator.cs new file mode 100644 index 00000000..b81a353d --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/ComputerFunctions/StarbaseDataCalculator.cs @@ -0,0 +1,30 @@ +using SuperStarTrek.Objects; +using SuperStarTrek.Resources; +using SuperStarTrek.Space; + +namespace SuperStarTrek.Systems.ComputerFunctions +{ + internal class StarbaseDataCalculator : NavigationCalculator + { + private readonly Enterprise _enterprise; + + public StarbaseDataCalculator(Enterprise enterprise, Output output) + : base("Starbase nav data", output) + { + _enterprise = enterprise; + } + + internal override void Execute(Quadrant quadrant) + { + if (!quadrant.HasStarbase) + { + Output.WriteLine(Strings.NoStarbase); + return; + } + + Output.WriteLine("From Enterprise to Starbase:"); + + WriteDirectionAndDistance(_enterprise.Sector, quadrant.Starbase.Sector); + } + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/ComputerFunctions/StatusReport.cs b/84 Super Star Trek/csharp/Systems/ComputerFunctions/StatusReport.cs new file mode 100644 index 00000000..f80ba7bb --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/ComputerFunctions/StatusReport.cs @@ -0,0 +1,41 @@ +using SuperStarTrek.Commands; +using SuperStarTrek.Objects; +using SuperStarTrek.Space; + +namespace SuperStarTrek.Systems.ComputerFunctions +{ + internal class StatusReport : ComputerFunction + { + private readonly Game _game; + private readonly Galaxy _galaxy; + private readonly Enterprise _enterprise; + + public StatusReport(Game game, Galaxy galaxy, Enterprise enterprise, Output output) + : base("Status report", output) + { + _game = game; + _galaxy = galaxy; + _enterprise = enterprise; + } + + 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); + } + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/ComputerFunctions/TorpedoDataCalculator.cs b/84 Super Star Trek/csharp/Systems/ComputerFunctions/TorpedoDataCalculator.cs new file mode 100644 index 00000000..6afa7b8c --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/ComputerFunctions/TorpedoDataCalculator.cs @@ -0,0 +1,33 @@ +using SuperStarTrek.Objects; +using SuperStarTrek.Resources; +using SuperStarTrek.Space; + +namespace SuperStarTrek.Systems.ComputerFunctions +{ + internal class TorpedoDataCalculator : NavigationCalculator + { + private readonly Enterprise _enterprise; + + public TorpedoDataCalculator(Enterprise enterprise, Output output) + : base("Photon torpedo data", output) + { + _enterprise = enterprise; + } + + internal override void Execute(Quadrant quadrant) + { + 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.Sector, klingon.Sector); + } + } + } +} \ No newline at end of file diff --git a/84 Super Star Trek/csharp/Systems/DamageControl.cs b/84 Super Star Trek/csharp/Systems/DamageControl.cs index 25f5ad78..6ce41bbe 100644 --- a/84 Super Star Trek/csharp/Systems/DamageControl.cs +++ b/84 Super Star Trek/csharp/Systems/DamageControl.cs @@ -24,6 +24,7 @@ namespace SuperStarTrek.Systems } else { + _output.NextLine(); WriteDamageReport(); } @@ -41,7 +42,7 @@ namespace SuperStarTrek.Systems public void WriteDamageReport() { - _output.NextLine().NextLine().WriteLine("Device State of Repair"); + _output.NextLine().WriteLine("Device State of Repair"); foreach (var system in _enterprise.Systems) { _output.Write(system.Name.PadRight(25)) diff --git a/84 Super Star Trek/csharp/Systems/LibraryComputer.cs b/84 Super Star Trek/csharp/Systems/LibraryComputer.cs new file mode 100644 index 00000000..b2e47d2a --- /dev/null +++ b/84 Super Star Trek/csharp/Systems/LibraryComputer.cs @@ -0,0 +1,47 @@ +using SuperStarTrek.Commands; +using SuperStarTrek.Space; +using SuperStarTrek.Systems.ComputerFunctions; + +namespace SuperStarTrek.Systems +{ + internal class LibraryComputer : Subsystem + { + private readonly Output _output; + private readonly Input _input; + private readonly ComputerFunction[] _functions; + + public LibraryComputer(Output output, Input input, params ComputerFunction[] functions) + : base("Library-Computer", Command.COM, output) + { + _output = output; + _input = input; + _functions = functions; + } + + 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) + { + 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}"); + } + } + } + } +} diff --git a/84 Super Star Trek/csharp/Utils/DirectionAndDistance.cs b/84 Super Star Trek/csharp/Utils/DirectionAndDistance.cs new file mode 100644 index 00000000..3fa9f4b8 --- /dev/null +++ b/84 Super Star Trek/csharp/Utils/DirectionAndDistance.cs @@ -0,0 +1,65 @@ +using System; +using SuperStarTrek.Space; + +namespace SuperStarTrek.Utils +{ + internal class DirectionAndDistance + { + private readonly float _fromX; + private readonly float _fromY; + + private DirectionAndDistance(float fromX, float fromY) + { + _fromX = fromX; + _fromY = fromY; + } + + public static DirectionAndDistance From(Coordinates coordinates) => From(coordinates.X, coordinates.Y); + + public static DirectionAndDistance From(float x, float y) => new DirectionAndDistance(x, y); + + public (float Direction, float Distance) To(Coordinates coordinates) => To(coordinates.X, coordinates.Y); + + public (float Direction, float Distance) To(float x, float y) + { + var deltaX = x - _fromX; + var deltaY = y - _fromY; + + return (GetDirection(deltaX, deltaY), GetDistance(deltaX, deltaY)); + } + + // The algorithm here is mathematically equivalent to the following code in the original, + // where X is deltaY and A is deltaX + // 8220 X=X-A:A=C1-W1:IFX<0THEN8350 + // 8250 IFA<0THEN8410 + // 8260 IFX>0THEN8280 + // 8270 IFA=0THENC1=5:GOTO8290 + // 8280 C1=1 + // 8290 IFABS(A)<=ABS(X)THEN8330 + // 8310 PRINT"DIRECTION =";C1+(((ABS(A)-ABS(X))+ABS(A))/ABS(A)):GOTO8460 + // 8330 PRINT"DIRECTION =";C1+(ABS(A)/ABS(X)):GOTO8460 + // 8350 IFA>0THENC1=3:GOTO8420 + // 8360 IFX<>0THENC1=5:GOTO8290 + // 8410 C1=7 + // 8420 IFABS(A)>=ABS(X)THEN8450 + // 8430 PRINT"DIRECTION =";C1+(((ABS(X)-ABS(A))+ABS(X))/ABS(X)):GOTO8460 + // 8450 PRINT"DIRECTION =";C1+(ABS(X)/ABS(A)) + // 8460 PRINT"DISTANCE =";SQR(X^2+A^2):IFH8=1THEN1990 + private float GetDirection(float deltaX, float deltaY) + { + var deltaXDominant = Math.Abs(deltaX) > Math.Abs(deltaY); + var fractionalPart = deltaXDominant ? deltaY / deltaX : -deltaX / deltaY; + var nearestCardinal = deltaXDominant switch + { + true => deltaX > 0 ? 7 : 3, + false => deltaY > 0 ? 1 : 5 + }; + + var direction = nearestCardinal + fractionalPart; + return direction < 1 ? direction + 8 : direction; + } + + private float GetDistance(float deltaX, float deltaY) => + (float)Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2)); + } +} \ No newline at end of file