Write N1MM's own Contest table rows for the built-in contests
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -135,3 +135,14 @@ a log has to score the same in both programs:
|
|||||||
- A maritime mobile counts for the country of the call it signs. CQ WW says it
|
- A maritime mobile counts for the country of the call it signs. CQ WW says it
|
||||||
counts for the zone alone.
|
counts for the zone alone.
|
||||||
- A callsign that places nowhere counts as a multiplier with an empty value.
|
- A callsign that places nowhere counts as a multiplier with an empty value.
|
||||||
|
|
||||||
|
## The Contest table
|
||||||
|
|
||||||
|
N1MM reads the `Contest` table when it opens a log. For the built-in contests
|
||||||
|
the row we write is N1MM's own (`N1mmContestRows`, taken from `om5m.s3db`):
|
||||||
|
display name, Cabrillo name, mode, dupe type, multiplier names, period, points
|
||||||
|
per contact and Cabrillo version. A contest that is not in that table, such as
|
||||||
|
a user-defined one, gets a row worked out from its rules.
|
||||||
|
|
||||||
|
The Cabrillo name in that row is not always the one our Cabrillo export uses.
|
||||||
|
For WAE the table says `DARC-WAEDC-CW`, and the export writes `WAE-CW`.
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ public sealed partial class AppSession
|
|||||||
scoreSent = DateTime.UtcNow;
|
scoreSent = DateTime.UtcNow;
|
||||||
ContestInstance instance = Logging.Instance;
|
ContestInstance instance = Logging.Instance;
|
||||||
StationInfo me = Settings.Station.ToStationInfo();
|
StationInfo me = Settings.Station.ToStationInfo();
|
||||||
IReadOnlyList<string> multiplierNames = N1mmMultiplierNames.For(Logging.Contest, me);
|
IReadOnlyList<string> multiplierNames = N1mmContestRows.MultiplierNames(Logging.Contest, me);
|
||||||
List<ScoreReportRow> rows = ScoreBreakdown.For(Logging.Log.Qsos, Logging.Contest)
|
List<ScoreReportRow> rows = ScoreBreakdown.For(Logging.Log.Qsos, Logging.Contest)
|
||||||
.Select(r => new ScoreReportRow(r.Band, r.Mode, r.Qsos, r.Multipliers, r.Points))
|
.Select(r => new ScoreReportRow(r.Band, r.Mode, r.Qsos, r.Multipliers, r.Points))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using Nonemm.Core;
|
||||||
|
|
||||||
|
namespace Nonemm.Contests;
|
||||||
|
|
||||||
|
/// What N1MM keeps in its `Contest` table for one contest. `Multipliers` are
|
||||||
|
/// the internal names such as `ZN` and `Sect`, without the `N/A` entries.
|
||||||
|
public sealed record N1mmContestRow(
|
||||||
|
string DisplayName,
|
||||||
|
string CabrilloName,
|
||||||
|
string Mode,
|
||||||
|
int DupeType,
|
||||||
|
IReadOnlyList<string> Multipliers,
|
||||||
|
int Period,
|
||||||
|
int PointsPerContact,
|
||||||
|
string CabrilloVersion);
|
||||||
|
|
||||||
|
/// The `Contest` table rows of the built-in contests, taken from a log N1MM
|
||||||
|
/// wrote. N1MM reads this table when it opens a log, so a log written here
|
||||||
|
/// carries N1MM's values rather than ones derived from our rules. The score
|
||||||
|
/// broadcast names its multiplier types from the same rows.
|
||||||
|
public static class N1mmContestRows
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, N1mmContestRow> Known =
|
||||||
|
new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
["CQWWCW"] = new("CQ WorldWide CW", "CQ-WW-CW", "CW", 2, ["ZN", "CountryPrefix"], 2, 3, "3.0"),
|
||||||
|
["CQWWSSB"] = new("CQ WorldWide SSB", "CQ-WW-SSB", "SSB", 2, ["ZN", "CountryPrefix"], 2, 3, "3.0"),
|
||||||
|
["CQWWRTTY"] = new("CQ WorldWide RTTY", "CQ-WW-RTTY", "RTTY", 2, ["ZN", "CountryPrefix", "Sect"], 2, 3, "3.0"),
|
||||||
|
["CQWPXCW"] = new("CQ WPX CW", "CQ-WPX-CW", "CW", 2, ["WPXPrefix"], 2, 2, "3.0"),
|
||||||
|
["CQWPXSSB"] = new("CQ WPX SSB", "CQ-WPX-SSB", "SSB", 2, ["WPXPrefix"], 2, 2, "3.0"),
|
||||||
|
["CQWPXRTTY"] = new("CQ WPX RTTY", "CQ-WPX-RTTY", "RTTY", 2, ["WPXPrefix"], 2, 2, "3.0"),
|
||||||
|
["ARRLDXCW"] = new("ARRL DX CW", "ARRL-DX-CW", "CW", 2, ["Sect"], 2, 3, "3.0"),
|
||||||
|
["ARRLDXSSB"] = new("ARRL DX SSB", "ARRL-DX-SSB", "SSB", 2, ["Sect"], 2, 3, "3.0"),
|
||||||
|
["IARU"] = new("Internatl Amateur Radio Union HF Contest", "IARU-HF", "Both", 3, ["ZN", "HQ"], 2, 1, "3.0"),
|
||||||
|
["SSCW"] = new("ARRL Sweepstakes CW (November)", "ARRL-SS-CW", "CW", 1, ["Sect"], 2, 2, "3.0"),
|
||||||
|
["SSSSB"] = new("ARRL Sweepstakes SSB (November)", "ARRL-SS-SSB", "SSB", 1, ["Sect"], 2, 2, "3.0"),
|
||||||
|
["WAECW"] = new("Worked All Europe - WAE CW", "DARC-WAEDC-CW", "CW", 3, ["CountryPrefix"], 1, 1, "3.0"),
|
||||||
|
["WAESSB"] = new("Worked All Europe - WAE SSB", "DARC-WAEDC-SSB", "SSB", 3, ["CountryPrefix"], 1, 1, "3.0"),
|
||||||
|
["WAERTTY"] = new("Worked All Europe - WAE RTTY", "DARC-WAEDC-RTTY", "RTTY", 3, ["CountryPrefix"], 1, 1, "3.0"),
|
||||||
|
["ARRLRTTY"] = new("ARRL RTTY Roundup (January)", "ARRL-RTTY", "RTTY", 2, ["Sect", "CountryPrefix"], 1, 1, "3.0"),
|
||||||
|
["NAQPCW"] = new("North American QSO Parties - CW", "NAQP-CW", "CW", 2, ["Sect", "NA"], 1, 1, "3.0"),
|
||||||
|
["NAQPSSB"] = new("North American QSO Parties - SSB", "NAQP-SSB", "SSB", 2, ["Sect", "NA"], 1, 1, "3.0"),
|
||||||
|
["NAQPRTTY"] = new("North American QSO Parties - RTTY", "NAQP-RTTY", "RTTY", 2, ["Sect", "NA"], 1, 1, "3.0"),
|
||||||
|
["OKOMDX"] = new("Czech and Slovak Republics DX", "OK-OM-DX", "CW", 2, ["Sect"], 1, 1, "3.0"),
|
||||||
|
["YOTA"] = new("YOTA Contest", "YOTA", "BOTH", 3, ["Exchange"], 2, 0, "3.0"),
|
||||||
|
["IOTA"] = new("RSGB Islands On The Air Contest", "RSGB-IOTA", "Both", 3, ["Sect"], 1, 1, "3.0"),
|
||||||
|
["EUDXC"] = new("EU DX Contest (not UDC)", "EUDXC", "Both", 3, ["Sect", "CountryPrefix"], 1, 1, "3.0"),
|
||||||
|
["CQ160CW"] = new("CQ World-Wide 160 Meter DX Contest - CW", "CQ-160-CW", "CW", 1, ["StateProvince", "CountryPrefix"], 2, 1, "3.0"),
|
||||||
|
["CQ160SSB"] = new("CQ World-Wide 160 Meter DX Contest - Phone", "CQ-160-SSB", "SSB", 1, ["StateProvince", "CountryPrefix"], 2, 1, "3.0"),
|
||||||
|
["XERTTY"] = new("Mexico RTTY Contest", "XE-RTTY", "RTTY", 2, ["Countryprefix", "Sect"], 1, 1, "3.0"),
|
||||||
|
["REFCW"] = new("French REF DX contest - CW", "REF-CW", "CW", 2, ["Sect", "CountryPrefix"], 1, 1, "3.0"),
|
||||||
|
["REFSSB"] = new("French REF DX contest - SSB", "REF-SSB", "SSB", 2, ["Sect", "CountryPrefix"], 1, 1, "3.0"),
|
||||||
|
["UKRAINDX"] = new("UKRAINIAN DX", "UKRAINIAN-DX", "BOTH", 3, ["CountryPrefix", "Sect"], 1, 1, "3.0"),
|
||||||
|
["BARTGRTTYS"] = new("BARTG RTTY Sprint contest (January)", "BARTG-SPRINT", "RTTY", 2, ["CountryPrefix", "Sect", "Continent"], 2, 1, "3.0"),
|
||||||
|
["ARRL10M"] = new("ARRL 10 Meter Contest", "ARRL-10", "Both", 3, ["StateProvince", "CountryPrefix"], 2, 1, "3.0"),
|
||||||
|
["RUSDXRTTY"] = new("Russian DX RTTY", "RADIO-WW-RTTY", "RTTY", 2, ["CountryPrefix", "Sect"], 1, 1, "3.0"),
|
||||||
|
["RDAC"] = new("Russian District Award Contest", "RDAC", "Both", 3, ["CountryPrefix", "Sect"], 1, 1, "3.0"),
|
||||||
|
["YOHFDX"] = new("Romanian Amateur Radio Federation - YO DX HF", "YO-DX-HF", "Both", 3, ["CountryPrefix", "Sect"], 1, 1, "3.0"),
|
||||||
|
["OCEANIACW"] = new("Oceania DX CW", "Oceania-DX-CW", "CW", 2, ["WPXPrefix"], 2, 2, "3.0"),
|
||||||
|
["OCEANIASSB"] = new("Oceania DX SSB", "Oceania-DX-SSB", "SSB", 2, ["WPXPrefix"], 2, 2, "3.0"),
|
||||||
|
["FDREG1"] = new("Internatl Amateur Radio Union, Region 1 Field Day", "FIELDDAY-REGION-1", "Both", 2, ["CountryPrefix"], 1, 1, "3.0"),
|
||||||
|
["SARTGRTTY"] = new("Scandinavian Amateur Radio, WorldWide RTTY", "SARTG-RTTY", "RTTY", 2, ["CountryPrefix", "Sect"], 2, 1, "3.0"),
|
||||||
|
["ALLASIACW"] = new("All Asian DX contest CW", "AADX-CW", "CW", 2, ["WPXPrefix"], 2, 1, "3.0"),
|
||||||
|
["ALLASIASSB"] = new("All Asian DX contest SSB", "AADX-SSB", "SSB", 2, ["WPXPrefix"], 2, 1, "3.0"),
|
||||||
|
["DX"] = new("General Logging", "DX", "Both", 3, ["CountryPrefix"], 32000, 1, "3.0"),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static N1mmContestRow? Find(string name) =>
|
||||||
|
Known.TryGetValue(name, out N1mmContestRow? row) ? row : null;
|
||||||
|
|
||||||
|
/// A contest not in the table gets the names its rules give.
|
||||||
|
public static IReadOnlyList<string> MultiplierNames(Contest contest, StationInfo me) =>
|
||||||
|
Find(contest.Name)?.Multipliers ?? contest.MultiplierNamesFor(me);
|
||||||
|
}
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
using Nonemm.Core;
|
|
||||||
|
|
||||||
namespace Nonemm.Contests;
|
|
||||||
|
|
||||||
/// The multiplier names N1MM keeps in the `Contest` table, such as `ZN`,
|
|
||||||
/// `CountryPrefix` and `Sect`. The score broadcast names its multiplier types
|
|
||||||
/// from them, and a contest's own `MultiplierNamesFor` holds the names the
|
|
||||||
/// score window shows instead (`Zones`, `Countries`). Taken from a log N1MM
|
|
||||||
/// wrote, without the `N/A` entries.
|
|
||||||
public static class N1mmMultiplierNames
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<string, IReadOnlyList<string>> Known =
|
|
||||||
new(StringComparer.OrdinalIgnoreCase)
|
|
||||||
{
|
|
||||||
["CQWWCW"] = ["ZN", "CountryPrefix"],
|
|
||||||
["CQWWSSB"] = ["ZN", "CountryPrefix"],
|
|
||||||
["CQWWRTTY"] = ["ZN", "CountryPrefix", "Sect"],
|
|
||||||
["CQWPXCW"] = ["WPXPrefix"],
|
|
||||||
["CQWPXSSB"] = ["WPXPrefix"],
|
|
||||||
["CQWPXRTTY"] = ["WPXPrefix"],
|
|
||||||
["ARRLDXCW"] = ["Sect"],
|
|
||||||
["ARRLDXSSB"] = ["Sect"],
|
|
||||||
["IARU"] = ["ZN", "HQ"],
|
|
||||||
["SSCW"] = ["Sect"],
|
|
||||||
["SSSSB"] = ["Sect"],
|
|
||||||
["WAECW"] = ["CountryPrefix"],
|
|
||||||
["WAESSB"] = ["CountryPrefix"],
|
|
||||||
["WAERTTY"] = ["CountryPrefix"],
|
|
||||||
["ARRLRTTY"] = ["Sect", "CountryPrefix"],
|
|
||||||
["NAQPCW"] = ["Sect", "NA"],
|
|
||||||
["NAQPSSB"] = ["Sect", "NA"],
|
|
||||||
["NAQPRTTY"] = ["Sect", "NA"],
|
|
||||||
["OKOMDX"] = ["Sect"],
|
|
||||||
["YOTA"] = ["Exchange"],
|
|
||||||
["IOTA"] = ["Sect"],
|
|
||||||
["EUDXC"] = ["Sect", "CountryPrefix"],
|
|
||||||
["CQ160CW"] = ["StateProvince", "CountryPrefix"],
|
|
||||||
["CQ160SSB"] = ["StateProvince", "CountryPrefix"],
|
|
||||||
["XERTTY"] = ["Countryprefix", "Sect"],
|
|
||||||
["REFCW"] = ["Sect", "CountryPrefix"],
|
|
||||||
["REFSSB"] = ["Sect", "CountryPrefix"],
|
|
||||||
["UKRAINDX"] = ["CountryPrefix", "Sect"],
|
|
||||||
["BARTGRTTYS"] = ["CountryPrefix", "Sect", "Continent"],
|
|
||||||
["ARRL10M"] = ["StateProvince", "CountryPrefix"],
|
|
||||||
["RUSDXRTTY"] = ["CountryPrefix", "Sect"],
|
|
||||||
["RDAC"] = ["CountryPrefix", "Sect"],
|
|
||||||
["YOHFDX"] = ["CountryPrefix", "Sect"],
|
|
||||||
["OCEANIACW"] = ["WPXPrefix"],
|
|
||||||
["OCEANIASSB"] = ["WPXPrefix"],
|
|
||||||
["FDREG1"] = ["CountryPrefix"],
|
|
||||||
["SARTGRTTY"] = ["CountryPrefix", "Sect"],
|
|
||||||
["ALLASIACW"] = ["WPXPrefix"],
|
|
||||||
["ALLASIASSB"] = ["WPXPrefix"],
|
|
||||||
["DX"] = ["CountryPrefix"],
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A contest not in the table gets the names its rules give.
|
|
||||||
public static IReadOnlyList<string> For(Contest contest, StationInfo me) =>
|
|
||||||
Known.TryGetValue(contest.Name, out IReadOnlyList<string>? names)
|
|
||||||
? names
|
|
||||||
: contest.MultiplierNamesFor(me);
|
|
||||||
}
|
|
||||||
@@ -5,12 +5,30 @@ using Nonemm.Storage;
|
|||||||
namespace Nonemm.Session;
|
namespace Nonemm.Session;
|
||||||
|
|
||||||
/// Turns a contest's rules into the definition row N1MM keeps in the `Contest`
|
/// Turns a contest's rules into the definition row N1MM keeps in the `Contest`
|
||||||
/// table, which it reads when it opens a log.
|
/// table, which it reads when it opens a log. A built-in contest takes N1MM's
|
||||||
|
/// own row; any other is worked out from its rules.
|
||||||
public static class ContestDefinitions
|
public static class ContestDefinitions
|
||||||
{
|
{
|
||||||
public static ContestDefinition For(Contest contest, StationInfo me)
|
public static ContestDefinition For(Contest contest, StationInfo me)
|
||||||
{
|
{
|
||||||
IReadOnlyList<string> mults = N1mmMultiplierNames.For(contest, me);
|
if (N1mmContestRows.Find(contest.Name) is { } row)
|
||||||
|
{
|
||||||
|
return new ContestDefinition
|
||||||
|
{
|
||||||
|
Name = contest.Name,
|
||||||
|
DisplayName = row.DisplayName,
|
||||||
|
CabrilloName = row.CabrilloName,
|
||||||
|
Mode = row.Mode,
|
||||||
|
DupeType = row.DupeType,
|
||||||
|
Multiplier1Name = NameAt(row.Multipliers, 0),
|
||||||
|
Multiplier2Name = NameAt(row.Multipliers, 1),
|
||||||
|
Multiplier3Name = NameAt(row.Multipliers, 2),
|
||||||
|
Period = row.Period,
|
||||||
|
PointsPerContact = row.PointsPerContact,
|
||||||
|
CabrilloVersion = row.CabrilloVersion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
IReadOnlyList<string> mults = contest.MultiplierNamesFor(me);
|
||||||
return new ContestDefinition
|
return new ContestDefinition
|
||||||
{
|
{
|
||||||
Name = contest.Name,
|
Name = contest.Name,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Nonemm.Contests.Rules;
|
using Nonemm.Contests.Rules;
|
||||||
|
using Nonemm.Contests.Udc;
|
||||||
using Nonemm.Core;
|
using Nonemm.Core;
|
||||||
using Nonemm.Storage;
|
using Nonemm.Storage;
|
||||||
|
|
||||||
@@ -53,6 +54,40 @@ public class ContestDefinitionsTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ContestWithSeveralModesIsBoth() =>
|
public void ContestWithSeveralModesIsBothAsN1mmWritesIt() =>
|
||||||
Assert.Equal("BOTH", ContestDefinitions.For(new IaruHf(), Germany).Mode);
|
Assert.Equal("Both", ContestDefinitions.For(new IaruHf(), Germany).Mode);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ABuiltInContestTakesTheRowN1mmWrote()
|
||||||
|
{
|
||||||
|
ContestDefinition definition = ContestDefinitions.For(new Wae(ModeCategory.Cw), Germany);
|
||||||
|
|
||||||
|
Assert.Equal("DARC-WAEDC-CW", definition.CabrilloName);
|
||||||
|
Assert.Equal("Worked All Europe - WAE CW", definition.DisplayName);
|
||||||
|
Assert.Equal((3, 1, 1), (definition.DupeType, definition.Period, definition.PointsPerContact));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AUserDefinedContestIsWorkedOutFromItsFile()
|
||||||
|
{
|
||||||
|
UserDefinedContest contest = new(UdcFile.Parse("""
|
||||||
|
[Contest]
|
||||||
|
Name=SAMPLETEST
|
||||||
|
DisplayName=Sample Test
|
||||||
|
CabrilloName=SAMPLE-TEST
|
||||||
|
Mode=CW
|
||||||
|
DupeType=2
|
||||||
|
Multiplier1Name=Countries
|
||||||
|
MultSqlString=CountryPrefix
|
||||||
|
IsMultPer=1
|
||||||
|
PointsPerContact=1
|
||||||
|
EntryWindowInfo=RCVText, 500, Exchange1Text, 500
|
||||||
|
FrameText=RcvRST Exch
|
||||||
|
DefaultContestExchange=599
|
||||||
|
"""));
|
||||||
|
|
||||||
|
ContestDefinition definition = ContestDefinitions.For(contest, Germany);
|
||||||
|
|
||||||
|
Assert.Equal(("Sample Test", "SAMPLE-TEST", "Countries"), (definition.DisplayName, definition.CabrilloName, definition.Multiplier1Name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user