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