diff --git a/docs/scoring-audit-2026-09-06.md b/docs/scoring-audit-2026-09-06.md index ef6ce3f..562d5f0 100644 --- a/docs/scoring-audit-2026-09-06.md +++ b/docs/scoring-audit-2026-09-06.md @@ -36,7 +36,7 @@ Notable examples: all six WAE RTTY instances matched every compared field. Field 4. **P1 — All Asian implements the wrong rules for Asian entrants.** [AllAsian.cs](../src/Nonemm.Contests/Rules/AllAsian.cs), lines 37 and 46, multiplies every Asian entrant's band points by three and always uses WPX prefixes. Reproductions: JA-to-JA on 20M scored 3 instead of 0; JA-to-UA9 scored 3 instead of 1; JA-to-K1 produced prefix `K1` instead of country `K`. Asian entrants count DXCC entities, same-entity QSOs do not score, and the threefold factor applies to non-Asian contacts. Both the local N1MM `CAA` implementation and the [2026 JARL rules](https://www.jarl.org/English/4_Library/A-4-3_Contests/aadx_eng.html) distinguish these cases. The existing tests only cover a non-Asian entrant. - **Fixed 2026-09-10.** `AllAsian` now asks which side the entrant is on. An Asian entrant cannot work its own country, is paid the band table once for Asia and three times for everywhere else, and counts DXCC entities; everybody else works Asia only and counts WPX prefixes. Maritime mobiles follow the published rule, which N1MM gets half right: a station at sea makes a valid contact with Asian stations alone, pays the Asian rate wherever it is, and never brings a multiplier. `AllAsianTests` covers both entrant sides, all four point values and every band. One cosmetic gap is left: the score window heads the column "Prefixes" for an Asian entrant as well, because `Contest.MultiplierNames` is not asked which station is entering. + **Fixed 2026-09-10.** `AllAsian` now asks which side the entrant is on. An Asian entrant cannot work its own country, is paid the band table once for Asia and three times for everywhere else, and counts DXCC entities; everybody else works Asia only and counts WPX prefixes. Maritime mobiles follow the published rule, which N1MM gets half right: a station at sea makes a valid contact with Asian stations alone, pays the Asian rate wherever it is, and never brings a multiplier. `AllAsianTests` covers both entrant sides, all four point values and every band. `Contest.MultiplierNames` became `MultiplierNamesFor(StationInfo me)`, like `ExchangeFieldsFor`, so the score window and the stored `Contest.Multiplier1Name` row both say "Countries" for an Asian entrant and "Prefixes" for everybody else. 5. **P1 — BARTG HF and Sprint are merged into one incompatible definition.** [BartgSprint.cs](../src/Nonemm.Contests/Rules/BartgSprint.cs) uses report, serial and time, and countries/areas per band, but advertises `BARTGRTTYS` and `BARTG-SPRINT`. [N1mmContestNames.cs](../src/Nonemm.Contests/N1mmContestNames.cs) also maps `BARTGSRTTY` to this class. N1MM has separate `CBARTGSpring` and `CBARTGSprint` classes. The January Sprint exchanges a serial only and counts countries/areas once across bands, as specified in the [BARTG Sprint rules](https://bartg.org.uk/wp-content/uploads/2025/05/bartg-sprint-rules-2025-v3.pdf). Split the definitions and preserve each one's identity, exchange, multiplier scope and export format. Agreement on the supplied March HF log does not validate Sprint support. diff --git a/src/Nonemm.App/AppSession.cs b/src/Nonemm.App/AppSession.cs index 9b584ab..6d1e601 100644 --- a/src/Nonemm.App/AppSession.cs +++ b/src/Nonemm.App/AppSession.cs @@ -213,7 +213,8 @@ public sealed class AppSession : IDisposable /// N1MM reads the `Contest` table when it opens a log, so the definition is /// written again whenever a contest is opened, not only when it is created. private void SaveDefinition(Contest contest) => - Store.SaveContestDefinition(ContestDefinitions.For(contest)); + Store.SaveContestDefinition( + ContestDefinitions.For(contest, Settings.Station.ToStationInfo())); public void OpenContest(int contestNumber) { diff --git a/src/Nonemm.App/Windows/EntryWindow.axaml.cs b/src/Nonemm.App/Windows/EntryWindow.axaml.cs index 1ed6eca..83ea719 100644 --- a/src/Nonemm.App/Windows/EntryWindow.axaml.cs +++ b/src/Nonemm.App/Windows/EntryWindow.axaml.cs @@ -875,7 +875,7 @@ public sealed partial class EntryWindow : Window BreakdownText.Text = string.Join( "/", new[] { Logging.Log.Tally.Qsos }.Concat( - Logging.Contest.MultiplierNames.Select( + Logging.Contest.MultiplierNamesFor(Logging.Me).Select( (_, at) => Logging.Log.Tally.MultiplierCount(at + 1)))); ScoreText.Text = $"{Logging.Log.TotalScore:N0}"; } diff --git a/src/Nonemm.Contests/Contest.cs b/src/Nonemm.Contests/Contest.cs index 1cb58a6..88555df 100644 --- a/src/Nonemm.Contests/Contest.cs +++ b/src/Nonemm.Contests/Contest.cs @@ -43,8 +43,10 @@ public interface Contest /// boxes where a contest does not say otherwise. IReadOnlyList LogFieldsFor(StationInfo me) => ExchangeFieldsFor(me); - /// Up to three names, in the order the score summary shows them. - IReadOnlyList MultiplierNames { get; } + /// Up to three names, in the order the score summary shows them. All Asian + /// counts entities for an Asian entrant and prefixes for everybody else, + /// so the station is passed in as it is for the entry boxes. + IReadOnlyList MultiplierNamesFor(StationInfo me); DupeScope DupeScope { get; } diff --git a/src/Nonemm.Contests/Rules/AllAsian.cs b/src/Nonemm.Contests/Rules/AllAsian.cs index 7ca7fc4..3aaad9e 100644 --- a/src/Nonemm.Contests/Rules/AllAsian.cs +++ b/src/Nonemm.Contests/Rules/AllAsian.cs @@ -26,7 +26,8 @@ public sealed class AllAsian : Contest new ExchangeField("Age", ExchangeSlot.Exchange1, ExchangeFieldKind.Number) { Width = 3 }, ]; - public IReadOnlyList MultiplierNames => ["Prefixes"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => + me.Continent == "AS" ? ["Countries"] : ["Prefixes"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/Arrl10Metre.cs b/src/Nonemm.Contests/Rules/Arrl10Metre.cs index 32fb1ab..e6e6e0d 100644 --- a/src/Nonemm.Contests/Rules/Arrl10Metre.cs +++ b/src/Nonemm.Contests/Rules/Arrl10Metre.cs @@ -41,7 +41,7 @@ public sealed class Arrl10Metre : Contest ? qso with { ReceivedNumber = number, Section = "" } : qso with { ReceivedNumber = 0, Section = typed.Trim().ToUpperInvariant() }; - public IReadOnlyList MultiplierNames => ["States", "Countries"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["States", "Countries"]; public DupeScope DupeScope => DupeScope.PerMode; diff --git a/src/Nonemm.Contests/Rules/ArrlDx.cs b/src/Nonemm.Contests/Rules/ArrlDx.cs index 9fd4e1c..b71c477 100644 --- a/src/Nonemm.Contests/Rules/ArrlDx.cs +++ b/src/Nonemm.Contests/Rules/ArrlDx.cs @@ -33,7 +33,7 @@ public sealed class ArrlDx : Contest : new ExchangeField("State", ExchangeSlot.Section, ExchangeFieldKind.UsStateOrCanadianProvince), ]; - public IReadOnlyList MultiplierNames => ["Mults"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Mults"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/BartgSprint.cs b/src/Nonemm.Contests/Rules/BartgSprint.cs index 2606964..bcc3acd 100644 --- a/src/Nonemm.Contests/Rules/BartgSprint.cs +++ b/src/Nonemm.Contests/Rules/BartgSprint.cs @@ -36,7 +36,7 @@ public sealed class BartgSprint : Contest /// N1MM. public bool StampsSentTime => true; - public IReadOnlyList MultiplierNames => ["Countries", "Areas", "Continents"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Countries", "Areas", "Continents"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/CqOneSixty.cs b/src/Nonemm.Contests/Rules/CqOneSixty.cs index e13cfab..05b77c4 100644 --- a/src/Nonemm.Contests/Rules/CqOneSixty.cs +++ b/src/Nonemm.Contests/Rules/CqOneSixty.cs @@ -40,7 +40,7 @@ public sealed class CqOneSixty : Contest ? qso with { Section = "" } : qso with { Section = typed.Trim().ToUpperInvariant() }; - public IReadOnlyList MultiplierNames => ["States", "Countries"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["States", "Countries"]; public DupeScope DupeScope => DupeScope.Once; diff --git a/src/Nonemm.Contests/Rules/CqWorldWide.cs b/src/Nonemm.Contests/Rules/CqWorldWide.cs index 067d58d..ebf5e62 100644 --- a/src/Nonemm.Contests/Rules/CqWorldWide.cs +++ b/src/Nonemm.Contests/Rules/CqWorldWide.cs @@ -60,7 +60,7 @@ public sealed class CqWorldWide : Contest && their is not null && their.Entity.PrimaryPrefix is not ("K" or "VE"); - public IReadOnlyList MultiplierNames => + public IReadOnlyList MultiplierNamesFor(StationInfo me) => IsRtty ? ["Zones", "Countries", "States"] : ["Zones", "Countries"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/CqWpx.cs b/src/Nonemm.Contests/Rules/CqWpx.cs index a17f1fe..72a1865 100644 --- a/src/Nonemm.Contests/Rules/CqWpx.cs +++ b/src/Nonemm.Contests/Rules/CqWpx.cs @@ -39,7 +39,7 @@ public sealed class CqWpx : Contest new ExchangeField("RcvNR", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), ]; - public IReadOnlyList MultiplierNames => ["Prefixes"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Prefixes"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/EuDxContest.cs b/src/Nonemm.Contests/Rules/EuDxContest.cs index 39f0a0d..d910d97 100644 --- a/src/Nonemm.Contests/Rules/EuDxContest.cs +++ b/src/Nonemm.Contests/Rules/EuDxContest.cs @@ -43,7 +43,7 @@ public sealed class EuDxContest : Contest ? qso with { Section = typed.Trim().ToUpperInvariant() } : qso with { Zone = int.TryParse(typed, out int zone) ? zone : 0 }; - public IReadOnlyList MultiplierNames => ["Codes", "Countries"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Codes", "Countries"]; public DupeScope DupeScope => DupeScope.PerBandAndMode; diff --git a/src/Nonemm.Contests/Rules/FieldDayRegionOne.cs b/src/Nonemm.Contests/Rules/FieldDayRegionOne.cs index cba27ae..8968a4f 100644 --- a/src/Nonemm.Contests/Rules/FieldDayRegionOne.cs +++ b/src/Nonemm.Contests/Rules/FieldDayRegionOne.cs @@ -21,7 +21,7 @@ public sealed class FieldDayRegionOne : Contest new ExchangeField("Nr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), ]; - public IReadOnlyList MultiplierNames => ["Countries"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Countries"]; public DupeScope DupeScope => DupeScope.PerBandAndMode; diff --git a/src/Nonemm.Contests/Rules/FrenchRef.cs b/src/Nonemm.Contests/Rules/FrenchRef.cs index 4eb0fac..2e27c3f 100644 --- a/src/Nonemm.Contests/Rules/FrenchRef.cs +++ b/src/Nonemm.Contests/Rules/FrenchRef.cs @@ -58,7 +58,7 @@ public sealed class FrenchRef : Contest new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 5 }, ]; - public IReadOnlyList MultiplierNames => ["Mults"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Mults"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/GeneralLogging.cs b/src/Nonemm.Contests/Rules/GeneralLogging.cs index f87e4a7..ebbe573 100644 --- a/src/Nonemm.Contests/Rules/GeneralLogging.cs +++ b/src/Nonemm.Contests/Rules/GeneralLogging.cs @@ -20,7 +20,7 @@ public sealed class GeneralLogging : Contest new ExchangeField("Comment", ExchangeSlot.Comment, ExchangeFieldKind.Text, IsRequired: false) { Width = 20 }, ]; - public IReadOnlyList MultiplierNames => []; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => []; public DupeScope DupeScope => DupeScope.PerBandAndMode; diff --git a/src/Nonemm.Contests/Rules/IaruHf.cs b/src/Nonemm.Contests/Rules/IaruHf.cs index 61c5aec..05f6647 100644 --- a/src/Nonemm.Contests/Rules/IaruHf.cs +++ b/src/Nonemm.Contests/Rules/IaruHf.cs @@ -22,7 +22,7 @@ public sealed class IaruHf : Contest new ExchangeField("ITU-Zone", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 6 }, ]; - public IReadOnlyList MultiplierNames => ["Zones", "HQ"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Zones", "HQ"]; public bool UsesItuZones => true; diff --git a/src/Nonemm.Contests/Rules/Iota.cs b/src/Nonemm.Contests/Rules/Iota.cs index 5c7793d..e74189c 100644 --- a/src/Nonemm.Contests/Rules/Iota.cs +++ b/src/Nonemm.Contests/Rules/Iota.cs @@ -29,7 +29,7 @@ public sealed class Iota : Contest }, ]; - public IReadOnlyList MultiplierNames => ["Islands"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Islands"]; public DupeScope DupeScope => DupeScope.PerBandAndMode; diff --git a/src/Nonemm.Contests/Rules/MexicoRtty.cs b/src/Nonemm.Contests/Rules/MexicoRtty.cs index 90864ad..fa2f9b0 100644 --- a/src/Nonemm.Contests/Rules/MexicoRtty.cs +++ b/src/Nonemm.Contests/Rules/MexicoRtty.cs @@ -37,7 +37,7 @@ public sealed class MexicoRtty : Contest Section = IsMexican(qso.CountryPrefix) ? typed.Trim().ToUpperInvariant() : "", }; - public IReadOnlyList MultiplierNames => ["Countries", "States"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Countries", "States"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/NorthAmericanQsoParty.cs b/src/Nonemm.Contests/Rules/NorthAmericanQsoParty.cs index 3773121..10771b4 100644 --- a/src/Nonemm.Contests/Rules/NorthAmericanQsoParty.cs +++ b/src/Nonemm.Contests/Rules/NorthAmericanQsoParty.cs @@ -28,7 +28,7 @@ public sealed class NorthAmericanQsoParty : Contest new ExchangeField("State", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 6 }, ]; - public IReadOnlyList MultiplierNames => ["Mults"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Mults"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/OceaniaDx.cs b/src/Nonemm.Contests/Rules/OceaniaDx.cs index 02f918d..c106b56 100644 --- a/src/Nonemm.Contests/Rules/OceaniaDx.cs +++ b/src/Nonemm.Contests/Rules/OceaniaDx.cs @@ -26,7 +26,7 @@ public sealed class OceaniaDx : Contest new ExchangeField("RcvNR", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), ]; - public IReadOnlyList MultiplierNames => ["Prefixes"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Prefixes"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/OkOmDx.cs b/src/Nonemm.Contests/Rules/OkOmDx.cs index d1fe323..0c95381 100644 --- a/src/Nonemm.Contests/Rules/OkOmDx.cs +++ b/src/Nonemm.Contests/Rules/OkOmDx.cs @@ -47,7 +47,7 @@ public sealed class OkOmDx : Contest Section = IsOkOm(qso.CountryPrefix) ? typed.Trim().ToUpperInvariant() : "", }; - public IReadOnlyList MultiplierNames => ["Mults"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Mults"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/RttyRoundup.cs b/src/Nonemm.Contests/Rules/RttyRoundup.cs index c50e466..e600700 100644 --- a/src/Nonemm.Contests/Rules/RttyRoundup.cs +++ b/src/Nonemm.Contests/Rules/RttyRoundup.cs @@ -38,7 +38,7 @@ public sealed class RttyRoundup : Contest ? qso with { ReceivedNumber = number, Section = "" } : qso with { ReceivedNumber = 0, Section = typed.Trim().ToUpperInvariant() }; - public IReadOnlyList MultiplierNames => ["Mults"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Mults"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/RussianDistrictAward.cs b/src/Nonemm.Contests/Rules/RussianDistrictAward.cs index 0d26566..e8dd6af 100644 --- a/src/Nonemm.Contests/Rules/RussianDistrictAward.cs +++ b/src/Nonemm.Contests/Rules/RussianDistrictAward.cs @@ -38,7 +38,7 @@ public sealed class RussianDistrictAward : Contest /// N1MM names a country multiplier for this contest and never counts one, /// and the score summary shows both, so the districts stay in the second /// column here too. - public IReadOnlyList MultiplierNames => ["Countries", "Districts"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Countries", "Districts"]; public DupeScope DupeScope => DupeScope.PerBandAndMode; diff --git a/src/Nonemm.Contests/Rules/RussianDxRtty.cs b/src/Nonemm.Contests/Rules/RussianDxRtty.cs index 0dd28e1..77eb4aa 100644 --- a/src/Nonemm.Contests/Rules/RussianDxRtty.cs +++ b/src/Nonemm.Contests/Rules/RussianDxRtty.cs @@ -37,7 +37,7 @@ public sealed class RussianDxRtty : Contest Section = typed.Trim().ToUpperInvariant(), }; - public IReadOnlyList MultiplierNames => ["Countries", "Oblasts"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Countries", "Oblasts"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/SartgRtty.cs b/src/Nonemm.Contests/Rules/SartgRtty.cs index 7d5ef9a..d241418 100644 --- a/src/Nonemm.Contests/Rules/SartgRtty.cs +++ b/src/Nonemm.Contests/Rules/SartgRtty.cs @@ -21,7 +21,7 @@ public sealed class SartgRtty : Contest new ExchangeField("RcvNr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), ]; - public IReadOnlyList MultiplierNames => ["Countries", "Areas"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Countries", "Areas"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/Sweepstakes.cs b/src/Nonemm.Contests/Rules/Sweepstakes.cs index b1dc065..35a5aea 100644 --- a/src/Nonemm.Contests/Rules/Sweepstakes.cs +++ b/src/Nonemm.Contests/Rules/Sweepstakes.cs @@ -49,7 +49,7 @@ public sealed class Sweepstakes : Contest new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.ArrlSection), ]; - public IReadOnlyList MultiplierNames => ["Sections"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Sections"]; public DupeScope DupeScope => DupeScope.Once; diff --git a/src/Nonemm.Contests/Rules/UkrainianDx.cs b/src/Nonemm.Contests/Rules/UkrainianDx.cs index 2e08b4a..970abca 100644 --- a/src/Nonemm.Contests/Rules/UkrainianDx.cs +++ b/src/Nonemm.Contests/Rules/UkrainianDx.cs @@ -45,7 +45,7 @@ public sealed class UkrainianDx : Contest new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 5 }, ]; - public IReadOnlyList MultiplierNames => ["Countries", "Oblasts"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Countries", "Oblasts"]; public DupeScope DupeScope => DupeScope.PerBandAndMode; diff --git a/src/Nonemm.Contests/Rules/Wae.cs b/src/Nonemm.Contests/Rules/Wae.cs index 912fae2..f5b73bb 100644 --- a/src/Nonemm.Contests/Rules/Wae.cs +++ b/src/Nonemm.Contests/Rules/Wae.cs @@ -55,7 +55,7 @@ public sealed class Wae : Contest new ExchangeField("Nr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), ]; - public IReadOnlyList MultiplierNames => ["Mults"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Mults"]; public DupeScope DupeScope => DupeScope.PerBand; diff --git a/src/Nonemm.Contests/Rules/YoDxHf.cs b/src/Nonemm.Contests/Rules/YoDxHf.cs index 4bb96e0..42535e7 100644 --- a/src/Nonemm.Contests/Rules/YoDxHf.cs +++ b/src/Nonemm.Contests/Rules/YoDxHf.cs @@ -45,7 +45,7 @@ public sealed class YoDxHf : Contest new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 5 }, ]; - public IReadOnlyList MultiplierNames => ["Countries", "Counties"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Countries", "Counties"]; public DupeScope DupeScope => DupeScope.PerBandAndMode; diff --git a/src/Nonemm.Contests/Rules/Yota.cs b/src/Nonemm.Contests/Rules/Yota.cs index 1135712..8ab6328 100644 --- a/src/Nonemm.Contests/Rules/Yota.cs +++ b/src/Nonemm.Contests/Rules/Yota.cs @@ -23,7 +23,7 @@ public sealed class Yota : Contest new ExchangeField("Age", ExchangeSlot.Exchange1, ExchangeFieldKind.Number) { Width = 3 }, ]; - public IReadOnlyList MultiplierNames => ["Ages"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["Ages"]; public DupeScope DupeScope => DupeScope.PerBandAndMode; diff --git a/src/Nonemm.Contests/Udc/UserDefinedContest.cs b/src/Nonemm.Contests/Udc/UserDefinedContest.cs index 87c76ad..e015f32 100644 --- a/src/Nonemm.Contests/Udc/UserDefinedContest.cs +++ b/src/Nonemm.Contests/Udc/UserDefinedContest.cs @@ -52,7 +52,7 @@ public sealed class UserDefinedContest : Contest public IReadOnlyList ExchangeFieldsFor(StationInfo me) => exchangeFields; - public IReadOnlyList MultiplierNames => + public IReadOnlyList MultiplierNamesFor(StationInfo me) => new[] { "Multiplier1Name", "Multiplier2Name", "Multiplier3Name" } .Take(multipliers.Count) .Select(key => file.Text(key)) diff --git a/src/Nonemm.Session/ContestDefinitions.cs b/src/Nonemm.Session/ContestDefinitions.cs index a08a2ca..bc0ae5a 100644 --- a/src/Nonemm.Session/ContestDefinitions.cs +++ b/src/Nonemm.Session/ContestDefinitions.cs @@ -8,9 +8,9 @@ namespace Nonemm.Session; /// table, which it reads when it opens a log. public static class ContestDefinitions { - public static ContestDefinition For(Contest contest) + public static ContestDefinition For(Contest contest, StationInfo me) { - IReadOnlyList mults = contest.MultiplierNames; + IReadOnlyList mults = contest.MultiplierNamesFor(me); return new ContestDefinition { Name = contest.Name, diff --git a/tests/Nonemm.Contests.Tests/AllAsianTests.cs b/tests/Nonemm.Contests.Tests/AllAsianTests.cs index 9fb614b..47dc349 100644 --- a/tests/Nonemm.Contests.Tests/AllAsianTests.cs +++ b/tests/Nonemm.Contests.Tests/AllAsianTests.cs @@ -80,6 +80,15 @@ public class AllAsianTests Assert.Empty(verdict.NewMultipliers); } + /// The column heading follows what is being counted. + [Fact] + public void TheMultiplierNameFollowsTheEntrant() + { + Contest contest = new AllAsian(ModeCategory.Phone); + Assert.Equal(["Countries"], contest.MultiplierNamesFor(TestLog.Japan)); + Assert.Equal(["Prefixes"], contest.MultiplierNamesFor(TestLog.Germany)); + } + /// An entrant outside Asia may only work Asia. [Fact] public void OutsideEntrantCannotWorkOutsideAsia() diff --git a/tests/Nonemm.Contests.Tests/CqWorldWideRttyTests.cs b/tests/Nonemm.Contests.Tests/CqWorldWideRttyTests.cs index 992814a..c2d41b5 100644 --- a/tests/Nonemm.Contests.Tests/CqWorldWideRttyTests.cs +++ b/tests/Nonemm.Contests.Tests/CqWorldWideRttyTests.cs @@ -68,7 +68,7 @@ public class CqWorldWideRttyTests [Fact] public void TheThreeMultiplierNamesAreShown() => - Assert.Equal(["Zones", "Countries", "States"], Rtty.MultiplierNames); + Assert.Equal(["Zones", "Countries", "States"], Rtty.MultiplierNamesFor(TestLog.UnitedStates)); [Fact] public void TheCabrilloNameIsTheRttyOne() => Assert.Equal("CQ-WW-RTTY", Rtty.CabrilloName); diff --git a/tests/Nonemm.Session.Tests/ContestDefinitionsTests.cs b/tests/Nonemm.Session.Tests/ContestDefinitionsTests.cs index dd7c798..0bdea34 100644 --- a/tests/Nonemm.Session.Tests/ContestDefinitionsTests.cs +++ b/tests/Nonemm.Session.Tests/ContestDefinitionsTests.cs @@ -6,10 +6,19 @@ namespace Nonemm.Session.Tests; public class ContestDefinitionsTests { + private static readonly StationInfo Germany = new() + { + Callsign = "DL1ABC", + CqZone = 14, + ItuZone = 28, + Continent = "EU", + CountryPrefix = "DL", + }; + [Fact] public void NamesAndModeComeFromTheContest() { - ContestDefinition definition = ContestDefinitions.For(new CqWorldWide(ModeCategory.Cw)); + ContestDefinition definition = ContestDefinitions.For(new CqWorldWide(ModeCategory.Cw), Germany); Assert.Equal("CQWWCW", definition.Name); Assert.Equal("CQ-WW-CW", definition.CabrilloName); Assert.Equal("CW", definition.Mode); @@ -18,7 +27,7 @@ public class ContestDefinitionsTests [Fact] public void MultiplierNamesFillUpToThree() { - ContestDefinition definition = ContestDefinitions.For(new CqWorldWide(ModeCategory.Cw)); + ContestDefinition definition = ContestDefinitions.For(new CqWorldWide(ModeCategory.Cw), Germany); Assert.Equal("Zones", definition.Multiplier1Name); Assert.Equal("Countries", definition.Multiplier2Name); Assert.Equal("N/A", definition.Multiplier3Name); @@ -33,10 +42,10 @@ public class ContestDefinitionsTests object made = contest == typeof(IaruHf) ? new IaruHf() : Activator.CreateInstance(contest, ModeCategory.Cw)!; - Assert.Equal(expected, ContestDefinitions.For((Contests.Contest)made).DupeType); + Assert.Equal(expected, ContestDefinitions.For((Contests.Contest)made, Germany).DupeType); } [Fact] public void ContestWithSeveralModesIsBoth() => - Assert.Equal("BOTH", ContestDefinitions.For(new IaruHf()).Mode); + Assert.Equal("BOTH", ContestDefinitions.For(new IaruHf(), Germany).Mode); } diff --git a/tests/Nonemm.Session.Tests/SkippedFieldTests.cs b/tests/Nonemm.Session.Tests/SkippedFieldTests.cs index 7d29b68..5bc574c 100644 --- a/tests/Nonemm.Session.Tests/SkippedFieldTests.cs +++ b/tests/Nonemm.Session.Tests/SkippedFieldTests.cs @@ -112,7 +112,7 @@ public class SkippedFieldTests public bool SkipsField(ExchangeField field, CountryLookup? their) => field.Slot == ExchangeSlot.Section && their?.Entity.PrimaryPrefix != "K"; - public IReadOnlyList MultiplierNames => ["States"]; + public IReadOnlyList MultiplierNamesFor(StationInfo me) => ["States"]; public DupeScope DupeScope => DupeScope.PerBand;