Public
Edited
Feb 4
4 forks
Insert cell
Insert cell
Insert cell
Insert cell
judge_matchup = ({ team_in_favor, team_against }) => {
let score = 0;

// Same school? SHAME
if (team_in_favor.school === team_against.school) {
score -= SAME_SCHOOL_PENALTY;
}

// Different levels? SHAME (proportional to the difference in level)
// The `** 1.2` is to make a higher level difference be more-more impactful than two small differences
let level_difference = Math.abs(team_in_favor.level - team_against.level);
score += -(
level_difference ** LEVEL_DIFFERENCE_EXPONENT *
LEVEL_DIFFERENCE_MULTIPLIER
);

// I'm just assuming debating in favor of a subject is harder than against,
// so being higher level when in favor is favorable for now
// (This makes the results a lot more stable, as it used to flicker a lot between A vs B and B vs A)
// (And shows that small score changes, like 0.1 do have an effect too!)
if (team_in_favor.level >= team_against.level) {
score += 0.1;
}

// Filter out previous matches
if (team_in_favor.seen.includes(team_against.id)) {
score += -5;
}

return score;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
get_matchup_v_matchup_matrix(matchups)
Insert cell
rank_matchups = function(matchups, generations) {
// Create a matrix of all the matchups pitted against eachother.
// It will contain 0 at the position if the matchups can't exist in the same round, 1 if it does.
// This is not flattened, so it actually looks like
// [
// [0, 1, 0, 0, 1, ...],
// [1, 0, 1, 1, 1, ...],
// [0, 1, 0, 0, 1, ...],
// [0, 1, 0, 0, 1, ...],
// [1, 1, 1, 1, 0, ...],
// ...
// ]
const matchup_v_matchup = get_matchup_v_matchup_matrix(matchups);

// Get the scores that the judge function assigned to the individual matchups.
// We also normalize them, which puts them all between 0 and 1.
// Every index in any "scores" array from now on, will have their index matched in the matchups array:
// initial_scores[0] is a score for matchups[0], current_scores[100] is a score for matchups[100],
const initial_scores = matchups.map(x => x.score);

// We need this extra variable because we will update this after every generation.
let current_scores = initial_scores;

// Save the intial scores (this is so the "visible progress" widget can show this)
let generations_result = [
_.sortBy(_.zip(matchups, current_scores), ([teams, score]) => -score)
];

// We will loop for `generations` times, but I like this more than a for loop 🙃
for (const gen of _.range(0, generations)) {
// Mathematical matrix-vector mulitplication,
// the "could be in the same round" matrix with the last scores we got for every matchup
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;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more