Files
Nonemm/src/Nonemm.Contests/Rules/Sweepstakes.cs
ericek111 3932f83147 Add the YOTA and OK/OM DX contests
Both are in the station's log and neither could be opened. The rules are
written from the published ones and checked by replaying the log.

YOTA scores a contact by the age the other operator sends: 13 points down to 10
for anyone 25 or under, whatever the continent, and 1 point or 3 for everyone
else. Every age worked on a band is a multiplier. The 2024, 2025 and 2026 logs
come out to the contact.

OK/OM DX is the first contest whose exchange differs by the other station's
country: a county from OK and OM stations, a serial number from everyone else,
in the two columns N1MM keeps them in, and `SkipsField` walks the entry window
past the box that does not apply. Points depend on which side the operator is
on, and Czechia and Slovakia count as the two countries they are. The 2023 log
comes out to the contact.

The Cabrillo exchange now gets the entry, because YOTA sends an age that only
the contest setup knows.

Two differences are left in the log, both from the .udc files the operator
used: their YOTA file only lists ages 7 to 25, so a younger operator scored as
an adult, and one OK/OM log was made with the file for stations outside OK and
OM while signing an OM call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
2026-08-31 09:16:44 +00:00

70 lines
2.7 KiB
C#

using Nonemm.Contests.Multipliers;
using Nonemm.Core;
namespace Nonemm.Contests.Rules;
/// ARRL November Sweepstakes. A station counts once for the whole contest
/// whatever the band, and the multiplier is the ARRL or RAC section.
public sealed class Sweepstakes : Contest
{
private readonly ModeCategory mode;
public Sweepstakes(ModeCategory mode) => this.mode = mode;
public string Name => mode == ModeCategory.Cw ? "SSCW" : "SSSSB";
public string DisplayName => $"ARRL Sweepstakes {ModeLabel()}";
public string CabrilloName => mode == ModeCategory.Cw ? "ARRL-SS-CW" : "ARRL-SS-SSB";
public IReadOnlyList<ExchangeField> ExchangeFieldsFor(StationInfo me) =>
[
new ExchangeField("Nr", ExchangeSlot.SerialNumber, ExchangeFieldKind.Number),
new ExchangeField("Prec", ExchangeSlot.Precedence, ExchangeFieldKind.Precedence),
new ExchangeField("Ck", ExchangeSlot.Check, ExchangeFieldKind.Check),
new ExchangeField("Sec", ExchangeSlot.Section, ExchangeFieldKind.ArrlSection),
];
public IReadOnlyList<string> MultiplierNames => ["Sections"];
public DupeScope DupeScope => DupeScope.Once;
public bool HasSerialNumbers => true;
public IReadOnlyList<ModeCategory> Modes => [mode];
public string SentExchangeFor(StationInfo me) =>
$"{me.Precedence} {me.Callsign} {me.Check:00} {me.ArrlSection}";
public int PointsFor(QsoContext qso) => 2;
public IReadOnlyList<Multiplier> MultipliersFor(QsoContext qso)
{
string section = qso.Qso.Section.Trim().ToUpperInvariant();
return ArrlSections.IsSection(section) ? [new Multiplier(1, section, "")] : [];
}
public int TotalScore(ScoreTally tally, ContestEntry entry) => tally.Points * tally.TotalMultipliers;
/// Sweepstakes puts the callsign after the serial number and precedence
/// rather than first, which is why the writer takes the whole column list.
public CabrilloExchange CabrilloExchange(Qso qso, StationInfo me, ContestEntry entry) =>
new(
[
new CabrilloField($"{qso.SentNumber}", 4),
new CabrilloField(me.Precedence, 1),
new CabrilloField(me.Callsign, 13),
new CabrilloField($"{me.Check:00}", 2),
new CabrilloField(me.ArrlSection, 3),
],
[
new CabrilloField($"{qso.ReceivedNumber}", 4),
new CabrilloField(qso.Precedence, 1),
new CabrilloField(qso.Call.Text, 13),
new CabrilloField($"{qso.Check:00}", 2),
new CabrilloField(qso.Section, 3),
]);
private string ModeLabel() => mode == ModeCategory.Cw ? "CW" : "SSB";
}