public ScoreCalculationResult CalculateScore()

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);
        }