You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
2.5 KiB
77 lines
2.5 KiB
// Code by Dicint |
|
|
|
const util = require('util'); |
|
const _ = require('lodash'); |
|
|
|
function getEloWinProbability(ra, rb) { |
|
return 1.0 / (1 + Math.pow(10, (rb - ra) / 400.0)); |
|
} |
|
|
|
// 传入具体某个人的情况 |
|
function getContestantSeed(contestantIndex, allContestants) { |
|
let seed = 1; |
|
let rating = allContestants[contestantIndex].currentRating; |
|
for (let i = 0; i < allContestants.length; i++) { |
|
if (contestantIndex != i) { |
|
seed += getEloWinProbability(allContestants[i].currentRating, rating); |
|
} |
|
} |
|
return seed; |
|
} |
|
|
|
function getRatingSeed(rating, allContestants) { |
|
return 1 + _.sum(allContestants.map(c => getEloWinProbability(c.currentRating, rating))); |
|
} |
|
|
|
function getAverageRank(contestant, allContestants) { |
|
const realRank = allContestants[contestant].rank; |
|
const expectedRank = getContestantSeed(contestant, allContestants); |
|
const average = Math.sqrt(realRank * expectedRank); |
|
|
|
return average; |
|
} |
|
|
|
function getRatingToRank(contestantIndex, allContestants) { |
|
let averageRank = getAverageRank(contestantIndex, allContestants); |
|
|
|
let left = 1;// contestant.getPrevRating() - 2 * minDelta; |
|
let right = 8000;// contestant.getPrevRating() + 2 * maxDelta; |
|
|
|
while (right - left > 1) { |
|
const mid = (left + right) / 2; |
|
const seed = getRatingSeed(mid, allContestants); |
|
if (seed < averageRank) { |
|
right = mid; |
|
} else { |
|
left = mid; |
|
} |
|
} |
|
return left; |
|
} |
|
|
|
function calculateDeltas(allContestants) { |
|
let deltas = []; |
|
const numberOfContestants = allContestants.length; |
|
for (let i = 0; i < allContestants.length; i++) { |
|
const expR = getRatingToRank(i, allContestants); |
|
deltas[i] = ((expR - allContestants[i].currentRating) / 2); |
|
} |
|
|
|
// Total sum should not be more than zero. |
|
const deltaSum = _.sum(deltas); |
|
const inc = -deltaSum / numberOfContestants - 1; |
|
deltas = deltas.map(d => d + inc); |
|
|
|
// Sum of top-4*sqrt should be adjusted to zero. |
|
const zeroSumCount = Math.min(Math.trunc(4 * Math.round(Math.sqrt(numberOfContestants))), numberOfContestants); |
|
const deltaSum2 = _.sum(deltas.slice(0, zeroSumCount)); |
|
const inc2 = Math.min(Math.max(-deltaSum2 / zeroSumCount, -10), 0); |
|
deltas = deltas.map(d => d + inc2); |
|
|
|
return deltas; |
|
} |
|
|
|
module.exports = function(allContestants) { |
|
const deltas = calculateDeltas(allContestants); |
|
return allContestants.map((contestant, i) => ({ user: contestant.user, rank: contestant.rank, currentRating: contestant.currentRating + deltas[i] })); |
|
} |