tr.appendChild(th);
};
-function subrank_partitions(games, parts, start_rank, tiebreakers) {
+function subrank_partitions(games, parts, start_rank, tiebreakers, func) {
let result = [];
for (let i = 0; i < parts.length; ++i) {
- let part = rank(games, parts[i], start_rank, tiebreakers);
+ let part = func(games, parts[i], start_rank, tiebreakers);
for (let j = 0; j < part.length; ++j) {
result.push(part[j]);
}
// Rule #0: Partition the teams by score.
let score_parts = partition(teams, function(a, b) { return b.pts - a.pts });
if (score_parts.length > 1) {
- return subrank_partitions(games, score_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, score_parts, start_rank, tiebreakers, rank);
}
// Rule #1: Head-to-head wins.
});
if (beat_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(beat_parts, 'head-to-head'));
- return subrank_partitions(games, beat_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, beat_parts, start_rank, tiebreakers, rank);
}
// Rule #2: Number of games played (fewer is better).
let nplayed_parts = partition(teams, function(a, b) { return a.nplayed - b.nplayed });
if (nplayed_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(nplayed_parts, 'fewer losses'));
- return subrank_partitions(games, nplayed_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, nplayed_parts, start_rank, tiebreakers, rank);
}
// Rule #3: Head-to-head goal difference (if all have played).
let h2h_gd_parts = partition(teams, function(a, b) { return b.h2h_gd - a.h2h_gd });
if (h2h_gd_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(h2h_gd_parts, 'head-to-head goal difference'));
- return subrank_partitions(games, h2h_gd_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, h2h_gd_parts, start_rank, tiebreakers, rank);
}
}
});
if (gd_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(gd_parts, 'goal difference versus common opponents'));
- return subrank_partitions(games, gd_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, gd_parts, start_rank, tiebreakers, rank);
}
// Rule #5: Head-to-head scored goals (if all have played).
let h2h_goals_parts = partition(teams, function(a, b) { return b.h2h_goals - a.h2h_goals });
if (h2h_goals_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(h2h_goals_parts, 'head-to-head scored goals'));
- return subrank_partitions(games, h2h_goals_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, h2h_goals_parts, start_rank, tiebreakers, rank);
}
}
});
if (goals_parts.length > 1) {
tiebreakers.push(explain_tiebreaker(goals_parts, 'goals scored against common opponents'));
- return subrank_partitions(games, goals_parts, start_rank, tiebreakers);
+ return subrank_partitions(games, goals_parts, start_rank, tiebreakers, rank);
+ }
+
+ // OK, it's a tie. Give them all the same rank.
+ let result = [];
+ for (let i = 0; i < teams.length; ++i) {
+ result.push(teams[i]);
+ result[i].rank = start_rank;
+ }
+ return result;
+};
+
+// Same, but with the simplified rules for ranking thirds. games isn't used and can be empty.
+function rank_thirds(games, teams, start_rank, tiebreakers) {
+ if (teams.length <= 1) {
+ // Only one team, so trivial.
+ teams[0].rank = start_rank;
+ return teams;
+ }
+
+ // Rule #1: Partition the teams by score.
+ let score_parts = partition(teams, function(a, b) { return b.pts - a.pts });
+ if (score_parts.length > 1) {
+ tiebreakers.push(explain_tiebreaker(score_parts, 'most games won'));
+ return subrank_partitions(games, score_parts, start_rank, tiebreakers, rank_thirds);
+ }
+
+ // Rule #2: Goal difference against common opponents.
+ let gd_parts = partition(teams, function(a, b) { return b.gd - a.gd });
+ if (gd_parts.length > 1) {
+ tiebreakers.push(explain_tiebreaker(gd_parts, 'goal difference'));
+ return subrank_partitions(games, gd_parts, start_rank, tiebreakers, rank_thirds);
+ }
+
+ // Rule #3: Goals scored.
+ let goal_parts = partition(teams, function(a, b) { return b.goals - a.goals });
+ if (goal_parts.length > 1) {
+ tiebreakers.push(explain_tiebreaker(goal_parts, 'goals scored'));
+ return subrank_partitions(games, goal_parts, start_rank, tiebreakers, rank_thirds);
}
// OK, it's a tie. Give them all the same rank.
function publish_group_ranks() {
publish_group_rank('Group A');
publish_group_rank('Group B');
+ publish_group_rank('Group C');
+}
+
+function get_ranked(response, group_name) {
+ let teams = parse_teams_from_spreadsheet(response);
+ let games = parse_games_from_spreadsheet(response, group_name, false);
+ apply_games_to_teams(games, teams);
+ let tiebreakers = [];
+ teams = rank(games, teams, 1, tiebreakers);
+ return teams;
+}
+
+// Pick out everything that is at rank N _or_ avoids rank N by lack of tiebreakers only.
+function pick_out_rank(teams, rank, candidates) {
+ let lowest_rank = teams[rank - 1].rank;
+
+ let count = 0;
+ for (const team of teams) {
+ if (team.rank >= lowest_rank && team.rank <= rank) {
+ ++count;
+ }
+ }
+
+ if (count >= teams.length / 2) {
+ // We have no info yet, ignore this group.
+ return;
+ }
+
+ for (const team of teams) {
+ if (team.rank >= lowest_rank && team.rank <= rank) {
+ candidates.push(team);
+ }
+ }
+}
+
+function publish_best_thirds() {
+ get_group('Group A', function(response_a) {
+ get_group('Group B', function(response_b) {
+ get_group('Group C', function(response_c) {
+ let A = get_ranked(response_a, 'Group A');
+ let B = get_ranked(response_b, 'Group B');
+ let C = get_ranked(response_c, 'Group C');
+
+ let candidates = [];
+ pick_out_rank(A, 3, candidates);
+ pick_out_rank(B, 3, candidates);
+ pick_out_rank(C, 3, candidates);
+
+ let tiebreakers = [];
+ let text = "";
+ if (candidates.length >= 2) {
+ let ranked = rank_thirds([], candidates, 1, tiebreakers);
+ text = "Best thirds: " + ranked[0].mediumname + ", " + ranked[1].mediumname + "\n" + tiebreakers.join("\n");
+ }
+ let updates = [];
+ updates.push({ "range": ultimateconfig['explain_third_cell'], "values": [ [ text ] ] });
+ let json = {
+ "valueInputOption": "USER_ENTERED",
+ "data": updates
+ };
+ possibly_update_oauth_key(function() {
+ post_json('https://sheets.googleapis.com/v4/spreadsheets/' + ultimateconfig['score_sheet_id'] + '/values:batchUpdate?key=' + ultimateconfig['api_key'], json, function(response) {}, current_oauth_access_token);
+ });
+ });
+ });
+ });
}
update_oauth_key();
setTimeout(function() {
publish_group_ranks();
- setInterval(function() { publish_group_ranks(); }, 60000);
+ publish_best_thirds();
+ setInterval(function() { publish_group_ranks(); publish_best_thirds(); }, 60000);
}, 5000);