]> git.sesse.net Git - ultimatescore/commitdiff
Add code for computing the best thirds.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 14 Oct 2019 22:04:50 +0000 (00:04 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 14 Oct 2019 22:04:50 +0000 (00:04 +0200)
carousel.js
update_sheets.js

index bbab9aafe300ea0787f291a32a75fd33efbaebf5..aa195c603fe54b3446e82e6c8d1be558a1e6b333 100644 (file)
@@ -24,10 +24,10 @@ function addth(tr, className, content) {
        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]);
                }
@@ -152,7 +152,7 @@ function rank(games, teams, start_rank, tiebreakers) {
        // 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.
@@ -174,7 +174,7 @@ function rank(games, teams, start_rank, tiebreakers) {
        });
        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).
@@ -183,7 +183,7 @@ function rank(games, teams, start_rank, tiebreakers) {
        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).
@@ -210,7 +210,7 @@ function rank(games, teams, start_rank, tiebreakers) {
                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);
                }
        }
 
@@ -253,7 +253,7 @@ function rank(games, teams, start_rank, tiebreakers) {
        });
        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).
@@ -261,7 +261,7 @@ function rank(games, teams, start_rank, tiebreakers) {
                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);
                }
        }
 
@@ -293,7 +293,45 @@ function rank(games, teams, start_rank, tiebreakers) {
        });
        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.
index 7867141460371be0a862db61f4b9f5061206a5cc..641c94db10e41597d28eb5af48f65e4f05ed2f36 100644 (file)
@@ -112,10 +112,77 @@ function publish_group_rank(group_name)
 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);