From: Steinar H. Gunderson Date: Sun, 21 May 2023 21:17:34 +0000 (+0200) Subject: Count time on offense and on defense. X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=133a9596bbe356ecda211eb95b8f9416bbe9a47e;p=pkanalytics Count time on offense and on defense. --- diff --git a/ultimate.js b/ultimate.js index 9ed8936..5c176e0 100644 --- a/ultimate.js +++ b/ultimate.js @@ -9,20 +9,27 @@ fetch('ultimate.json') .then(response => response.json()) .then(response => { global_json = response; process_matches(global_json); }); -function attribute_player_time(player, to, from) { +function attribute_player_time(player, to, from, offense) { + let delta_time; if (player.on_field_since > from) { // Player came in while play happened (without a stoppage!?). - player.playing_time_ms += to - player.on_field_since; + delta_time = to - player.on_field_since; } else { - player.playing_time_ms += to - from; + delta_time = to - from; + } + player.playing_time_ms += delta_time; + if (offense === true) { + player.offensive_playing_time_ms += delta_time; + } else if (offense === false) { + player.defensive_playing_time_ms += delta_time; } } -function take_off_field(player, t, live_since) { +function take_off_field(player, t, live_since, offense) { if (live_since === null) { // Play isn't live, so nothing to do. } else { - attribute_player_time(player, t, live_since); + attribute_player_time(player, t, live_since, offense); } if (player.on_field_since !== null) { // Just a safeguard; out without in should never happen. player.field_time_ms += t - player.on_field_since; @@ -158,6 +165,8 @@ function process_matches(json) { 'interceptions': 0, 'points_played': 0, 'playing_time_ms': 0, + 'offensive_playing_time_ms': 0, + 'defensive_playing_time_ms': 0, 'field_time_ms': 0, // For efficiency. @@ -185,6 +194,8 @@ function process_matches(json) { players['globals'] = { 'points_played': 0, 'playing_time_ms': 0, + 'offensive_playing_time_ms': 0, + 'defensive_playing_time_ms': 0, 'field_time_ms': 0, 'offensive_points_completed': 0, @@ -200,7 +211,7 @@ function process_matches(json) { let handler = null; let prev_handler = null; let live_since = null; - let offense = null; + let offense = null; // True/false/null (unknown). let puller = null; let pull_started = null; let last_pull_was_ours = null; // Effectively whether we're playing an O or D point (not affected by turnovers). @@ -220,7 +231,7 @@ function process_matches(json) { if (type === 'in' && p.on_field_since === null) { p.on_field_since = t; } else if (type === 'out') { - take_off_field(p, t, live_since); + take_off_field(p, t, live_since, offense); } // Liveness management @@ -242,7 +253,7 @@ function process_matches(json) { p.last_point_seen = point_num; ++p.points_played; } - attribute_player_time(p, t, live_since); + attribute_player_time(p, t, live_since, offense); if (type !== 'stoppage') { if (last_pull_was_ours === true) { // D point. @@ -276,6 +287,11 @@ function process_matches(json) { } if (live_since !== null) { globals.playing_time_ms += t - live_since; + if (offense === true) { + globals.offensive_playing_time_ms += t - live_since; + } else if (offense === false) { + globals.defensive_playing_time_ms += t - live_since; + } } live_since = null; @@ -317,12 +333,31 @@ function process_matches(json) { puller = pull_started = null; } - // Offense/defense management (TODO: use it for actual counting) + // Offense/defense management + let last_offense = offense; if (type === 'set_defense' || type === 'goal' || type === 'throwaway' || type === 'drop') { offense = false; } else if (type === 'set_offense' || type === 'their_goal' || type === 'their_throwaway' || type === 'defense' || type === 'interception') { offense = true; } + if (last_offense !== offense && live_since !== null) { + // Switched offense/defense status, so attribute this drive as needed, + // and update live_since to take that into account. + for (const [q,p] of Object.entries(players)) { + if (p.on_field_since === null) { + continue; + } + attribute_player_time(p, t, live_since, last_offense); + } + globals.playing_time_ms += t - live_since; + if (offense === true) { + globals.offensive_playing_time_ms += t - live_since; + } else if (offense === false) { + globals.defensive_playing_time_ms += t - live_since; + } + live_since = t; + } + if (type === 'pull') { last_pull_was_ours = true; } else if (type === 'their_pull') { @@ -403,6 +438,11 @@ function process_matches(json) { } if (live_since !== null && last_goal !== null) { globals.playing_time_ms += last_goal - live_since; + if (offense === true) { + globals.offensive_playing_time_ms += last_goal - live_since; + } else if (offense === false) { + globals.defensive_playing_time_ms += last_goal - live_since; + } } } @@ -741,6 +781,8 @@ function make_table_playing_time(players) { add_th(header, 'Player'); add_th(header, 'Points played'); add_th(header, 'Time played'); + add_th(header, 'O time'); + add_th(header, 'D time'); add_th(header, 'Time on field'); add_th(header, 'O points'); add_th(header, 'D points'); @@ -753,6 +795,8 @@ function make_table_playing_time(players) { add_3cell(row, p.name, 'name'); // TODO: number? add_3cell(row, p.points_played); add_3cell(row, Math.floor(p.playing_time_ms / 60000) + ' min'); + add_3cell(row, Math.floor(p.offensive_playing_time_ms / 60000) + ' min'); + add_3cell(row, Math.floor(p.defensive_playing_time_ms / 60000) + ' min'); add_3cell(row, Math.floor(p.field_time_ms / 60000) + ' min'); add_3cell(row, p.offensive_points_completed); add_3cell(row, p.defensive_points_completed); @@ -765,6 +809,8 @@ function make_table_playing_time(players) { add_3cell(row, ''); add_3cell(row, globals.points_played); add_3cell(row, Math.floor(globals.playing_time_ms / 60000) + ' min'); + add_3cell(row, Math.floor(globals.offensive_playing_time_ms / 60000) + ' min'); + add_3cell(row, Math.floor(globals.defensive_playing_time_ms / 60000) + ' min'); add_3cell(row, Math.floor(globals.field_time_ms / 60000) + ' min'); add_3cell(row, globals.offensive_points_completed); add_3cell(row, globals.defensive_points_completed);