Read the .udc CallHist multiplier and BonusPoints2
MultSqlString = CallHist counts every station the call history file lists, once per callsign, which is how N1MM dedups it. The file reaches the scoring code through ContestLog.History, and a QsoContext now carries it; without a file loaded nothing counts. BonusPoints2 = +50, calls.txt reads the callsigns from the support-files folder and adds 50 to the contact, *2 doubles it and a plain 50 scores 50 instead. The folder comes from the registry, which the app passes when it reads the .udc files. N1MM's other form, where the file is grids.txt and the bonus is looked up by grid square, is not read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RoGtneMQaz4M9w7Kk49AVD
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
namespace Nonemm.Contests.Udc;
|
||||
|
||||
/// `BonusPoints2 = <value>, <file>`: a list of callsigns in a file in the
|
||||
/// support-files folder, and what a contact with one of those stations is
|
||||
/// worth. `+50` adds 50 to the points, `*2` doubles them, and a plain `50`
|
||||
/// scores 50 instead of them.
|
||||
///
|
||||
/// The file is N1MM's: one callsign per line, followed by a comma and whatever
|
||||
/// else the line holds. N1MM's other form, where the file is `grids.txt` and
|
||||
/// the bonus is looked up by grid square, is not read.
|
||||
public sealed class UdcBonusCalls
|
||||
{
|
||||
private readonly HashSet<string> calls = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly char how;
|
||||
private readonly int value;
|
||||
|
||||
public UdcBonusCalls(string setting, string? supportFolder)
|
||||
{
|
||||
string[] parts = setting.Split(',', StringSplitOptions.TrimEntries);
|
||||
if (parts.Length < 2 || parts[0].Length == 0 || supportFolder is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string amount = parts[0];
|
||||
how = amount[0] is '+' or '*' ? amount[0] : ' ';
|
||||
if (!int.TryParse(how == ' ' ? amount : amount[1..], out value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (string line in Lines(Path.Combine(supportFolder, parts[1])))
|
||||
{
|
||||
string call = line.Split(',')[0].Trim();
|
||||
if (call.Length > 0)
|
||||
{
|
||||
calls.Add(call);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDefined => calls.Count > 0;
|
||||
|
||||
/// What the contact scores once the bonus is in. A station that is not on
|
||||
/// the list scores what it scored.
|
||||
public int Apply(int points, string call) =>
|
||||
calls.Contains(call.Trim()) switch
|
||||
{
|
||||
false => points,
|
||||
true => how switch
|
||||
{
|
||||
'+' => points + value,
|
||||
'*' => points * value,
|
||||
_ => value,
|
||||
},
|
||||
};
|
||||
|
||||
/// A file that is not there leaves the bonus off rather than stopping the
|
||||
/// contest from opening: the operator can put it in and reload the support
|
||||
/// files.
|
||||
private static IReadOnlyList<string> Lines(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return File.ReadAllLines(path);
|
||||
}
|
||||
catch (Exception e) when (e is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ namespace Nonemm.Contests.Udc;
|
||||
/// One of a user-defined contest's up-to-three multipliers: where its value
|
||||
/// comes from, which stations may bring it in, and how often it may be counted.
|
||||
///
|
||||
/// Left out: `CallHist`, which counts a value from the call history file, and
|
||||
/// needs that file loaded before a multiplier can be worked out.
|
||||
/// `CallHist` counts a station the call history file lists, so it needs that
|
||||
/// file loaded; without one nothing counts.
|
||||
public sealed class UdcMultiplier
|
||||
{
|
||||
private readonly int index;
|
||||
@@ -106,10 +106,16 @@ public sealed class UdcMultiplier
|
||||
"SGRID" => Truncate(qso.Qso.GridSquare, 6),
|
||||
"FIELD" => Truncate(qso.Qso.GridSquare, 2),
|
||||
"CONTINENT" => qso.Continent,
|
||||
"CALLHIST" => Listed(qso),
|
||||
"FIRSTQSO" => "first",
|
||||
_ => null,
|
||||
};
|
||||
|
||||
/// `CallHist` makes every station in the call history file a multiplier,
|
||||
/// counted by callsign. N1MM dedups them on the callsign the same way.
|
||||
private static string? Listed(QsoContext qso) =>
|
||||
qso.History.Find(qso.Qso.Call.Text) is null ? null : qso.Qso.Call.Text.ToUpperInvariant();
|
||||
|
||||
private static string? CountryOnContinent(QsoContext qso, string continent) =>
|
||||
continent.Equals(qso.Continent, StringComparison.OrdinalIgnoreCase) ? qso.CountryPrefix : null;
|
||||
|
||||
|
||||
@@ -16,8 +16,12 @@ public sealed class UserDefinedContest : Contest
|
||||
private readonly IReadOnlyList<string> workableStations;
|
||||
private readonly UdcCabrillo cabrillo;
|
||||
private readonly UdcCabrilloFormat cabrilloFormat;
|
||||
private readonly UdcBonusCalls bonusCalls;
|
||||
|
||||
public UserDefinedContest(UdcFile file)
|
||||
/// `supportFolder` is where `BonusPoints2` reads its list of callsigns
|
||||
/// from, which is N1MM's support-files folder. Without it that setting is
|
||||
/// left off.
|
||||
public UserDefinedContest(UdcFile file, string? supportFolder = null)
|
||||
{
|
||||
this.file = file;
|
||||
points = new UdcPoints(file.Text("PointsPerContact", "1"));
|
||||
@@ -28,6 +32,7 @@ public sealed class UserDefinedContest : Contest
|
||||
workableStations = file.List("IsWorkable");
|
||||
cabrillo = new UdcCabrillo(file.Text("CabrilloString"));
|
||||
cabrilloFormat = new UdcCabrilloFormat(file.Number("CabrilloFormat", 1));
|
||||
bonusCalls = new UdcBonusCalls(file.Text("BonusPoints2"), supportFolder);
|
||||
}
|
||||
|
||||
public static UserDefinedContest Load(string path) =>
|
||||
@@ -97,7 +102,8 @@ public sealed class UserDefinedContest : Contest
|
||||
}
|
||||
string sent = SentExchange(qso);
|
||||
double scored = points.For(qso, sent) * pointsMultiplier.For(qso);
|
||||
return (int)Math.Round(scored) + bonusPoints.For(qso, sent);
|
||||
int total = (int)Math.Round(scored) + bonusPoints.For(qso, sent);
|
||||
return bonusCalls.Apply(total, qso.Qso.Call.Text);
|
||||
}
|
||||
|
||||
/// What this station sends: the exchange the operator set up for the
|
||||
|
||||
Reference in New Issue
Block a user