function parse_teams_from_spreadsheet(response) {
let teams = [];
- for (let i = 2; response.values[i].length >= 1; ++i) {
+ for (let i = 1; i < response.values.length && response.values[i].length >= 1; ++i) {
teams.push({
"name": response.values[i][0],
"mediumname": response.values[i][1],
return games;
};
-function apply_games_to_teams(games, teams, ignored_teams, ret_ignored_games)
+function get_team_code(teams, str) {
+ for (const team of teams) {
+ if (team.name === str || team.mediumname === str || team.shortname === str) {
+ return team.shortname;
+ }
+ }
+ return str;
+}
+
+function get_all_group_games(teams, groups, cb) {
+ get_sheet('Results', function(response) {
+ let games = [];
+ for (const region of ultimateconfig['group_match_scores']) {
+ for (let row = region.first_row; row <= region.last_row; ++row) {
+ let team1 = get_team_code(teams, response.values[row - 1][region.team1_column]);
+ let team2 = get_team_code(teams, response.values[row - 1][region.team2_column]);
+ if (team1 === undefined || team2 === undefined || team1 === '' || team2 === '' || team1 === null || team2 === null) {
+ continue;
+ }
+ let group_name = region.group_name;
+ if (group_name === undefined) {
+ // Infer group from whatever group both teams are in.
+ for (const [group, teams] of Object.entries(groups)) {
+ if (teams.indexOf(team1) != -1 && teams.indexOf(team2) != -1) {
+ group_name = group;
+ break;
+ }
+ }
+ }
+ let game = {
+ "name1": team1,
+ "name2": team2,
+ "score1": parseInt(response.values[row - 1][region.team1_score_column]),
+ "score2": parseInt(response.values[row - 1][region.team2_score_column]),
+ "group_name": group_name
+ };
+ if (region.stream_time_column !== undefined) {
+ game["streamtime"] = response.values[row - 1][region.stream_time_column].replace('.', ':');
+ game["streamday"] = region.stream_day;
+ }
+ games.push(game);
+ }
+ }
+ cb(games);
+ });
+};
+
+function apply_games_to_teams(games, teams, group_name, ignored_teams, ret_ignored_games)
{
let teams_to_idx = make_teams_to_idx(teams);
let ignored_teams_idx;
teams[i].pts = 0;
}
for (let i = 0; i < games.length; ++i) {
+ if (games[i].group_name !== group_name) {
+ continue;
+ }
let idx1 = teams_to_idx[games[i].name1];
let idx2 = teams_to_idx[games[i].name2];
if (games[i].score1 === undefined || games[i].score2 === undefined ||
return teams.filter(function(team) { return team.ngames > 0; });
}
+// So that we can just have one team list, and let membership be defined by the group list.
+function filter_teams_by_group(teams, groups, group)
+{
+ return teams.filter(function(team) {
+ return groups[group].indexOf(team.shortname) != -1;
+ });
+}
+
function display_group_parsed(teams, games, group_name)
{
document.getElementById('entire-bug').style.display = 'none';
- apply_games_to_teams(games, teams);
+ apply_games_to_teams(games, teams, group_name);
let tiebreakers = [];
teams = rank(games, teams, 1, tiebreakers);
// Stream schedule
let max_list_len = 7;
-function display_stream_schedule(response, group_name) {
- let teams = parse_teams_from_spreadsheet(response);
- let games = parse_games_from_spreadsheet(response, group_name, true);
- display_stream_schedule_parsed(teams, games, 0);
-};
-
function sort_game_list(games) {
games = games.filter(function(game) { return game.streamtime !== undefined && game.streamtime.match(/[0-9]+:[0-9]+/) != null; });
games.sort(function(a, b) {
carousel.style.display = 'table';
};
-function get_group(group_name, cb)
+function get_sheet(sheet_name, cb)
{
let req = new XMLHttpRequest();
req.onload = function(e) {
- cb(JSON.parse(req.responseText), group_name);
+ cb(JSON.parse(req.responseText));
};
- req.open('GET', 'https://sheets.googleapis.com/v4/spreadsheets/' + ultimateconfig['score_sheet_id'] + '/values/\'' + group_name + '\'!A1:J50?key=' + ultimateconfig['api_key']);
+ req.open('GET', 'https://sheets.googleapis.com/v4/spreadsheets/' + ultimateconfig['score_sheet_id'] + '/values/\'' + sheet_name + '\'!A1:Z50?key=' + ultimateconfig['api_key']);
req.send();
}
-function showgroup(group_name)
+function get_group(group_name, cb)
+{
+ get_sheet(group_name, function(response) {
+ cb(response, group_name);
+ });
+}
+
+function get_teams(cb)
+{
+ get_sheet('Teams', function(response) {
+ cb(parse_teams_from_spreadsheet(response));
+ });
+}
+
+function get_groups(cb)
{
- get_group(group_name, function(response, group_name) {
- let teams = parse_teams_from_spreadsheet(response);
- let games = parse_games_from_spreadsheet(response, group_name, false);
- teams = filter_teams(teams, response);
- display_group_parsed(teams, games, group_name);
- publish_group_rank(response, group_name); // Update the spreadsheet in the background.
+ get_sheet('Groups', function(response) {
+ let groups = {};
+ for (let i = 1; i < response.values.length && response.values[i].length >= 1; ++i) {
+ let team = response.values[i][0];
+ let group = response.values[i][1];
+ if (groups[group] === undefined) {
+ groups[group] = [];
+ }
+ groups[group].push(team);
+ }
+ cb(groups);
});
}
+function showgroup(group_name)
+{
+ get_teams(function(teams) {
+ get_groups(function(groups) {
+ get_all_group_games(teams, groups, function(games) {
+ teams = filter_teams_by_group(teams, groups, group_name);
+ display_group_parsed(teams, games, group_name);
+ publish_group_rank(response, group_name); // Update the spreadsheet in the background.
+ });
+ });
+ });
+}
function showgroup_from_state()
{
function showschedule(page)
{
- let teams = [];
- let games = [];
- let groups_to_get = [
- 'Group A',
- 'Group B',
- 'Group C',
- 'Playoffs 9th-13th',
- 'Playoffs'
- ];
- let num_left = groups_to_get.length;
-
- let cb = function(response, group_name) {
- teams = teams.concat(parse_teams_from_spreadsheet(response));
- games = games.concat(parse_games_from_spreadsheet(response, group_name, true));
- if (--num_left == 0) {
- display_stream_schedule_parsed(teams, games, 0);
- }
- };
-
- for (const group of groups_to_get) {
- get_group(group, cb);
- }
+ get_teams(function(teams) {
+ get_groups(function(groups) {
+ get_all_group_games(teams, groups, function(games) {
+ get_all_playoff_games(teams, groups, games, function(playoff_games) {
+ games = games.concat(playoff_games);
+ games = games.filter(function(game) { return game.streamday !== undefined; });
+ display_stream_schedule_parsed(teams, games, 0);
+ });
+ });
+ });
+ });
};
function do_series(series)
function showcarousel()
{
- let teams_per_group = [];
- let games_per_group = [];
- let combined_teams = [];
- let combined_games = [];
let groups_to_get = [
'Group A',
'Group B',
'Group C',
- 'Playoffs 9th-13th',
- 'Playoffs'
+ 'Playoffs',
+ 'Playoffs 9th–11th',
+ 'Playoffs 12th–14th'
];
- let num_left = groups_to_get.length;
-
- let cb = function(response, group_name) {
- let teams = parse_teams_from_spreadsheet(response);
- let games = parse_games_from_spreadsheet(response, group_name, true);
- teams = filter_teams(teams, response);
- teams_per_group[group_name] = teams;
- games_per_group[group_name] = games;
-
- combined_teams = combined_teams.concat(teams);
- combined_games = combined_games.concat(games);
- if (--num_left == 0) {
- let series = [
- [ 13000, function() { display_group_parsed(teams_per_group['Group A'], games_per_group['Group A'], 'Group A'); } ],
- [ 2000, function() { hidetable(); } ],
- [ 13000, function() { display_group_parsed(teams_per_group['Group B'], games_per_group['Group B'], 'Group B'); } ],
- [ 2000, function() { hidetable(); } ],
- [ 13000, function() { display_group_parsed(teams_per_group['Group C'], games_per_group['Group C'], 'Group C'); } ],
- [ 2000, function() { hidetable(); } ],
- [ 13000, function() { display_group_parsed(teams_per_group['Playoffs 9th-13th'], games_per_group['Playoffs 9th-13th'], 'Playoffs 9th–13th'); } ],
- [ 2000, function() { hidetable(); } ]
- ];
- let num_pages = find_num_pages(combined_games);
- for (let page = 0; page < num_pages; ++page) {
- series.push([ 13000, function() { display_stream_schedule_parsed(combined_teams, combined_games, page); } ]);
- series.push([ 2000, function() { hidetable(); } ]);
- }
-
- do_series(series);
- }
- };
+ get_teams(function(teams) {
+ get_groups(function(groups) {
+ get_all_group_games(teams, groups, function(games) {
+ get_all_playoff_games(teams, groups, games, function(playoff_games) {
+ games = games.concat(playoff_games);
+ games = games.filter(function(game) { return game.streamday !== undefined; });
+
+ let series = [
+ [ 13000, function() { display_group_parsed(filter_teams_by_group(teams, groups, 'Group A'), games, 'Group A'); } ],
+ [ 2000, function() { hidetable(); } ],
+ [ 13000, function() { display_group_parsed(filter_teams_by_group(teams, groups, 'Group B'), games, 'Group B'); } ],
+ [ 2000, function() { hidetable(); } ],
+ [ 13000, function() { display_group_parsed(filter_teams_by_group(teams, groups, 'Group C'), games, 'Group C'); } ],
+ [ 2000, function() { hidetable(); } ],
+ // We don't show the playoff groups, since we don't even know whether they have data.
+ ];
+ let num_pages = find_num_pages(games);
+ for (let page = 0; page < num_pages; ++page) {
+ series.push([ 13000, function() { display_stream_schedule_parsed(teams, games, page); } ]);
+ series.push([ 2000, function() { hidetable(); } ]);
+ }
- for (const group of groups_to_get) {
- get_group(group, cb);
- }
+ do_series(series);
+ });
+ });
+ });
+ });
};
function stopcarousel()
}
}
-function publish_group_rank(response, group_name)
+function publish_group_rank(teams, games, group_name)
{
let updates = [];
let config = ultimateconfig['group_cells'][group_name];
let cols = config['score_sheet_cols'];
- let teams = parse_teams_from_spreadsheet(response);
- let games = parse_games_from_spreadsheet(response, group_name, false);
- apply_games_to_teams(games, teams);
- teams = filter_teams(teams, response);
+ apply_games_to_teams(games, teams, group_name);
// Write the points total to the unsorted columns.
if (config['point_total_start_row'] !== null) {
});
}
-function montecarlo(responses) {
+function montecarlo(all_teams, groups, games, groups_to_calc) {
let pseudo_group_names = ['X', 'Y', 'Z'];
- let real_group_names = ['A', 'B', 'C'];
- let teams = [], games = [], teams_to_idx = [];
+ let real_group_names = ['A', 'B', 'C']; // Better be corresponding to groups_to_calc...
+ let teams_to_idx = [];
let third_groups = [];
let busted_thirds = false;
- for (const response of responses) {
- let teams_group = parse_teams_from_spreadsheet(response);
- let games_group = parse_games_from_spreadsheet(response, 'irrelevant group name', true);
- apply_games_to_teams(games_group, teams_group);
-
+ // Split teams by group.
+ let teams = [];
+ for (let group_idx = 0; group_idx < groups_to_calc.length; ++group_idx) {
+ let teams_group = filter_teams_by_group(all_teams, groups, groups_to_calc[group_idx]);
teams.push(teams_group);
- games.push(games_group);
teams_to_idx.push(make_teams_to_idx(teams_group));
}
for (let simulation_idx = 0; simulation_idx < 100; ++simulation_idx) { // 100 seems to be enough.
let thirds = [], ignoreds = [];
- let groups = [];
- for (let group_idx = 0; group_idx < responses.length; ++group_idx) {
+ let calc_groups = [];
+
+ for (let group_idx = 0; group_idx < groups_to_calc.length; ++group_idx) {
+ let teams_copy = [];
+ for (const team of teams[group_idx]) {
+ teams_copy.push(Object.assign({}, team));
+ }
+
// Fill in random results. We deliberately use a uniform [-13,+13]
// model here, since we are interested in the extremal results.
// Of course, not all real games go to 13, but the risk of that
// influencing the tiebreakers is very slim.
let games_copy = [], games_with_synth = [];
- for (const game of games[group_idx]) {
+ for (const game of games) {
games_copy.push(Object.assign({}, game));
}
- let teams_copy = [];
- for (const team of teams[group_idx]) {
- teams_copy.push(Object.assign({}, team));
- }
for (let i = 0; i < games_copy.length; ++i) {
let idx1 = teams_to_idx[group_idx][games_copy[i].name1];
if (games_copy[i].score1 === undefined || games_copy[i].score2 === undefined ||
isNaN(games_copy[i].score1) || isNaN(games_copy[i].score2) ||
games_copy[i].score1 == games_copy[i].score2) {
- // These were skipped by apply_games_to_teams() above.
+ // These were skipped by apply_games_to_teams() earlier.
let score1 = 0, score2 = 0;
let r = Math.floor(Math.random() * 26);
if (r < 13) {
}
}
- groups.push({
+ calc_groups.push({
"games": games_with_synth,
"teams": teams_copy
});
let tiebreakers = [];
if (ultimateconfig['kick_fifth_from_third']) {
// Recompute scores (but not ranks!) without the ignored games. (thirds point to these objects.)
- for (let group_idx = 0; group_idx < responses.length; ++group_idx) {
- apply_games_to_teams(groups[group_idx].games, groups[group_idx].teams, ignoreds);
+ for (let group_idx = 0; group_idx < groups_to_calc.length; ++group_idx) {
+ apply_games_to_teams(calc_groups[group_idx].games, calc_groups[group_idx].teams, groups_to_calc[group_idx], ignoreds);
}
}
let ranked = rank_thirds([], thirds, 1, tiebreakers);
if (simulation_idx == 0) {
third_groups = ranked;
} else {
- for (let i = 0; i < responses.length; ++i) {
+ for (let i = 0; i < groups_to_calc.length; ++i) {
if (third_groups[i].group_idx !== ranked[i].group_idx) {
third_groups[i].group_idx = null;
}
}
let replacements = [];
- for (let group_idx = 0; group_idx < responses.length; ++group_idx) {
+ for (let group_idx = 0; group_idx < groups_to_calc.length; ++group_idx) {
if (third_groups[group_idx].group_idx !== null) {
replacements.push([ pseudo_group_names[group_idx], real_group_names[third_groups[group_idx].group_idx] ]);
}
}
- for (let group_idx = 0; group_idx < responses.length; ++group_idx) {
+ for (let group_idx = 0; group_idx < groups_to_calc.length; ++group_idx) {
for (let i = 0; i < teams[group_idx].length; ++i) {
if (teams[group_idx][i].simulated_rank !== null) {
replacements.push([ real_group_names[group_idx] + teams[group_idx][i].simulated_rank, teams[group_idx][i].shortname ]);
return str;
}
-function fill_playoff(replacements, teams) {
+function fill_playoff(all_teams, groups, replacements, teams) {
let team_expansions = {};
- for (const group of teams) {
- for (const team of group) {
- team_expansions[team.name] = team_expansions[team.mediumname] = team_expansions[team.shortname] =
- [ team.name, team.mediumname, team.shortname ];
- }
+ for (const team of all_teams) {
+ team_expansions[team.name] = team_expansions[team.mediumname] = team_expansions[team.shortname] =
+ [ team.name, team.mediumname, team.shortname ];
}
let games = ultimateconfig['playoff_games'];
get_results('Results', function(response) {
let updates = [], meta_updates = [];
- let game_num = 0;
for (const game of games) {
let team1 = do_replacements(game[0], replacements);
let team2 = do_replacements(game[1], replacements);
updates.push({ "range": cell_score1, "values": [ [ game[4] ] ] });
meta_updates.push({ "mergeCells": { "range": range, "mergeType": "MERGE_ALL" }});
}
-
- if (game[2] == 0) { // Stream field.
- // Game.
- let ss_row = ultimateconfig['playoff_games_start_row_detail_sheet'] + game_num;
- updates.push({
- "range": "Playoffs!A" + ss_row + ":J" + ss_row,
- "values": [ [ team1, team2, score1, score2, "", "", "", game_day, response['values'][row - 1][1].replace(".",":"), game[6] ] ]
- });
-
- // Team codes.
- updates.push({
- "range": "Playoffs!A" + (2 * game_num + 3) + ":C" + (2 * game_num + 3),
- "values": [ names_for_team(team1, team_expansions) ]
- });
- updates.push({
- "range": "Playoffs!A" + (2 * game_num + 4) + ":C" + (2 * game_num + 4),
- "values": [ names_for_team(team2, team_expansions) ]
- });
-
- ++game_num;
- }
}
let json = {
"valueInputOption": "USER_ENTERED",
"requests": meta_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) {
- get_group('Playoffs 9th-14th', function(response_l) { publish_group_rank(response_l, 'Playoffs 9th-14th'); });
- }, current_oauth_access_token);
- post_json('https://sheets.googleapis.com/v4/spreadsheets/' + ultimateconfig['score_sheet_id'] + ':batchUpdate?key=' + ultimateconfig['api_key'], meta_json, function(response) {}, current_oauth_access_token);
+ if (updates.length > 0) {
+ post_json('https://sheets.googleapis.com/v4/spreadsheets/' + ultimateconfig['score_sheet_id'] + '/values:batchUpdate?key=' + ultimateconfig['api_key'], json, function(response) {
+ get_all_group_games(all_teams, groups, function(group_games) {
+ // NOTE: filter_teams_by_group will be delayed by one cycle
+ // after W P1 etc. becomes determined for the first time.
+ let teams_l1 = filter_teams_by_group(all_teams, groups, 'Playoffs 9th–11th');
+ let teams_l2 = filter_teams_by_group(all_teams, groups, 'Playoffs 12th–14th');
+ publish_group_rank(teams_l1, group_games, 'Playoffs 9th–11th');
+ publish_group_rank(teams_l2, group_games, 'Playoffs 12th–14th');
+ });
+ }, current_oauth_access_token);
+ }
+ if (meta_updates.length > 0) {
+ post_json('https://sheets.googleapis.com/v4/spreadsheets/' + ultimateconfig['score_sheet_id'] + ':batchUpdate?key=' + ultimateconfig['api_key'], meta_json, function(response) {}, current_oauth_access_token);
+ }
});
});
}
}
function publish_group_ranks() {
- get_group('Group A', function(response_a) {
- get_group('Group B', function(response_b) {
- get_group('Group C', function(response_c) {
- publish_group_rank(response_a, 'Group A');
- publish_group_rank(response_b, 'Group B');
- publish_group_rank(response_c, 'Group C');
-
- let replacements = montecarlo([response_a, response_b, response_c]);
- let team_a = parse_teams_from_spreadsheet(response_a);
- let team_b = parse_teams_from_spreadsheet(response_b);
- let team_c = parse_teams_from_spreadsheet(response_c);
- fill_playoff(replacements, [team_a, team_b, team_c]);
+ get_teams(function(teams) {
+ get_groups(function(groups) {
+ get_all_group_games(teams, groups, function(games) {
+ let teams_a = filter_teams_by_group(teams, groups, 'Group A');
+ let teams_b = filter_teams_by_group(teams, groups, 'Group B');
+ let teams_c = filter_teams_by_group(teams, groups, 'Group C');
+ publish_group_rank(teams_a, games, 'Group A');
+ publish_group_rank(teams_b, games, 'Group B');
+ publish_group_rank(teams_c, games, 'Group C');
+
+ let replacements = montecarlo(teams, groups, games, ['Group A', 'Group B', 'Group C']);
+ fill_playoff(teams, groups, replacements, [teams_a, teams_b, teams_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);
- teams = filter_teams(teams, response);
+function get_all_playoff_games(teams, groups, group_games, cb) {
+ let replacements = montecarlo(teams, groups, group_games, ['Group A', 'Group B', 'Group C']);
+ let games = ultimateconfig['playoff_games'];
+ get_results('Results', function(response) {
+ let playoff_games = [];
+ for (const game of games) {
+ let team1 = do_replacements(game[0], replacements);
+ let team2 = do_replacements(game[1], replacements);
+ let row = ultimateconfig['playoff_games_start_row'] + game[3];
+ let cols = ultimateconfig['playoff_games_cols'][game[2]];
+ let score1 = response['values'][row - 1][cols[1]];
+ let score2 = response['values'][row - 1][cols[2]];
+ let streamday = game[7];
+ if (streamday === undefined && game[2] === 0) { // Stream field is by default on stream.
+ streamday = 7;
+ }
+ playoff_games.push({
+ "name1": team1,
+ "name2": team2,
+ "score1": parseInt(score1),
+ "score2": parseInt(score2),
+ "streamday": streamday,
+ "streamtime": response['values'][row - 1][1].replace('.', ':'),
+ "group_name": game[6]
+ });
+ }
+ cb(playoff_games);
+ });
+}
+
+function get_ranked(teams, games, group_name) {
+ apply_games_to_teams(games, teams, group_name);
let tiebreakers = [];
teams = rank(games, teams, 1, tiebreakers);
return teams;
function publish_best_thirds() {
if (!ultimateconfig['best_thirds']) return;
- 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');
+ get_teams(function(teams) {
+ get_groups(function(groups) {
+ get_all_group_games(teams, groups, function(games) {
+ let teams_a = filter_teams_by_group(teams, groups, 'Group A');
+ let teams_b = filter_teams_by_group(teams, groups, 'Group B');
+ let teams_c = filter_teams_by_group(teams, groups, 'Group C');
+ let A = get_ranked(teams_a, games, 'Group A');
+ let B = get_ranked(teams_b, games, 'Group B');
+ let C = get_ranked(teams_c, games, 'Group C');
let candidates = [];
pick_out_rank(A, 3, candidates);
C = jsonclone(C);
// Recompute scores (but not ranks!) without the ignored games.
- let games_a = parse_games_from_spreadsheet(response_a, 'Group A', false);
- apply_games_to_teams(games_a, A, ignoreds, ignored_games);
- let games_b = parse_games_from_spreadsheet(response_b, 'Group B', false);
- apply_games_to_teams(games_b, B, ignoreds, ignored_games);
- let games_c = parse_games_from_spreadsheet(response_c, 'Group C', false);
- apply_games_to_teams(games_c, C, ignoreds, ignored_games);
+ apply_games_to_teams(games, A, 'Group A', ignoreds, ignored_games);
+ apply_games_to_teams(games, B, 'Group B', ignoreds, ignored_games);
+ apply_games_to_teams(games, C, 'Group C', ignoreds, ignored_games);
// Filter out ignored games involving the candidate thirds.
let candidates_to_idx = make_teams_to_idx(candidates);