t123yh
7 years ago
4 changed files with 197 additions and 2 deletions
@ -0,0 +1,77 @@
|
||||
// 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, currentRating: contestant.currentRating + deltas[i] })); |
||||
} |
@ -0,0 +1,34 @@
|
||||
<% this.adminPage = 'rating'; %> |
||||
<% include admin_header %> |
||||
<form action="<%= syzoj.utils.makeUrl(['admin', 'rating', 'add']) %>" method="post" class="ui form"> |
||||
<div class="field"> |
||||
<label>比赛</label> |
||||
<div class="ui fluid search selection dropdown"> |
||||
<input type="hidden" name="contest"> |
||||
<i class="dropdown icon"></i> |
||||
<div class="default text">选择比赛</div> |
||||
<div class="menu"> |
||||
<% for (const contest of contests) { %> |
||||
<div class="item" data-value="<%= contest.id %>"><%= contest.title %></div> |
||||
<% } %> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<button class="ui blue button" name="type" value="doit" type="submit">计算此比赛的 Rating</button> |
||||
</form> |
||||
|
||||
注意:如果删除一个比赛的 Rating,则该比赛之上的所有比赛也将被删除,Rating 将还原至该比赛之前的状态! |
||||
<form class="have-csrf" action="<%= syzoj.utils.makeUrl(['admin', 'rating', 'delete']) %>" method="POST"> |
||||
<input type="hidden" name="_csrf" value="<%= req.csrfToken() %>" /> |
||||
<div class="ui relaxed divided list"> |
||||
<% for (const calc of calcs) { %> |
||||
<div class="item"> |
||||
<%= calc.contest.title %> |
||||
<button name="calc_id" value="<%= calc.id %>" type="submit" style="color: #000; padding: 0; border: none; background: none;"><i class="remove icon"></i></button> |
||||
</div> |
||||
<% } %> |
||||
</div> |
||||
</div> |
||||
</form> |
||||
<% include admin_footer %> |
||||
<script>$('.selection.dropdown').dropdown();</script> |
Loading…
Reference in new issue