Merge pull request #97 from drewjcooper/84-super-star-trek-csharp

84 Super Star Trek CSharp
This commit is contained in:
Jeff Atwood
2021-02-27 16:18:31 -08:00
committed by GitHub
34 changed files with 1060 additions and 0 deletions

12
.gitignore vendored
View File

@@ -0,0 +1,12 @@
/01 Acey Ducey/csharp/obj
/47 Hi-Lo/csharp/obj
/.vs
/33 Dice/csharp/obj
/01 Acey Ducey/csharp/.vs/AceyDucey
/01 Acey Ducey/csharp/bin/Debug/netcoreapp3.1
/33 Dice/csharp/bin/Debug/net5.0
/basic-computer-games-dot-net/.vs/basic-computer-games-dot-net/v16
*/csharp/bin
*/csharp/obj

View 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
}
}

View 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;
}
}

View 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");
}
}

View 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();
}
}
}
}

View 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() => "<*>";
}
}

View 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+";
}
}

View File

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

View File

@@ -0,0 +1,7 @@
namespace SuperStarTrek.Objects
{
internal class Starbase
{
public override string ToString() => ">!<";
}
}

View 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}? ");
}
}

View File

@@ -0,0 +1,42 @@
// SUPER STARTREK - MAY 16,1978 - REQUIRES 24K MEMORY
//
// **** **** STAR TREK **** ****
// **** SIMULATION OF A MISSION OF THE STARSHIP ENTERPRISE,
// **** AS SEEN ON THE STAR TREK TV SHOW.
// **** ORIGIONAL PROGRAM BY MIKE MAYFIELD, MODIFIED VERSION
// **** PUBLISHED IN DEC'S "101 BASIC GAMES", BY DAVE AHL.
// **** MODIFICATIONS TO THE LATTER (PLUS DEBUGGING) BY BOB
// **** LEEDOM - APRIL & DECEMBER 1974,
// **** WITH A LITTLE HELP FROM HIS FRIENDS . . .
// **** COMMENTS, EPITHETS, AND SUGGESTIONS SOLICITED --
// **** SEND TO: R. C. LEEDOM
// **** WESTINGHOUSE DEFENSE & ELECTRONICS SYSTEMS CNTR.
// **** BOX 746, M.S. 338
// **** BALTIMORE, MD 21203
// ****
// **** CONVERTED TO MICROSOFT 8 K BASIC 3/16/78 BY JOHN GORDERS
// **** LINE NUMBERS FROM VERSION STREK7 OF 1/12/75 PRESERVED AS
// **** MUCH AS POSSIBLE WHILE USING MULTIPLE STATEMENTS PER LINE
// **** SOME LINES ARE LONGER THAN 72 CHARACTERS; THIS WAS DONE
// **** BY USING "?" INSTEAD OF "PRINT" WHEN ENTERING LINES
// ****
// **** CONVERTED TO MICROSOFT C# 2/20/21 BY ANDREW COOPER
// ****
namespace SuperStarTrek
{
internal class Program
{
static void Main()
{
var game = new Game();
game.DoIntroduction();
do
{
game.Play();
} while (game.Replay());
}
}
}

View 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;
}
}

View File

@@ -0,0 +1 @@
COMBAT AREA CONDITION RED

View File

@@ -0,0 +1,24 @@
,------*------,
,------------- '--- ------'
'-------- --' / /
,---' '-------/ /--,
'----------------'
THE USS ENTERPRISE --- NCC-1701

View File

@@ -0,0 +1,106 @@
INSTRUCTIONS FOR 'SUPER STAR TREK'
1. When you see "Command ?" printed, enter one of the legal
commands (NAV, SRS, LRS, PHA, TOR, SHE, DAM, COM, OR XXX).
2. If you should type in an illegal command, you'll get a short
list of the legal commands printed out.
3. Some commands require you to enter data (for example, the
'NAV' command comes back with 'Course (1-9) ?'.) If you
type in illegal data (like negative numbers), then command
will be aborted.
The galaxy is divided into an 8 X 8 quadrant grid,
and each quadrant is further divided into an 8 X 8 sector grid.
You will be assigned a starting point somewhere in the
galaxy to begin a tour of duty as commander of the starship
Enterprise; your mission: to seek and destroy the fleet of
Klingon warships which are menacing the United Federation of
Planets.
You have the following commands available to you as captain
of the starship Enterprise:
NAV command = Warp Engine Control
Course is in a circular numerical 4 3 2
vector arrangement as shown . . .
integer and real values may be ...
used. (Thus course 1.5 is half- 5 ---*--- 1
way between 1 and 2. ...
. . .
Values may approach 9.0, which 6 7 8
itself is equivalent to 1.0
COURSE
One warp factor is the size of
one quadrant. Therefore, to get
from quadrant 6,5 to 5,5, you WOULD
use course 3, warp factor 1.
SRS command = Short Range Sensor Scan
Shows you a scan of your present quadrant.
Symbology on your sensor screen is as follows:
<*> = Your starship's position
+K+ = Klingon battle cruiser
>!< = Federation starbase (refuel/repair/re-arm here!)
* = Star
A condensed 'status report' will also be presented.
LRS command = Long Range Sensor Scan
Shows conditions in space for one quadrant on each side
of the Enterprise (which is in the middle of the 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
Klingons.
Example - 207 = 2 Klingons, No starbases, & 7 stars.
PHA command = Phaser Control
Allows you to destroy the Klingon battle cruisers by
zapping them with suitably large units of energy to
deplete their shield power. (Remember, Klingons have
phasers, too!)
TOR command = Photon Torpedo Control
Torpedo course is the same as used in warp engine control.
If you hit the Klingon vessel, he is destroyed and
cannot fire back at you. If you miss, you are subject to
his phaser fire. In either case, you are also subject to
the phaser fire of all other Klingons in the quadrant.
The library-computer (COM command) has an option to
compute torpedo trajectory for you (Option 2).
SHE command = Shield Control
Defines the number of energy units to be assigned to the
shields. Energy is taken from total ship's energy. Note
that the status display total energy includes shield energy.
DAM command = Damage Control Report
Gives the state of repair of all devices. Where a negative
'state of repair' shows that the device is temporarily
damaged.
COM command = Library-Computer
The library-computer contains six options:
Option 0 = Cumulative Galactic Record
This option shows computer memory of the results of all
previous short and long range sensor scans.
Option 1 = Status Report
This option shows the number of Klingons, Stardates,
and starbases remaining in the game.
Option 2 = Photon Torpedo Data
Which gives directions and distance from the Enterprise
to all Klingons in your quadrant.
Option 3 = Starbase Nav Data
This option gives direction and distance to any
starbase within your quadrant.
Option 4 = Direction/Distance Calculator
This option allows you to enter coordinates for
direction/distance calculations.
Option 5 = Galactic Region Name Map
This option prints the names of the sixteen major
galactic regions referred to in the game.

View File

@@ -0,0 +1 @@
SHIELDS DANGEROUSLY LOW

View 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.

View 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'

View File

@@ -0,0 +1 @@
Shields dropped for docking purposes

View File

@@ -0,0 +1 @@
*** Short Range Sensors are out ***

View File

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

View File

@@ -0,0 +1,31 @@
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace SuperStarTrek.Resources
{
internal static class Strings
{
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 = "")
{
var streamName = $"SuperStarTrek.Resources.{name}.txt";
using var stream = Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(streamName);
using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}
}
}

View File

@@ -0,0 +1,27 @@
*************************************
* *
* *
* * * SUPER STAR TREK * * *
* *
* *
*************************************

View 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}";
}
}

View 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; }
}
}

View 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);
}
}

View 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() ?? " "));
}
}

View 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;
}
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\*.txt" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,34 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperStarTrek", "SuperStarTrek.csproj", "{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|x64.ActiveCfg = Debug|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|x64.Build.0 = Debug|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|x86.ActiveCfg = Debug|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|x86.Build.0 = Debug|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|Any CPU.Build.0 = Release|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|x64.ActiveCfg = Release|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|x64.Build.0 = Release|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|x86.ActiveCfg = Release|Any CPU
{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View 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;
}
}
}

View 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}";
}
}
}

View 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);
}
}