Name the All Asian multiplier after what it counts

The score window and the stored Contest.Multiplier1Name row both said
"Prefixes" for an Asian entrant, which counts DXCC entities.

Contest.MultiplierNames is now MultiplierNamesFor(StationInfo me), the
same shape as ExchangeFieldsFor. Every other contest returns the same
list it did before; All Asian returns "Countries" from Asia and
"Prefixes" from everywhere else. ContestDefinitions.For takes the
station as well, because that name is written to the log for N1MM.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157i7PiZyqBkquxS9A6FTdQ
This commit is contained in:
2026-09-10 20:47:51 +00:00
co-authored by Claude Opus 5
parent f5e3d9ed8a
commit b5d9f1e21c
36 changed files with 62 additions and 40 deletions
+1 -1
View File
@@ -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. 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. 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.
+2 -1
View File
@@ -213,7 +213,8 @@ public sealed class AppSession : IDisposable
/// N1MM reads the `Contest` table when it opens a log, so the definition is /// 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. /// written again whenever a contest is opened, not only when it is created.
private void SaveDefinition(Contest contest) => private void SaveDefinition(Contest contest) =>
Store.SaveContestDefinition(ContestDefinitions.For(contest)); Store.SaveContestDefinition(
ContestDefinitions.For(contest, Settings.Station.ToStationInfo()));
public void OpenContest(int contestNumber) public void OpenContest(int contestNumber)
{ {
+1 -1
View File
@@ -875,7 +875,7 @@ public sealed partial class EntryWindow : Window
BreakdownText.Text = string.Join( BreakdownText.Text = string.Join(
"/", "/",
new[] { Logging.Log.Tally.Qsos }.Concat( new[] { Logging.Log.Tally.Qsos }.Concat(
Logging.Contest.MultiplierNames.Select( Logging.Contest.MultiplierNamesFor(Logging.Me).Select(
(_, at) => Logging.Log.Tally.MultiplierCount(at + 1)))); (_, at) => Logging.Log.Tally.MultiplierCount(at + 1))));
ScoreText.Text = $"{Logging.Log.TotalScore:N0}"; ScoreText.Text = $"{Logging.Log.TotalScore:N0}";
} }
+4 -2
View File
@@ -43,8 +43,10 @@ public interface Contest
/// boxes where a contest does not say otherwise. /// boxes where a contest does not say otherwise.
IReadOnlyList<ExchangeField> LogFieldsFor(StationInfo me) => ExchangeFieldsFor(me); IReadOnlyList<ExchangeField> LogFieldsFor(StationInfo me) => ExchangeFieldsFor(me);
/// Up to three names, in the order the score summary shows them. /// Up to three names, in the order the score summary shows them. All Asian
IReadOnlyList<string> MultiplierNames { get; } /// 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<string> MultiplierNamesFor(StationInfo me);
DupeScope DupeScope { get; } DupeScope DupeScope { get; }
+2 -1
View File
@@ -26,7 +26,8 @@ public sealed class AllAsian : Contest
new ExchangeField("Age", ExchangeSlot.Exchange1, ExchangeFieldKind.Number) { Width = 3 }, new ExchangeField("Age", ExchangeSlot.Exchange1, ExchangeFieldKind.Number) { Width = 3 },
]; ];
public IReadOnlyList<string> MultiplierNames => ["Prefixes"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) =>
me.Continent == "AS" ? ["Countries"] : ["Prefixes"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -41,7 +41,7 @@ public sealed class Arrl10Metre : Contest
? qso with { ReceivedNumber = number, Section = "" } ? qso with { ReceivedNumber = number, Section = "" }
: qso with { ReceivedNumber = 0, Section = typed.Trim().ToUpperInvariant() }; : qso with { ReceivedNumber = 0, Section = typed.Trim().ToUpperInvariant() };
public IReadOnlyList<string> MultiplierNames => ["States", "Countries"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["States", "Countries"];
public DupeScope DupeScope => DupeScope.PerMode; public DupeScope DupeScope => DupeScope.PerMode;
+1 -1
View File
@@ -33,7 +33,7 @@ public sealed class ArrlDx : Contest
: new ExchangeField("State", ExchangeSlot.Section, ExchangeFieldKind.UsStateOrCanadianProvince), : new ExchangeField("State", ExchangeSlot.Section, ExchangeFieldKind.UsStateOrCanadianProvince),
]; ];
public IReadOnlyList<string> MultiplierNames => ["Mults"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Mults"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -36,7 +36,7 @@ public sealed class BartgSprint : Contest
/// N1MM. /// N1MM.
public bool StampsSentTime => true; public bool StampsSentTime => true;
public IReadOnlyList<string> MultiplierNames => ["Countries", "Areas", "Continents"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Countries", "Areas", "Continents"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -40,7 +40,7 @@ public sealed class CqOneSixty : Contest
? qso with { Section = "" } ? qso with { Section = "" }
: qso with { Section = typed.Trim().ToUpperInvariant() }; : qso with { Section = typed.Trim().ToUpperInvariant() };
public IReadOnlyList<string> MultiplierNames => ["States", "Countries"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["States", "Countries"];
public DupeScope DupeScope => DupeScope.Once; public DupeScope DupeScope => DupeScope.Once;
+1 -1
View File
@@ -60,7 +60,7 @@ public sealed class CqWorldWide : Contest
&& their is not null && their is not null
&& their.Entity.PrimaryPrefix is not ("K" or "VE"); && their.Entity.PrimaryPrefix is not ("K" or "VE");
public IReadOnlyList<string> MultiplierNames => public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) =>
IsRtty ? ["Zones", "Countries", "States"] : ["Zones", "Countries"]; IsRtty ? ["Zones", "Countries", "States"] : ["Zones", "Countries"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -39,7 +39,7 @@ public sealed class CqWpx : Contest
new ExchangeField("RcvNR", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), new ExchangeField("RcvNR", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number),
]; ];
public IReadOnlyList<string> MultiplierNames => ["Prefixes"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Prefixes"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -43,7 +43,7 @@ public sealed class EuDxContest : Contest
? qso with { Section = typed.Trim().ToUpperInvariant() } ? qso with { Section = typed.Trim().ToUpperInvariant() }
: qso with { Zone = int.TryParse(typed, out int zone) ? zone : 0 }; : qso with { Zone = int.TryParse(typed, out int zone) ? zone : 0 };
public IReadOnlyList<string> MultiplierNames => ["Codes", "Countries"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Codes", "Countries"];
public DupeScope DupeScope => DupeScope.PerBandAndMode; public DupeScope DupeScope => DupeScope.PerBandAndMode;
@@ -21,7 +21,7 @@ public sealed class FieldDayRegionOne : Contest
new ExchangeField("Nr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), new ExchangeField("Nr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number),
]; ];
public IReadOnlyList<string> MultiplierNames => ["Countries"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Countries"];
public DupeScope DupeScope => DupeScope.PerBandAndMode; public DupeScope DupeScope => DupeScope.PerBandAndMode;
+1 -1
View File
@@ -58,7 +58,7 @@ public sealed class FrenchRef : Contest
new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 5 }, new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 5 },
]; ];
public IReadOnlyList<string> MultiplierNames => ["Mults"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Mults"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -20,7 +20,7 @@ public sealed class GeneralLogging : Contest
new ExchangeField("Comment", ExchangeSlot.Comment, ExchangeFieldKind.Text, IsRequired: false) { Width = 20 }, new ExchangeField("Comment", ExchangeSlot.Comment, ExchangeFieldKind.Text, IsRequired: false) { Width = 20 },
]; ];
public IReadOnlyList<string> MultiplierNames => []; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => [];
public DupeScope DupeScope => DupeScope.PerBandAndMode; public DupeScope DupeScope => DupeScope.PerBandAndMode;
+1 -1
View File
@@ -22,7 +22,7 @@ public sealed class IaruHf : Contest
new ExchangeField("ITU-Zone", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 6 }, new ExchangeField("ITU-Zone", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 6 },
]; ];
public IReadOnlyList<string> MultiplierNames => ["Zones", "HQ"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Zones", "HQ"];
public bool UsesItuZones => true; public bool UsesItuZones => true;
+1 -1
View File
@@ -29,7 +29,7 @@ public sealed class Iota : Contest
}, },
]; ];
public IReadOnlyList<string> MultiplierNames => ["Islands"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Islands"];
public DupeScope DupeScope => DupeScope.PerBandAndMode; public DupeScope DupeScope => DupeScope.PerBandAndMode;
+1 -1
View File
@@ -37,7 +37,7 @@ public sealed class MexicoRtty : Contest
Section = IsMexican(qso.CountryPrefix) ? typed.Trim().ToUpperInvariant() : "", Section = IsMexican(qso.CountryPrefix) ? typed.Trim().ToUpperInvariant() : "",
}; };
public IReadOnlyList<string> MultiplierNames => ["Countries", "States"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Countries", "States"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
@@ -28,7 +28,7 @@ public sealed class NorthAmericanQsoParty : Contest
new ExchangeField("State", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 6 }, new ExchangeField("State", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 6 },
]; ];
public IReadOnlyList<string> MultiplierNames => ["Mults"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Mults"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -26,7 +26,7 @@ public sealed class OceaniaDx : Contest
new ExchangeField("RcvNR", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), new ExchangeField("RcvNR", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number),
]; ];
public IReadOnlyList<string> MultiplierNames => ["Prefixes"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Prefixes"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -47,7 +47,7 @@ public sealed class OkOmDx : Contest
Section = IsOkOm(qso.CountryPrefix) ? typed.Trim().ToUpperInvariant() : "", Section = IsOkOm(qso.CountryPrefix) ? typed.Trim().ToUpperInvariant() : "",
}; };
public IReadOnlyList<string> MultiplierNames => ["Mults"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Mults"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -38,7 +38,7 @@ public sealed class RttyRoundup : Contest
? qso with { ReceivedNumber = number, Section = "" } ? qso with { ReceivedNumber = number, Section = "" }
: qso with { ReceivedNumber = 0, Section = typed.Trim().ToUpperInvariant() }; : qso with { ReceivedNumber = 0, Section = typed.Trim().ToUpperInvariant() };
public IReadOnlyList<string> MultiplierNames => ["Mults"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Mults"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
@@ -38,7 +38,7 @@ public sealed class RussianDistrictAward : Contest
/// N1MM names a country multiplier for this contest and never counts one, /// 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 /// and the score summary shows both, so the districts stay in the second
/// column here too. /// column here too.
public IReadOnlyList<string> MultiplierNames => ["Countries", "Districts"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Countries", "Districts"];
public DupeScope DupeScope => DupeScope.PerBandAndMode; public DupeScope DupeScope => DupeScope.PerBandAndMode;
+1 -1
View File
@@ -37,7 +37,7 @@ public sealed class RussianDxRtty : Contest
Section = typed.Trim().ToUpperInvariant(), Section = typed.Trim().ToUpperInvariant(),
}; };
public IReadOnlyList<string> MultiplierNames => ["Countries", "Oblasts"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Countries", "Oblasts"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -21,7 +21,7 @@ public sealed class SartgRtty : Contest
new ExchangeField("RcvNr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), new ExchangeField("RcvNr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number),
]; ];
public IReadOnlyList<string> MultiplierNames => ["Countries", "Areas"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Countries", "Areas"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -49,7 +49,7 @@ public sealed class Sweepstakes : Contest
new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.ArrlSection), new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.ArrlSection),
]; ];
public IReadOnlyList<string> MultiplierNames => ["Sections"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Sections"];
public DupeScope DupeScope => DupeScope.Once; public DupeScope DupeScope => DupeScope.Once;
+1 -1
View File
@@ -45,7 +45,7 @@ public sealed class UkrainianDx : Contest
new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 5 }, new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 5 },
]; ];
public IReadOnlyList<string> MultiplierNames => ["Countries", "Oblasts"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Countries", "Oblasts"];
public DupeScope DupeScope => DupeScope.PerBandAndMode; public DupeScope DupeScope => DupeScope.PerBandAndMode;
+1 -1
View File
@@ -55,7 +55,7 @@ public sealed class Wae : Contest
new ExchangeField("Nr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number), new ExchangeField("Nr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number),
]; ];
public IReadOnlyList<string> MultiplierNames => ["Mults"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Mults"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;
+1 -1
View File
@@ -45,7 +45,7 @@ public sealed class YoDxHf : Contest
new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 5 }, new ExchangeField("Sect", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 5 },
]; ];
public IReadOnlyList<string> MultiplierNames => ["Countries", "Counties"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Countries", "Counties"];
public DupeScope DupeScope => DupeScope.PerBandAndMode; public DupeScope DupeScope => DupeScope.PerBandAndMode;
+1 -1
View File
@@ -23,7 +23,7 @@ public sealed class Yota : Contest
new ExchangeField("Age", ExchangeSlot.Exchange1, ExchangeFieldKind.Number) { Width = 3 }, new ExchangeField("Age", ExchangeSlot.Exchange1, ExchangeFieldKind.Number) { Width = 3 },
]; ];
public IReadOnlyList<string> MultiplierNames => ["Ages"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["Ages"];
public DupeScope DupeScope => DupeScope.PerBandAndMode; public DupeScope DupeScope => DupeScope.PerBandAndMode;
@@ -52,7 +52,7 @@ public sealed class UserDefinedContest : Contest
public IReadOnlyList<ExchangeField> ExchangeFieldsFor(StationInfo me) => exchangeFields; public IReadOnlyList<ExchangeField> ExchangeFieldsFor(StationInfo me) => exchangeFields;
public IReadOnlyList<string> MultiplierNames => public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) =>
new[] { "Multiplier1Name", "Multiplier2Name", "Multiplier3Name" } new[] { "Multiplier1Name", "Multiplier2Name", "Multiplier3Name" }
.Take(multipliers.Count) .Take(multipliers.Count)
.Select(key => file.Text(key)) .Select(key => file.Text(key))
+2 -2
View File
@@ -8,9 +8,9 @@ namespace Nonemm.Session;
/// table, which it reads when it opens a log. /// table, which it reads when it opens a log.
public static class ContestDefinitions public static class ContestDefinitions
{ {
public static ContestDefinition For(Contest contest) public static ContestDefinition For(Contest contest, StationInfo me)
{ {
IReadOnlyList<string> mults = contest.MultiplierNames; IReadOnlyList<string> mults = contest.MultiplierNamesFor(me);
return new ContestDefinition return new ContestDefinition
{ {
Name = contest.Name, Name = contest.Name,
@@ -80,6 +80,15 @@ public class AllAsianTests
Assert.Empty(verdict.NewMultipliers); 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. /// An entrant outside Asia may only work Asia.
[Fact] [Fact]
public void OutsideEntrantCannotWorkOutsideAsia() public void OutsideEntrantCannotWorkOutsideAsia()
@@ -68,7 +68,7 @@ public class CqWorldWideRttyTests
[Fact] [Fact]
public void TheThreeMultiplierNamesAreShown() => public void TheThreeMultiplierNamesAreShown() =>
Assert.Equal(["Zones", "Countries", "States"], Rtty.MultiplierNames); Assert.Equal(["Zones", "Countries", "States"], Rtty.MultiplierNamesFor(TestLog.UnitedStates));
[Fact] [Fact]
public void TheCabrilloNameIsTheRttyOne() => Assert.Equal("CQ-WW-RTTY", Rtty.CabrilloName); public void TheCabrilloNameIsTheRttyOne() => Assert.Equal("CQ-WW-RTTY", Rtty.CabrilloName);
@@ -6,10 +6,19 @@ namespace Nonemm.Session.Tests;
public class ContestDefinitionsTests public class ContestDefinitionsTests
{ {
private static readonly StationInfo Germany = new()
{
Callsign = "DL1ABC",
CqZone = 14,
ItuZone = 28,
Continent = "EU",
CountryPrefix = "DL",
};
[Fact] [Fact]
public void NamesAndModeComeFromTheContest() 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("CQWWCW", definition.Name);
Assert.Equal("CQ-WW-CW", definition.CabrilloName); Assert.Equal("CQ-WW-CW", definition.CabrilloName);
Assert.Equal("CW", definition.Mode); Assert.Equal("CW", definition.Mode);
@@ -18,7 +27,7 @@ public class ContestDefinitionsTests
[Fact] [Fact]
public void MultiplierNamesFillUpToThree() 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("Zones", definition.Multiplier1Name);
Assert.Equal("Countries", definition.Multiplier2Name); Assert.Equal("Countries", definition.Multiplier2Name);
Assert.Equal("N/A", definition.Multiplier3Name); Assert.Equal("N/A", definition.Multiplier3Name);
@@ -33,10 +42,10 @@ public class ContestDefinitionsTests
object made = contest == typeof(IaruHf) object made = contest == typeof(IaruHf)
? new IaruHf() ? new IaruHf()
: Activator.CreateInstance(contest, ModeCategory.Cw)!; : 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] [Fact]
public void ContestWithSeveralModesIsBoth() => public void ContestWithSeveralModesIsBoth() =>
Assert.Equal("BOTH", ContestDefinitions.For(new IaruHf()).Mode); Assert.Equal("BOTH", ContestDefinitions.For(new IaruHf(), Germany).Mode);
} }
@@ -112,7 +112,7 @@ public class SkippedFieldTests
public bool SkipsField(ExchangeField field, CountryLookup? their) => public bool SkipsField(ExchangeField field, CountryLookup? their) =>
field.Slot == ExchangeSlot.Section && their?.Entity.PrimaryPrefix != "K"; field.Slot == ExchangeSlot.Section && their?.Entity.PrimaryPrefix != "K";
public IReadOnlyList<string> MultiplierNames => ["States"]; public IReadOnlyList<string> MultiplierNamesFor(StationInfo me) => ["States"];
public DupeScope DupeScope => DupeScope.PerBand; public DupeScope DupeScope => DupeScope.PerBand;