Score the last three cases the way N1MM scores them

The three differences left after replaying the log were ours on purpose. A log
has to score the same in both programs, so they now follow N1MM:

- ARRL DX and CQ WW RTTY count the 14 Canadian areas N1MM lists as `48SDC14P`,
  which keeps Newfoundland apart from Labrador. A station sending the postal
  code is counted as the area the contest names.
- A maritime mobile is scored from the call it signs. CQ WW says it counts for
  the zone alone; N1MM gives it the country, and so do we.
- A callsign the country file cannot place counts as a multiplier with an empty
  value, so every unplaceable call worked on a band shares one multiplier.

29 of the 37 contest instances in the log now agree with N1MM on every contact
and on the totals. What is left is the country file of the day and rows whose
cached points N1MM never worked out again after a call was corrected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
This commit is contained in:
2026-08-31 08:36:44 +00:00
parent 04568d0ca3
commit 5c0ca558c1
8 changed files with 69 additions and 60 deletions

View File

@@ -22,37 +22,41 @@ public static class StatesAndProvinces
};
/// The Canadian areas as the contests that split them name them:
/// Newfoundland apart from Labrador, and the territories spelled out.
/// Newfoundland apart from Labrador, and the territories spelled out. N1MM
/// calls this list `48SDC14P` — 48 states, DC and 14 provinces — and both
/// ARRL DX and CQ WW RTTY count them this way.
public static readonly IReadOnlyList<string> SplitCanadianAreas =
["NWT", "NF", "LB", "PEI"];
["NB", "NS", "QC", "ON", "MB", "SK", "AB", "BC", "NWT", "NF", "LB", "NU", "YT", "PEI"];
/// What ARRL DX counts for a DX station: the contiguous states, the
/// District of Columbia, and the Canadian provinces and territories.
/// What ARRL DX counts for a DX station: the 48 contiguous states, the
/// District of Columbia and those 14 areas. Alaska and Hawaii are DX for
/// this contest and count for none of it.
public static readonly IReadOnlySet<string> ArrlDxMultipliers =
new HashSet<string>(ContiguousStates.Concat(["DC"]).Concat(Provinces), StringComparer.OrdinalIgnoreCase);
new HashSet<string>(
ContiguousStates.Concat(["DC"]).Concat(SplitCanadianAreas),
StringComparer.OrdinalIgnoreCase);
/// The same province under another name. Canadian stations send the older
/// abbreviations as often as the postal codes, and ARRL DX counts the
/// province, not the spelling, so `NF` and `LB` are both Newfoundland and
/// Labrador and count once between them. N1MM counts them as two.
/// The same area under another name. A Canadian station sends the postal
/// code as often as the abbreviation the contest lists, and `NL` is read as
/// Newfoundland, which is the half of that province a station signing the
/// postal code is nearly always in; Labrador signs `LB`.
private static readonly IReadOnlyDictionary<string, string> OtherSpellings =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["NF"] = "NL",
["LB"] = "NL",
["NFL"] = "NL",
["NL"] = "NF",
["NFL"] = "NF",
["QB"] = "QC",
["PQ"] = "QC",
["PEI"] = "PE",
["NWT"] = "NT",
["PE"] = "PEI",
["NT"] = "NWT",
};
/// The province or state a received ARRL DX exchange names, or the exchange
/// as it was sent when it names none.
/// The area a received ARRL DX exchange names, or the exchange as it was
/// sent when it names none.
public static string ArrlDxArea(string received)
{
string text = received.Trim();
return OtherSpellings.TryGetValue(text, out string? province) ? province : text.ToUpperInvariant();
return OtherSpellings.TryGetValue(text, out string? area) ? area : text.ToUpperInvariant();
}
/// What CQ WW RTTY counts: the 48 contiguous states, DC, and the Canadian
@@ -60,9 +64,7 @@ public static class StatesAndProvinces
/// splits Newfoundland from Labrador and spells out `NWT` and `PEI`.
public static readonly IReadOnlySet<string> CqWwRttyAreas =
new HashSet<string>(
ContiguousStates
.Concat(["DC", "NB", "NS", "QC", "ON", "MB", "SK", "AB", "BC", "NU", "YT"])
.Concat(SplitCanadianAreas),
ContiguousStates.Concat(["DC"]).Concat(SplitCanadianAreas),
StringComparer.OrdinalIgnoreCase);
public static bool IsStateOrProvince(string text) =>

View File

@@ -102,10 +102,7 @@ public sealed class CqWorldWide : Contest
{
found.Add(new Multiplier(1, qso.Qso.Zone.ToString(), band));
}
if (qso.CountryPrefix.Length > 0 && qso.Qso.Call.CountsForEntity)
{
found.Add(new Multiplier(2, qso.CountryPrefix, band));
}
found.Add(new Multiplier(2, qso.CountryPrefix, band));
string home = qso.Qso.Section.Trim().ToUpperInvariant();
if (IsRtty && StatesAndProvinces.CqWwRttyAreas.Contains(home))
{

View File

@@ -84,14 +84,11 @@ public sealed class Wae : Contest
public IReadOnlyList<Multiplier> MultipliersFor(QsoContext qso)
{
int weight = WeightFor(qso.Band);
if (weight == 0 || !IsWorkable(qso) || !qso.Qso.Call.CountsForEntity)
if (weight == 0 || !IsWorkable(qso))
{
return [];
}
string value = MultiplierValue(qso);
return value.Length == 0
? []
: [new Multiplier(1, value, qso.Band?.Name ?? "", weight)];
return [new Multiplier(1, MultiplierValue(qso), qso.Band?.Name ?? "", weight)];
}
public int TotalScore(ScoreTally tally, ContestEntry entry) => tally.Points * tally.TotalMultipliers;