mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-23 07:29:02 -08:00
Merge pull request #250 from drewjcooper/84-super-star-trek-csharp
84 super star trek - C#
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace SuperStarTrek
|
namespace SuperStarTrek.Commands
|
||||||
{
|
{
|
||||||
internal enum Command
|
internal enum Command
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace SuperStarTrek
|
namespace SuperStarTrek.Commands
|
||||||
{
|
{
|
||||||
internal static class CommandExtensions
|
internal static class CommandExtensions
|
||||||
{
|
{
|
||||||
23
84 Super Star Trek/csharp/Commands/CommandResult.cs
Normal file
23
84 Super Star Trek/csharp/Commands/CommandResult.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace SuperStarTrek.Commands
|
||||||
|
{
|
||||||
|
internal class CommandResult
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using SuperStarTrek.Objects;
|
|||||||
using SuperStarTrek.Resources;
|
using SuperStarTrek.Resources;
|
||||||
using SuperStarTrek.Space;
|
using SuperStarTrek.Space;
|
||||||
using SuperStarTrek.Systems;
|
using SuperStarTrek.Systems;
|
||||||
|
using SuperStarTrek.Systems.ComputerFunctions;
|
||||||
using static System.StringComparison;
|
using static System.StringComparison;
|
||||||
|
|
||||||
namespace SuperStarTrek
|
namespace SuperStarTrek
|
||||||
@@ -11,12 +12,12 @@ namespace SuperStarTrek
|
|||||||
{
|
{
|
||||||
private readonly Output _output;
|
private readonly Output _output;
|
||||||
private readonly Input _input;
|
private readonly Input _input;
|
||||||
|
private readonly Random _random;
|
||||||
|
|
||||||
private int _initialStardate;
|
private int _initialStardate;
|
||||||
private int _finalStarDate;
|
private int _finalStarDate;
|
||||||
private double _currentStardate;
|
private float _currentStardate;
|
||||||
private Coordinates _currentQuadrant;
|
private Coordinates _currentQuadrant;
|
||||||
private Coordinates _currentSector;
|
|
||||||
private Galaxy _galaxy;
|
private Galaxy _galaxy;
|
||||||
private int _initialKlingonCount;
|
private int _initialKlingonCount;
|
||||||
private Enterprise _enterprise;
|
private Enterprise _enterprise;
|
||||||
@@ -25,19 +26,17 @@ namespace SuperStarTrek
|
|||||||
{
|
{
|
||||||
_output = new Output();
|
_output = new Output();
|
||||||
_input = new Input(_output);
|
_input = new Input(_output);
|
||||||
|
_random = new Random();
|
||||||
}
|
}
|
||||||
|
|
||||||
public double Stardate => _currentStardate;
|
public float Stardate => _currentStardate;
|
||||||
|
public float StardatesRemaining => _finalStarDate - _currentStardate;
|
||||||
|
|
||||||
public void DoIntroduction()
|
public void DoIntroduction()
|
||||||
{
|
{
|
||||||
_output.Write(Strings.Title);
|
_output.Write(Strings.Title);
|
||||||
|
|
||||||
_output.Write("Do you need instructions (Y/N)? ");
|
if (_input.GetYesNo("Do you need instructions", Input.YesNoMode.FalseOnN))
|
||||||
var response = Console.ReadLine();
|
|
||||||
_output.WriteLine();
|
|
||||||
|
|
||||||
if (!response.Equals("N", InvariantCultureIgnoreCase))
|
|
||||||
{
|
{
|
||||||
_output.Write(Strings.Instructions);
|
_output.Write(Strings.Instructions);
|
||||||
|
|
||||||
@@ -47,8 +46,61 @@ namespace SuperStarTrek
|
|||||||
|
|
||||||
public void Play()
|
public void Play()
|
||||||
{
|
{
|
||||||
var quadrant = Initialise();
|
Initialise();
|
||||||
var gameOver = false;
|
var gameOver = false;
|
||||||
|
var newQuadrantText = Strings.StartText;
|
||||||
|
|
||||||
|
while (!gameOver)
|
||||||
|
{
|
||||||
|
_enterprise.Quadrant.Display(Strings.NowEntering);
|
||||||
|
|
||||||
|
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.Enterprise);
|
||||||
_output.Write(
|
_output.Write(
|
||||||
@@ -62,36 +114,21 @@ namespace SuperStarTrek
|
|||||||
|
|
||||||
_input.WaitForAnyKeyButEnter("when ready to accept command");
|
_input.WaitForAnyKeyButEnter("when ready to accept command");
|
||||||
|
|
||||||
_enterprise.Enter(quadrant, Strings.StartText);
|
_enterprise.StartIn(BuildCurrentQuadrant());
|
||||||
|
|
||||||
while (!gameOver)
|
|
||||||
{
|
|
||||||
var command = _input.GetCommand();
|
|
||||||
|
|
||||||
gameOver = command == Command.XXX || _enterprise.Execute(command);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Quadrant Initialise()
|
private Quadrant BuildCurrentQuadrant() =>
|
||||||
{
|
new Quadrant(_galaxy[_currentQuadrant], _enterprise, _random, _galaxy, _input, _output);
|
||||||
var random = new Random();
|
|
||||||
|
|
||||||
_currentStardate = _initialStardate = random.GetInt(20, 40) * 100;
|
|
||||||
_finalStarDate = _initialStardate + random.GetInt(25, 35);
|
|
||||||
|
|
||||||
_currentQuadrant = random.GetCoordinate();
|
|
||||||
_currentSector = random.GetCoordinate();
|
|
||||||
|
|
||||||
_galaxy = new Galaxy();
|
|
||||||
_initialKlingonCount = _galaxy.KlingonCount;
|
|
||||||
|
|
||||||
_enterprise = new Enterprise(3000, random.GetCoordinate());
|
|
||||||
_enterprise.Add(new ShortRangeSensors(_enterprise, _galaxy, this, _output))
|
|
||||||
.Add(new ShieldControl(_enterprise, _output, _input));
|
|
||||||
|
|
||||||
return new Quadrant(_galaxy[_currentQuadrant], _enterprise);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Replay() => _galaxy.StarbaseCount > 0 && _input.GetString(Strings.ReplayPrompt, "Aye");
|
public 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using SuperStarTrek.Commands;
|
||||||
|
using SuperStarTrek.Space;
|
||||||
using static System.StringComparison;
|
using static System.StringComparison;
|
||||||
|
|
||||||
namespace SuperStarTrek
|
namespace SuperStarTrek
|
||||||
@@ -25,14 +27,14 @@ namespace SuperStarTrek
|
|||||||
return Console.ReadLine();
|
return Console.ReadLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
public double GetNumber(string prompt)
|
public float GetNumber(string prompt)
|
||||||
{
|
{
|
||||||
_output.Prompt(prompt);
|
_output.Prompt(prompt);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var response = Console.ReadLine();
|
var response = Console.ReadLine();
|
||||||
if (double.TryParse(response, out var value))
|
if (float.TryParse(response, out var value))
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -42,7 +44,14 @@ namespace SuperStarTrek
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetNumber(string prompt, double minValue, double maxValue, out double value)
|
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})");
|
value = GetNumber($"{prompt} ({minValue}-{maxValue})");
|
||||||
|
|
||||||
@@ -58,7 +67,7 @@ namespace SuperStarTrek
|
|||||||
{
|
{
|
||||||
var response = GetString("Command");
|
var response = GetString("Command");
|
||||||
|
|
||||||
if (response != "" &&
|
if (response.Length >= 3 &&
|
||||||
Enum.TryParse(response.Substring(0, 3), ignoreCase: true, out Command parsedCommand))
|
Enum.TryParse(response.Substring(0, 3), ignoreCase: true, out Command parsedCommand))
|
||||||
{
|
{
|
||||||
return parsedCommand;
|
return parsedCommand;
|
||||||
@@ -72,5 +81,79 @@ namespace SuperStarTrek
|
|||||||
_output.WriteLine();
|
_output.WriteLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum YesNoMode
|
||||||
|
{
|
||||||
|
TrueOnY,
|
||||||
|
FalseOnN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using SuperStarTrek.Commands;
|
||||||
using SuperStarTrek.Resources;
|
using SuperStarTrek.Resources;
|
||||||
using SuperStarTrek.Space;
|
using SuperStarTrek.Space;
|
||||||
using SuperStarTrek.Systems;
|
using SuperStarTrek.Systems;
|
||||||
@@ -10,84 +12,71 @@ namespace SuperStarTrek.Objects
|
|||||||
internal class Enterprise
|
internal class Enterprise
|
||||||
{
|
{
|
||||||
private readonly int _maxEnergy;
|
private readonly int _maxEnergy;
|
||||||
|
private readonly Output _output;
|
||||||
private readonly List<Subsystem> _systems;
|
private readonly List<Subsystem> _systems;
|
||||||
private readonly Dictionary<Command, Subsystem> _commandExecutors;
|
private readonly Dictionary<Command, Subsystem> _commandExecutors;
|
||||||
|
private readonly Random _random;
|
||||||
|
private readonly Input _input;
|
||||||
private Quadrant _quadrant;
|
private Quadrant _quadrant;
|
||||||
private ShieldControl _shieldControl;
|
|
||||||
|
|
||||||
public Enterprise(int maxEnergy, Coordinates sector)
|
public Enterprise(int maxEnergy, Coordinates sector, Output output, Random random, Input input)
|
||||||
{
|
{
|
||||||
Sector = sector;
|
SectorCoordinates = sector;
|
||||||
TotalEnergy = _maxEnergy = maxEnergy;
|
TotalEnergy = _maxEnergy = maxEnergy;
|
||||||
|
|
||||||
_systems = new List<Subsystem>();
|
_systems = new List<Subsystem>();
|
||||||
_commandExecutors = new Dictionary<Command, Subsystem>();
|
_commandExecutors = new Dictionary<Command, Subsystem>();
|
||||||
|
_output = output;
|
||||||
|
_random = random;
|
||||||
|
_input = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coordinates Quadrant => _quadrant.Coordinates;
|
public Quadrant Quadrant => _quadrant;
|
||||||
public Coordinates Sector { get; }
|
public Coordinates QuadrantCoordinates => _quadrant.Coordinates;
|
||||||
public string Condition => GetCondition();
|
public Coordinates SectorCoordinates { get; private set; }
|
||||||
public double Shields => _shieldControl.Energy;
|
|
||||||
public double Energy => TotalEnergy - Shields;
|
|
||||||
public double TotalEnergy { get; private set; }
|
|
||||||
public int TorpedoCount { get; }
|
|
||||||
|
|
||||||
public bool IsDocked { get; private set; }
|
public string Condition => GetCondition();
|
||||||
|
public LibraryComputer Computer => (LibraryComputer)_commandExecutors[Command.COM];
|
||||||
|
public ShieldControl ShieldControl => (ShieldControl)_commandExecutors[Command.SHE];
|
||||||
|
public float Energy => TotalEnergy - ShieldControl.ShieldEnergy;
|
||||||
|
public float TotalEnergy { get; private set; }
|
||||||
|
public int DamagedSystemCount => _systems.Count(s => s.IsDamaged);
|
||||||
|
public IEnumerable<Subsystem> Systems => _systems;
|
||||||
|
public PhotonTubes PhotonTubes => (PhotonTubes)_commandExecutors[Command.TOR];
|
||||||
|
public bool IsDocked => _quadrant.EnterpriseIsNextToStarbase;
|
||||||
|
public bool IsStranded => TotalEnergy < 10 || Energy < 10 && ShieldControl.IsDamaged;
|
||||||
|
|
||||||
public Enterprise Add(Subsystem system)
|
public Enterprise Add(Subsystem system)
|
||||||
{
|
{
|
||||||
_systems.Add(system);
|
_systems.Add(system);
|
||||||
_commandExecutors[system.Command] = system;
|
_commandExecutors[system.Command] = system;
|
||||||
|
|
||||||
if (system is ShieldControl shieldControl) { _shieldControl = shieldControl; }
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetDamageReport()
|
public void StartIn(Quadrant quadrant)
|
||||||
{
|
|
||||||
var report = new StringBuilder();
|
|
||||||
report.AppendLine().AppendLine().AppendLine("Device State of Repair");
|
|
||||||
foreach (var system in _systems)
|
|
||||||
{
|
|
||||||
report.Append(system.Name.PadRight(25)).AppendLine(system.Condition.ToString(" 0.00;-0.00"));
|
|
||||||
}
|
|
||||||
report.AppendLine();
|
|
||||||
return report.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Enter(Quadrant quadrant, string entryTextFormat)
|
|
||||||
{
|
{
|
||||||
_quadrant = quadrant;
|
_quadrant = quadrant;
|
||||||
|
quadrant.Display(Strings.StartText);
|
||||||
var _output = new Output();
|
|
||||||
_output.Write(entryTextFormat, quadrant);
|
|
||||||
|
|
||||||
if (quadrant.HasKlingons)
|
|
||||||
{
|
|
||||||
_output.Write(Strings.CombatArea);
|
|
||||||
if (Shields <= 200) { _output.Write(Strings.LowShields); }
|
|
||||||
}
|
|
||||||
|
|
||||||
IsDocked = quadrant.EnterpriseIsNextToStarbase;
|
|
||||||
|
|
||||||
Execute(Command.SRS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCondition() =>
|
private string GetCondition() =>
|
||||||
(_quadrant.HasKlingons, Energy / _maxEnergy) switch
|
(_quadrant.HasKlingons, Energy / _maxEnergy) switch
|
||||||
{
|
{
|
||||||
(true, _) => "*Red*",
|
(true, _) => "*Red*",
|
||||||
(_, < 0.1) => "Yellow",
|
(_, < 0.1f) => "Yellow",
|
||||||
_ => "Green"
|
_ => "Green"
|
||||||
};
|
};
|
||||||
|
|
||||||
public bool Execute(Command command)
|
public CommandResult Execute(Command command)
|
||||||
{
|
{
|
||||||
_commandExecutors[command].ExecuteCommand(_quadrant);
|
if (command == Command.XXX) { return CommandResult.GameOver; }
|
||||||
return false;
|
|
||||||
|
return _commandExecutors[command].ExecuteCommand(_quadrant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Refuel() => TotalEnergy = _maxEnergy;
|
||||||
|
|
||||||
internal bool Recognises(string command)
|
internal bool Recognises(string command)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
@@ -99,5 +88,143 @@ namespace SuperStarTrek.Objects
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => "<*>";
|
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($" <Shields down to {ShieldControl.ShieldEnergy} units>");
|
||||||
|
|
||||||
|
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<string>();
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,42 @@
|
|||||||
|
using SuperStarTrek.Commands;
|
||||||
|
using SuperStarTrek.Space;
|
||||||
|
|
||||||
namespace SuperStarTrek.Objects
|
namespace SuperStarTrek.Objects
|
||||||
{
|
{
|
||||||
internal class Klingon
|
internal class Klingon
|
||||||
{
|
{
|
||||||
private double _energy;
|
private readonly Random _random;
|
||||||
|
|
||||||
public Klingon()
|
public Klingon(Coordinates sector, Random random)
|
||||||
{
|
{
|
||||||
_energy = new Random().GetDouble(100, 300);
|
Sector = sector;
|
||||||
|
_random = random;
|
||||||
|
Energy = _random.GetFloat(100, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float Energy { get; private set; }
|
||||||
|
public Coordinates Sector { get; private set; }
|
||||||
|
|
||||||
public override string ToString() => "+K+";
|
public override string ToString() => "+K+";
|
||||||
|
|
||||||
|
public 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,44 @@
|
|||||||
|
using SuperStarTrek.Resources;
|
||||||
|
using SuperStarTrek.Space;
|
||||||
|
|
||||||
namespace SuperStarTrek.Objects
|
namespace SuperStarTrek.Objects
|
||||||
{
|
{
|
||||||
internal class Starbase
|
internal class Starbase
|
||||||
{
|
{
|
||||||
|
private readonly Input _input;
|
||||||
|
private readonly Output _output;
|
||||||
|
private readonly float _repairDelay;
|
||||||
|
|
||||||
|
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() => ">!<";
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,37 @@ namespace SuperStarTrek
|
|||||||
{
|
{
|
||||||
internal class Output
|
internal class Output
|
||||||
{
|
{
|
||||||
public void Write(string text) => Console.Write(text);
|
public Output Write(string text)
|
||||||
public void Write(string format, params object[] args) => Console.Write(format, args);
|
{
|
||||||
public void WriteLine(string text = "") => Console.WriteLine(text);
|
Console.Write(text);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void NextLine() => Console.WriteLine();
|
public Output Write(string format, params object[] args)
|
||||||
|
{
|
||||||
|
Console.Write(format, args);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Output WriteLine(string text = "")
|
||||||
|
{
|
||||||
|
Console.WriteLine(text);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Output NextLine()
|
||||||
|
{
|
||||||
|
Console.WriteLine();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Output Prompt(string text = "")
|
||||||
|
{
|
||||||
|
Console.Write($"{text}? ");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public void Prompt(string text = "") => Console.Write($"{text}? ");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace SuperStarTrek
|
|||||||
{
|
{
|
||||||
static void Main()
|
static void Main()
|
||||||
{
|
{
|
||||||
|
var foo = Utils.DirectionAndDistance.From(1,1).To(4,5);
|
||||||
var game = new Game();
|
var game = new Game();
|
||||||
|
|
||||||
game.DoIntroduction();
|
game.DoIntroduction();
|
||||||
|
|||||||
@@ -6,20 +6,20 @@ namespace SuperStarTrek
|
|||||||
{
|
{
|
||||||
private static readonly System.Random _random = new();
|
private static readonly System.Random _random = new();
|
||||||
|
|
||||||
public Coordinates GetCoordinate() => new Coordinates(Get1To8Inclusive(), Get1To8Inclusive());
|
public 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:
|
// 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)
|
// 475 DEF FNR(R)=INT(RND(R)*7.98+1.01)
|
||||||
// Returns a value from 1 to 8, inclusive.
|
// Returns a value from 1 to 8, inclusive.
|
||||||
// Note there's a slight bias away from the extreme values, 1 and 8.
|
// Note there's a slight bias away from the extreme values, 1 and 8.
|
||||||
public int Get1To8Inclusive() => (int)(_random.NextDouble() * 7.98 + 1.01);
|
public int Get1To8Inclusive() => (int)(GetFloat() * 7.98 + 1.01);
|
||||||
|
|
||||||
public int GetInt(int inclusiveMinValue, int exclusiveMaxValue) =>
|
public int GetInt(int inclusiveMinValue, int exclusiveMaxValue) =>
|
||||||
_random.Next(inclusiveMinValue, exclusiveMaxValue);
|
_random.Next(inclusiveMinValue, exclusiveMaxValue);
|
||||||
|
|
||||||
public double GetDouble() => _random.NextDouble();
|
public float GetFloat() => (float)_random.NextDouble();
|
||||||
|
|
||||||
public double GetDouble(double inclusiveMinValue, double exclusiveMaxValue)
|
public float GetFloat(float inclusiveMinValue, float exclusiveMaxValue)
|
||||||
=> _random.NextDouble() * (exclusiveMaxValue - inclusiveMinValue) + inclusiveMinValue;
|
=> GetFloat() * (exclusiveMaxValue - inclusiveMinValue) + inclusiveMinValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
4
84 Super Star Trek/csharp/Resources/Congratulations.txt
Normal file
4
84 Super Star Trek/csharp/Resources/Congratulations.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Congratulations, Captain! The last Klingon battle cruiser
|
||||||
|
menacing the Federation has been destroyed.
|
||||||
|
|
||||||
|
Your efficiency rating is {0}.
|
||||||
3
84 Super Star Trek/csharp/Resources/CourtMartial.txt
Normal file
3
84 Super Star Trek/csharp/Resources/CourtMartial.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
Starfleet Command reviewing your record to consider
|
||||||
|
court martial!
|
||||||
1
84 Super Star Trek/csharp/Resources/Destroyed.txt
Normal file
1
84 Super Star Trek/csharp/Resources/Destroyed.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
The Enterprise has been destroyed. The Federation will be conquered.
|
||||||
3
84 Super Star Trek/csharp/Resources/EndOfMission.txt
Normal file
3
84 Super Star Trek/csharp/Resources/EndOfMission.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Is is stardate {0}.
|
||||||
|
There were {1} Klingon battle cruisers left at
|
||||||
|
the end of your mission.
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
INSTRUCTIONS FOR 'SUPER STAR TREK'
|
INSTRUCTIONS FOR 'SUPER STAR TREK'
|
||||||
|
|
||||||
1. When you see "Command ?" printed, enter one of the legal
|
1. When you see "Command ?" printed, enter one of the legal
|
||||||
|
|||||||
2
84 Super Star Trek/csharp/Resources/NoEnemyShips.txt
Normal file
2
84 Super Star Trek/csharp/Resources/NoEnemyShips.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Science Officer Spock reports, 'Sensors show no enemy ships
|
||||||
|
in this quadrant'
|
||||||
1
84 Super Star Trek/csharp/Resources/NoStarbase.txt
Normal file
1
84 Super Star Trek/csharp/Resources/NoStarbase.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Mr. Spock reports, 'Sensors show no starbases in this quadrant.'
|
||||||
2
84 Super Star Trek/csharp/Resources/NowEntering.txt
Normal file
2
84 Super Star Trek/csharp/Resources/NowEntering.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Now entering {0} quadrant . . .
|
||||||
|
|
||||||
5
84 Super Star Trek/csharp/Resources/PermissionDenied.txt
Normal file
5
84 Super Star Trek/csharp/Resources/PermissionDenied.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Lt. Uhura report message from Starfleet Command:
|
||||||
|
'Permission to attempt crossing of galactic perimeter
|
||||||
|
is hereby *Denied*. Shut down your engines.'
|
||||||
|
Chief Engineer Scott reports, 'Warp engines shut down
|
||||||
|
at sector {0} of quadrant {1}.'
|
||||||
1
84 Super Star Trek/csharp/Resources/Protected.txt
Normal file
1
84 Super Star Trek/csharp/Resources/Protected.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Starbase shields protect the Enterprise
|
||||||
8
84 Super Star Trek/csharp/Resources/RegionNames.txt
Normal file
8
84 Super Star Trek/csharp/Resources/RegionNames.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
Antares Sirius
|
||||||
|
Rigel Deneb
|
||||||
|
Procyon Capella
|
||||||
|
Vega Betelgeuse
|
||||||
|
Canopus Aldebaran
|
||||||
|
Altair Regulus
|
||||||
|
Sagittarius Arcturus
|
||||||
|
Pollux Spica
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
That does it, Captain!! You are hereby relieved of command
|
||||||
|
and sentenced to 99 stardates at hard labor on Cygnus 12!!
|
||||||
2
84 Super Star Trek/csharp/Resources/RepairEstimate.txt
Normal file
2
84 Super Star Trek/csharp/Resources/RepairEstimate.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Technicians standing by to effect repairs to your ship;
|
||||||
|
Estimated time to repair: {0} stardates.
|
||||||
1
84 Super Star Trek/csharp/Resources/RepairPrompt.txt
Normal file
1
84 Super Star Trek/csharp/Resources/RepairPrompt.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Will you authorize the repair order (Y/N)
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
The Federation is in need of a new starship commander
|
The Federation is in need of a new starship commander
|
||||||
for a similar mission -- if there is a volunteer
|
for a similar mission -- if there is a volunteer
|
||||||
let him step forward and enter 'Aye'
|
let him step forward and enter 'Aye'
|
||||||
3
84 Super Star Trek/csharp/Resources/Stranded.txt
Normal file
3
84 Super Star Trek/csharp/Resources/Stranded.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
** FATAL ERROR ** You've just stranded your ship in space
|
||||||
|
You have insufficient maneuvering energy, and shield control
|
||||||
|
is presently incapable of cross-circuiting to engine room!!
|
||||||
@@ -7,14 +7,29 @@ namespace SuperStarTrek.Resources
|
|||||||
internal static class Strings
|
internal static class Strings
|
||||||
{
|
{
|
||||||
public static string CombatArea => GetResource();
|
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();
|
||||||
|
public static string EndOfMission => GetResource();
|
||||||
public static string Enterprise => GetResource();
|
public static string Enterprise => GetResource();
|
||||||
public static string Instructions => GetResource();
|
public static string Instructions => GetResource();
|
||||||
public static string LowShields => GetResource();
|
public static string LowShields => GetResource();
|
||||||
|
public static string NoEnemyShips => GetResource();
|
||||||
|
public static string NoStarbase => GetResource();
|
||||||
|
public static string NowEntering => GetResource();
|
||||||
public static string Orders => GetResource();
|
public static string Orders => GetResource();
|
||||||
|
public static string PermissionDenied => GetResource();
|
||||||
|
public static string Protected => GetResource();
|
||||||
|
public static string RegionNames => GetResource();
|
||||||
|
public static string RelievedOfCommand => GetResource();
|
||||||
|
public static string RepairEstimate => GetResource();
|
||||||
|
public static string RepairPrompt => GetResource();
|
||||||
public static string ReplayPrompt => GetResource();
|
public static string ReplayPrompt => GetResource();
|
||||||
public static string ShieldsDropped => GetResource();
|
public static string ShieldsDropped => GetResource();
|
||||||
public static string ShortRangeSensorsOut => GetResource();
|
public static string ShortRangeSensorsOut => GetResource();
|
||||||
public static string StartText => GetResource();
|
public static string StartText => GetResource();
|
||||||
|
public static string Stranded => GetResource();
|
||||||
public static string Title => GetResource();
|
public static string Title => GetResource();
|
||||||
|
|
||||||
private static string GetResource([CallerMemberName] string name = "")
|
private static string GetResource([CallerMemberName] string name = "")
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
public Coordinates(int x, int y)
|
|
||||||
{
|
|
||||||
X = Validated(x, nameof(x));
|
|
||||||
Y = Validated(y, nameof(y));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int X { get; }
|
|
||||||
public int Y { get; }
|
|
||||||
|
|
||||||
private int Validated(int value, string argumentName)
|
|
||||||
{
|
|
||||||
if (value >= 1 && value <= 8) { return value; }
|
|
||||||
|
|
||||||
throw new ArgumentOutOfRangeException(argumentName, value, "Must be 1 to 8 inclusive");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => $"{X} , {Y}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
67
84 Super Star Trek/csharp/Space/Coordinates.cs
Normal file
67
84 Super Star Trek/csharp/Space/Coordinates.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using SuperStarTrek.Utils;
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
public Coordinates(int x, int y)
|
||||||
|
{
|
||||||
|
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 { get; }
|
||||||
|
public int SubRegionIndex { get; }
|
||||||
|
|
||||||
|
private 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace SuperStarTrek.Space
|
namespace SuperStarTrek.Space
|
||||||
{
|
{
|
||||||
@@ -24,7 +25,7 @@ namespace SuperStarTrek.Space
|
|||||||
(0, 1)
|
(0, 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
public Course(double direction)
|
internal Course(float direction)
|
||||||
{
|
{
|
||||||
if (direction < 1 || direction > 9)
|
if (direction < 1 || direction > 9)
|
||||||
{
|
{
|
||||||
@@ -44,7 +45,53 @@ namespace SuperStarTrek.Space
|
|||||||
DeltaY = baseCardinal.DeltaY + (nextCardinal.DeltaY - baseCardinal.DeltaY) * fractionalDirection;
|
DeltaY = baseCardinal.DeltaY + (nextCardinal.DeltaY - baseCardinal.DeltaY) * fractionalDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double DeltaX { get; }
|
internal float DeltaX { get; }
|
||||||
public double DeltaY { get; }
|
internal float DeltaY { get; }
|
||||||
|
|
||||||
|
internal IEnumerable<Coordinates> GetSectorsFrom(Coordinates start)
|
||||||
|
{
|
||||||
|
(float x, float y) = start;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
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 (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 == -1)
|
||||||
|
{
|
||||||
|
newQuadrant -= 1;
|
||||||
|
newSector = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newQuadrant switch
|
||||||
|
{
|
||||||
|
< 0 => (false, 0, 0),
|
||||||
|
> 7 => (false, 7, 7),
|
||||||
|
_ => (true, newQuadrant, newSector)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,37 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using SuperStarTrek.Objects;
|
||||||
|
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;
|
private readonly QuadrantInfo[][] _quadrants;
|
||||||
|
private readonly Random _random;
|
||||||
|
|
||||||
public Galaxy()
|
static Galaxy()
|
||||||
{
|
{
|
||||||
var random = new Random();
|
_regionNames = Strings.RegionNames.Split(new[] { ' ', '\n' }, RemoveEmptyEntries | TrimEntries);
|
||||||
|
_subRegionIdentifiers = new[] { "I", "II", "III", "IV" };
|
||||||
|
}
|
||||||
|
|
||||||
_quadrants = Enumerable.Range(1, 8).Select(x =>
|
public Galaxy(Random random)
|
||||||
Enumerable.Range(1, 8).Select(y => QuadrantInfo.Create(new Coordinates(x, y), "")).ToArray())
|
{
|
||||||
|
_random = random;
|
||||||
|
|
||||||
|
_quadrants = Enumerable
|
||||||
|
.Range(0, 8)
|
||||||
|
.Select(x => Enumerable
|
||||||
|
.Range(0, 8)
|
||||||
|
.Select(y => new Coordinates(x, y))
|
||||||
|
.Select(c => QuadrantInfo.Create(c, GetQuadrantName(c)))
|
||||||
|
.ToArray())
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
if (StarbaseCount == 0)
|
if (StarbaseCount == 0)
|
||||||
@@ -26,9 +46,22 @@ namespace SuperStarTrek.Space
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuadrantInfo this[Coordinates coordinate] => _quadrants[coordinate.X - 1][coordinate.Y - 1];
|
public QuadrantInfo this[Coordinates coordinate] => _quadrants[coordinate.X][coordinate.Y];
|
||||||
|
|
||||||
public int KlingonCount => _quadrants.SelectMany(q => q).Sum(q => q.KlingonCount);
|
public int KlingonCount => _quadrants.SelectMany(q => q).Sum(q => q.KlingonCount);
|
||||||
public int StarbaseCount => _quadrants.SelectMany(q => q).Count(q => q.HasStarbase);
|
public int StarbaseCount => _quadrants.SelectMany(q => q).Count(q => q.HasStarbase);
|
||||||
|
public IEnumerable<IEnumerable<QuadrantInfo>> Quadrants => _quadrants;
|
||||||
|
|
||||||
|
private static string GetQuadrantName(Coordinates coordinates) =>
|
||||||
|
$"{_regionNames[coordinates.RegionIndex]} {_subRegionIdentifiers[coordinates.SubRegionIndex]}";
|
||||||
|
|
||||||
|
public IEnumerable<IEnumerable<QuadrantInfo>> GetNeighborhood(Quadrant quadrant) =>
|
||||||
|
Enumerable.Range(-1, 3)
|
||||||
|
.Select(dx => dx + quadrant.Coordinates.X)
|
||||||
|
.Select(x => GetNeighborhoodRow(quadrant, x));
|
||||||
|
private IEnumerable<QuadrantInfo> 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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using SuperStarTrek.Commands;
|
||||||
using SuperStarTrek.Objects;
|
using SuperStarTrek.Objects;
|
||||||
|
using SuperStarTrek.Resources;
|
||||||
|
|
||||||
namespace SuperStarTrek.Space
|
namespace SuperStarTrek.Space
|
||||||
{
|
{
|
||||||
@@ -10,42 +12,56 @@ namespace SuperStarTrek.Space
|
|||||||
private readonly QuadrantInfo _info;
|
private readonly QuadrantInfo _info;
|
||||||
private readonly Random _random;
|
private readonly Random _random;
|
||||||
private readonly Dictionary<Coordinates, object> _sectors;
|
private readonly Dictionary<Coordinates, object> _sectors;
|
||||||
private readonly Coordinates _enterpriseSector;
|
private readonly Enterprise _enterprise;
|
||||||
private readonly Coordinates _starbaseSector;
|
private readonly Output _output;
|
||||||
|
private bool _displayed = false;
|
||||||
|
|
||||||
public Quadrant(QuadrantInfo info, Enterprise enterprise)
|
public Quadrant(
|
||||||
|
QuadrantInfo info,
|
||||||
|
Enterprise enterprise,
|
||||||
|
Random random,
|
||||||
|
Galaxy galaxy,
|
||||||
|
Input input,
|
||||||
|
Output output)
|
||||||
{
|
{
|
||||||
_info = info;
|
_info = info;
|
||||||
_random = new Random();
|
_random = random;
|
||||||
|
_output = output;
|
||||||
|
Galaxy = galaxy;
|
||||||
|
|
||||||
_enterpriseSector = enterprise.Sector;
|
info.MarkAsKnown();
|
||||||
_sectors = new Dictionary<Coordinates, object> { [enterprise.Sector] = enterprise };
|
_sectors = new() { [enterprise.SectorCoordinates] = _enterprise = enterprise };
|
||||||
PositionObject(() => new Klingon(), _info.KlingonCount);
|
PositionObject(sector => new Klingon(sector, _random), _info.KlingonCount);
|
||||||
if (_info.HasStarbase)
|
if (_info.HasStarbase)
|
||||||
{
|
{
|
||||||
_starbaseSector = PositionObject(() => new Starbase());
|
Starbase = PositionObject(sector => new Starbase(sector, _random, input, output));
|
||||||
}
|
}
|
||||||
PositionObject(() => new Star(), _info.StarCount);
|
PositionObject(_ => new Star(), _info.StarCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coordinates Coordinates => _info.Coordinates;
|
public Coordinates Coordinates => _info.Coordinates;
|
||||||
public bool HasKlingons => _info.KlingonCount > 0;
|
public bool HasKlingons => _info.KlingonCount > 0;
|
||||||
|
public int KlingonCount => _info.KlingonCount;
|
||||||
public bool HasStarbase => _info.HasStarbase;
|
public bool HasStarbase => _info.HasStarbase;
|
||||||
|
public Starbase Starbase { get; }
|
||||||
|
internal Galaxy Galaxy { get; }
|
||||||
public bool EnterpriseIsNextToStarbase =>
|
public bool EnterpriseIsNextToStarbase =>
|
||||||
_info.HasStarbase &&
|
_info.HasStarbase &&
|
||||||
Math.Abs(_enterpriseSector.X - _starbaseSector.X) <= 1 &&
|
Math.Abs(_enterprise.SectorCoordinates.X - Starbase.Sector.X) <= 1 &&
|
||||||
Math.Abs(_enterpriseSector.Y - _starbaseSector.Y) <= 1;
|
Math.Abs(_enterprise.SectorCoordinates.Y - Starbase.Sector.Y) <= 1;
|
||||||
|
|
||||||
|
internal IEnumerable<Klingon> Klingons => _sectors.Values.OfType<Klingon>();
|
||||||
|
|
||||||
public override string ToString() => _info.Name;
|
public override string ToString() => _info.Name;
|
||||||
|
|
||||||
private Coordinates PositionObject(Func<object> objectFactory)
|
private T PositionObject<T>(Func<Coordinates, T> objectFactory)
|
||||||
{
|
{
|
||||||
var sector = GetRandomEmptySector();
|
var sector = GetRandomEmptySector();
|
||||||
_sectors[sector] = objectFactory.Invoke();
|
_sectors[sector] = objectFactory.Invoke(sector);
|
||||||
return sector;
|
return (T)_sectors[sector];
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PositionObject(Func<object> objectFactory, int count)
|
private void PositionObject(Func<Coordinates, object> objectFactory, int count)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -53,6 +69,91 @@ namespace SuperStarTrek.Space
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void Display(string textFormat)
|
||||||
|
{
|
||||||
|
if (_displayed) { return; }
|
||||||
|
|
||||||
|
_output.Write(textFormat, this);
|
||||||
|
|
||||||
|
if (_info.KlingonCount > 0)
|
||||||
|
{
|
||||||
|
_output.Write(Strings.CombatArea);
|
||||||
|
if (_enterprise.ShieldControl.ShieldEnergy <= 200) { _output.Write(Strings.LowShields); }
|
||||||
|
}
|
||||||
|
|
||||||
|
_enterprise.Execute(Command.SRS);
|
||||||
|
|
||||||
|
_displayed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
_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);
|
||||||
|
_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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandResult.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
private Coordinates GetRandomEmptySector()
|
private Coordinates GetRandomEmptySector()
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
@@ -65,15 +166,21 @@ namespace SuperStarTrek.Space
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> GetDisplayLines() => Enumerable.Range(1, 8).Select(x => GetDisplayLine(x));
|
public IEnumerable<string> GetDisplayLines() => Enumerable.Range(0, 8).Select(x => GetDisplayLine(x));
|
||||||
|
|
||||||
private string GetDisplayLine(int x)
|
private string GetDisplayLine(int x)
|
||||||
=> string.Join(
|
=> string.Join(
|
||||||
" ",
|
" ",
|
||||||
Enumerable
|
Enumerable
|
||||||
.Range(1, 8)
|
.Range(0, 8)
|
||||||
.Select(y => new Coordinates(x, y))
|
.Select(y => new Coordinates(x, y))
|
||||||
.Select(c => _sectors.GetValueOrDefault(c))
|
.Select(c => _sectors.GetValueOrDefault(c))
|
||||||
.Select(o => o?.ToString() ?? " "));
|
.Select(o => o?.ToString() ?? " "));
|
||||||
|
|
||||||
|
internal void SetEnterpriseSector(Coordinates sector)
|
||||||
|
{
|
||||||
|
_sectors.Remove(_enterprise.SectorCoordinates);
|
||||||
|
_sectors[sector] = _enterprise;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
|
using SuperStarTrek.Objects;
|
||||||
|
|
||||||
namespace SuperStarTrek.Space
|
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 QuadrantInfo(Coordinates coordinates, string name, int klingonCount, int starCount, bool hasStarbase)
|
||||||
{
|
{
|
||||||
Coordinates = coordinates;
|
Coordinates = coordinates;
|
||||||
@@ -20,14 +24,14 @@ namespace SuperStarTrek.Space
|
|||||||
public static QuadrantInfo Create(Coordinates coordinates, string name)
|
public static QuadrantInfo Create(Coordinates coordinates, string name)
|
||||||
{
|
{
|
||||||
var random = new Random();
|
var random = new Random();
|
||||||
var klingonCount = random.GetDouble() switch
|
var klingonCount = random.GetFloat() switch
|
||||||
{
|
{
|
||||||
> 0.98 => 3,
|
> 0.98f => 3,
|
||||||
> 0.95 => 2,
|
> 0.95f => 2,
|
||||||
> 0.80 => 1,
|
> 0.80f => 1,
|
||||||
_ => 0
|
_ => 0
|
||||||
};
|
};
|
||||||
var hasStarbase = random.GetDouble() > 0.96;
|
var hasStarbase = random.GetFloat() > 0.96f;
|
||||||
var starCount = random.Get1To8Inclusive();
|
var starCount = random.Get1To8Inclusive();
|
||||||
|
|
||||||
return new QuadrantInfo(coordinates, name, klingonCount, starCount, hasStarbase);
|
return new QuadrantInfo(coordinates, name, klingonCount, starCount, hasStarbase);
|
||||||
@@ -36,5 +40,25 @@ namespace SuperStarTrek.Space
|
|||||||
internal void AddKlingon() => KlingonCount += 1;
|
internal void AddKlingon() => KlingonCount += 1;
|
||||||
|
|
||||||
internal void AddStarbase() => HasStarbase = true;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
84 Super Star Trek/csharp/StringExtensions.cs
Normal file
7
84 Super Star Trek/csharp/StringExtensions.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace SuperStarTrek
|
||||||
|
{
|
||||||
|
internal static class StringExtensions
|
||||||
|
{
|
||||||
|
internal static string Pluralize(this string singular, int quantity) => singular + (quantity > 1 ? "s" : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<string> GetRowData() =>
|
||||||
|
Galaxy.Quadrants.Select(row => " " + string.Join(" ", row));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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:")
|
||||||
|
.Write($"You are at quadrant {_enterprise.QuadrantCoordinates}")
|
||||||
|
.WriteLine($" sector {_enterprise.SectorCoordinates}")
|
||||||
|
.WriteLine("Please enter");
|
||||||
|
|
||||||
|
WriteDirectionAndDistance(
|
||||||
|
_input.GetCoordinates(" Initial coordinates"),
|
||||||
|
_input.GetCoordinates(" Final coordinates"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<string> 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(" ----- ----- ----- ----- ----- ----- ----- -----");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<string> GetRowData() =>
|
||||||
|
Strings.RegionNames.Split('\n').Select(n => n.TrimEnd('\r'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.SectorCoordinates, quadrant.Starbase.Sector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.SectorCoordinates, klingon.Sector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
84 Super Star Trek/csharp/Systems/DamageControl.cs
Normal file
54
84 Super Star Trek/csharp/Systems/DamageControl.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using SuperStarTrek.Commands;
|
||||||
|
using SuperStarTrek.Objects;
|
||||||
|
using SuperStarTrek.Space;
|
||||||
|
|
||||||
|
namespace SuperStarTrek.Systems
|
||||||
|
{
|
||||||
|
internal class DamageControl : Subsystem
|
||||||
|
{
|
||||||
|
private readonly Enterprise _enterprise;
|
||||||
|
private readonly Output _output;
|
||||||
|
|
||||||
|
public DamageControl(Enterprise enterprise, Output output)
|
||||||
|
: base("Damage Control", Command.DAM, output)
|
||||||
|
{
|
||||||
|
_enterprise = enterprise;
|
||||||
|
_output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
|
||||||
|
{
|
||||||
|
if (IsDamaged)
|
||||||
|
{
|
||||||
|
_output.WriteLine("Damage Control report not available");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_output.NextLine();
|
||||||
|
WriteDamageReport();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_enterprise.DamagedSystemCount > 0 && _enterprise.IsDocked)
|
||||||
|
{
|
||||||
|
if (quadrant.Starbase.TryRepair(_enterprise, out var repairTime))
|
||||||
|
{
|
||||||
|
WriteDamageReport();
|
||||||
|
return CommandResult.Elapsed(repairTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandResult.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteDamageReport()
|
||||||
|
{
|
||||||
|
_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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
84 Super Star Trek/csharp/Systems/LibraryComputer.cs
Normal file
47
84 Super Star Trek/csharp/Systems/LibraryComputer.cs
Normal file
@@ -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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
84 Super Star Trek/csharp/Systems/LongRangeSensors.cs
Normal file
39
84 Super Star Trek/csharp/Systems/LongRangeSensors.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using SuperStarTrek.Commands;
|
||||||
|
using SuperStarTrek.Objects;
|
||||||
|
using SuperStarTrek.Resources;
|
||||||
|
using SuperStarTrek.Space;
|
||||||
|
|
||||||
|
namespace SuperStarTrek.Systems
|
||||||
|
{
|
||||||
|
internal class LongRangeSensors : Subsystem
|
||||||
|
{
|
||||||
|
private readonly Galaxy _galaxy;
|
||||||
|
private readonly Output _output;
|
||||||
|
|
||||||
|
public LongRangeSensors(Galaxy galaxy, Output output)
|
||||||
|
: base("Long Range Sensors", Command.LRS, output)
|
||||||
|
{
|
||||||
|
_galaxy = galaxy;
|
||||||
|
_output = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
84 Super Star Trek/csharp/Systems/PhaserControl.cs
Normal file
97
84 Super Star Trek/csharp/Systems/PhaserControl.cs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using SuperStarTrek.Commands;
|
||||||
|
using SuperStarTrek.Objects;
|
||||||
|
using SuperStarTrek.Resources;
|
||||||
|
using SuperStarTrek.Space;
|
||||||
|
|
||||||
|
namespace SuperStarTrek.Systems
|
||||||
|
{
|
||||||
|
internal class PhaserControl : Subsystem
|
||||||
|
{
|
||||||
|
private readonly Enterprise _enterprise;
|
||||||
|
private readonly Output _output;
|
||||||
|
private readonly Input _input;
|
||||||
|
private readonly Random _random;
|
||||||
|
|
||||||
|
public PhaserControl(Enterprise enterprise, Output output, Input input, Random random)
|
||||||
|
: base("Phaser Control", Command.PHA, output)
|
||||||
|
{
|
||||||
|
_enterprise = enterprise;
|
||||||
|
_output = output;
|
||||||
|
_input = input;
|
||||||
|
_random = random;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool CanExecuteCommand() => IsOperational("Phasers inoperative");
|
||||||
|
|
||||||
|
protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetPhaserStrength()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
_output.WriteLine($"Energy available = {_enterprise.Energy} units");
|
||||||
|
var phaserStrength = _input.GetNumber("Number of units to fire");
|
||||||
|
|
||||||
|
if (phaserStrength <= _enterprise.Energy) { return phaserStrength; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetPerTargetPhaserStrength(float phaserStrength, int targetCount)
|
||||||
|
{
|
||||||
|
if (_enterprise.Computer.IsDamaged)
|
||||||
|
{
|
||||||
|
phaserStrength *= _random.GetFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
return phaserStrength / targetCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResolveHitOn(Klingon klingon, float perEnemyStrength, Quadrant quadrant)
|
||||||
|
{
|
||||||
|
var distance = _enterprise.SectorCoordinates.GetDistanceTo(klingon.Sector);
|
||||||
|
var hitStrength = (int)(perEnemyStrength / distance * (2 + _random.GetFloat()));
|
||||||
|
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
84 Super Star Trek/csharp/Systems/PhotonTubes.cs
Normal file
66
84 Super Star Trek/csharp/Systems/PhotonTubes.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using SuperStarTrek.Commands;
|
||||||
|
using SuperStarTrek.Objects;
|
||||||
|
using SuperStarTrek.Space;
|
||||||
|
|
||||||
|
namespace SuperStarTrek.Systems
|
||||||
|
{
|
||||||
|
internal class PhotonTubes : Subsystem
|
||||||
|
{
|
||||||
|
private readonly int _tubeCount;
|
||||||
|
private readonly Enterprise _enterprise;
|
||||||
|
private readonly Output _output;
|
||||||
|
private readonly Input _input;
|
||||||
|
|
||||||
|
public PhotonTubes(int tubeCount, Enterprise enterprise, Output output, Input input)
|
||||||
|
: base("Photon Tubes", Command.TOR, output)
|
||||||
|
{
|
||||||
|
TorpedoCount = _tubeCount = tubeCount;
|
||||||
|
_enterprise = enterprise;
|
||||||
|
_output = output;
|
||||||
|
_input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using SuperStarTrek.Commands;
|
||||||
using SuperStarTrek.Objects;
|
using SuperStarTrek.Objects;
|
||||||
using SuperStarTrek.Space;
|
using SuperStarTrek.Space;
|
||||||
|
|
||||||
@@ -10,36 +11,35 @@ namespace SuperStarTrek.Systems
|
|||||||
private readonly Input _input;
|
private readonly Input _input;
|
||||||
|
|
||||||
public ShieldControl(Enterprise enterprise, Output output, Input input)
|
public ShieldControl(Enterprise enterprise, Output output, Input input)
|
||||||
: base("Shield Control", Command.SHE)
|
: base("Shield Control", Command.SHE, output)
|
||||||
{
|
{
|
||||||
_enterprise = enterprise;
|
_enterprise = enterprise;
|
||||||
_output = output;
|
_output = output;
|
||||||
_input = input;
|
_input = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double Energy { get; private set; }
|
public float ShieldEnergy { get; set; }
|
||||||
|
|
||||||
public override void ExecuteCommand(Quadrant quadrant)
|
protected override bool CanExecuteCommand() => IsOperational("{name} inoperable");
|
||||||
{
|
|
||||||
if (Condition < 0)
|
|
||||||
{
|
|
||||||
_output.WriteLine("Shield Control inoperable");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
|
||||||
|
{
|
||||||
_output.WriteLine($"Energy available = {_enterprise.TotalEnergy}");
|
_output.WriteLine($"Energy available = {_enterprise.TotalEnergy}");
|
||||||
var requested = _input.GetNumber($"Number of units to shields");
|
var requested = _input.GetNumber($"Number of units to shields");
|
||||||
|
|
||||||
if (Validate(requested))
|
if (Validate(requested))
|
||||||
{
|
{
|
||||||
Energy = requested;
|
ShieldEnergy = requested;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_output.WriteLine("<SHIELDS UNCHANGED>");
|
_output.WriteLine("<SHIELDS UNCHANGED>");
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Validate(double requested)
|
return CommandResult.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Validate(float requested)
|
||||||
{
|
{
|
||||||
if (requested > _enterprise.TotalEnergy)
|
if (requested > _enterprise.TotalEnergy)
|
||||||
{
|
{
|
||||||
@@ -47,7 +47,11 @@ namespace SuperStarTrek.Systems
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return requested >= 0 && requested != Energy;
|
return requested >= 0 && requested != ShieldEnergy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void AbsorbHit(int hitStrength) => ShieldEnergy -= hitStrength;
|
||||||
|
|
||||||
|
internal void DropShields() => ShieldEnergy = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using SuperStarTrek.Commands;
|
||||||
using SuperStarTrek.Objects;
|
using SuperStarTrek.Objects;
|
||||||
using SuperStarTrek.Resources;
|
using SuperStarTrek.Resources;
|
||||||
using SuperStarTrek.Space;
|
using SuperStarTrek.Space;
|
||||||
@@ -16,7 +17,7 @@ namespace SuperStarTrek.Systems
|
|||||||
private readonly Output _output;
|
private readonly Output _output;
|
||||||
|
|
||||||
public ShortRangeSensors(Enterprise enterprise, Galaxy galaxy, Game game, Output output)
|
public ShortRangeSensors(Enterprise enterprise, Galaxy galaxy, Game game, Output output)
|
||||||
: base("Short Range Sensors", Command.SRS)
|
: base("Short Range Sensors", Command.SRS, output)
|
||||||
{
|
{
|
||||||
_enterprise = enterprise;
|
_enterprise = enterprise;
|
||||||
_galaxy = galaxy;
|
_galaxy = galaxy;
|
||||||
@@ -24,7 +25,7 @@ namespace SuperStarTrek.Systems
|
|||||||
_output = output;
|
_output = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ExecuteCommand(Quadrant quadrant)
|
protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
|
||||||
{
|
{
|
||||||
if (_enterprise.IsDocked)
|
if (_enterprise.IsDocked)
|
||||||
{
|
{
|
||||||
@@ -42,17 +43,19 @@ namespace SuperStarTrek.Systems
|
|||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(l => _output.WriteLine(l));
|
.ForEach(l => _output.WriteLine(l));
|
||||||
_output.WriteLine("---------------------------------");
|
_output.WriteLine("---------------------------------");
|
||||||
|
|
||||||
|
return CommandResult.Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> GetStatusLines()
|
public IEnumerable<string> GetStatusLines()
|
||||||
{
|
{
|
||||||
yield return $"Stardate {_game.Stardate}";
|
yield return $"Stardate {_game.Stardate}";
|
||||||
yield return $"Condition {_enterprise.Condition}";
|
yield return $"Condition {_enterprise.Condition}";
|
||||||
yield return $"Quadrant {_enterprise.Quadrant}";
|
yield return $"Quadrant {_enterprise.QuadrantCoordinates}";
|
||||||
yield return $"Sector {_enterprise.Sector}";
|
yield return $"Sector {_enterprise.SectorCoordinates}";
|
||||||
yield return $"Photon torpedoes {_enterprise.TorpedoCount}";
|
yield return $"Photon torpedoes {_enterprise.PhotonTubes.TorpedoCount}";
|
||||||
yield return $"Total energy {Math.Ceiling(_enterprise.Energy)}";
|
yield return $"Total energy {Math.Ceiling(_enterprise.TotalEnergy)}";
|
||||||
yield return $"Shields {(int)_enterprise.Shields}";
|
yield return $"Shields {(int)_enterprise.ShieldControl.ShieldEnergy}";
|
||||||
yield return $"Klingons remaining {_galaxy.KlingonCount}";
|
yield return $"Klingons remaining {_galaxy.KlingonCount}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using SuperStarTrek.Commands;
|
||||||
using SuperStarTrek.Space;
|
using SuperStarTrek.Space;
|
||||||
|
|
||||||
namespace SuperStarTrek.Systems
|
namespace SuperStarTrek.Systems
|
||||||
{
|
{
|
||||||
internal abstract class Subsystem
|
internal abstract class Subsystem
|
||||||
{
|
{
|
||||||
protected Subsystem(string name, Command command)
|
private readonly Output _output;
|
||||||
|
|
||||||
|
protected Subsystem(string name, Command command, Output output)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Command = command;
|
Command = command;
|
||||||
Condition = 0;
|
Condition = 0;
|
||||||
|
_output = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public double Condition { get; }
|
public float Condition { get; private set; }
|
||||||
|
public bool IsDamaged => Condition < 0;
|
||||||
public Command Command { get; }
|
public Command Command { get; }
|
||||||
|
|
||||||
public abstract void ExecuteCommand(Quadrant quadrant);
|
protected virtual bool CanExecuteCommand() => true;
|
||||||
|
|
||||||
|
protected bool IsOperational(string notOperationalMessage)
|
||||||
|
{
|
||||||
|
if (IsDamaged)
|
||||||
|
{
|
||||||
|
_output.WriteLine(notOperationalMessage.Replace("{name}", Name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandResult ExecuteCommand(Quadrant quadrant)
|
||||||
|
=> CanExecuteCommand() ? ExecuteCommandCore(quadrant) : CommandResult.Ok;
|
||||||
|
|
||||||
|
protected abstract CommandResult ExecuteCommandCore(Quadrant quadrant);
|
||||||
|
|
||||||
|
public virtual void Repair()
|
||||||
|
{
|
||||||
|
if (IsDamaged)
|
||||||
|
{
|
||||||
|
Condition = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
84 Super Star Trek/csharp/Systems/WarpEngines.cs
Normal file
82
84 Super Star Trek/csharp/Systems/WarpEngines.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using SuperStarTrek.Commands;
|
||||||
|
using SuperStarTrek.Objects;
|
||||||
|
using SuperStarTrek.Space;
|
||||||
|
|
||||||
|
namespace SuperStarTrek.Systems
|
||||||
|
{
|
||||||
|
internal class WarpEngines : Subsystem
|
||||||
|
{
|
||||||
|
private readonly Enterprise _enterprise;
|
||||||
|
private readonly Output _output;
|
||||||
|
private readonly Input _input;
|
||||||
|
|
||||||
|
public WarpEngines(Enterprise enterprise, Output output, Input input)
|
||||||
|
: base("Warp Engines", Command.NAV, output)
|
||||||
|
{
|
||||||
|
_enterprise = enterprise;
|
||||||
|
_output = output;
|
||||||
|
_input = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
|
||||||
|
{
|
||||||
|
if (_input.TryGetCourse("Course", " Lt. Sulu", out var course) &&
|
||||||
|
TryGetWarpFactor(out var warpFactor) &&
|
||||||
|
TryGetDistanceToMove(warpFactor, out var distanceToMove))
|
||||||
|
{
|
||||||
|
var result = quadrant.KlingonsMoveAndFire();
|
||||||
|
if (result.IsGameOver) { return result; }
|
||||||
|
|
||||||
|
_enterprise.RepairSystems(warpFactor);
|
||||||
|
_enterprise.VaryConditionOfRandomSystem();
|
||||||
|
var timeElapsed = _enterprise.Move(course, warpFactor, distanceToMove);
|
||||||
|
|
||||||
|
if (_enterprise.IsDocked)
|
||||||
|
{
|
||||||
|
_enterprise.ShieldControl.DropShields();
|
||||||
|
_enterprise.Refuel();
|
||||||
|
_enterprise.PhotonTubes.ReplenishTorpedoes();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandResult.Elapsed(timeElapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandResult.Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetWarpFactor(out float warpFactor)
|
||||||
|
{
|
||||||
|
var maximumWarp = IsDamaged ? 0.2f : 8;
|
||||||
|
if (_input.TryGetNumber("Warp Factor", 0, maximumWarp, out warpFactor))
|
||||||
|
{
|
||||||
|
return warpFactor > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_output.WriteLine(
|
||||||
|
IsDamaged && warpFactor > maximumWarp
|
||||||
|
? "Warp engines are damaged. Maximum speed = warp 0.2"
|
||||||
|
: $" Chief Engineer Scott reports, 'The engines won't take warp {warpFactor} !'");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetDistanceToMove(float warpFactor, out int distanceToTravel)
|
||||||
|
{
|
||||||
|
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} !'");
|
||||||
|
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
84 Super Star Trek/csharp/Utils/DirectionAndDistance.cs
Normal file
65
84 Super Star Trek/csharp/Utils/DirectionAndDistance.cs
Normal file
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user