using System;
namespace Eu07.BlackBox;
/// What the driver of the user's train went by: the lights it is to show depend on it.
public enum Movement : byte
{
/// No driver, or standing by.
Other = 0,
/// A train run: Pc 1 or Pc 2 at the head, Pc 5 at the tail.
Train = 1,
/// Shunting: Tb 1 at both ends.
Shunting = 2,
/// A recording made before it was written.
Unknown = 0xFF
}
/// The light signals at an end of a train.
public enum HeadCode
{
Off,
/// The head of a train: both headlights and the upper one.
Pc1,
/// The head of a train on the wrong track: the left red marker, the right headlight and the upper one.
Pc2,
/// The tail of a train: both red markers.
Pc5,
/// Shunting: one headlight.
Tb1,
/// Any other pattern.
Other
}
/// The light signals a train shows and those it is to show, as the simulator's driver sets them.
public static class HeadCodes
{
private const Lights Signals = Lights.HeadlightLeft | Lights.RedMarkerLeft | Lights.HeadlightUpper | Lights.HeadlightRight |
Lights.RedMarkerRight | Lights.RearEndSignals;
/// The signal the lamps given show; the high beams and the auxiliary lamps tell nothing.
public static HeadCode Of(Lights lights)
{
var lit = lights & Signals;
if ((lit & Lights.RearEndSignals) != 0)
lit = (lit & ~Lights.RearEndSignals) | Lights.RedMarkerLeft | Lights.RedMarkerRight;
return lit switch
{
Lights.None => HeadCode.Off,
Lights.HeadlightLeft | Lights.HeadlightRight | Lights.HeadlightUpper => HeadCode.Pc1,
Lights.RedMarkerLeft | Lights.HeadlightRight | Lights.HeadlightUpper => HeadCode.Pc2,
Lights.RedMarkerLeft | Lights.RedMarkerRight => HeadCode.Pc5,
Lights.HeadlightLeft or Lights.HeadlightRight => HeadCode.Tb1,
_ => HeadCode.Other
};
}
/// The signal the head of the train is to show, null when nothing tells it.
public static HeadCode? RequiredHead(CabSample sample) => sample.Movement switch
{
Movement.Train => sample.LightHintHead < 0 ? HeadCode.Pc1 : Of((Lights)sample.LightHintHead),
Movement.Shunting => HeadCode.Tb1,
_ => null
};
/// The signal the tail of the train is to show, null when nothing tells it.
public static HeadCode? RequiredTail(CabSample sample) => sample.Movement switch
{
Movement.Train => sample.LightHintTail < 0 ? HeadCode.Pc5 : Of((Lights)sample.LightHintTail),
Movement.Shunting => HeadCode.Tb1,
_ => null
};
/// Whether both ends showed what they were to while the train moved; null when nothing tells it.
public static bool? Right(CabSample sample)
{
if (RequiredHead(sample) is not { } head || RequiredTail(sample) is not { } tail)
return null;
return Of(sample.LightsHead) == head && Of(sample.LightsTail) == tail;
}
/// The name of a signal as the rules give it.
public static string Name(HeadCode code) => code switch
{
HeadCode.Pc1 => "Pc 1",
HeadCode.Pc2 => "Pc 2",
HeadCode.Pc5 => "Pc 5",
HeadCode.Tb1 => "Tb 1",
HeadCode.Off => "—",
_ => "?"
};
}