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

7 years ago
// 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] }));
7 years ago
}