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 => "—", _ => "?" }; }