Add pulse contract and isolation valves
This commit is contained in:
@@ -40,9 +40,10 @@ public partial class InventoryStrip : HBoxContainer
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Label m_WaterLabel = null!;
|
|
||||||
private Label m_ElectricLabel = null!;
|
private Label m_ElectricLabel = null!;
|
||||||
|
|
||||||
private Label m_FuelLabel = null!;
|
private Label m_FuelLabel = null!;
|
||||||
private Label m_HeatLabel = null!;
|
private Label m_HeatLabel = null!;
|
||||||
|
|
||||||
|
private Label m_WaterLabel = null!;
|
||||||
}
|
}
|
||||||
@@ -121,6 +121,7 @@ public abstract class Balancing
|
|||||||
public abstract int DefaultLevelHeight { get; }
|
public abstract int DefaultLevelHeight { get; }
|
||||||
public abstract int MinimumLevelSize { get; }
|
public abstract int MinimumLevelSize { get; }
|
||||||
public abstract int ForecastHorizon { get; }
|
public abstract int ForecastHorizon { get; }
|
||||||
|
public abstract int StepsPerPulse { get; }
|
||||||
public abstract float MinValue { get; }
|
public abstract float MinValue { get; }
|
||||||
public abstract float MaxValue { get; }
|
public abstract float MaxValue { get; }
|
||||||
public abstract float FuelSafe { get; }
|
public abstract float FuelSafe { get; }
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ public class NormalBalancing : Balancing
|
|||||||
public override int DefaultLevelHeight => 12;
|
public override int DefaultLevelHeight => 12;
|
||||||
public override int MinimumLevelSize => 4;
|
public override int MinimumLevelSize => 4;
|
||||||
public override int ForecastHorizon => 6;
|
public override int ForecastHorizon => 6;
|
||||||
|
public override int StepsPerPulse => 3;
|
||||||
public override float MinValue => 0;
|
public override float MinValue => 0;
|
||||||
public override float MaxValue => 10;
|
public override float MaxValue => 10;
|
||||||
public override float FuelSafe => 1.5f;
|
public override float FuelSafe => 1.5f;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ public enum EEditorTool
|
|||||||
Wall,
|
Wall,
|
||||||
Underground,
|
Underground,
|
||||||
Flow,
|
Flow,
|
||||||
|
IsolationValve,
|
||||||
Consumer,
|
Consumer,
|
||||||
Junction,
|
Junction,
|
||||||
Door,
|
Door,
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ public static class LevelEditor
|
|||||||
EEditorTool.Wall => level.SetTerrain(position, ECellTerrain.Wall),
|
EEditorTool.Wall => level.SetTerrain(position, ECellTerrain.Wall),
|
||||||
EEditorTool.Underground => SetUnderground(level, position, command.Carrier),
|
EEditorTool.Underground => SetUnderground(level, position, command.Carrier),
|
||||||
EEditorTool.Flow => SetCarrierProp(level, position, EPropType.Flow, command.Carrier),
|
EEditorTool.Flow => SetCarrierProp(level, position, EPropType.Flow, command.Carrier),
|
||||||
|
EEditorTool.IsolationValve => SetCarrierProp(level, position, EPropType.IsolationValve, command.Carrier),
|
||||||
EEditorTool.Consumer => SetFloorProp(level, position, new() { Type = EPropType.Consumer, SwitchState = EPropSwitchState.Enabled }),
|
EEditorTool.Consumer => SetFloorProp(level, position, new() { Type = EPropType.Consumer, SwitchState = EPropSwitchState.Enabled }),
|
||||||
EEditorTool.Junction => SetFloorProp(level, position, new() { Type = EPropType.Junction }),
|
EEditorTool.Junction => SetFloorProp(level, position, new() { Type = EPropType.Junction }),
|
||||||
EEditorTool.Door => ToggleOrSetDoor(level, position),
|
EEditorTool.Door => ToggleOrSetDoor(level, position),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public sealed class LevelValidator
|
|||||||
ValidateRobot(level, errors);
|
ValidateRobot(level, errors);
|
||||||
ValidateCells(level, errors);
|
ValidateCells(level, errors);
|
||||||
ValidateDoors(level, errors);
|
ValidateDoors(level, errors);
|
||||||
|
ValidateIsolationValves(level, errors);
|
||||||
ValidateLeaks(level, errors);
|
ValidateLeaks(level, errors);
|
||||||
ValidateReactors(level, errors, warnings);
|
ValidateReactors(level, errors, warnings);
|
||||||
ValidateJunctions(level, errors);
|
ValidateJunctions(level, errors);
|
||||||
@@ -19,6 +20,25 @@ public sealed class LevelValidator
|
|||||||
return new() { Errors = errors, Warnings = warnings };
|
return new() { Errors = errors, Warnings = warnings };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ValidateIsolationValves(LevelState level, List<ValidationIssue> errors)
|
||||||
|
{
|
||||||
|
foreach (var position in LevelTraversal.AllPositions(level))
|
||||||
|
{
|
||||||
|
var prop = level.GetProp(position);
|
||||||
|
if (prop.Type != EPropType.IsolationValve)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!level.IsFloor(position))
|
||||||
|
{
|
||||||
|
errors.Add(new("Isolation valve must be placed on a floor cell.", position));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!level.GetUnderground(position, prop.Carrier).IsPresent)
|
||||||
|
errors.Add(new("Isolation valve must sit on its matching underground carrier.", position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void ValidateDimensions(LevelState level, List<ValidationIssue> errors)
|
private static void ValidateDimensions(LevelState level, List<ValidationIssue> errors)
|
||||||
{
|
{
|
||||||
if (level.Width < Balancing.Current.MinimumLevelSize || level.Height < Balancing.Current.MinimumLevelSize)
|
if (level.Width < Balancing.Current.MinimumLevelSize || level.Height < Balancing.Current.MinimumLevelSize)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ public enum EPropType
|
|||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
Flow,
|
Flow,
|
||||||
|
IsolationValve,
|
||||||
Consumer,
|
Consumer,
|
||||||
Junction,
|
Junction,
|
||||||
Door,
|
Door,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
public sealed record GlobalState
|
public sealed record GlobalState
|
||||||
{
|
{
|
||||||
public int Turn { get; init; }
|
public int Turn { get; init; }
|
||||||
|
public int Pulse { get; init; }
|
||||||
|
public int Step { get; init; }
|
||||||
public ELevelState LevelState { get; init; } = ELevelState.Stable;
|
public ELevelState LevelState { get; init; } = ELevelState.Stable;
|
||||||
public string Status { get; init; } = "STABLE";
|
public string Status { get; init; } = "STABLE";
|
||||||
public bool TerminalLoss { get; init; }
|
public bool TerminalLoss { get; init; }
|
||||||
|
|||||||
@@ -26,4 +26,5 @@ public sealed record PropState
|
|||||||
public EDoorState DoorState { get; init; } = EDoorState.Closed;
|
public EDoorState DoorState { get; init; } = EDoorState.Closed;
|
||||||
|
|
||||||
public bool IsEnabled => SwitchState == EPropSwitchState.Enabled;
|
public bool IsEnabled => SwitchState == EPropSwitchState.Enabled;
|
||||||
|
public bool IsOpen => SwitchState == EPropSwitchState.Enabled;
|
||||||
}
|
}
|
||||||
@@ -9,22 +9,17 @@ public sealed class SimulationEngine
|
|||||||
|
|
||||||
public LevelState InteractProp(LevelState level)
|
public LevelState InteractProp(LevelState level)
|
||||||
{
|
{
|
||||||
return PlayerActionSystem.InteractProp(level, ResolveStep);
|
return PlayerActionSystem.InteractProp(level, ResolvePulse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelState InteractLeak(LevelState level, ECarrierType carrier, bool useRemedy)
|
public LevelState InteractLeak(LevelState level, ECarrierType carrier, bool useRemedy)
|
||||||
{
|
{
|
||||||
return PlayerActionSystem.InteractLeak(level, carrier, useRemedy, ResolveStep);
|
return PlayerActionSystem.InteractLeak(level, carrier, useRemedy, ResolvePulse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelState ApplyHeatShield(LevelState level)
|
public LevelState ApplyHeatShield(LevelState level)
|
||||||
{
|
{
|
||||||
return PlayerActionSystem.ApplyHeatShield(level, ResolveStep);
|
return PlayerActionSystem.ApplyHeatShield(level, ResolvePulse);
|
||||||
}
|
|
||||||
|
|
||||||
private LevelState ResolveStep(LevelState level)
|
|
||||||
{
|
|
||||||
return ResolveStep(level, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelState ActivateReactor(LevelState level)
|
public LevelState ActivateReactor(LevelState level)
|
||||||
@@ -32,14 +27,26 @@ public sealed class SimulationEngine
|
|||||||
return ReactorSystem.Activate(level);
|
return ReactorSystem.Activate(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelState EndTurn(LevelState level)
|
public LevelState AdvancePulseForDebug(LevelState level)
|
||||||
|
{
|
||||||
|
return ResolvePulse(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LevelState AdvanceStepForDebug(LevelState level)
|
||||||
{
|
{
|
||||||
return ResolveStep(level);
|
return ResolveStep(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use AdvancePulseForDebug. Player-facing wait/turn advancement is not part of normal gameplay.")]
|
||||||
|
public LevelState EndTurn(LevelState level)
|
||||||
|
{
|
||||||
|
return AdvancePulseForDebug(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use AdvancePulseForDebug. Player-facing wait/turn advancement is not part of normal gameplay.")]
|
||||||
public LevelState AdvanceTurn(LevelState level)
|
public LevelState AdvanceTurn(LevelState level)
|
||||||
{
|
{
|
||||||
return ResolveStep(level);
|
return AdvancePulseForDebug(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<Forecast> Forecast(LevelState level)
|
public IReadOnlyList<Forecast> Forecast(LevelState level)
|
||||||
@@ -47,7 +54,41 @@ public sealed class SimulationEngine
|
|||||||
return ForecastSystem.Forecast(level, simulated => ResolveStep(simulated, false));
|
return ForecastSystem.Forecast(level, simulated => ResolveStep(simulated, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private LevelState ResolveStep(LevelState level, bool refreshForecasts)
|
private LevelState ResolvePulse(LevelState level)
|
||||||
|
{
|
||||||
|
var next = ValidateAndPropagate(level);
|
||||||
|
if (next.Global.LevelState == ELevelState.Lost)
|
||||||
|
return next;
|
||||||
|
|
||||||
|
for (var i = 0; i < Balancing.Current.StepsPerPulse; i++)
|
||||||
|
next = ResolveStepContent(next);
|
||||||
|
|
||||||
|
next = ReactorSystem.DeriveState(next);
|
||||||
|
next = SurfaceInteractionSystem.AdvanceDurations(next);
|
||||||
|
next = next with {
|
||||||
|
Global = next.Global with {
|
||||||
|
Turn = next.Global.Pulse + 1,
|
||||||
|
Pulse = next.Global.Pulse + 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return next with { Forecasts = Forecast(next) };
|
||||||
|
}
|
||||||
|
|
||||||
|
private LevelState ResolveStep(LevelState level, bool refreshForecasts = true)
|
||||||
|
{
|
||||||
|
var next = ValidateAndPropagate(level);
|
||||||
|
if (next.Global.LevelState == ELevelState.Lost)
|
||||||
|
return next;
|
||||||
|
|
||||||
|
next = ResolveStepContent(next);
|
||||||
|
next = ReactorSystem.DeriveState(next);
|
||||||
|
next = SurfaceInteractionSystem.AdvanceDurations(next);
|
||||||
|
|
||||||
|
return refreshForecasts ? next with { Forecasts = Forecast(next) } : next;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LevelState ValidateAndPropagate(LevelState level)
|
||||||
{
|
{
|
||||||
var report = m_Validator.Validate(level);
|
var report = m_Validator.Validate(level);
|
||||||
if (!report.IsValid)
|
if (!report.IsValid)
|
||||||
@@ -57,18 +98,16 @@ public sealed class SimulationEngine
|
|||||||
next = NetworkPropagationSystem.Propagate(next);
|
next = NetworkPropagationSystem.Propagate(next);
|
||||||
next = ConsumerSystem.Resolve(next);
|
next = ConsumerSystem.Resolve(next);
|
||||||
next = StructuralIntegritySystem.Resolve(next);
|
next = StructuralIntegritySystem.Resolve(next);
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LevelState ResolveStepContent(LevelState level)
|
||||||
|
{
|
||||||
|
var next = level;
|
||||||
next = LeakSystem.Inject(next);
|
next = LeakSystem.Inject(next);
|
||||||
next = SurfaceInteractionSystem.Resolve(next);
|
next = SurfaceInteractionSystem.Resolve(next);
|
||||||
next = RobotSafetySystem.Resolve(next);
|
next = next with { Global = next.Global with { Step = next.Global.Step + 1 } };
|
||||||
next = ReactorSystem.DeriveState(next);
|
return next;
|
||||||
next = SurfaceInteractionSystem.AdvanceDurations(next);
|
|
||||||
next = next with {
|
|
||||||
Global = next.Global with {
|
|
||||||
Turn = next.Global.Turn + 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return refreshForecasts ? next with { Forecasts = Forecast(next) } : next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly LevelValidator m_Validator = new();
|
private readonly LevelValidator m_Validator = new();
|
||||||
|
|||||||
@@ -25,4 +25,16 @@ public static class SurfaceStateExtensions
|
|||||||
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
|
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsUnsafe(this SurfaceState surface)
|
||||||
|
{
|
||||||
|
return surface.Heat >= Balancing.Current.RobotHeatSafetyThreshold
|
||||||
|
|| surface.Electricity >= Balancing.Current.RobotElectricitySafetyThreshold
|
||||||
|
|| surface.IsWetElectricUnsafe();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsWetElectricUnsafe(this SurfaceState surface)
|
||||||
|
{
|
||||||
|
return surface.Water > Balancing.Current.WaterSafe && surface.Electricity > Balancing.Current.ElectricitySafe;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -60,6 +60,9 @@ internal static class NetworkPropagationSystem
|
|||||||
if (!level.GetUnderground(next, carrier).CarriesFlow)
|
if (!level.GetUnderground(next, carrier).CarriesFlow)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (IsClosedValveBoundary(level, current.Position, next, carrier))
|
||||||
|
continue;
|
||||||
|
|
||||||
var weights = BranchWeights(current.Position, next, junctions);
|
var weights = BranchWeights(current.Position, next, junctions);
|
||||||
var amountFactor = current.AmountFactor * weights.Amount;
|
var amountFactor = current.AmountFactor * weights.Amount;
|
||||||
var intensityFactor = current.IntensityFactor * weights.Intensity;
|
var intensityFactor = current.IntensityFactor * weights.Intensity;
|
||||||
@@ -75,6 +78,17 @@ internal static class NetworkPropagationSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsClosedValveBoundary(LevelState level, GridPosition from, GridPosition to, ECarrierType carrier)
|
||||||
|
{
|
||||||
|
return IsClosedValve(level, from, carrier) || IsClosedValve(level, to, carrier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsClosedValve(LevelState level, GridPosition position, ECarrierType carrier)
|
||||||
|
{
|
||||||
|
return level.GetProp(position) is { Type: EPropType.IsolationValve, Carrier: var valveCarrier, SwitchState: EPropSwitchState.Disabled }
|
||||||
|
&& valveCarrier == carrier;
|
||||||
|
}
|
||||||
|
|
||||||
private static (float Amount, float Intensity) BranchWeights(GridPosition from, GridPosition to, IReadOnlyDictionary<GridPosition, JunctionFlow> junctions)
|
private static (float Amount, float Intensity) BranchWeights(GridPosition from, GridPosition to, IReadOnlyDictionary<GridPosition, JunctionFlow> junctions)
|
||||||
{
|
{
|
||||||
if (!junctions.TryGetValue(from, out var junction))
|
if (!junctions.TryGetValue(from, out var junction))
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ internal static class PlayerActionSystem
|
|||||||
if (!CanAct(level) || !level.IsFloor(destination) || level.Robot.Position.ManhattanDistance(destination) != 1)
|
if (!CanAct(level) || !level.IsFloor(destination) || level.Robot.Position.ManhattanDistance(destination) != 1)
|
||||||
return Refuse(level, "MOVE BLOCKED");
|
return Refuse(level, "MOVE BLOCKED");
|
||||||
|
|
||||||
return level with {
|
var next = level with {
|
||||||
Robot = level.Robot with {
|
Robot = level.Robot with {
|
||||||
Position = destination,
|
Position = destination,
|
||||||
HeatImmunitySteps = Math.Max(0, level.Robot.HeatImmunitySteps - 1)
|
HeatImmunitySteps = Math.Max(0, level.Robot.HeatImmunitySteps - 1)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return RobotSafetySystem.ResolveEntry(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LevelState InteractProp(LevelState level, Func<LevelState, LevelState> resolveLengthyAction)
|
public static LevelState InteractProp(LevelState level, Func<LevelState, LevelState> resolveLengthyAction)
|
||||||
@@ -26,7 +28,7 @@ internal static class PlayerActionSystem
|
|||||||
return Refuse(level, "NO PROP");
|
return Refuse(level, "NO PROP");
|
||||||
|
|
||||||
var next = prop.Type switch {
|
var next = prop.Type switch {
|
||||||
EPropType.Flow or EPropType.Consumer => ToggleProp(level, position, prop),
|
EPropType.Flow or EPropType.Consumer or EPropType.IsolationValve => ToggleProp(level, position, prop),
|
||||||
EPropType.Junction => CycleJunctionMode(level, position, prop),
|
EPropType.Junction => CycleJunctionMode(level, position, prop),
|
||||||
EPropType.Door => ToggleDoor(level, position, prop),
|
EPropType.Door => ToggleDoor(level, position, prop),
|
||||||
EPropType.AllSeeingEyeTerminal => level with { Global = level.Global with { Status = "ALL-SEEING-EYE AVAILABLE" } },
|
EPropType.AllSeeingEyeTerminal => level with { Global = level.Global with { Status = "ALL-SEEING-EYE AVAILABLE" } },
|
||||||
|
|||||||
@@ -3,12 +3,17 @@
|
|||||||
internal static class RobotSafetySystem
|
internal static class RobotSafetySystem
|
||||||
{
|
{
|
||||||
public static LevelState Resolve(LevelState level)
|
public static LevelState Resolve(LevelState level)
|
||||||
|
{
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LevelState ResolveEntry(LevelState level)
|
||||||
{
|
{
|
||||||
var surface = level.GetSurface(level.Robot.Position);
|
var surface = level.GetSurface(level.Robot.Position);
|
||||||
var unsafeElement = surface.Fuel >= Balancing.Current.RobotFuelSafetyThreshold || surface.Water >= Balancing.Current.RobotWaterSafetyThreshold || surface.Electricity >= Balancing.Current.RobotElectricitySafetyThreshold;
|
var unsafeElement = surface.Electricity >= Balancing.Current.RobotElectricitySafetyThreshold || surface.IsWetElectricUnsafe();
|
||||||
var unsafeHeat = surface.Heat >= Balancing.Current.RobotHeatSafetyThreshold && level.Robot.HeatImmunitySteps <= 0;
|
var unsafeHeat = surface.Heat >= Balancing.Current.RobotHeatSafetyThreshold && level.Robot.HeatImmunitySteps <= 0;
|
||||||
return unsafeElement || unsafeHeat
|
return unsafeElement || unsafeHeat
|
||||||
? level with { Global = level.Global with { LevelState = ELevelState.Lost, TerminalLoss = true, Status = "ROBOT LOST" } }
|
? level with { Global = level.Global with { LevelState = ELevelState.Lost, TerminalLoss = true, Status = "UNSAFE ENTRY LOSS" } }
|
||||||
: level;
|
: level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,57 @@ public sealed class SimulationEngineTests
|
|||||||
var next = m_Engine.InteractProp(level);
|
var next = m_Engine.InteractProp(level);
|
||||||
|
|
||||||
Assert.Equal(EDoorState.Open, next.GetProp(new(3, 2)).DoorState);
|
Assert.Equal(EDoorState.Open, next.GetProp(new(3, 2)).DoorState);
|
||||||
Assert.Equal(1, next.Global.Turn);
|
Assert.Equal(1, next.Global.Pulse);
|
||||||
|
Assert.Equal(Balancing.Current.StepsPerPulse, next.Global.Step);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EveryAcceptedLengthyActionAdvancesOneFixedPulse()
|
||||||
|
{
|
||||||
|
var level = DoorLevel() with { Robot = new() { Position = new(3, 2) } };
|
||||||
|
|
||||||
|
var next = m_Engine.InteractProp(level);
|
||||||
|
|
||||||
|
Assert.Equal(1, next.Global.Pulse);
|
||||||
|
Assert.Equal(Balancing.Current.StepsPerPulse, next.Global.Step);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DebugStepAdvancementDoesNotAdvancePulse()
|
||||||
|
{
|
||||||
|
var level = BuildReadyLevel();
|
||||||
|
|
||||||
|
var next = m_Engine.AdvanceStepForDebug(level);
|
||||||
|
|
||||||
|
Assert.Equal(0, next.Global.Pulse);
|
||||||
|
Assert.Equal(1, next.Global.Step);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IsolationValveOpenAllowsPropagationAndClosedBlocksDownstreamFeed()
|
||||||
|
{
|
||||||
|
var open = IsolationValveLevel(EPropSwitchState.Enabled);
|
||||||
|
var closed = IsolationValveLevel(EPropSwitchState.Disabled);
|
||||||
|
|
||||||
|
var openResult = m_Engine.AdvancePulseForDebug(open);
|
||||||
|
var closedResult = m_Engine.AdvancePulseForDebug(closed);
|
||||||
|
|
||||||
|
Assert.True(openResult.GetUnderground(new(3, 2), ECarrierType.Fuel).Amount > 0);
|
||||||
|
Assert.Equal(ELevelState.Ready, openResult.Global.LevelState);
|
||||||
|
Assert.Equal(0, closedResult.GetUnderground(new(3, 2), ECarrierType.Fuel).Amount);
|
||||||
|
Assert.NotEqual(ELevelState.Ready, closedResult.Global.LevelState);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TogglingIsolationValveIsLengthyAndAdvancesOneFixedPulse()
|
||||||
|
{
|
||||||
|
var level = IsolationValveLevel(EPropSwitchState.Enabled) with { Robot = new() { Position = new(2, 2) } };
|
||||||
|
|
||||||
|
var next = m_Engine.InteractProp(level);
|
||||||
|
|
||||||
|
Assert.Equal(EPropSwitchState.Disabled, next.GetProp(new(2, 2)).SwitchState);
|
||||||
|
Assert.Equal(1, next.Global.Pulse);
|
||||||
|
Assert.Equal(Balancing.Current.StepsPerPulse, next.Global.Step);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -320,5 +370,22 @@ public sealed class SimulationEngineTests
|
|||||||
return level.SetProp(new(2, 3), new() { Type = EPropType.Junction, Carrier = ECarrierType.Fuel, JunctionMode = mode });
|
return level.SetProp(new(2, 3), new() { Type = EPropType.Junction, Carrier = ECarrierType.Fuel, JunctionMode = mode });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static LevelState IsolationValveLevel(EPropSwitchState valveState)
|
||||||
|
{
|
||||||
|
var level = LevelState.Create("Valve", 6, 5);
|
||||||
|
level = AddLine(level, ECarrierType.Fuel, new(1, 2), new(2, 2));
|
||||||
|
level = AddLine(level, ECarrierType.Fuel, new(2, 2), new(3, 2));
|
||||||
|
level = level.SetProp(new(1, 2), new() { Type = EPropType.Flow, Carrier = ECarrierType.Fuel });
|
||||||
|
level = level.SetProp(new(2, 2), new() { Type = EPropType.IsolationValve, Carrier = ECarrierType.Fuel, SwitchState = valveState });
|
||||||
|
level = level.SetProp(new(3, 2), new() { Type = EPropType.ReactorControl, ReactorId = 1 });
|
||||||
|
return level with {
|
||||||
|
RequiredFuelConsumers = 0,
|
||||||
|
RequiredWaterConsumers = 0,
|
||||||
|
RequiredElectricityConsumers = 0,
|
||||||
|
Robot = new() { Position = new(3, 2) },
|
||||||
|
Reactors = [new() { ReactorId = 1, ControlPosition = new(3, 2) }]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private readonly SimulationEngine m_Engine = new();
|
private readonly SimulationEngine m_Engine = new();
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user