mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-28 21:54:17 -08:00
Main game loop with Short Range Scan and Shield Control
This commit is contained in:
34
84 Super Star Trek/csharp/Command.cs
Normal file
34
84 Super Star Trek/csharp/Command.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace SuperStarTrek
|
||||
{
|
||||
internal enum Command
|
||||
{
|
||||
[Description("To set course")]
|
||||
NAV,
|
||||
|
||||
[Description("For short range sensor scan")]
|
||||
SRS,
|
||||
|
||||
[Description("For long range sensor scan")]
|
||||
LRS,
|
||||
|
||||
[Description("To fire phasers")]
|
||||
PHA,
|
||||
|
||||
[Description("To fire photon torpedoes")]
|
||||
TOR,
|
||||
|
||||
[Description("To raise or lower shields")]
|
||||
SHE,
|
||||
|
||||
[Description("For damage control reports")]
|
||||
DAM,
|
||||
|
||||
[Description("To call on library-computer")]
|
||||
COM,
|
||||
|
||||
[Description("To resign your command")]
|
||||
XXX
|
||||
}
|
||||
}
|
||||
14
84 Super Star Trek/csharp/CommandExtensions.cs
Normal file
14
84 Super Star Trek/csharp/CommandExtensions.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using System.Reflection;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace SuperStarTrek
|
||||
{
|
||||
internal static class CommandExtensions
|
||||
{
|
||||
internal static string GetDescription(this Command command) =>
|
||||
typeof(Command)
|
||||
.GetField(command.ToString())
|
||||
.GetCustomAttribute<DescriptionAttribute>()
|
||||
.Description;
|
||||
}
|
||||
}
|
||||
97
84 Super Star Trek/csharp/Game.cs
Normal file
97
84 Super Star Trek/csharp/Game.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using SuperStarTrek.Objects;
|
||||
using SuperStarTrek.Resources;
|
||||
using SuperStarTrek.Space;
|
||||
using SuperStarTrek.Systems;
|
||||
using static System.StringComparison;
|
||||
|
||||
namespace SuperStarTrek
|
||||
{
|
||||
internal class Game
|
||||
{
|
||||
private readonly Output _output;
|
||||
private readonly Input _input;
|
||||
|
||||
private int _initialStardate;
|
||||
private int _finalStarDate;
|
||||
private double _currentStardate;
|
||||
private Coordinates _currentQuadrant;
|
||||
private Coordinates _currentSector;
|
||||
private Galaxy _galaxy;
|
||||
private int _initialKlingonCount;
|
||||
private Enterprise _enterprise;
|
||||
|
||||
public Game()
|
||||
{
|
||||
_output = new Output();
|
||||
_input = new Input(_output);
|
||||
}
|
||||
|
||||
public double Stardate => _currentStardate;
|
||||
|
||||
public void DoIntroduction()
|
||||
{
|
||||
_output.Write(Strings.Title);
|
||||
|
||||
_output.Write("Do you need instructions (Y/N)? ");
|
||||
var response = Console.ReadLine();
|
||||
_output.WriteLine();
|
||||
|
||||
if (!response.Equals("N", InvariantCultureIgnoreCase))
|
||||
{
|
||||
_output.Write(Strings.Instructions);
|
||||
|
||||
_input.WaitForAnyKeyButEnter("to continue");
|
||||
}
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
var quadrant = Initialise();
|
||||
var gameOver = false;
|
||||
|
||||
_output.Write(Strings.Enterprise);
|
||||
_output.Write(
|
||||
Strings.Orders,
|
||||
_galaxy.KlingonCount,
|
||||
_finalStarDate,
|
||||
_finalStarDate - _initialStardate,
|
||||
_galaxy.StarbaseCount > 1 ? "are" : "is",
|
||||
_galaxy.StarbaseCount,
|
||||
_galaxy.StarbaseCount > 1 ? "s" : "");
|
||||
|
||||
_input.WaitForAnyKeyButEnter("when ready to accept command");
|
||||
|
||||
_enterprise.Enter(quadrant, Strings.StartText);
|
||||
|
||||
while (!gameOver)
|
||||
{
|
||||
var command = _input.GetCommand();
|
||||
|
||||
gameOver = command == Command.XXX || _enterprise.Execute(command);
|
||||
}
|
||||
}
|
||||
|
||||
private Quadrant Initialise()
|
||||
{
|
||||
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");
|
||||
}
|
||||
}
|
||||
76
84 Super Star Trek/csharp/Input.cs
Normal file
76
84 Super Star Trek/csharp/Input.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using static System.StringComparison;
|
||||
|
||||
namespace SuperStarTrek
|
||||
{
|
||||
internal class Input
|
||||
{
|
||||
private readonly Output _output;
|
||||
|
||||
public Input(Output output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
public void WaitForAnyKeyButEnter(string prompt)
|
||||
{
|
||||
_output.Write($"Hit any key but Enter {prompt} ");
|
||||
while (Console.ReadKey(intercept: true).Key == ConsoleKey.Enter);
|
||||
}
|
||||
|
||||
public string GetString(string prompt)
|
||||
{
|
||||
_output.Prompt(prompt);
|
||||
return Console.ReadLine();
|
||||
}
|
||||
|
||||
public double GetNumber(string prompt)
|
||||
{
|
||||
_output.Prompt(prompt);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var response = Console.ReadLine();
|
||||
if (double.TryParse(response, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
_output.WriteLine("!Number expected - retry input line");
|
||||
_output.Prompt();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetNumber(string prompt, double minValue, double maxValue, out double value)
|
||||
{
|
||||
value = GetNumber($"{prompt} ({minValue}-{maxValue})");
|
||||
|
||||
return value >= minValue && value <= maxValue;
|
||||
}
|
||||
|
||||
internal bool GetString(string replayPrompt, string trueValue)
|
||||
=> GetString(replayPrompt).Equals(trueValue, InvariantCultureIgnoreCase);
|
||||
|
||||
public Command GetCommand()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
var response = GetString("Command");
|
||||
|
||||
if (response != "" &&
|
||||
Enum.TryParse(response.Substring(0, 3), ignoreCase: true, out Command parsedCommand))
|
||||
{
|
||||
return parsedCommand;
|
||||
}
|
||||
|
||||
_output.WriteLine("Enter one of the following:");
|
||||
foreach (var command in Enum.GetValues(typeof(Command)).OfType<Command>())
|
||||
{
|
||||
_output.WriteLine($" {command} ({command.GetDescription()})");
|
||||
}
|
||||
_output.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
103
84 Super Star Trek/csharp/Objects/Enterprise.cs
Normal file
103
84 Super Star Trek/csharp/Objects/Enterprise.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using SuperStarTrek.Resources;
|
||||
using SuperStarTrek.Space;
|
||||
using SuperStarTrek.Systems;
|
||||
|
||||
namespace SuperStarTrek.Objects
|
||||
{
|
||||
internal class Enterprise
|
||||
{
|
||||
private readonly int _maxEnergy;
|
||||
private readonly List<Subsystem> _systems;
|
||||
private readonly Dictionary<Command, Subsystem> _commandExecutors;
|
||||
private Quadrant _quadrant;
|
||||
private ShieldControl _shieldControl;
|
||||
|
||||
public Enterprise(int maxEnergy, Coordinates sector)
|
||||
{
|
||||
Sector = sector;
|
||||
TotalEnergy = _maxEnergy = maxEnergy;
|
||||
|
||||
_systems = new List<Subsystem>();
|
||||
_commandExecutors = new Dictionary<Command, Subsystem>();
|
||||
}
|
||||
|
||||
public Coordinates Quadrant => _quadrant.Coordinates;
|
||||
public Coordinates Sector { get; }
|
||||
public string Condition => GetCondition();
|
||||
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 Enterprise Add(Subsystem system)
|
||||
{
|
||||
_systems.Add(system);
|
||||
_commandExecutors[system.Command] = system;
|
||||
|
||||
if (system is ShieldControl shieldControl) { _shieldControl = shieldControl; }
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public string GetDamageReport()
|
||||
{
|
||||
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;
|
||||
|
||||
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() =>
|
||||
(_quadrant.HasKlingons, Energy / _maxEnergy) switch
|
||||
{
|
||||
(true, _) => "*Red*",
|
||||
(_, < 0.1) => "Yellow",
|
||||
_ => "Green"
|
||||
};
|
||||
|
||||
public bool Execute(Command command)
|
||||
{
|
||||
_commandExecutors[command].ExecuteCommand(_quadrant);
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool Recognises(string command)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal string GetCommandList()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override string ToString() => "<*>";
|
||||
}
|
||||
}
|
||||
14
84 Super Star Trek/csharp/Objects/Klingon.cs
Normal file
14
84 Super Star Trek/csharp/Objects/Klingon.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace SuperStarTrek.Objects
|
||||
{
|
||||
internal class Klingon
|
||||
{
|
||||
private double _energy;
|
||||
|
||||
public Klingon()
|
||||
{
|
||||
_energy = new Random().GetDouble(100, 300);
|
||||
}
|
||||
|
||||
public override string ToString() => "+K+";
|
||||
}
|
||||
}
|
||||
7
84 Super Star Trek/csharp/Objects/Star.cs
Normal file
7
84 Super Star Trek/csharp/Objects/Star.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace SuperStarTrek.Objects
|
||||
{
|
||||
internal class Star
|
||||
{
|
||||
public override string ToString() => " * ";
|
||||
}
|
||||
}
|
||||
7
84 Super Star Trek/csharp/Objects/Starbase.cs
Normal file
7
84 Super Star Trek/csharp/Objects/Starbase.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace SuperStarTrek.Objects
|
||||
{
|
||||
internal class Starbase
|
||||
{
|
||||
public override string ToString() => ">!<";
|
||||
}
|
||||
}
|
||||
15
84 Super Star Trek/csharp/Output.cs
Normal file
15
84 Super Star Trek/csharp/Output.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SuperStarTrek
|
||||
{
|
||||
internal class Output
|
||||
{
|
||||
public void Write(string text) => Console.Write(text);
|
||||
public void Write(string format, params object[] args) => Console.Write(format, args);
|
||||
public void WriteLine(string text = "") => Console.WriteLine(text);
|
||||
|
||||
public void NextLine() => Console.WriteLine();
|
||||
|
||||
public void Prompt(string text = "") => Console.Write($"{text}? ");
|
||||
}
|
||||
}
|
||||
@@ -23,31 +23,20 @@
|
||||
// **** CONVERTED TO MICROSOFT C# 2/20/21 BY ANDREW COOPER
|
||||
// ****
|
||||
|
||||
using System;
|
||||
|
||||
using static System.StringComparison;
|
||||
|
||||
namespace SuperStarTrek
|
||||
{
|
||||
class Program
|
||||
internal class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
static void Main()
|
||||
{
|
||||
Console.WriteLine(Strings.Title);
|
||||
var game = new Game();
|
||||
|
||||
Console.Write("Do you need instructions (Y/N)? ");
|
||||
var response = Console.ReadLine();
|
||||
Console.WriteLine();
|
||||
game.DoIntroduction();
|
||||
|
||||
if (!response.Equals("N", InvariantCultureIgnoreCase))
|
||||
do
|
||||
{
|
||||
Console.WriteLine(Strings.Instructions);
|
||||
|
||||
Console.WriteLine("Press <Enter> to continue...");
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
Console.WriteLine(Strings.Enterprise);
|
||||
game.Play();
|
||||
} while (game.Replay());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
25
84 Super Star Trek/csharp/Random.cs
Normal file
25
84 Super Star Trek/csharp/Random.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using SuperStarTrek.Space;
|
||||
|
||||
namespace SuperStarTrek
|
||||
{
|
||||
internal class Random
|
||||
{
|
||||
private static readonly System.Random _random = new();
|
||||
|
||||
public Coordinates GetCoordinate() => new Coordinates(Get1To8Inclusive(), Get1To8Inclusive());
|
||||
|
||||
// Duplicates the algorithm used in the original code to get an integer value from 1 to 8, inclusive:
|
||||
// 475 DEF FNR(R)=INT(RND(R)*7.98+1.01)
|
||||
// Returns a value from 1 to 8, inclusive.
|
||||
// Note there's a slight bias away from the extreme values, 1 and 8.
|
||||
public int Get1To8Inclusive() => (int)(_random.NextDouble() * 7.98 + 1.01);
|
||||
|
||||
public int GetInt(int inclusiveMinValue, int exclusiveMaxValue) =>
|
||||
_random.Next(inclusiveMinValue, exclusiveMaxValue);
|
||||
|
||||
public double GetDouble() => _random.NextDouble();
|
||||
|
||||
public double GetDouble(double inclusiveMinValue, double exclusiveMaxValue)
|
||||
=> _random.NextDouble() * (exclusiveMaxValue - inclusiveMinValue) + inclusiveMinValue;
|
||||
}
|
||||
}
|
||||
1
84 Super Star Trek/csharp/Resources/CombatArea.txt
Normal file
1
84 Super Star Trek/csharp/Resources/CombatArea.txt
Normal file
@@ -0,0 +1 @@
|
||||
COMBAT AREA CONDITION RED
|
||||
@@ -53,7 +53,7 @@ LRS command = Long Range Sensor Scan
|
||||
The scan is coded in the form ###, where the units digit
|
||||
is the number of stars, the tens digit is the number of
|
||||
starbases, and the hundreds digit is the number of
|
||||
Kilngons.
|
||||
Klingons.
|
||||
|
||||
Example - 207 = 2 Klingons, No starbases, & 7 stars.
|
||||
|
||||
@@ -103,3 +103,4 @@ COM command = Library-Computer
|
||||
Option 5 = Galactic Region Name Map
|
||||
This option prints the names of the sixteen major
|
||||
galactic regions referred to in the game.
|
||||
|
||||
|
||||
1
84 Super Star Trek/csharp/Resources/LowShields.txt
Normal file
1
84 Super Star Trek/csharp/Resources/LowShields.txt
Normal file
@@ -0,0 +1 @@
|
||||
SHIELDS DANGEROUSLY LOW
|
||||
6
84 Super Star Trek/csharp/Resources/Orders.txt
Normal file
6
84 Super Star Trek/csharp/Resources/Orders.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
Your orders are as follows:
|
||||
Destroy the {0} Klingon warships which have invaded
|
||||
the galaxy before they can attack federation headquarters
|
||||
on stardate {1}. This gives you {2} days. There {3}
|
||||
{4} starbase{5} in the galaxy for resupplying your ship.
|
||||
|
||||
3
84 Super Star Trek/csharp/Resources/ReplayPrompt.txt
Normal file
3
84 Super Star Trek/csharp/Resources/ReplayPrompt.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The Federation is in need of a new starship commander
|
||||
for a similar mission -- if there is a volunteer
|
||||
let him step forward and enter 'Aye'
|
||||
1
84 Super Star Trek/csharp/Resources/ShieldsDropped.txt
Normal file
1
84 Super Star Trek/csharp/Resources/ShieldsDropped.txt
Normal file
@@ -0,0 +1 @@
|
||||
Shields dropped for docking purposes
|
||||
@@ -0,0 +1 @@
|
||||
*** Short Range Sensors are out ***
|
||||
5
84 Super Star Trek/csharp/Resources/StartText.txt
Normal file
5
84 Super Star Trek/csharp/Resources/StartText.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
Your mission begins with your starship located
|
||||
in the galactic quadrant, '{0}'.
|
||||
|
||||
@@ -2,15 +2,20 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using static System.StringComparison;
|
||||
|
||||
namespace SuperStarTrek
|
||||
namespace SuperStarTrek.Resources
|
||||
{
|
||||
internal static class Strings
|
||||
{
|
||||
public static string Title => GetResource();
|
||||
public static string Instructions => GetResource();
|
||||
public static string CombatArea => GetResource();
|
||||
public static string Enterprise => GetResource();
|
||||
public static string Instructions => GetResource();
|
||||
public static string LowShields => GetResource();
|
||||
public static string Orders => GetResource();
|
||||
public static string ReplayPrompt => GetResource();
|
||||
public static string ShieldsDropped => GetResource();
|
||||
public static string ShortRangeSensorsOut => GetResource();
|
||||
public static string StartText => GetResource();
|
||||
public static string Title => GetResource();
|
||||
|
||||
private static string GetResource([CallerMemberName] string name = "")
|
||||
{
|
||||
27
84 Super Star Trek/csharp/Space/Coordinate.cs
Normal file
27
84 Super Star Trek/csharp/Space/Coordinate.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
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}";
|
||||
}
|
||||
}
|
||||
50
84 Super Star Trek/csharp/Space/Course.cs
Normal file
50
84 Super Star Trek/csharp/Space/Course.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace SuperStarTrek.Space
|
||||
{
|
||||
// Implements the course calculations from the original code:
|
||||
// 530 FORI=1TO9:C(I,1)=0:C(I,2)=0:NEXTI
|
||||
// 540 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1
|
||||
// 600 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1
|
||||
//
|
||||
// 3110 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1))
|
||||
// 3140 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1))
|
||||
internal class Course
|
||||
{
|
||||
private static readonly (int DeltaX, int DeltaY)[] cardinals = new[]
|
||||
{
|
||||
(0, 1),
|
||||
(-1, 1),
|
||||
(-1, 0),
|
||||
(-1, -1),
|
||||
(0, -1),
|
||||
(1, -1),
|
||||
(1, 0),
|
||||
(1, 1),
|
||||
(0, 1)
|
||||
};
|
||||
|
||||
public Course(double direction)
|
||||
{
|
||||
if (direction < 1 || direction > 9)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(direction),
|
||||
direction,
|
||||
"Must be between 1 and 9, inclusive.");
|
||||
}
|
||||
|
||||
var cardinalDirection = (int)(direction - 1) % 8;
|
||||
var fractionalDirection = direction - (int)direction;
|
||||
|
||||
var baseCardinal = cardinals[cardinalDirection];
|
||||
var nextCardinal = cardinals[cardinalDirection + 1];
|
||||
|
||||
DeltaX = baseCardinal.DeltaX + (nextCardinal.DeltaX - baseCardinal.DeltaX) * fractionalDirection;
|
||||
DeltaY = baseCardinal.DeltaY + (nextCardinal.DeltaY - baseCardinal.DeltaY) * fractionalDirection;
|
||||
}
|
||||
|
||||
public double DeltaX { get; }
|
||||
public double DeltaY { get; }
|
||||
}
|
||||
}
|
||||
34
84 Super Star Trek/csharp/Space/Galaxy.cs
Normal file
34
84 Super Star Trek/csharp/Space/Galaxy.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace SuperStarTrek.Space
|
||||
{
|
||||
internal class Galaxy
|
||||
{
|
||||
private readonly QuadrantInfo[][] _quadrants;
|
||||
|
||||
public Galaxy()
|
||||
{
|
||||
var random = new Random();
|
||||
|
||||
_quadrants = Enumerable.Range(1, 8).Select(x =>
|
||||
Enumerable.Range(1, 8).Select(y => QuadrantInfo.Create(new Coordinates(x, y), "")).ToArray())
|
||||
.ToArray();
|
||||
|
||||
if (StarbaseCount == 0)
|
||||
{
|
||||
var randomQuadrant = this[random.GetCoordinate()];
|
||||
randomQuadrant.AddStarbase();
|
||||
|
||||
if (randomQuadrant.KlingonCount < 2)
|
||||
{
|
||||
randomQuadrant.AddKlingon();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public QuadrantInfo this[Coordinates coordinate] => _quadrants[coordinate.X - 1][coordinate.Y - 1];
|
||||
|
||||
public int KlingonCount => _quadrants.SelectMany(q => q).Sum(q => q.KlingonCount);
|
||||
public int StarbaseCount => _quadrants.SelectMany(q => q).Count(q => q.HasStarbase);
|
||||
}
|
||||
}
|
||||
79
84 Super Star Trek/csharp/Space/Quadrant.cs
Normal file
79
84 Super Star Trek/csharp/Space/Quadrant.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SuperStarTrek.Objects;
|
||||
|
||||
namespace SuperStarTrek.Space
|
||||
{
|
||||
internal class Quadrant
|
||||
{
|
||||
private readonly QuadrantInfo _info;
|
||||
private readonly Random _random;
|
||||
private readonly Dictionary<Coordinates, object> _sectors;
|
||||
private readonly Coordinates _enterpriseSector;
|
||||
private readonly Coordinates _starbaseSector;
|
||||
|
||||
public Quadrant(QuadrantInfo info, Enterprise enterprise)
|
||||
{
|
||||
_info = info;
|
||||
_random = new Random();
|
||||
|
||||
_enterpriseSector = enterprise.Sector;
|
||||
_sectors = new Dictionary<Coordinates, object> { [enterprise.Sector] = enterprise };
|
||||
PositionObject(() => new Klingon(), _info.KlingonCount);
|
||||
if (_info.HasStarbase)
|
||||
{
|
||||
_starbaseSector = PositionObject(() => new Starbase());
|
||||
}
|
||||
PositionObject(() => new Star(), _info.StarCount);
|
||||
}
|
||||
|
||||
public Coordinates Coordinates => _info.Coordinates;
|
||||
public bool HasKlingons => _info.KlingonCount > 0;
|
||||
public bool HasStarbase => _info.HasStarbase;
|
||||
public bool EnterpriseIsNextToStarbase =>
|
||||
_info.HasStarbase &&
|
||||
Math.Abs(_enterpriseSector.X - _starbaseSector.X) <= 1 &&
|
||||
Math.Abs(_enterpriseSector.Y - _starbaseSector.Y) <= 1;
|
||||
|
||||
public override string ToString() => _info.Name;
|
||||
|
||||
private Coordinates PositionObject(Func<object> objectFactory)
|
||||
{
|
||||
var sector = GetRandomEmptySector();
|
||||
_sectors[sector] = objectFactory.Invoke();
|
||||
return sector;
|
||||
}
|
||||
|
||||
private void PositionObject(Func<object> objectFactory, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
PositionObject(objectFactory);
|
||||
}
|
||||
}
|
||||
|
||||
private Coordinates GetRandomEmptySector()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var sector = _random.GetCoordinate();
|
||||
if (!_sectors.ContainsKey(sector))
|
||||
{
|
||||
return sector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetDisplayLines() => Enumerable.Range(1, 8).Select(x => GetDisplayLine(x));
|
||||
|
||||
private string GetDisplayLine(int x)
|
||||
=> string.Join(
|
||||
" ",
|
||||
Enumerable
|
||||
.Range(1, 8)
|
||||
.Select(y => new Coordinates(x, y))
|
||||
.Select(c => _sectors.GetValueOrDefault(c))
|
||||
.Select(o => o?.ToString() ?? " "));
|
||||
}
|
||||
}
|
||||
40
84 Super Star Trek/csharp/Space/QuadrantInfo.cs
Normal file
40
84 Super Star Trek/csharp/Space/QuadrantInfo.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
namespace SuperStarTrek.Space
|
||||
{
|
||||
internal class QuadrantInfo
|
||||
{
|
||||
private QuadrantInfo(Coordinates coordinates, string name, int klingonCount, int starCount, bool hasStarbase)
|
||||
{
|
||||
Coordinates = coordinates;
|
||||
Name = name;
|
||||
KlingonCount = klingonCount;
|
||||
StarCount = starCount;
|
||||
HasStarbase = hasStarbase;
|
||||
}
|
||||
|
||||
public Coordinates Coordinates { get; }
|
||||
public string Name { get; }
|
||||
public int KlingonCount { get; private set; }
|
||||
public bool HasStarbase { get; private set; }
|
||||
public int StarCount { get; }
|
||||
|
||||
public static QuadrantInfo Create(Coordinates coordinates, string name)
|
||||
{
|
||||
var random = new Random();
|
||||
var klingonCount = random.GetDouble() switch
|
||||
{
|
||||
> 0.98 => 3,
|
||||
> 0.95 => 2,
|
||||
> 0.80 => 1,
|
||||
_ => 0
|
||||
};
|
||||
var hasStarbase = random.GetDouble() > 0.96;
|
||||
var starCount = random.Get1To8Inclusive();
|
||||
|
||||
return new QuadrantInfo(coordinates, name, klingonCount, starCount, hasStarbase);
|
||||
}
|
||||
|
||||
internal void AddKlingon() => KlingonCount += 1;
|
||||
|
||||
internal void AddStarbase() => HasStarbase = true;
|
||||
}
|
||||
}
|
||||
53
84 Super Star Trek/csharp/Systems/ShieldControl.cs
Normal file
53
84 Super Star Trek/csharp/Systems/ShieldControl.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using SuperStarTrek.Objects;
|
||||
using SuperStarTrek.Space;
|
||||
|
||||
namespace SuperStarTrek.Systems
|
||||
{
|
||||
internal class ShieldControl : Subsystem
|
||||
{
|
||||
private readonly Enterprise _enterprise;
|
||||
private readonly Output _output;
|
||||
private readonly Input _input;
|
||||
|
||||
public ShieldControl(Enterprise enterprise, Output output, Input input)
|
||||
: base("Shield Control", Command.SHE)
|
||||
{
|
||||
_enterprise = enterprise;
|
||||
_output = output;
|
||||
_input = input;
|
||||
}
|
||||
|
||||
public double Energy { get; private set; }
|
||||
|
||||
public override void ExecuteCommand(Quadrant quadrant)
|
||||
{
|
||||
if (Condition < 0)
|
||||
{
|
||||
_output.WriteLine("Shield Control inoperable");
|
||||
return;
|
||||
}
|
||||
|
||||
_output.WriteLine($"Energy available = {_enterprise.TotalEnergy}");
|
||||
var requested = _input.GetNumber($"Number of units to shields");
|
||||
|
||||
if (Validate(requested))
|
||||
{
|
||||
Energy = requested;
|
||||
return;
|
||||
}
|
||||
|
||||
_output.WriteLine("<SHIELDS UNCHANGED>");
|
||||
}
|
||||
|
||||
private bool Validate(double requested)
|
||||
{
|
||||
if (requested > _enterprise.TotalEnergy)
|
||||
{
|
||||
_output.WriteLine("Shield Control reports, 'This is not the Federation Treasury.'");
|
||||
return false;
|
||||
}
|
||||
|
||||
return requested >= 0 && requested != Energy;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
84 Super Star Trek/csharp/Systems/ShortRangeSensors.cs
Normal file
59
84 Super Star Trek/csharp/Systems/ShortRangeSensors.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SuperStarTrek.Objects;
|
||||
using SuperStarTrek.Resources;
|
||||
using SuperStarTrek.Space;
|
||||
|
||||
namespace SuperStarTrek.Systems
|
||||
{
|
||||
internal class ShortRangeSensors : Subsystem
|
||||
{
|
||||
private readonly Enterprise _enterprise;
|
||||
private readonly Galaxy _galaxy;
|
||||
private readonly Game _game;
|
||||
private readonly Output _output;
|
||||
|
||||
public ShortRangeSensors(Enterprise enterprise, Galaxy galaxy, Game game, Output output)
|
||||
: base("Short Range Sensors", Command.SRS)
|
||||
{
|
||||
_enterprise = enterprise;
|
||||
_galaxy = galaxy;
|
||||
_game = game;
|
||||
_output = output;
|
||||
}
|
||||
|
||||
public override void ExecuteCommand(Quadrant quadrant)
|
||||
{
|
||||
if (_enterprise.IsDocked)
|
||||
{
|
||||
_output.WriteLine(Strings.ShieldsDropped);
|
||||
}
|
||||
|
||||
if (Condition < 0)
|
||||
{
|
||||
_output.WriteLine(Strings.ShortRangeSensorsOut);
|
||||
}
|
||||
|
||||
_output.WriteLine("---------------------------------");
|
||||
quadrant.GetDisplayLines()
|
||||
.Zip(GetStatusLines(), (sectors, status) => $" {sectors} {status}")
|
||||
.ToList()
|
||||
.ForEach(l => _output.WriteLine(l));
|
||||
_output.WriteLine("---------------------------------");
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetStatusLines()
|
||||
{
|
||||
yield return $"Stardate {_game.Stardate}";
|
||||
yield return $"Condition {_enterprise.Condition}";
|
||||
yield return $"Quadrant {_enterprise.Quadrant}";
|
||||
yield return $"Sector {_enterprise.Sector}";
|
||||
yield return $"Photon torpedoes {_enterprise.TorpedoCount}";
|
||||
yield return $"Total energy {Math.Ceiling(_enterprise.Energy)}";
|
||||
yield return $"Shields {(int)_enterprise.Shields}";
|
||||
yield return $"Klingons remaining {_galaxy.KlingonCount}";
|
||||
}
|
||||
}
|
||||
}
|
||||
20
84 Super Star Trek/csharp/Systems/Subsystem.cs
Normal file
20
84 Super Star Trek/csharp/Systems/Subsystem.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using SuperStarTrek.Space;
|
||||
|
||||
namespace SuperStarTrek.Systems
|
||||
{
|
||||
internal abstract class Subsystem
|
||||
{
|
||||
protected Subsystem(string name, Command command)
|
||||
{
|
||||
Name = name;
|
||||
Command = command;
|
||||
Condition = 0;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public double Condition { get; }
|
||||
public Command Command { get; }
|
||||
|
||||
public abstract void ExecuteCommand(Quadrant quadrant);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user