using Nonemm.Core; namespace Nonemm.Contests.Rules; /// The EU DX Contest. Its "Europe" is the European Union: the member states, /// their outermost regions and their overseas territories, and nothing else. /// The exchange is the country and district code of the other station. /// /// A contact with a station in that list is worth 10 points, or 2 inside your /// own country. Everything else is 3 points on your own continent and 5 from /// another. public sealed class EuDxContest : Contest { /// The member states, and the entities that are legally part of one: the /// Azores and Madeira, the Canaries, Ceuta and Melilla, the French overseas /// departments, Cyprus, and the Dutch and Danish territories. private static readonly IReadOnlySet Union = new HashSet(StringComparer.OrdinalIgnoreCase) { "9A", "9H", "CT", "CU", "DL", "EA", "EA6", "EI", "ES", "F", "HA", "I", "IS", "IT9", "LX", "LY", "LZ", "OE", "OH", "OH0", "OJ0", "OK", "OM", "ON", "OX", "OZ", "PA", "S5", "SM", "SP", "SV", "SV5", "SV9", "TK", "YL", "YO", "5B", "CT3", "EA8", "EA9", "FO", "FP", "FY", "IG9", "IH9", "P4", "PJ0", "PJ2", "PJ4", "PJ5", "PJ6", "PJ7", "PJ8", }; public string Name => "EUDXC"; public string DisplayName => "EU DX Contest"; public string CabrilloName => "EUDXC"; public IReadOnlyList ExchangeFieldsFor(StationInfo me) => [ new ExchangeField("Snt", ExchangeSlot.SentReport, ExchangeFieldKind.Report, IsRequired: false), new ExchangeField("Rcv", ExchangeSlot.ReceivedReport, ExchangeFieldKind.Report), new ExchangeField("Exch", ExchangeSlot.Exchange1, ExchangeFieldKind.Text) { Width = 5 }, ]; /// A station in the union sends a district code, which goes in the section /// column; everyone else sends a CQ zone, which goes in the zone column. public Qso FromExchange1(Qso qso, string typed) => IsUnion(qso.CountryPrefix) ? qso with { Section = typed.Trim().ToUpperInvariant() } : qso with { Zone = int.TryParse(typed, out int zone) ? zone : 0 }; public IReadOnlyList MultiplierNames => ["Codes", "Countries"]; public DupeScope DupeScope => DupeScope.PerBandAndMode; public bool HasSerialNumbers => false; public IReadOnlyList Modes => [ModeCategory.Cw, ModeCategory.Phone]; public string SentExchangeFor(StationInfo me) => ""; public int PointsFor(QsoContext qso) { if (IsUnion(qso.CountryPrefix)) { return qso.IsSameCountry ? 2 : 10; } if (!qso.IsSameContinent) { return 5; } return IsUnion(qso.Me.CountryPrefix) || !qso.IsSameCountry ? 3 : 2; } /// The district code and the country each count once per band. Only a /// station in the union sends a code; everyone else sends a CQ zone, which /// counts for nothing. public IReadOnlyList MultipliersFor(QsoContext qso) { string band = qso.Band?.Name ?? ""; List found = []; string code = qso.Qso.Section.Trim().ToUpperInvariant(); if (IsUnion(qso.CountryPrefix) && code.Length > 0) { found.Add(new Multiplier(1, code, band)); } if (qso.CountryPrefix.Length > 0) { found.Add(new Multiplier(2, qso.CountryPrefix, band)); } return found; } public int TotalScore(ScoreTally tally, ContestEntry entry) => tally.Points * tally.TotalMultipliers; public CabrilloExchange CabrilloExchange(Qso qso, StationInfo me, ContestEntry entry) => new( [ new CabrilloField(me.Callsign, 13), new CabrilloField(qso.SentReport, 3), new CabrilloField(entry.SentExchange, 5), ], [ new CabrilloField(qso.Call.Text, 13), new CabrilloField(qso.ReceivedReport, 3), new CabrilloField(qso.Section.Length > 0 ? qso.Section : qso.Exchange1, 5), ]); private static bool IsUnion(string countryPrefix) => Union.Contains(countryPrefix); }