in #U5b9e#U8df5#U9879#U76ee/2019_MSC_#U9ec4#U91d1#U70b9/#U5fae#U8f6f#U9ec4#U91d1#U70b9#U7a0b#U5e8f#U5de5#U5177/OfflineGame/Player/OfflineScoreboard/Utils/ScoreHelper.cs [28:116]
public ScoreCalculationResult CalculateScore(int roundIdx, double[] submitted)
{
var historicalNumbers = _lines[roundIdx + 1].Split(_delimiter)
.Skip(1) // 在计算当前轮的结果时,去掉历史数据里的黄金点,并重新计算。
.Select(double.Parse)
.ToList();
var indexedValidSubmittedSets = historicalNumbers
.Select((n, numIdx) => new { Number = n, PlayerIndex = numIdx / 2 }) // 每人提交2个数,我们可以通过整数除法来得到对应的玩家序号。
.GroupBy(idxedNum => idxedNum.PlayerIndex)
.Select(group => new
{
PlayerIndex = group.Key,
Submitted = group.Select(idxedNum => idxedNum.Number).ToArray()
}).Where(idxedSubmittedSet => idxedSubmittedSet.Submitted.All(num => num > 0))
.ToList();
int bonus = indexedValidSubmittedSets.Count; // 计算加分时,不计当前玩家。
if (submitted != null)
{
indexedValidSubmittedSets.Add(new
{
PlayerIndex = historicalNumbers.Count / 2, // 当前玩家被加在最后。
Submitted = submitted
});
}
int allPlayerCount = historicalNumbers.Count / 2 + 1;
int validPlayerCount = indexedValidSubmittedSets.Count;
var validNumbers = indexedValidSubmittedSets
.SelectMany(idxedSet => idxedSet.Submitted)
.ToList();
// 黄金点的计算,不涉及非法的数字,在历史数据中即为 0。
var newG = validNumbers.Average() * 0.618;
Func<double, double> getDistance = n => Abs(n - newG);
var sortedDistances = validNumbers
.Select(getDistance)
.Distinct()
.OrderBy(d => d)
.ToList();
// 以数组存储,方便进行集合运算。
var smallestDis = new[] { sortedDistances.First() };
var biggestDis = new[] { sortedDistances.Last() };
int?[] scores = new int?[allPlayerCount]; // 通过额外的 null 状态来辨别不合法数据,即 0。
bool nearest = false;
bool farthest = false;
foreach (var idxedNumSet in indexedValidSubmittedSets)
{
// 当前的参与者提交的数据放在最后。
int playerIdx = idxedNumSet.PlayerIndex;
bool isRealPlayer = playerIdx == allPlayerCount - 1;
int thisRoundScore = 0;
// 提交的数可能在黄金点左右都有。会产生一样的距离。
// Except和Intersect都是集合运算,此处先去重,以满足集合特性。
var distinctDis = idxedNumSet.Submitted.Select(getDistance).Distinct().ToArray();
var notSmallestDis = distinctDis.Except(smallestDis).ToArray();
if (notSmallestDis.Length < distinctDis.Length)
{
// 存在某个提交的数,属于距黄金点最近的一组。并且最多只得一次分。
thisRoundScore += bonus;
// 必须是当前游戏者我们才将 HasNearest 设为 true。
nearest |= isRealPlayer;
}
if (notSmallestDis.Intersect(biggestDis).Any())
{
// 除了最近的数以外,还存在最远的数。并且最多只扣一次分。
// 如果所有数都一样,从上面可知,算是得分。
thisRoundScore -= 2;
farthest |= isRealPlayer;
}
scores[playerIdx] = thisRoundScore;
}
return new ScoreCalculationResult(scores, nearest: nearest, farthest: farthest);
}