Files
Nonemm/src/Nonemm.Contests/Rules/UkrainianDx.cs
ericek111 264036f0f2 Add the twelve contests left in the station's log
REF, the Ukrainian DX contest, the BARTG Sprint, ARRL 10M, Russian DX RTTY,
RDAC, YO DX HF, Oceania DX, IARU Region 1 Field Day, SARTG RTTY, All Asian and
SA 10. Every contest name in the log now opens, and every one of these but
Field Day scores its log contact for contact as N1MM did.

Most are written from N1MM's own class and checked against the log. Three
things the log settled that the class does not say plainly: ARRL 10M counts its
multipliers once per mode rather than once, the BARTG Sprint counts a continent
once in the contest while its countries and call areas count per band, and RDAC
counts nothing but the districts and scores nothing for a station that sends
none.

All Asian and SA 10 have no class in N1MM, which logs them from a .udc file, so
those two are written from the published rules and the log. docs/unfinished.md
lists what is still not settled.

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

90 lines
3.1 KiB
C#

using Nonemm.Core;
using Nonemm.Core.Country;
namespace Nonemm.Contests.Rules;
/// The Ukrainian DX contest. A Ukrainian station sends its oblast, everyone
/// else a serial number, and a contact with Ukraine is worth 10 points to a
/// station outside it. The multipliers are the countries and the oblasts, each
/// once per band.
public sealed class UkrainianDx : Contest
{
public string Name => "UKRAINDX";
public string DisplayName => "Ukrainian DX";
public string CabrilloName => "UKRAINIAN-DX";
public IReadOnlyList<ExchangeField> ExchangeFieldsFor(StationInfo me) =>
[
new ExchangeField("RST", ExchangeSlot.ReceivedReport, ExchangeFieldKind.Report),
new ExchangeField("Oblast", ExchangeSlot.Section, ExchangeFieldKind.Text) { Width = 3 },
new ExchangeField("Nr", ExchangeSlot.Exchange1, ExchangeFieldKind.Number),
];
public bool SkipsField(ExchangeField field, CountryLookup? their) => field.Slot switch
{
ExchangeSlot.Section => their is not null && !IsUkrainian(their.Entity.PrimaryPrefix),
ExchangeSlot.Exchange1 => their is not null && IsUkrainian(their.Entity.PrimaryPrefix),
_ => false,
};
public IReadOnlyList<string> MultiplierNames => ["Countries", "Oblasts"];
public DupeScope DupeScope => DupeScope.PerBandAndMode;
public bool HasSerialNumbers => true;
public IReadOnlyList<ModeCategory> Modes => [ModeCategory.Cw, ModeCategory.Phone];
public string SentExchangeFor(StationInfo me) =>
IsUkrainian(me.CountryPrefix) ? me.County : "001";
public int PointsFor(QsoContext qso)
{
if (IsUkrainian(qso.CountryPrefix))
{
return IsUkrainian(qso.Me.CountryPrefix) ? 1 : 10;
}
if (qso.IsSameCountry)
{
return 1;
}
return qso.IsSameContinent ? 2 : 3;
}
public IReadOnlyList<Multiplier> MultipliersFor(QsoContext qso)
{
string band = qso.Band?.Name ?? "";
List<Multiplier> found = [];
if (qso.CountryPrefix.Length > 0)
{
found.Add(new Multiplier(1, qso.CountryPrefix, band));
}
string oblast = qso.Qso.Section.Trim().ToUpperInvariant();
if (IsUkrainian(qso.CountryPrefix) && oblast.Length > 0)
{
found.Add(new Multiplier(2, oblast, band));
}
return found;
}
public int TotalScore(ScoreTally tally, ContestEntry entry) =>
tally.Points * tally.TotalMultipliers;
public CabrilloExchange CabrilloExchange(Qso qso, StationInfo me, ContestEntry entry) =>
new(
[
new CabrilloField(me.Callsign, 13),
new CabrilloField(qso.SentReport, 3),
new CabrilloField(SentExchangeFor(me), 6),
],
[
new CabrilloField(qso.Call.Text, 13),
new CabrilloField(qso.ReceivedReport, 3),
new CabrilloField(qso.Section.Length > 0 ? qso.Section : qso.Exchange1, 6),
]);
private static bool IsUkrainian(string countryPrefix) => countryPrefix == "UR";
}