rank_matchups = function(matchups, generations) {
const matchup_v_matchup = get_matchup_v_matchup_matrix(matchups);
const initial_scores = matchups.map(x => x.score);
let current_scores = initial_scores;
let generations_result = [
_.sortBy(_.zip(matchups, current_scores), ([teams, score]) => -score)
];
for (const gen of _.range(0, generations)) {
const raw_scores = multiply_matrix_with_vector(
matchup_v_matchup,
current_scores
);
// This gives us the new rank, but it misses something. I tried just the above before, but that made
// all the results were strange. School were pitted against eachother, even with penalties in the 1000s.
// I think that is because when we just multiply with the last scores,
// the "I can co-exist with X other matchups" easily outperforms any "I am actually a good matchup" metric.
// To counter that, I take the new score for every matchup, and multiply it with the initial ones we saved.
const with_initial_scores = multiply_vector_with_vector(
raw_scores,
initial_scores
);
// Set and yield the new scores so we can apply those in the next generation.
// The fact we keep updating the scores is the power move of this algorithm.
current_scores = normalize_vector(with_initial_scores);
// _.zip is really cool, it takes two arrays and "zips" them together in one new array:
// _.zip([1, 2, 3, 4], ["one", "two", "three", "four"])
// = [ [1, "one"], [2, "two"], [3, "three"], [4, "four"] ]
let matchups_with_their_ranking_score = _.zip(matchups, current_scores);
// We use the score we just zipped with the matchups to sort them, and we save the whole thing :D
generations_result.push(
_.sortBy(matchups_with_their_ranking_score, ([teams, score]) => -score)
);
}
// In the actual thing we'd want this to just return the last result, instead of all of them
return generations_result;
}