Split simulation models
This commit is contained in:
17
src/ReactorMaintenance.Simulation/GridPositionExtensions.cs
Normal file
17
src/ReactorMaintenance.Simulation/GridPositionExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public static class GridPositionExtensions
|
||||
{
|
||||
public static IEnumerable<GridPosition> Neighbors(this GridPosition position)
|
||||
{
|
||||
yield return new(position.X, position.Y - 1);
|
||||
yield return new(position.X + 1, position.Y);
|
||||
yield return new(position.X, position.Y + 1);
|
||||
yield return new(position.X - 1, position.Y);
|
||||
}
|
||||
|
||||
public static int ManhattanDistance(this GridPosition position, GridPosition other)
|
||||
{
|
||||
return Math.Abs(position.X - other.X) + Math.Abs(position.Y - other.Y);
|
||||
}
|
||||
}
|
||||
116
src/ReactorMaintenance.Simulation/LevelStateExtensions.cs
Normal file
116
src/ReactorMaintenance.Simulation/LevelStateExtensions.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public static class LevelStateExtensions
|
||||
{
|
||||
public static bool InBounds(this LevelState level, GridPosition position)
|
||||
{
|
||||
return position.X >= 0 && position.Y >= 0 && position.X < level.Width && position.Y < level.Height;
|
||||
}
|
||||
|
||||
public static int Index(this LevelState level, GridPosition position)
|
||||
{
|
||||
if (!level.InBounds(position))
|
||||
throw new ArgumentOutOfRangeException(nameof(position), $"Position {position.X},{position.Y} is outside {level.Width}x{level.Height}.");
|
||||
|
||||
return position.Y * level.Width + position.X;
|
||||
}
|
||||
|
||||
public static ECellTerrain GetTerrain(this LevelState level, GridPosition position)
|
||||
{
|
||||
return level.Terrain[level.Index(position)];
|
||||
}
|
||||
|
||||
public static UndergroundCell GetUnderground(this LevelState level, GridPosition position, ECarrierType carrier)
|
||||
{
|
||||
return level.Layer(carrier)[level.Index(position)];
|
||||
}
|
||||
|
||||
public static SurfaceState GetSurface(this LevelState level, GridPosition position)
|
||||
{
|
||||
return level.Surface[level.Index(position)];
|
||||
}
|
||||
|
||||
public static PropState GetProp(this LevelState level, GridPosition position)
|
||||
{
|
||||
return level.Props[level.Index(position)];
|
||||
}
|
||||
|
||||
public static bool IsFloor(this LevelState level, GridPosition position)
|
||||
{
|
||||
return level.InBounds(position) && level.GetTerrain(position) == ECellTerrain.Floor;
|
||||
}
|
||||
|
||||
public static bool IsClosedDoorEdge(this LevelState level, GridPosition a, GridPosition b)
|
||||
{
|
||||
return level.Doors.Any(door => door.State == EDoorState.Closed && SameEdge(door.A, door.B, a, b));
|
||||
}
|
||||
|
||||
public static LevelState SetTerrain(this LevelState level, GridPosition position, ECellTerrain terrain)
|
||||
{
|
||||
var next = level.Terrain.ToArray();
|
||||
next[level.Index(position)] = terrain;
|
||||
var updated = level with { Terrain = next };
|
||||
return terrain == ECellTerrain.Wall ? updated.ClearFloorOnlyState(position) : updated;
|
||||
}
|
||||
|
||||
public static LevelState SetUnderground(this LevelState level, GridPosition position, ECarrierType carrier, UndergroundCell cell)
|
||||
{
|
||||
var next = level.Layer(carrier).ToArray();
|
||||
next[level.Index(position)] = cell;
|
||||
return carrier switch {
|
||||
ECarrierType.Fuel => level with { Fuel = next },
|
||||
ECarrierType.Coolant => level with { Coolant = next },
|
||||
ECarrierType.Electricity => level with { Electricity = next },
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
|
||||
};
|
||||
}
|
||||
|
||||
public static LevelState SetSurface(this LevelState level, GridPosition position, SurfaceState surface)
|
||||
{
|
||||
var next = level.Surface.ToArray();
|
||||
next[level.Index(position)] = surface.Clamp();
|
||||
return level with { Surface = next };
|
||||
}
|
||||
|
||||
public static LevelState SetProp(this LevelState level, GridPosition position, PropState prop)
|
||||
{
|
||||
var next = level.Props.ToArray();
|
||||
next[level.Index(position)] = prop;
|
||||
return level with { Props = next };
|
||||
}
|
||||
|
||||
public static LevelState WithRuntimeArrays(this LevelState level, UndergroundCell[] fuel, UndergroundCell[] coolant, UndergroundCell[] electricity, SurfaceState[] surface, PropState[] props)
|
||||
{
|
||||
return level with {
|
||||
Fuel = fuel,
|
||||
Coolant = coolant,
|
||||
Electricity = electricity,
|
||||
Surface = surface,
|
||||
Props = props
|
||||
};
|
||||
}
|
||||
|
||||
public static IReadOnlyList<UndergroundCell> Layer(this LevelState level, ECarrierType carrier)
|
||||
{
|
||||
return carrier switch {
|
||||
ECarrierType.Fuel => level.Fuel,
|
||||
ECarrierType.Coolant => level.Coolant,
|
||||
ECarrierType.Electricity => level.Electricity,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
|
||||
};
|
||||
}
|
||||
|
||||
private static LevelState ClearFloorOnlyState(this LevelState level, GridPosition position)
|
||||
{
|
||||
return level.SetSurface(position, new())
|
||||
.SetProp(position, new())
|
||||
.SetUnderground(position, ECarrierType.Fuel, new())
|
||||
.SetUnderground(position, ECarrierType.Coolant, new())
|
||||
.SetUnderground(position, ECarrierType.Electricity, new());
|
||||
}
|
||||
|
||||
private static bool SameEdge(GridPosition edgeA, GridPosition edgeB, GridPosition a, GridPosition b)
|
||||
{
|
||||
return edgeA == a && edgeB == b || edgeA == b && edgeB == a;
|
||||
}
|
||||
}
|
||||
54
src/ReactorMaintenance.Simulation/LevelStateFactory.cs
Normal file
54
src/ReactorMaintenance.Simulation/LevelStateFactory.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public static class LevelStateFactory
|
||||
{
|
||||
public static LevelState Create(string name, int width, int height)
|
||||
{
|
||||
if (width < Balancing.Current.MinimumLevelSize || height < Balancing.Current.MinimumLevelSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(width), $"Levels must be at least {Balancing.Current.MinimumLevelSize}x{Balancing.Current.MinimumLevelSize}.");
|
||||
|
||||
return new() {
|
||||
Name = name,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Terrain = CreateTerrain(width, height),
|
||||
Fuel = CreateUnderground(width, height),
|
||||
Coolant = CreateUnderground(width, height),
|
||||
Electricity = CreateUnderground(width, height),
|
||||
Surface = CreateSurface(width, height),
|
||||
Props = CreateProps(width, height),
|
||||
Robot = new() { Position = new(1, 1) },
|
||||
Forecasts = Array.Empty<Forecast>()
|
||||
};
|
||||
}
|
||||
|
||||
public static ECellTerrain[] CreateTerrain(int width, int height)
|
||||
{
|
||||
var terrain = Enumerable.Repeat(ECellTerrain.Floor, width * height).ToArray();
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
if (x == 0 || y == 0 || x == width - 1 || y == height - 1)
|
||||
terrain[y * width + x] = ECellTerrain.Wall;
|
||||
}
|
||||
}
|
||||
|
||||
return terrain;
|
||||
}
|
||||
|
||||
public static UndergroundCell[] CreateUnderground(int width, int height)
|
||||
{
|
||||
return Enumerable.Range(0, width * height).Select(_ => new UndergroundCell()).ToArray();
|
||||
}
|
||||
|
||||
public static SurfaceState[] CreateSurface(int width, int height)
|
||||
{
|
||||
return Enumerable.Range(0, width * height).Select(_ => new SurfaceState()).ToArray();
|
||||
}
|
||||
|
||||
public static PropState[] CreateProps(int width, int height)
|
||||
{
|
||||
return Enumerable.Range(0, width * height).Select(_ => new PropState()).ToArray();
|
||||
}
|
||||
}
|
||||
@@ -1,558 +0,0 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ECellTerrain
|
||||
{
|
||||
Floor,
|
||||
Wall
|
||||
}
|
||||
|
||||
public enum ECarrierType
|
||||
{
|
||||
Fuel,
|
||||
Coolant,
|
||||
Electricity
|
||||
}
|
||||
|
||||
public enum EUndergroundState
|
||||
{
|
||||
Absent,
|
||||
Intact,
|
||||
Leaking
|
||||
}
|
||||
|
||||
public enum EPropType
|
||||
{
|
||||
None,
|
||||
Flow,
|
||||
Consumer,
|
||||
TJunction,
|
||||
CrossJunction,
|
||||
Door,
|
||||
AllSeeingEyeTerminal,
|
||||
RemedySupply,
|
||||
ReactorControl
|
||||
}
|
||||
|
||||
public enum EPropSwitchState
|
||||
{
|
||||
Disabled,
|
||||
Enabled
|
||||
}
|
||||
|
||||
public enum EConsumerServiceState
|
||||
{
|
||||
Unknown,
|
||||
Disabled,
|
||||
Starved,
|
||||
Supplied,
|
||||
Producing
|
||||
}
|
||||
|
||||
public enum ETJunctionMode
|
||||
{
|
||||
ZeroFour,
|
||||
OneThree,
|
||||
TwoTwo,
|
||||
ThreeOne,
|
||||
FourZero
|
||||
}
|
||||
|
||||
public enum ECrossJunctionMode
|
||||
{
|
||||
ZeroThreeThree,
|
||||
ThreeZeroThree,
|
||||
ThreeThreeZero,
|
||||
TwoTwoTwo
|
||||
}
|
||||
|
||||
public enum EDoorState
|
||||
{
|
||||
Open,
|
||||
Closed
|
||||
}
|
||||
|
||||
public enum ERemedyType
|
||||
{
|
||||
FuelNeutralizer,
|
||||
CoolantNeutralizer,
|
||||
ElectricityNeutralizer,
|
||||
HeatShield
|
||||
}
|
||||
|
||||
public enum ELevelState
|
||||
{
|
||||
Stable,
|
||||
Caution,
|
||||
Critical,
|
||||
Ready,
|
||||
Lost,
|
||||
Won
|
||||
}
|
||||
|
||||
public enum EForecastKind
|
||||
{
|
||||
TerminalLoss,
|
||||
ReactorReady,
|
||||
ConsumerStarved,
|
||||
HazardGrowth,
|
||||
RuleEvent
|
||||
}
|
||||
|
||||
public enum ERuleEventPhase
|
||||
{
|
||||
StartOfSimulation,
|
||||
EndOfTurn
|
||||
}
|
||||
|
||||
public enum ERulePredicateKind
|
||||
{
|
||||
TurnAtLeast,
|
||||
LevelStateIs,
|
||||
ReactorReadyIs,
|
||||
ReactorLostIs,
|
||||
ReactorWonIs,
|
||||
PropStateAt,
|
||||
ConsumerStateAt,
|
||||
NetworkBandAt,
|
||||
SurfaceBandAt,
|
||||
RobotAt,
|
||||
RobotInventoryAtLeast,
|
||||
AllSeeingEyeUnlocked
|
||||
}
|
||||
|
||||
public enum ERuleEffectKind
|
||||
{
|
||||
StartLeak,
|
||||
WorsenLeak,
|
||||
RepairNetworkCell,
|
||||
DisableNetworkCell,
|
||||
SetPropEnabled,
|
||||
AddSurfaceHazard,
|
||||
RemoveSurfaceHazard,
|
||||
AddHeat,
|
||||
RemoveHeat,
|
||||
AddInventory,
|
||||
RemoveInventory,
|
||||
MarkTerminalLoss,
|
||||
EmitWarning
|
||||
}
|
||||
|
||||
public enum ENetworkValueKind
|
||||
{
|
||||
Amount,
|
||||
Intensity
|
||||
}
|
||||
|
||||
public enum EBand
|
||||
{
|
||||
Safe,
|
||||
Caution,
|
||||
Critical
|
||||
}
|
||||
|
||||
public enum EPairEffect
|
||||
{
|
||||
Hold,
|
||||
FuelFlow,
|
||||
CoolFlow,
|
||||
ChargeFlow,
|
||||
HeatFlow,
|
||||
HeatFlow2,
|
||||
Warm1,
|
||||
Warm2,
|
||||
Quench1,
|
||||
Quench2,
|
||||
Short1,
|
||||
Short2,
|
||||
Ignite1,
|
||||
Ignite2
|
||||
}
|
||||
|
||||
public sealed record GridPosition(int X, int Y)
|
||||
{
|
||||
public IEnumerable<GridPosition> Neighbors()
|
||||
{
|
||||
yield return new(X, Y - 1);
|
||||
yield return new(X + 1, Y);
|
||||
yield return new(X, Y + 1);
|
||||
yield return new(X - 1, Y);
|
||||
}
|
||||
|
||||
public int ManhattanDistance(GridPosition other)
|
||||
{
|
||||
return Math.Abs(X - other.X) + Math.Abs(Y - other.Y);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record UndergroundCell
|
||||
{
|
||||
public EUndergroundState State { get; init; }
|
||||
public float Amount { get; init; }
|
||||
public float Intensity { get; init; }
|
||||
|
||||
public bool IsPresent => State != EUndergroundState.Absent;
|
||||
public bool CarriesFlow => State is EUndergroundState.Intact or EUndergroundState.Leaking;
|
||||
}
|
||||
|
||||
public sealed record SurfaceState
|
||||
{
|
||||
public float Fuel { get; init; }
|
||||
public float Coolant { get; init; }
|
||||
public float Electricity { get; init; }
|
||||
public float Heat { get; init; }
|
||||
public int FuelBlockTurns { get; init; }
|
||||
public int CoolantBlockTurns { get; init; }
|
||||
public int ElectricityBlockTurns { get; init; }
|
||||
|
||||
public SurfaceState Clamp()
|
||||
{
|
||||
var balancing = Balancing.Current;
|
||||
return this with {
|
||||
Fuel = balancing.ClampValue(Fuel),
|
||||
Coolant = balancing.ClampValue(Coolant),
|
||||
Electricity = balancing.ClampValue(Electricity),
|
||||
Heat = balancing.ClampValue(Heat),
|
||||
FuelBlockTurns = Math.Max(0, FuelBlockTurns),
|
||||
CoolantBlockTurns = Math.Max(0, CoolantBlockTurns),
|
||||
ElectricityBlockTurns = Math.Max(0, ElectricityBlockTurns)
|
||||
};
|
||||
}
|
||||
|
||||
public bool Blocks(ECarrierType carrier)
|
||||
{
|
||||
return carrier switch {
|
||||
ECarrierType.Fuel => FuelBlockTurns > 0,
|
||||
ECarrierType.Coolant => CoolantBlockTurns > 0,
|
||||
ECarrierType.Electricity => ElectricityBlockTurns > 0,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record PropState
|
||||
{
|
||||
public EPropType Type { get; init; }
|
||||
public ECarrierType Carrier { get; init; }
|
||||
public EPropSwitchState SwitchState { get; init; } = EPropSwitchState.Enabled;
|
||||
public EConsumerServiceState ServiceState { get; init; } = EConsumerServiceState.Unknown;
|
||||
public ETJunctionMode TJunctionMode { get; init; } = ETJunctionMode.TwoTwo;
|
||||
public ECrossJunctionMode CrossJunctionMode { get; init; } = ECrossJunctionMode.TwoTwoTwo;
|
||||
public ERemedyType RemedyType { get; init; }
|
||||
public bool Depleted { get; init; }
|
||||
public int ReactorId { get; init; }
|
||||
|
||||
public bool IsEnabled => SwitchState == EPropSwitchState.Enabled;
|
||||
}
|
||||
|
||||
public sealed record DoorState
|
||||
{
|
||||
public GridPosition A { get; init; } = new(0, 0);
|
||||
public GridPosition B { get; init; } = new(0, 0);
|
||||
public EDoorState State { get; init; } = EDoorState.Closed;
|
||||
}
|
||||
|
||||
public sealed record LeakState
|
||||
{
|
||||
public ECarrierType Carrier { get; init; }
|
||||
public GridPosition UndergroundPosition { get; init; } = new(0, 0);
|
||||
public GridPosition AccessPosition { get; init; } = new(0, 0);
|
||||
public bool Repaired { get; init; }
|
||||
}
|
||||
|
||||
public sealed record ReactorBinding
|
||||
{
|
||||
public int ReactorId { get; init; }
|
||||
public GridPosition ControlPosition { get; init; } = new(0, 0);
|
||||
public GridPosition FuelConsumerPosition { get; init; } = new(0, 0);
|
||||
public GridPosition CoolantConsumerPosition { get; init; } = new(0, 0);
|
||||
public GridPosition ElectricityConsumerPosition { get; init; } = new(0, 0);
|
||||
public bool Ready { get; init; }
|
||||
public bool Activated { get; init; }
|
||||
}
|
||||
|
||||
public sealed record RobotState
|
||||
{
|
||||
public GridPosition Position { get; init; } = new(1, 1);
|
||||
public int FuelNeutralizers { get; init; }
|
||||
public int CoolantNeutralizers { get; init; }
|
||||
public int ElectricityNeutralizers { get; init; }
|
||||
public int HeatShields { get; init; }
|
||||
public int HeatImmunitySteps { get; init; }
|
||||
|
||||
public int Count(ERemedyType remedy)
|
||||
{
|
||||
return remedy switch {
|
||||
ERemedyType.FuelNeutralizer => FuelNeutralizers,
|
||||
ERemedyType.CoolantNeutralizer => CoolantNeutralizers,
|
||||
ERemedyType.ElectricityNeutralizer => ElectricityNeutralizers,
|
||||
ERemedyType.HeatShield => HeatShields,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(remedy), remedy, "Unsupported remedy.")
|
||||
};
|
||||
}
|
||||
|
||||
public RobotState Add(ERemedyType remedy, int amount)
|
||||
{
|
||||
return remedy switch {
|
||||
ERemedyType.FuelNeutralizer => this with { FuelNeutralizers = ClampInventory(FuelNeutralizers + amount) },
|
||||
ERemedyType.CoolantNeutralizer => this with { CoolantNeutralizers = ClampInventory(CoolantNeutralizers + amount) },
|
||||
ERemedyType.ElectricityNeutralizer => this with { ElectricityNeutralizers = ClampInventory(ElectricityNeutralizers + amount) },
|
||||
ERemedyType.HeatShield => this with { HeatShields = ClampInventory(HeatShields + amount) },
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(remedy), remedy, "Unsupported remedy.")
|
||||
};
|
||||
}
|
||||
|
||||
public RobotState Spend(ERemedyType remedy)
|
||||
{
|
||||
return Count(remedy) <= 0 ? this : Add(remedy, -1);
|
||||
}
|
||||
|
||||
private static int ClampInventory(int value)
|
||||
{
|
||||
return Math.Clamp(value, 0, Balancing.Current.InventoryCapacityPerRemedy);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record RulePredicate
|
||||
{
|
||||
public ERulePredicateKind Kind { get; init; }
|
||||
public GridPosition Position { get; init; } = new(0, 0);
|
||||
public int ReactorId { get; init; }
|
||||
public int Turn { get; init; }
|
||||
public ELevelState LevelState { get; init; }
|
||||
public EPropSwitchState PropSwitchState { get; init; }
|
||||
public EConsumerServiceState ConsumerServiceState { get; init; }
|
||||
public ECarrierType Carrier { get; init; }
|
||||
public ENetworkValueKind NetworkValue { get; init; }
|
||||
public ERemedyType Remedy { get; init; }
|
||||
public EBand Band { get; init; }
|
||||
public int InventoryCount { get; init; }
|
||||
public bool BoolValue { get; init; }
|
||||
}
|
||||
|
||||
public sealed record RuleEffect
|
||||
{
|
||||
public ERuleEffectKind Kind { get; init; }
|
||||
public GridPosition Position { get; init; } = new(0, 0);
|
||||
public GridPosition? AccessPosition { get; init; }
|
||||
public ECarrierType Carrier { get; init; }
|
||||
public ERemedyType Remedy { get; init; }
|
||||
public float Amount { get; init; }
|
||||
public EPropSwitchState PropSwitchState { get; init; }
|
||||
public string Message { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed record RuleEventState
|
||||
{
|
||||
public string Id { get; init; } = string.Empty;
|
||||
public bool Enabled { get; init; } = true;
|
||||
public bool Repeat { get; init; }
|
||||
public bool Triggered { get; init; }
|
||||
public int Priority { get; init; }
|
||||
public ERuleEventPhase Phase { get; init; }
|
||||
public IReadOnlyList<RulePredicate> Predicates { get; init; } = Array.Empty<RulePredicate>();
|
||||
public IReadOnlyList<RuleEffect> Effects { get; init; } = Array.Empty<RuleEffect>();
|
||||
public string ForecastText { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed record Forecast(EForecastKind Kind, GridPosition? Position, int Turns, string Message);
|
||||
public sealed record ValidationIssue(string Message, GridPosition? Position = null);
|
||||
|
||||
public sealed record ValidationReport
|
||||
{
|
||||
public IReadOnlyList<ValidationIssue> Errors { get; init; } = Array.Empty<ValidationIssue>();
|
||||
public IReadOnlyList<ValidationIssue> Warnings { get; init; } = Array.Empty<ValidationIssue>();
|
||||
public bool IsValid => Errors.Count == 0;
|
||||
}
|
||||
|
||||
public sealed record GlobalState
|
||||
{
|
||||
public int Turn { get; init; }
|
||||
public int ActionsRemaining { get; init; } = Balancing.Current.ActionsPerTurn;
|
||||
public ELevelState LevelState { get; init; } = ELevelState.Stable;
|
||||
public string Status { get; init; } = "STABLE";
|
||||
public bool AllSeeingEyeUnlocked { get; init; }
|
||||
public bool TerminalLoss { get; init; }
|
||||
public IReadOnlyList<string> Warnings { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public sealed record LevelState
|
||||
{
|
||||
public static LevelState Create(string name, int width, int height)
|
||||
{
|
||||
if (width < Balancing.Current.MinimumLevelSize || height < Balancing.Current.MinimumLevelSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(width), $"Levels must be at least {Balancing.Current.MinimumLevelSize}x{Balancing.Current.MinimumLevelSize}.");
|
||||
|
||||
var terrain = Enumerable.Repeat(ECellTerrain.Floor, width * height).ToArray();
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
if (x == 0 || y == 0 || x == width - 1 || y == height - 1)
|
||||
terrain[y * width + x] = ECellTerrain.Wall;
|
||||
}
|
||||
}
|
||||
|
||||
var level = new LevelState {
|
||||
Name = name,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Terrain = terrain,
|
||||
Fuel = CreateUnderground(width, height),
|
||||
Coolant = CreateUnderground(width, height),
|
||||
Electricity = CreateUnderground(width, height),
|
||||
Surface = CreateSurface(width, height),
|
||||
Props = CreateProps(width, height),
|
||||
Robot = new() { Position = new(1, 1) }
|
||||
};
|
||||
|
||||
return level with { Forecasts = Array.Empty<Forecast>() };
|
||||
}
|
||||
|
||||
public bool InBounds(GridPosition position)
|
||||
{
|
||||
return position.X >= 0 && position.Y >= 0 && position.X < Width && position.Y < Height;
|
||||
}
|
||||
|
||||
public int Index(GridPosition position)
|
||||
{
|
||||
EnsureInBounds(position);
|
||||
return position.Y * Width + position.X;
|
||||
}
|
||||
|
||||
public ECellTerrain GetTerrain(GridPosition position)
|
||||
{
|
||||
return Terrain[Index(position)];
|
||||
}
|
||||
|
||||
public UndergroundCell GetUnderground(GridPosition position, ECarrierType carrier)
|
||||
{
|
||||
return Layer(carrier)[Index(position)];
|
||||
}
|
||||
|
||||
public SurfaceState GetSurface(GridPosition position)
|
||||
{
|
||||
return Surface[Index(position)];
|
||||
}
|
||||
|
||||
public PropState GetProp(GridPosition position)
|
||||
{
|
||||
return Props[Index(position)];
|
||||
}
|
||||
|
||||
public bool IsFloor(GridPosition position)
|
||||
{
|
||||
return InBounds(position) && GetTerrain(position) == ECellTerrain.Floor;
|
||||
}
|
||||
|
||||
public bool IsClosedDoorEdge(GridPosition a, GridPosition b)
|
||||
{
|
||||
return Doors.Any(door => door.State == EDoorState.Closed && SameEdge(door.A, door.B, a, b));
|
||||
}
|
||||
|
||||
public LevelState SetTerrain(GridPosition position, ECellTerrain terrain)
|
||||
{
|
||||
var next = Terrain.ToArray();
|
||||
next[Index(position)] = terrain;
|
||||
var level = this with { Terrain = next };
|
||||
return terrain == ECellTerrain.Wall ? level.ClearFloorOnlyState(position) : level;
|
||||
}
|
||||
|
||||
public LevelState SetUnderground(GridPosition position, ECarrierType carrier, UndergroundCell cell)
|
||||
{
|
||||
var next = Layer(carrier).ToArray();
|
||||
next[Index(position)] = cell;
|
||||
return carrier switch {
|
||||
ECarrierType.Fuel => this with { Fuel = next },
|
||||
ECarrierType.Coolant => this with { Coolant = next },
|
||||
ECarrierType.Electricity => this with { Electricity = next },
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
|
||||
};
|
||||
}
|
||||
|
||||
public LevelState SetSurface(GridPosition position, SurfaceState surface)
|
||||
{
|
||||
var next = Surface.ToArray();
|
||||
next[Index(position)] = surface.Clamp();
|
||||
return this with { Surface = next };
|
||||
}
|
||||
|
||||
public LevelState SetProp(GridPosition position, PropState prop)
|
||||
{
|
||||
var next = Props.ToArray();
|
||||
next[Index(position)] = prop;
|
||||
return this with { Props = next };
|
||||
}
|
||||
|
||||
public LevelState WithRuntimeArrays(UndergroundCell[] fuel, UndergroundCell[] coolant, UndergroundCell[] electricity, SurfaceState[] surface, PropState[] props)
|
||||
{
|
||||
return this with {
|
||||
Fuel = fuel,
|
||||
Coolant = coolant,
|
||||
Electricity = electricity,
|
||||
Surface = surface,
|
||||
Props = props
|
||||
};
|
||||
}
|
||||
|
||||
public IReadOnlyList<UndergroundCell> Layer(ECarrierType carrier)
|
||||
{
|
||||
return carrier switch {
|
||||
ECarrierType.Fuel => Fuel,
|
||||
ECarrierType.Coolant => Coolant,
|
||||
ECarrierType.Electricity => Electricity,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
|
||||
};
|
||||
}
|
||||
|
||||
private LevelState ClearFloorOnlyState(GridPosition position)
|
||||
{
|
||||
return SetSurface(position, new())
|
||||
.SetProp(position, new())
|
||||
.SetUnderground(position, ECarrierType.Fuel, new())
|
||||
.SetUnderground(position, ECarrierType.Coolant, new())
|
||||
.SetUnderground(position, ECarrierType.Electricity, new());
|
||||
}
|
||||
|
||||
private void EnsureInBounds(GridPosition position)
|
||||
{
|
||||
if (!InBounds(position))
|
||||
throw new ArgumentOutOfRangeException(nameof(position), $"Position {position.X},{position.Y} is outside {Width}x{Height}.");
|
||||
}
|
||||
|
||||
private static bool SameEdge(GridPosition edgeA, GridPosition edgeB, GridPosition a, GridPosition b)
|
||||
{
|
||||
return edgeA == a && edgeB == b || edgeA == b && edgeB == a;
|
||||
}
|
||||
|
||||
private static UndergroundCell[] CreateUnderground(int width, int height)
|
||||
{
|
||||
return Enumerable.Range(0, width * height).Select(_ => new UndergroundCell()).ToArray();
|
||||
}
|
||||
|
||||
private static SurfaceState[] CreateSurface(int width, int height)
|
||||
{
|
||||
return Enumerable.Range(0, width * height).Select(_ => new SurfaceState()).ToArray();
|
||||
}
|
||||
|
||||
private static PropState[] CreateProps(int width, int height)
|
||||
{
|
||||
return Enumerable.Range(0, width * height).Select(_ => new PropState()).ToArray();
|
||||
}
|
||||
|
||||
public string Name { get; init; } = "New Reactor";
|
||||
public int Width { get; init; } = Balancing.Current.DefaultLevelWidth;
|
||||
public int Height { get; init; } = Balancing.Current.DefaultLevelHeight;
|
||||
public ECellTerrain[] Terrain { get; init; } = Enumerable.Repeat(ECellTerrain.Floor, Balancing.Current.DefaultLevelWidth * Balancing.Current.DefaultLevelHeight).ToArray();
|
||||
public UndergroundCell[] Fuel { get; init; } = CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public UndergroundCell[] Coolant { get; init; } = CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public UndergroundCell[] Electricity { get; init; } = CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public SurfaceState[] Surface { get; init; } = CreateSurface(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public PropState[] Props { get; init; } = CreateProps(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public IReadOnlyList<DoorState> Doors { get; init; } = Array.Empty<DoorState>();
|
||||
public IReadOnlyList<LeakState> Leaks { get; init; } = Array.Empty<LeakState>();
|
||||
public IReadOnlyList<ReactorBinding> Reactors { get; init; } = Array.Empty<ReactorBinding>();
|
||||
public IReadOnlyList<RuleEventState> RuleEvents { get; init; } = Array.Empty<RuleEventState>();
|
||||
public RobotState Robot { get; init; } = new();
|
||||
public GlobalState Global { get; init; } = new();
|
||||
public IReadOnlyList<Forecast> Forecasts { get; init; } = Array.Empty<Forecast>();
|
||||
}
|
||||
8
src/ReactorMaintenance.Simulation/Models/DoorState.cs
Normal file
8
src/ReactorMaintenance.Simulation/Models/DoorState.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record DoorState
|
||||
{
|
||||
public GridPosition A { get; init; } = new(0, 0);
|
||||
public GridPosition B { get; init; } = new(0, 0);
|
||||
public EDoorState State { get; init; } = EDoorState.Closed;
|
||||
}
|
||||
8
src/ReactorMaintenance.Simulation/Models/EBand.cs
Normal file
8
src/ReactorMaintenance.Simulation/Models/EBand.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EBand
|
||||
{
|
||||
Safe,
|
||||
Caution,
|
||||
Critical
|
||||
}
|
||||
8
src/ReactorMaintenance.Simulation/Models/ECarrierType.cs
Normal file
8
src/ReactorMaintenance.Simulation/Models/ECarrierType.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ECarrierType
|
||||
{
|
||||
Fuel,
|
||||
Coolant,
|
||||
Electricity
|
||||
}
|
||||
7
src/ReactorMaintenance.Simulation/Models/ECellTerrain.cs
Normal file
7
src/ReactorMaintenance.Simulation/Models/ECellTerrain.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ECellTerrain
|
||||
{
|
||||
Floor,
|
||||
Wall
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EConsumerServiceState
|
||||
{
|
||||
Unknown,
|
||||
Disabled,
|
||||
Starved,
|
||||
Supplied,
|
||||
Producing
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ECrossJunctionMode
|
||||
{
|
||||
ZeroThreeThree,
|
||||
ThreeZeroThree,
|
||||
ThreeThreeZero,
|
||||
TwoTwoTwo
|
||||
}
|
||||
7
src/ReactorMaintenance.Simulation/Models/EDoorState.cs
Normal file
7
src/ReactorMaintenance.Simulation/Models/EDoorState.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EDoorState
|
||||
{
|
||||
Open,
|
||||
Closed
|
||||
}
|
||||
10
src/ReactorMaintenance.Simulation/Models/EForecastKind.cs
Normal file
10
src/ReactorMaintenance.Simulation/Models/EForecastKind.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EForecastKind
|
||||
{
|
||||
TerminalLoss,
|
||||
ReactorReady,
|
||||
ConsumerStarved,
|
||||
HazardGrowth,
|
||||
RuleEvent
|
||||
}
|
||||
11
src/ReactorMaintenance.Simulation/Models/ELevelState.cs
Normal file
11
src/ReactorMaintenance.Simulation/Models/ELevelState.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ELevelState
|
||||
{
|
||||
Stable,
|
||||
Caution,
|
||||
Critical,
|
||||
Ready,
|
||||
Lost,
|
||||
Won
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ENetworkValueKind
|
||||
{
|
||||
Amount,
|
||||
Intensity
|
||||
}
|
||||
19
src/ReactorMaintenance.Simulation/Models/EPairEffect.cs
Normal file
19
src/ReactorMaintenance.Simulation/Models/EPairEffect.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EPairEffect
|
||||
{
|
||||
Hold,
|
||||
FuelFlow,
|
||||
CoolFlow,
|
||||
ChargeFlow,
|
||||
HeatFlow,
|
||||
HeatFlow2,
|
||||
Warm1,
|
||||
Warm2,
|
||||
Quench1,
|
||||
Quench2,
|
||||
Short1,
|
||||
Short2,
|
||||
Ignite1,
|
||||
Ignite2
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EPropSwitchState
|
||||
{
|
||||
Disabled,
|
||||
Enabled
|
||||
}
|
||||
14
src/ReactorMaintenance.Simulation/Models/EPropType.cs
Normal file
14
src/ReactorMaintenance.Simulation/Models/EPropType.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EPropType
|
||||
{
|
||||
None,
|
||||
Flow,
|
||||
Consumer,
|
||||
TJunction,
|
||||
CrossJunction,
|
||||
Door,
|
||||
AllSeeingEyeTerminal,
|
||||
RemedySupply,
|
||||
ReactorControl
|
||||
}
|
||||
9
src/ReactorMaintenance.Simulation/Models/ERemedyType.cs
Normal file
9
src/ReactorMaintenance.Simulation/Models/ERemedyType.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ERemedyType
|
||||
{
|
||||
FuelNeutralizer,
|
||||
CoolantNeutralizer,
|
||||
ElectricityNeutralizer,
|
||||
HeatShield
|
||||
}
|
||||
18
src/ReactorMaintenance.Simulation/Models/ERuleEffectKind.cs
Normal file
18
src/ReactorMaintenance.Simulation/Models/ERuleEffectKind.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ERuleEffectKind
|
||||
{
|
||||
StartLeak,
|
||||
WorsenLeak,
|
||||
RepairNetworkCell,
|
||||
DisableNetworkCell,
|
||||
SetPropEnabled,
|
||||
AddSurfaceHazard,
|
||||
RemoveSurfaceHazard,
|
||||
AddHeat,
|
||||
RemoveHeat,
|
||||
AddInventory,
|
||||
RemoveInventory,
|
||||
MarkTerminalLoss,
|
||||
EmitWarning
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ERuleEventPhase
|
||||
{
|
||||
StartOfSimulation,
|
||||
EndOfTurn
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ERulePredicateKind
|
||||
{
|
||||
TurnAtLeast,
|
||||
LevelStateIs,
|
||||
ReactorReadyIs,
|
||||
ReactorLostIs,
|
||||
ReactorWonIs,
|
||||
PropStateAt,
|
||||
ConsumerStateAt,
|
||||
NetworkBandAt,
|
||||
SurfaceBandAt,
|
||||
RobotAt,
|
||||
RobotInventoryAtLeast,
|
||||
AllSeeingEyeUnlocked
|
||||
}
|
||||
10
src/ReactorMaintenance.Simulation/Models/ETJunctionMode.cs
Normal file
10
src/ReactorMaintenance.Simulation/Models/ETJunctionMode.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ETJunctionMode
|
||||
{
|
||||
ZeroFour,
|
||||
OneThree,
|
||||
TwoTwo,
|
||||
ThreeOne,
|
||||
FourZero
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EUndergroundState
|
||||
{
|
||||
Absent,
|
||||
Intact,
|
||||
Leaking
|
||||
}
|
||||
3
src/ReactorMaintenance.Simulation/Models/Forecast.cs
Normal file
3
src/ReactorMaintenance.Simulation/Models/Forecast.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record Forecast(EForecastKind Kind, GridPosition? Position, int Turns, string Message);
|
||||
12
src/ReactorMaintenance.Simulation/Models/GlobalState.cs
Normal file
12
src/ReactorMaintenance.Simulation/Models/GlobalState.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record GlobalState
|
||||
{
|
||||
public int Turn { get; init; }
|
||||
public int ActionsRemaining { get; init; } = Balancing.Current.ActionsPerTurn;
|
||||
public ELevelState LevelState { get; init; } = ELevelState.Stable;
|
||||
public string Status { get; init; } = "STABLE";
|
||||
public bool AllSeeingEyeUnlocked { get; init; }
|
||||
public bool TerminalLoss { get; init; }
|
||||
public IReadOnlyList<string> Warnings { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
3
src/ReactorMaintenance.Simulation/Models/GridPosition.cs
Normal file
3
src/ReactorMaintenance.Simulation/Models/GridPosition.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record GridPosition(int X, int Y);
|
||||
9
src/ReactorMaintenance.Simulation/Models/LeakState.cs
Normal file
9
src/ReactorMaintenance.Simulation/Models/LeakState.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record LeakState
|
||||
{
|
||||
public ECarrierType Carrier { get; init; }
|
||||
public GridPosition UndergroundPosition { get; init; } = new(0, 0);
|
||||
public GridPosition AccessPosition { get; init; } = new(0, 0);
|
||||
public bool Repaired { get; init; }
|
||||
}
|
||||
26
src/ReactorMaintenance.Simulation/Models/LevelState.cs
Normal file
26
src/ReactorMaintenance.Simulation/Models/LevelState.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record LevelState
|
||||
{
|
||||
public static LevelState Create(string name, int width, int height)
|
||||
{
|
||||
return LevelStateFactory.Create(name, width, height);
|
||||
}
|
||||
|
||||
public string Name { get; init; } = "New Reactor";
|
||||
public int Width { get; init; } = Balancing.Current.DefaultLevelWidth;
|
||||
public int Height { get; init; } = Balancing.Current.DefaultLevelHeight;
|
||||
public ECellTerrain[] Terrain { get; init; } = LevelStateFactory.CreateTerrain(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public UndergroundCell[] Fuel { get; init; } = LevelStateFactory.CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public UndergroundCell[] Coolant { get; init; } = LevelStateFactory.CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public UndergroundCell[] Electricity { get; init; } = LevelStateFactory.CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public SurfaceState[] Surface { get; init; } = LevelStateFactory.CreateSurface(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public PropState[] Props { get; init; } = LevelStateFactory.CreateProps(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public IReadOnlyList<DoorState> Doors { get; init; } = Array.Empty<DoorState>();
|
||||
public IReadOnlyList<LeakState> Leaks { get; init; } = Array.Empty<LeakState>();
|
||||
public IReadOnlyList<ReactorBinding> Reactors { get; init; } = Array.Empty<ReactorBinding>();
|
||||
public IReadOnlyList<RuleEventState> RuleEvents { get; init; } = Array.Empty<RuleEventState>();
|
||||
public RobotState Robot { get; init; } = new();
|
||||
public GlobalState Global { get; init; } = new();
|
||||
public IReadOnlyList<Forecast> Forecasts { get; init; } = Array.Empty<Forecast>();
|
||||
}
|
||||
16
src/ReactorMaintenance.Simulation/Models/PropState.cs
Normal file
16
src/ReactorMaintenance.Simulation/Models/PropState.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record PropState
|
||||
{
|
||||
public EPropType Type { get; init; }
|
||||
public ECarrierType Carrier { get; init; }
|
||||
public EPropSwitchState SwitchState { get; init; } = EPropSwitchState.Enabled;
|
||||
public EConsumerServiceState ServiceState { get; init; } = EConsumerServiceState.Unknown;
|
||||
public ETJunctionMode TJunctionMode { get; init; } = ETJunctionMode.TwoTwo;
|
||||
public ECrossJunctionMode CrossJunctionMode { get; init; } = ECrossJunctionMode.TwoTwoTwo;
|
||||
public ERemedyType RemedyType { get; init; }
|
||||
public bool Depleted { get; init; }
|
||||
public int ReactorId { get; init; }
|
||||
|
||||
public bool IsEnabled => SwitchState == EPropSwitchState.Enabled;
|
||||
}
|
||||
12
src/ReactorMaintenance.Simulation/Models/ReactorBinding.cs
Normal file
12
src/ReactorMaintenance.Simulation/Models/ReactorBinding.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record ReactorBinding
|
||||
{
|
||||
public int ReactorId { get; init; }
|
||||
public GridPosition ControlPosition { get; init; } = new(0, 0);
|
||||
public GridPosition FuelConsumerPosition { get; init; } = new(0, 0);
|
||||
public GridPosition CoolantConsumerPosition { get; init; } = new(0, 0);
|
||||
public GridPosition ElectricityConsumerPosition { get; init; } = new(0, 0);
|
||||
public bool Ready { get; init; }
|
||||
public bool Activated { get; init; }
|
||||
}
|
||||
11
src/ReactorMaintenance.Simulation/Models/RobotState.cs
Normal file
11
src/ReactorMaintenance.Simulation/Models/RobotState.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record RobotState
|
||||
{
|
||||
public GridPosition Position { get; init; } = new(1, 1);
|
||||
public int FuelNeutralizers { get; init; }
|
||||
public int CoolantNeutralizers { get; init; }
|
||||
public int ElectricityNeutralizers { get; init; }
|
||||
public int HeatShields { get; init; }
|
||||
public int HeatImmunitySteps { get; init; }
|
||||
}
|
||||
13
src/ReactorMaintenance.Simulation/Models/RuleEffect.cs
Normal file
13
src/ReactorMaintenance.Simulation/Models/RuleEffect.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record RuleEffect
|
||||
{
|
||||
public ERuleEffectKind Kind { get; init; }
|
||||
public GridPosition Position { get; init; } = new(0, 0);
|
||||
public GridPosition? AccessPosition { get; init; }
|
||||
public ECarrierType Carrier { get; init; }
|
||||
public ERemedyType Remedy { get; init; }
|
||||
public float Amount { get; init; }
|
||||
public EPropSwitchState PropSwitchState { get; init; }
|
||||
public string Message { get; init; } = string.Empty;
|
||||
}
|
||||
14
src/ReactorMaintenance.Simulation/Models/RuleEventState.cs
Normal file
14
src/ReactorMaintenance.Simulation/Models/RuleEventState.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record RuleEventState
|
||||
{
|
||||
public string Id { get; init; } = string.Empty;
|
||||
public bool Enabled { get; init; } = true;
|
||||
public bool Repeat { get; init; }
|
||||
public bool Triggered { get; init; }
|
||||
public int Priority { get; init; }
|
||||
public ERuleEventPhase Phase { get; init; }
|
||||
public IReadOnlyList<RulePredicate> Predicates { get; init; } = Array.Empty<RulePredicate>();
|
||||
public IReadOnlyList<RuleEffect> Effects { get; init; } = Array.Empty<RuleEffect>();
|
||||
public string ForecastText { get; init; } = string.Empty;
|
||||
}
|
||||
18
src/ReactorMaintenance.Simulation/Models/RulePredicate.cs
Normal file
18
src/ReactorMaintenance.Simulation/Models/RulePredicate.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record RulePredicate
|
||||
{
|
||||
public ERulePredicateKind Kind { get; init; }
|
||||
public GridPosition Position { get; init; } = new(0, 0);
|
||||
public int ReactorId { get; init; }
|
||||
public int Turn { get; init; }
|
||||
public ELevelState LevelState { get; init; }
|
||||
public EPropSwitchState PropSwitchState { get; init; }
|
||||
public EConsumerServiceState ConsumerServiceState { get; init; }
|
||||
public ECarrierType Carrier { get; init; }
|
||||
public ENetworkValueKind NetworkValue { get; init; }
|
||||
public ERemedyType Remedy { get; init; }
|
||||
public EBand Band { get; init; }
|
||||
public int InventoryCount { get; init; }
|
||||
public bool BoolValue { get; init; }
|
||||
}
|
||||
12
src/ReactorMaintenance.Simulation/Models/SurfaceState.cs
Normal file
12
src/ReactorMaintenance.Simulation/Models/SurfaceState.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record SurfaceState
|
||||
{
|
||||
public float Fuel { get; init; }
|
||||
public float Coolant { get; init; }
|
||||
public float Electricity { get; init; }
|
||||
public float Heat { get; init; }
|
||||
public int FuelBlockTurns { get; init; }
|
||||
public int CoolantBlockTurns { get; init; }
|
||||
public int ElectricityBlockTurns { get; init; }
|
||||
}
|
||||
11
src/ReactorMaintenance.Simulation/Models/UndergroundCell.cs
Normal file
11
src/ReactorMaintenance.Simulation/Models/UndergroundCell.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record UndergroundCell
|
||||
{
|
||||
public EUndergroundState State { get; init; }
|
||||
public float Amount { get; init; }
|
||||
public float Intensity { get; init; }
|
||||
|
||||
public bool IsPresent => State != EUndergroundState.Absent;
|
||||
public bool CarriesFlow => State is EUndergroundState.Intact or EUndergroundState.Leaking;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record ValidationIssue(string Message, GridPosition? Position = null);
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed record ValidationReport
|
||||
{
|
||||
public IReadOnlyList<ValidationIssue> Errors { get; init; } = Array.Empty<ValidationIssue>();
|
||||
public IReadOnlyList<ValidationIssue> Warnings { get; init; } = Array.Empty<ValidationIssue>();
|
||||
public bool IsValid => Errors.Count == 0;
|
||||
}
|
||||
36
src/ReactorMaintenance.Simulation/RobotStateExtensions.cs
Normal file
36
src/ReactorMaintenance.Simulation/RobotStateExtensions.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public static class RobotStateExtensions
|
||||
{
|
||||
public static int Count(this RobotState robot, ERemedyType remedy)
|
||||
{
|
||||
return remedy switch {
|
||||
ERemedyType.FuelNeutralizer => robot.FuelNeutralizers,
|
||||
ERemedyType.CoolantNeutralizer => robot.CoolantNeutralizers,
|
||||
ERemedyType.ElectricityNeutralizer => robot.ElectricityNeutralizers,
|
||||
ERemedyType.HeatShield => robot.HeatShields,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(remedy), remedy, "Unsupported remedy.")
|
||||
};
|
||||
}
|
||||
|
||||
public static RobotState Add(this RobotState robot, ERemedyType remedy, int amount)
|
||||
{
|
||||
return remedy switch {
|
||||
ERemedyType.FuelNeutralizer => robot with { FuelNeutralizers = ClampInventory(robot.FuelNeutralizers + amount) },
|
||||
ERemedyType.CoolantNeutralizer => robot with { CoolantNeutralizers = ClampInventory(robot.CoolantNeutralizers + amount) },
|
||||
ERemedyType.ElectricityNeutralizer => robot with { ElectricityNeutralizers = ClampInventory(robot.ElectricityNeutralizers + amount) },
|
||||
ERemedyType.HeatShield => robot with { HeatShields = ClampInventory(robot.HeatShields + amount) },
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(remedy), remedy, "Unsupported remedy.")
|
||||
};
|
||||
}
|
||||
|
||||
public static RobotState Spend(this RobotState robot, ERemedyType remedy)
|
||||
{
|
||||
return robot.Count(remedy) <= 0 ? robot : robot.Add(remedy, -1);
|
||||
}
|
||||
|
||||
private static int ClampInventory(int value)
|
||||
{
|
||||
return Math.Clamp(value, 0, Balancing.Current.InventoryCapacityPerRemedy);
|
||||
}
|
||||
}
|
||||
28
src/ReactorMaintenance.Simulation/SurfaceStateExtensions.cs
Normal file
28
src/ReactorMaintenance.Simulation/SurfaceStateExtensions.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public static class SurfaceStateExtensions
|
||||
{
|
||||
public static SurfaceState Clamp(this SurfaceState surface)
|
||||
{
|
||||
var balancing = Balancing.Current;
|
||||
return surface with {
|
||||
Fuel = balancing.ClampValue(surface.Fuel),
|
||||
Coolant = balancing.ClampValue(surface.Coolant),
|
||||
Electricity = balancing.ClampValue(surface.Electricity),
|
||||
Heat = balancing.ClampValue(surface.Heat),
|
||||
FuelBlockTurns = Math.Max(0, surface.FuelBlockTurns),
|
||||
CoolantBlockTurns = Math.Max(0, surface.CoolantBlockTurns),
|
||||
ElectricityBlockTurns = Math.Max(0, surface.ElectricityBlockTurns)
|
||||
};
|
||||
}
|
||||
|
||||
public static bool Blocks(this SurfaceState surface, ECarrierType carrier)
|
||||
{
|
||||
return carrier switch {
|
||||
ECarrierType.Fuel => surface.FuelBlockTurns > 0,
|
||||
ECarrierType.Coolant => surface.CoolantBlockTurns > 0,
|
||||
ECarrierType.Electricity => surface.ElectricityBlockTurns > 0,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user