in #U5b9e#U8df5#U9879#U76ee/2019_MSC_#U9ec4#U91d1#U70b9/#U5fae#U8f6f#U9ec4#U91d1#U70b9#U7a0b#U5e8f#U5de5#U5177/OfflineGame/Player/OfflineScoreboard/MainWindow.xaml.cs [33:140]
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
Func<string, Tuple<double, double>> getNumberProcedure;
if (ExecuteShellCmdToggle.IsChecked ?? false)
{
var cmd = ShellCmdTextBox.Text; // 不能在其他线程访问TextBox控件。通过局部变量的方式进行包装以跨线程。
getNumberProcedure = (input) =>
{
var shellResult = InvokeShellCmdHelper(cmd, input);
TryPostLog(shellResult.StdErr);
TryPostLog(shellResult.Exception?.Message);
if (shellResult.Candidate != null)
{
TryPostLog("Submitted " + string.Join(", ", YieldFromTuple(shellResult.Candidate)));
}
return shellResult.Candidate;
};
}
else
{
getNumberProcedure = Bot.NumberHelper.GetNumber;
}
var lines = File.ReadAllLines(@"Data\SecondData.txt");
var meta = lines[0].Split(Delimiter).Select(int.Parse).ToList();
var totalRound = meta[0];
var columnCount = meta[1];
int nearestCount = 0;
int farthestCount = 0;
TimeSpan sumTime = TimeSpan.Zero;
TimeSpan maxTime = TimeSpan.MinValue;
var scoreHelper = new Utils.ScoreHelper(lines, Delimiter);
int?[] scores = new int?[columnCount / 2 + 1]; // 使用 Nullable<int> 作为得分的类型,以辨别重头到尾都没有提交过合法值的情况。
for (int i = 0; i < totalRound; i++)
{
var br = await Task.Run(() =>
{
var input = $"{i}{Delimiter}{columnCount}";
if (i > 0)
{
// 避免没有数据时,末尾会有一个空行。否则可能影响解析。
input += NewLine;
input += string.Join(NewLine, lines.Skip(1).Take(i));
}
var botResult = new BotResult();
var sw = Stopwatch.StartNew();
try
{
var submittedNumbers = getNumberProcedure(input);
botResult.Candidate = submittedNumbers;
}
catch (Exception)
{
}
botResult.ElapsedTime = sw.Elapsed;
return botResult;
});
// 检查提交的数字是否有效。
var submitted = br.Candidate == null ? null : YieldFromTuple(br.Candidate).ToArray();
if (submitted != null)
{
if (!submitted.All(IsValidNumber))
{
// submitted 变量不为 null,即确实提交了有效的数字。
submitted = null;
}
}
var scoreResult = scoreHelper.CalculateScore(i, submitted);
scores = scores.Zip(
scoreResult.Scores,
(accum, thisRound) => thisRound.HasValue ? (thisRound.Value + (accum ?? 0)) : accum // 第一次得到分数时(包括0分),初始化总分数。
).ToArray();
nearestCount += (scoreResult.HasNearest ? 1 : 0);
farthestCount += (scoreResult.HasFarthest ? 1 : 0);
// 不管数字有效与否,总是统计运行时间。
sumTime += br.ElapsedTime;
maxTime = maxTime < br.ElapsedTime ? br.ElapsedTime : maxTime;
int? score = scores.Last();
int ranking = scores.Select((s, idx) => new { Score = s, PlayerIndex = idx }) // 绑定得分和序号(从0开始)。我们只用唯一序号来标识参加者,而不用可能重复的得分数。
.OrderByDescending(playerScore => playerScore.Score) // 先按得分排序
.Select((playerScore, rankingIdx) => new { PlayerScore = playerScore, RankingIdx = rankingIdx }) // 按排序后的顺序再赋予一次序号(从0开始)
.First(orderedSi => orderedSi.PlayerScore.PlayerIndex == scores.Length - 1) // 找到玩家序号是最大值的数据块,就是当前玩家的。
.RankingIdx + 1; // 当前玩家的排序后序号 + 1,就是当前玩家的排名(从1开始)。
NearestText.Text = $"{nearestCount}/{i + 1}";
FarthestText.Text = $"{farthestCount}/{i + 1}";
ScoreText.Text = score?.ToString() ?? "N/A";
ScoreText.Foreground = score > 0 ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red);
MaxAveScoreText.Text = $"{scores.Max()} & {scores.Average():.##}";
RankingText.Text = $"{ranking}/{scores.Length}";
AveTimeText.Text = $"{sumTime.TotalMilliseconds / (i + 1)}ms";
MaxTimeText.Text = $"{maxTime.TotalMilliseconds}ms";
}
}