From d3afda615f0c1b4f16e7d7ada39df4ee6481bb3c Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Wed, 10 May 2023 21:21:51 +0200 Subject: [PATCH] Implement O and D efficiency in the viewer. --- ultimate.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/ultimate.js b/ultimate.js index 3b72f01..330bdd6 100644 --- a/ultimate.js +++ b/ultimate.js @@ -54,6 +54,12 @@ function process_matches(json) { 'points_played': 0, 'playing_time_ms': 0, + // For efficiency. + 'offensive_points_completed': 0, + 'offensive_points_won': 0, + 'defensive_points_completed': 0, + 'defensive_points_won': 0, + 'offensive_soft_plus': 0, 'offensive_soft_minus': 0, 'defensive_soft_plus': 0, @@ -76,6 +82,7 @@ function process_matches(json) { let offense = null; 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). let point_num = 0; for (const [q,p] of Object.entries(players)) { p.on_field_since = null; @@ -98,14 +105,27 @@ function process_matches(json) { live_since = t; } else if (type === 'goal' || type === 'their_goal' || type === 'stoppage') { for (const [q,p] of Object.entries(players)) { - if (p.on_field_since !== null) { - if (p.last_point_seen !== point_num) { - // In case the player did nothing this point, - // not even subbing in. - p.last_point_seen = point_num; - ++p.points_played; + if (p.on_field_since === null) { + continue; + } + if (p.last_point_seen !== point_num) { + // In case the player did nothing this point, + // not even subbing in. + p.last_point_seen = point_num; + ++p.points_played; + } + attribute_player_time(p, t, live_since); + + if (last_pull_was_ours === true) { // D point. + ++p.defensive_points_completed; + if (type === 'goal') { + ++p.defensive_points_won; + } + } else if (last_pull_was_ours === false) { // O point. + ++p.offensive_points_completed; + if (type === 'goal') { + ++p.offensive_points_won; } - attribute_player_time(p, t, live_since); } } live_since = null; @@ -142,6 +162,25 @@ function process_matches(json) { } else if (type === 'set_offense' || type === 'their_goal' || type === 'their_throwaway' || type === 'defense' || type === 'interception') { offense = true; } + if (type === 'pull') { + last_pull_was_ours = true; + } else if (type === 'their_pull') { + last_pull_was_ours = false; + } else if (type === 'set_offense' && last_pull_was_ours === null) { + // set_offense could either be “changed to offense for some reason + // we could not express”, or “we started in the middle of a point, + // and we are offense”. We assume that if we already saw the pull, + // it's the former, and if not, it's the latter; thus, the === null + // test above. (It could also be “we set offense before the pull, + // so that we get the right button enabled”, in which case it will + // be overwritten by the next pull/their_pull event anyway.) + last_pull_was_ours = false; + } else if (type === 'set_defense' && last_pull_was_ours === null) { + // Similar. + last_pull_was_ours = true; + } else if (type === 'goal' || type === 'their_goal') { + last_pull_was_ours = null; + } // Event management if (type === 'catch' || type === 'goal') { @@ -258,6 +297,8 @@ function make_table_general(players) { add_cell(header, 'th', 'Player'); add_cell(header, 'th', '+/-'); add_cell(header, 'th', 'Soft +/-'); + add_cell(header, 'th', 'O efficiency'); + add_cell(header, 'th', 'D efficiency'); add_cell(header, 'th', 'Points played'); add_cell(header, 'th', 'Time played'); rows.push(header); @@ -267,9 +308,13 @@ function make_table_general(players) { let row = document.createElement('tr'); let pm = p.goals + p.assists + p.hockey_assists + p.defenses - p.throwaways - p.drops; let soft_pm = p.offensive_soft_plus + p.defensive_soft_plus - p.offensive_soft_minus - p.defensive_soft_minus; + let o_efficiency = (p.offensive_points_won / p.offensive_points_completed) * 2 - 1; + let d_efficiency = (p.defensive_points_won / p.defensive_points_completed) * 2 - 1; add_cell(row, 'td', p.name); // TODO: number? add_cell(row, 'td', pm > 0 ? ('+' + pm) : pm); add_cell(row, 'td', soft_pm > 0 ? ('+' + soft_pm) : soft_pm); + add_cell(row, 'td', p.offensive_points_completed > 0 ? o_efficiency.toFixed(2) : 'N/A'); + add_cell(row, 'td', p.defensive_points_completed > 0 ? d_efficiency.toFixed(2) : 'N/A'); add_cell(row, 'td', p.points_played); add_cell(row, 'td', Math.floor(p.playing_time_ms / 60000) + ' min'); rows.push(row); -- 2.39.2