Mark a dupe in the contact type column, as N1MM does
N1MM writes a contact type on every contact it logs: `D` for a station already worked, `N` for one the entry may not work at all, and empty for an ordinary contact. Nothing here wrote the column, so a dupe counted as a contact worth no points instead of dropping out of the score, and the two places that read the column did nothing: the QTC pile offered dupes that N1MM leaves out, and the statistics window could not leave dupes out either. The marking is in one place, `ContestLog.ApplyVerdict`, and every contest is marked the same way. What changes between contests is what counts as a dupe and which stations are workable, and both come from the contest, as they do in N1MM's `Contact.IsDupe`. No contest module in N1MM marks differently. Our own callsign is not workable either, which is what N1MM adds on top of the contest rule. Two details follow N1MM as well: QTC traffic keeps an empty column, because its `IsDupe` only marks a row while the program is logging contacts; and a mark that came with the contact stands, because N1MM marks a contact when it logs or edits one and never clears marks in a pass over the log. Editing a contact clears the column so it is worked out again, which is what N1MM does at the same point: an edit can move a contact to a band where it is no longer a dupe. A dupe is now out of the score altogether — contacts, points and multipliers — which is N1MM's `ScoreClass.AddContact`. The QSO count still counts WAE QTC rows as contacts; that is the other half of the scoring audit's item 14. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0157i7PiZyqBkquxS9A6FTdQ
This commit is contained in:
@@ -128,6 +128,12 @@ public interface Contest
|
||||
/// bring no multiplier and cannot be a dupe.
|
||||
bool IsContact(Qso qso) => true;
|
||||
|
||||
/// False for a station this entry may not work at all: the wrong continent
|
||||
/// in WAE, our own callsign, and so on. N1MM marks such a contact `N` in
|
||||
/// the contact type column and gives it no points. Most contests take
|
||||
/// every station, so the answer is usually yes.
|
||||
bool IsWorkable(QsoContext qso) => true;
|
||||
|
||||
IReadOnlyList<Multiplier> MultipliersFor(QsoContext qso);
|
||||
|
||||
/// The claimed score. The entry is here because a contest can multiply
|
||||
|
||||
@@ -170,8 +170,41 @@ public sealed class ContestLog
|
||||
History = History,
|
||||
};
|
||||
|
||||
private static Qso ApplyVerdict(Qso qso, Verdict verdict) =>
|
||||
ApplyMultiplierFlags(qso, verdict.NewMultipliers) with { Points = verdict.Points };
|
||||
private Qso ApplyVerdict(Qso qso, Verdict verdict) =>
|
||||
ApplyMultiplierFlags(qso, verdict.NewMultipliers) with
|
||||
{
|
||||
Points = verdict.Points,
|
||||
ContactType = ContactTypeOf(qso),
|
||||
};
|
||||
|
||||
/// N1MM's contact type column, which it writes in `Contact.IsDupe`: `D`
|
||||
/// for a station already worked, `N` for one this entry may not work at
|
||||
/// all, and empty for an ordinary contact. Every contest is marked the
|
||||
/// same way; what changes between them is what counts as a dupe and which
|
||||
/// stations are workable, and both of those come from the contest here as
|
||||
/// they do there.
|
||||
///
|
||||
/// Traffic keeps an empty column, which is what N1MM's QTC window writes:
|
||||
/// its `IsDupe` only marks a row while the program is logging contacts.
|
||||
private string ContactTypeOf(Qso qso)
|
||||
{
|
||||
if (!contest.IsContact(qso))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
// a mark that came with the contact stands: N1MM marks a contact when
|
||||
// it logs or edits it and never clears the mark in a pass over the
|
||||
// log, so a row that arrived from another station or from an N1MM
|
||||
// database keeps what it was given
|
||||
if (IsWorked(qso) || qso.ContactType.Equals("D", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "D";
|
||||
}
|
||||
// N1MM adds our own callsign to whatever the contest says
|
||||
bool workable = contest.IsWorkable(ContextFor(qso))
|
||||
&& !qso.Call.Text.Equals(me.Callsign, StringComparison.OrdinalIgnoreCase);
|
||||
return workable ? "" : "N";
|
||||
}
|
||||
|
||||
private static Qso ApplyMultiplierFlags(Qso qso, IReadOnlyList<Multiplier> claimed) => qso with
|
||||
{
|
||||
|
||||
@@ -66,7 +66,7 @@ public sealed class AllAsian : Contest
|
||||
]);
|
||||
|
||||
/// One side of every contact has to be in Asia.
|
||||
private static bool IsWorkable(QsoContext qso) =>
|
||||
public bool IsWorkable(QsoContext qso) =>
|
||||
qso.Continent == "AS" || qso.Me.Continent == "AS";
|
||||
|
||||
/// 160 metres is worth 3, 80 and 10 metres 2, and every other band 1.
|
||||
|
||||
@@ -61,7 +61,7 @@ public sealed class OceaniaDx : Contest
|
||||
]);
|
||||
|
||||
/// One side of every contact has to be in Oceania.
|
||||
private static bool IsWorkable(QsoContext qso) =>
|
||||
public bool IsWorkable(QsoContext qso) =>
|
||||
qso.Continent == "OC" || qso.Me.Continent == "OC";
|
||||
|
||||
private static int PointsOn(Band? band) => band?.Name switch
|
||||
|
||||
Reference in New Issue
Block a user