From 9cf2b82c8e08cbd8224d06ab576ee87ac2f8f8b0 Mon Sep 17 00:00:00 2001 From: "Eevee (Evelyn Woods)" Date: Sun, 3 Jan 2021 13:48:11 -0700 Subject: [PATCH] Change the bulk test results into a (slightly more compact) table --- js/main.js | 120 +++++++++++++++++++++++++++++++++-------------------- style.css | 53 ++++++++++++++++++----- 2 files changed, 118 insertions(+), 55 deletions(-) diff --git a/js/main.js b/js/main.js index 7379c08..2861d39 100644 --- a/js/main.js +++ b/js/main.js @@ -2480,12 +2480,21 @@ class PackTestDialog extends DialogOverlay { this.current_status = mk('p', "Ready"); - this.results = mk('ol.packtest-results.packtest-colorcoded'); + this.results = mk('table.packtest-results.packtest-colorcoded', + mk('thead', mk('tr', + mk('th.-level', "Level"), + mk('th.-result', "Result"), + mk('th.-clock', "Play time"), + mk('th.-delta', "Replay delta"), + mk('th.-speed', "Run speed"), + )), + mk('tbody'), + ); this.results.addEventListener('click', ev => { - let li = ev.target.closest('li'); - if (! li) + let tbody = ev.target.closest('tbody'); + if (! tbody) return; - let index = li.getAttribute('data-index'); + let index = tbody.getAttribute('data-index'); if (index === undefined) return; this.close(); @@ -2521,6 +2530,7 @@ class PackTestDialog extends DialogOverlay { async run(handle) { let pack = this.conductor.stored_game; + let tileset = this.conductor.tileset; let dummy_sfx = { set_player_position() {}, play() {}, @@ -2540,7 +2550,9 @@ class PackTestDialog extends DialogOverlay { } } - this.results.textContent = ''; + for (let tbody of this.results.querySelectorAll('tbody')) { + tbody.remove(); + } for (let li of this.results_summary.childNodes) { li.removeAttribute('data-status'); } @@ -2553,28 +2565,55 @@ class PackTestDialog extends DialogOverlay { for (let i = 0; i < num_levels; i++) { let stored_level, level; let status_li = this.results_summary.childNodes[i]; - let record_result = (token, short_status, comment, include_canvas) => { + let level_start_time = performance.now(); + let record_result = (token, short_status, include_canvas, comment) => { let level_title = stored_level ? stored_level.title : "???"; status_li.setAttribute('data-status', token); status_li.setAttribute('title', `${short_status} (#${i + 1} ${level_title})`); - let li = mk( - 'li', {'data-status': token, 'data-index': i}, - `#${i + 1} ${level_title}: `, - comment); - status_li.onclick = () => { - li.scrollIntoView(); - }; - if (include_canvas && level) { - let canvas = mk('canvas', { - width: this.renderer.canvas.width, - height: this.renderer.canvas.height, - }); - this.renderer.set_level(level); - this.renderer.draw(); - canvas.getContext('2d').drawImage(this.renderer.canvas, 0, 0, canvas.width, canvas.height); - li.append(canvas); + let tbody = mk('tbody', {'data-status': token, 'data-index': i}); + status_li.addEventListener('click', () => { + tbody.scrollIntoView(); + }); + + let tr = mk('tr', + mk('td.-level', `#${i + 1} ${level_title}`), + mk('td.-result', short_status), + ); + if (level) { + tr.append( + mk('td.-clock', util.format_duration(level.tic_counter / TICS_PER_SECOND)), + mk('td.-delta', util.format_duration((level.tic_counter - stored_level.replay.duration) / TICS_PER_SECOND, 2)), + mk('td.-speed', ((level.tic_counter / TICS_PER_SECOND) / (performance.now() - level_start_time) * 1000).toFixed(2) + '×'), + ); } - this.results.append(li); + else { + tr.append(mk('td.-clock'), mk('td.-delta'), mk('td.-speed')); + } + tbody.append(tr); + + if (comment) { + tbody.append(mk('tr', mk('td.-full', {colspan: 5}, comment))); + } + if (include_canvas && level) { + try { + let canvas = mk('canvas', { + width: Math.min(this.renderer.canvas.width, level.size_x * tileset.size_x), + height: Math.min(this.renderer.canvas.height, level.size_y * tileset.size_y), + }); + this.renderer.set_level(level); + this.renderer.draw(); + canvas.getContext('2d').drawImage( + this.renderer.canvas, 0, 0, + this.renderer.canvas.width, this.renderer.canvas.height); + tbody.append(mk('tr', mk('td.-full', {colspan: 5}, canvas))); + } + catch (e) { + console.error(e); + tbody.append(mk('tr', mk('td.-full', {colspan: 5}, + `Internal error while trying to capture screenshot: ${e}`))); + } + } + this.results.append(tbody); if (level) { total_tics += level.tic_counter; @@ -2584,7 +2623,7 @@ class PackTestDialog extends DialogOverlay { try { stored_level = pack.load_level(i); if (! stored_level.has_replay) { - record_result('no-replay', "N/A", "No replay available"); + record_result('no-replay', "No replay"); continue; } @@ -2605,39 +2644,26 @@ class PackTestDialog extends DialogOverlay { if (level.tic_counter < replay.duration - 10) { // Early exit is dubious (e.g. this happened sometimes before multiple // players were implemented correctly) - record_result( - 'early', "Won early", - `Exited early after ${util.format_duration(level.tic_counter / TICS_PER_SECOND)} with ${util.format_duration((replay.duration - level.tic_counter) / TICS_PER_SECOND)} left in the replay`, - true); + record_result('early', "Won early", true); } else { - record_result( - 'success', "Won", - `Exited successfully after ${util.format_duration(level.tic_counter / TICS_PER_SECOND)} (delta ${level.tic_counter - replay.duration})`); + record_result('success', "Won"); } num_passed += 1; break; } else if (level.state === 'failure') { - record_result( - 'failure', "Lost", - `Died at ${util.format_duration(level.tic_counter / TICS_PER_SECOND)} (tic ${level.tic_counter}/${replay.duration}, ${Math.floor(level.tic_counter / replay.duration * 100)}%)`, - true); + record_result('failure', "Lost", true); break; } else if (level.tic_counter >= replay.duration + 200) { - record_result( - 'short', "Out of input", - `Replay completed without exiting; ran for ${util.format_duration(replay.duration / TICS_PER_SECOND)}, gave up after 10 more seconds`, - true); + record_result('short', "Out of input", true); break; } if (level.tic_counter % 20 === 1) { if (handle.cancel) { - record_result( - 'interrupted', "Interrupted", - "Interrupted"); + record_result('interrupted', "Interrupted"); this.current_status.textContent = `Interrupted on level ${i + 1}/${num_levels}; ${num_passed} passed`; return; } @@ -2656,7 +2682,7 @@ class PackTestDialog extends DialogOverlay { catch (e) { console.error(e); record_result( - 'error', "Error", + 'error', "Error", true, `Replay failed due to internal error (see console for traceback): ${e}`); } } @@ -2687,7 +2713,13 @@ class PackTestDialog extends DialogOverlay { } } - let final_status = `Finished! Simulated ${util.format_duration(total_tics / TICS_PER_SECOND)} of play time in ${util.format_duration((performance.now() - t0) / 1000)}; ${num_passed}/${num_levels} levels passed`; + let total_game_time = total_tics / TICS_PER_SECOND; + let total_wall_time = (performance.now() - t0) / 1000; + let final_status = `Finished! +Simulated ${util.format_duration(total_game_time)} of play time +in ${util.format_duration(total_wall_time)} +(${(total_game_time / total_wall_time).toFixed(2)}×); +${num_passed}/${num_levels} levels passed`; if (num_passed === num_levels) { final_status += "! Congratulations! 🎆"; } else { diff --git a/style.css b/style.css index eaa98d8..4315fcd 100644 --- a/style.css +++ b/style.css @@ -808,35 +808,66 @@ ol.packtest-summary > li { flex: 1; } .packtest-results { + width: 100%; + table-layout: fixed; + border-collapse: collapse; margin-bottom: 1em; } -.packtest-results > li { - padding: 0.25em; - margin: 0.25em 0; +.packtest-results th { + font-weight: normal; } -.packtest-results > li > canvas { +.packtest-results tbody { + cursor: pointer; + border-top: 1px solid #0004; +} +.packtest-results tbody:hover { + outline: 2px solid #000; +} +.packtest-results td { + padding: 0.25em; +} +.packtest-results .-level { + text-align: left; +} +.packtest-results .-result { + width: 8em; + text-align: center; +} +.packtest-results .-clock { + width: 6em; + text-align: right; +} +.packtest-results .-delta { + width: 8em; + text-align: right; +} +.packtest-results .-speed { + width: 8em; + text-align: right; +} +.packtest-results canvas { display: block; margin: 0.5em auto; } -ol.packtest-colorcoded > li[data-status=no-replay] { +.packtest-colorcoded [data-status=no-replay] { background: hsl(0, 0%, 25%); } -ol.packtest-colorcoded > li[data-status=running] { +.packtest-colorcoded [data-status=running] { background: hsl(30, 100%, 75%); } -ol.packtest-colorcoded > li[data-status=success] { +.packtest-colorcoded [data-status=success] { background: hsl(120, 60%, 75%); } -ol.packtest-colorcoded > li[data-status=early] { +.packtest-colorcoded [data-status=early] { background: hsl(75, 60%, 75%); } -ol.packtest-colorcoded > li[data-status=failure] { +.packtest-colorcoded [data-status=failure] { background: hsl(0, 60%, 60%); } -ol.packtest-colorcoded > li[data-status=short] { +.packtest-colorcoded [data-status=short] { background: hsl(330, 60%, 75%); } -ol.packtest-colorcoded > li[data-status=error] { +.packtest-colorcoded [data-status=error] { background: black; color: white; }