fe-map: data and tolerance changes re-score without moving cities; caption for the maximum-only case
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+21
-10
@@ -203,7 +203,6 @@ footer{padding-block:18px 30px;color:var(--faint);font-size:13px}
|
|||||||
</table>
|
</table>
|
||||||
<div class="hist"><span>Map distance ÷ timed length, every flight</span><canvas id="hist" aria-label="Histogram of map distance divided by timed length"></canvas></div>
|
<div class="hist"><span>Map distance ÷ timed length, every flight</span><canvas id="hist" aria-label="Histogram of map distance divided by timed length"></canvas></div>
|
||||||
<div><h2 style="margin-bottom:6px">Farthest out</h2><ul class="worst" id="worst"></ul></div>
|
<div><h2 style="margin-bottom:6px">Farthest out</h2><ul class="worst" id="worst"></ul></div>
|
||||||
<p class="note" id="flnote" hidden>Flight times are off, so every remaining measurement is a maximum distance. Any flat map drawn small enough meets them all, so a score of 0 here says nothing about the shape. The triangles do not depend on the layout.</p>
|
|
||||||
<p class="note" id="globenote">Globe: the same rules applied to great-circle distances on a sphere of radius 6,371 km.</p>
|
<p class="note" id="globenote">Globe: the same rules applied to great-circle distances on a sphere of radius 6,371 km.</p>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
@@ -472,7 +471,16 @@ function restart() {
|
|||||||
$("#run").textContent = "Solve";
|
$("#run").textContent = "Solve";
|
||||||
update(true);
|
update(true);
|
||||||
}
|
}
|
||||||
function continueSolve() { // data or tolerance changed: keep the current layout and solve again
|
function dataChanged() { // data or tolerance changed: re-score the current layout; only Solve moves the cities
|
||||||
|
if (!sv) return restart();
|
||||||
|
buildActive();
|
||||||
|
const asDrawn = sv.total === 0 && !sv.done && !sv.changed, watch = sv.watch, X = Float64Array.from(sv.X);
|
||||||
|
sv = makeSolver(2, X);
|
||||||
|
if (running) sv.watch = watch; // mid-solve: carry on with the new set
|
||||||
|
else { sv.watch = asDrawn; sv.changed = !asDrawn; $("#run").textContent = "Solve"; }
|
||||||
|
update(true);
|
||||||
|
}
|
||||||
|
function continueSolve() { // Solve pressed on a finished layout: solve again from it
|
||||||
if (!sv) return restart();
|
if (!sv) return restart();
|
||||||
buildActive(); const X = Float64Array.from(sv.X); sv = makeSolver(2, X); sv.watch = false; running = true; $("#run").textContent = "Pause"; update(true);
|
buildActive(); const X = Float64Array.from(sv.X); sv = makeSolver(2, X); sv.watch = false; running = true; $("#run").textContent = "Pause"; update(true);
|
||||||
}
|
}
|
||||||
@@ -499,7 +507,6 @@ function globeScore() {
|
|||||||
let SC = null, GS = null;
|
let SC = null, GS = null;
|
||||||
function update(full) {
|
function update(full) {
|
||||||
SC = score(sv.X, 2); if (full) GS = globeScore();
|
SC = score(sv.X, 2); if (full) GS = globeScore();
|
||||||
$("#flnote").hidden = S.fl;
|
|
||||||
const nAct = { rt: act.l.filter(l => l.kind === "rt").length, rr: act.l.filter(l => l.kind === "rail" || l.kind === "road").length, sat: act.l.filter(l => l.kind === "sat").length };
|
const nAct = { rt: act.l.filter(l => l.kind === "rt").length, rr: act.l.filter(l => l.kind === "rail" || l.kind === "road").length, sat: act.l.filter(l => l.kind === "sat").length };
|
||||||
const row = (label, a, b, n, cls) => `<tr><th scope="row">${label}</th><td class="${a ? cls : "ok"}">${fmt(a)}<span class="of"> / ${fmt(n)}</span></td><td class="${b ? cls : "ok"}">${fmt(b)}</td></tr>`;
|
const row = (label, a, b, n, cls) => `<tr><th scope="row">${label}</th><td class="${a ? cls : "ok"}">${fmt(a)}<span class="of"> / ${fmt(n)}</span></td><td class="${b ? cls : "ok"}">${fmt(b)}</td></tr>`;
|
||||||
const nf = S.fl ? FL.length : 0;
|
const nf = S.fl ? FL.length : 0;
|
||||||
@@ -699,13 +706,13 @@ $("#run").addEventListener("click", () => {
|
|||||||
running = !running; $("#run").textContent = running ? "Pause" : "Solve";
|
running = !running; $("#run").textContent = running ? "Pause" : "Solve";
|
||||||
});
|
});
|
||||||
$("#reset").addEventListener("click", () => restart());
|
$("#reset").addEventListener("click", () => restart());
|
||||||
for (const [id, key] of [["#d-fl", "fl"], ["#d-rt", "rt"], ["#d-rr", "rr"], ["#d-sat", "sat"]]) $(id).addEventListener("change", e => { S[key] = e.target.checked; continueSolve(); });
|
for (const [id, key] of [["#d-fl", "fl"], ["#d-rt", "rt"], ["#d-rr", "rr"], ["#d-sat", "sat"]]) $(id).addEventListener("change", e => { S[key] = e.target.checked; dataChanged(); });
|
||||||
$("#lo").addEventListener("input", e => { $("#lov").textContent = e.target.value + "%"; });
|
$("#lo").addEventListener("input", e => { $("#lov").textContent = e.target.value + "%"; });
|
||||||
$("#hi").addEventListener("input", e => { $("#hiv").textContent = e.target.value + "%"; });
|
$("#hi").addEventListener("input", e => { $("#hiv").textContent = e.target.value + "%"; });
|
||||||
$("#lo").addEventListener("change", e => { S.lo = 1 - e.target.value / 100; continueSolve(); });
|
$("#lo").addEventListener("change", e => { S.lo = 1 - e.target.value / 100; dataChanged(); });
|
||||||
$("#hi").addEventListener("change", e => { S.hi = e.target.value / 100 - 1; continueSolve(); });
|
$("#hi").addEventListener("change", e => { S.hi = e.target.value / 100 - 1; dataChanged(); });
|
||||||
for (const b of document.querySelectorAll("#light button")) b.addEventListener("click", () => {
|
for (const b of document.querySelectorAll("#light button")) b.addEventListener("click", () => {
|
||||||
S.vac = b.dataset.v === "vacuum"; for (const x of document.querySelectorAll("#light button")) x.setAttribute("aria-pressed", String(x === b)); continueSolve();
|
S.vac = b.dataset.v === "vacuum"; for (const x of document.querySelectorAll("#light button")) x.setAttribute("aria-pressed", String(x === b)); dataChanged();
|
||||||
});
|
});
|
||||||
$("#dl").addEventListener("click", () => {
|
$("#dl").addEventListener("click", () => {
|
||||||
const blob = new Blob([JSON.stringify(D)], { type: "application/json" }); const a = document.createElement("a");
|
const blob = new Blob([JSON.stringify(D)], { type: "application/json" }); const a = document.createElement("a");
|
||||||
@@ -790,10 +797,14 @@ const pace = sv => sv.total < 100 ? 0.35 : sv.stage === 0 ? (sv.total < 200 ? 1
|
|||||||
let frame = 0, capText = "";
|
let frame = 0, capText = "";
|
||||||
function caption(k) {
|
function caption(k) {
|
||||||
let t;
|
let t;
|
||||||
if (!sv.done && sv.total === 0 && !running)
|
if (sv.changed && sv.total === 0 && !running)
|
||||||
|
t = `<b>Measurements changed: ${fmt(k)} broken on this layout.</b> It was solved for the previous set. Press <b>Solve</b> to fit it to these.`;
|
||||||
|
else if (!sv.done && sv.total === 0 && !running)
|
||||||
t = `<b>${START_LABEL[S.start]}, as drawn: ${fmt(k)} measurements broken.</b> Red: a flight longer than its aircraft can fly, or (dashed) a ping faster than light. Amber: a flight far shorter than it takes to fly. Press <b>Solve</b> to let the cities move.`;
|
t = `<b>${START_LABEL[S.start]}, as drawn: ${fmt(k)} measurements broken.</b> Red: a flight longer than its aircraft can fly, or (dashed) a ping faster than light. Amber: a flight far shorter than it takes to fly. Press <b>Solve</b> to let the cities move.`;
|
||||||
else if (!sv.done)
|
else if (!sv.done)
|
||||||
t = `${running ? "Moving" : "Paused moving"} the cities to fit the measurements. Grey lines: each city's move from the starting map.`;
|
t = `${running ? "Moving" : "Paused moving"} the cities to fit the measurements. Grey lines: each city's move from the starting map.`;
|
||||||
|
else if (!S.fl)
|
||||||
|
t = `<b>Done: ${fmt(k)} measurements broken.</b> Without flight times, every measurement left is a maximum distance (no farther apart than this), and pulling cities closer always meets a maximum. These measurements can show that a map as drawn is wrong, but no flat map can fail them.`;
|
||||||
else if (sel >= 0) {
|
else if (sel >= 0) {
|
||||||
let n = 0, kept = 0, long = 0, short = 0;
|
let n = 0, kept = 0, long = 0, short = 0;
|
||||||
for (const f of act.f) if (f.a === sel || f.b === sel) { n++; const r = SC.ratio[f.i]; if (r > 1 + S.hi) long++; else if (r < 1 - S.lo) short++; else kept++; }
|
for (const f of act.f) if (f.a === sel || f.b === sel) { n++; const r = SC.ratio[f.i]; if (r > 1 + S.hi) long++; else if (r < 1 - S.lo) short++; else kept++; }
|
||||||
@@ -805,12 +816,12 @@ function loop() {
|
|||||||
frame++;
|
frame++;
|
||||||
if (sv && running && !sv.done) {
|
if (sv && running && !sv.done) {
|
||||||
if (sv.watch) { sv.acc += pace(sv); const n = Math.floor(sv.acc); sv.acc -= n; if (n) work(sv, 9, n); } else work(sv, 9, 400);
|
if (sv.watch) { sv.acc += pace(sv); const n = Math.floor(sv.acc); sv.acc -= n; if (n) work(sv, 9, n); } else work(sv, 9, 400);
|
||||||
if (sv.done) { running = false; $("#run").textContent = "Solve"; if (sv.watch && sel < 0) sel = SANTIAGO; }
|
if (sv.done) { running = false; $("#run").textContent = "Solve"; if (sv.watch && sel < 0 && S.fl) sel = SANTIAGO; }
|
||||||
}
|
}
|
||||||
if (sv && (running || frame % 6 === 0)) update(frame % 30 === 0);
|
if (sv && (running || frame % 6 === 0)) update(frame % 30 === 0);
|
||||||
if (sv) {
|
if (sv) {
|
||||||
const k = SC.long.length + SC.short.length + SC.rt.length + SC.rr.length + SC.sat.length;
|
const k = SC.long.length + SC.short.length + SC.rt.length + SC.rr.length + SC.sat.length;
|
||||||
const head = sv.done ? (sv.isBest ? `Best of ${D.stats.n_starts}` : "Done") : sv.total === 0 && !running ? `${START_LABEL[S.start]}, as drawn` : (running ? "Solving, " : "Paused, ") + STAGE(sv.stage);
|
const head = sv.done ? (sv.isBest ? `Best of ${D.stats.n_starts}` : "Done") : sv.total === 0 && !running ? (sv.changed ? "Measurements changed" : `${START_LABEL[S.start]}, as drawn`) : (running ? "Solving, " : "Paused, ") + STAGE(sv.stage);
|
||||||
$("#status").innerHTML = `${head} · broken <b>${fmt(k)}</b>`;
|
$("#status").innerHTML = `${head} · broken <b>${fmt(k)}</b>`;
|
||||||
caption(k);
|
caption(k);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-10
@@ -203,7 +203,6 @@ footer{padding-block:18px 30px;color:var(--faint);font-size:13px}
|
|||||||
</table>
|
</table>
|
||||||
<div class="hist"><span>Map distance ÷ timed length, every flight</span><canvas id="hist" aria-label="Histogram of map distance divided by timed length"></canvas></div>
|
<div class="hist"><span>Map distance ÷ timed length, every flight</span><canvas id="hist" aria-label="Histogram of map distance divided by timed length"></canvas></div>
|
||||||
<div><h2 style="margin-bottom:6px">Farthest out</h2><ul class="worst" id="worst"></ul></div>
|
<div><h2 style="margin-bottom:6px">Farthest out</h2><ul class="worst" id="worst"></ul></div>
|
||||||
<p class="note" id="flnote" hidden>Flight times are off, so every remaining measurement is a maximum distance. Any flat map drawn small enough meets them all, so a score of 0 here says nothing about the shape. The triangles do not depend on the layout.</p>
|
|
||||||
<p class="note" id="globenote">Globe: the same rules applied to great-circle distances on a sphere of radius 6,371 km.</p>
|
<p class="note" id="globenote">Globe: the same rules applied to great-circle distances on a sphere of radius 6,371 km.</p>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
@@ -472,7 +471,16 @@ function restart() {
|
|||||||
$("#run").textContent = "Solve";
|
$("#run").textContent = "Solve";
|
||||||
update(true);
|
update(true);
|
||||||
}
|
}
|
||||||
function continueSolve() { // data or tolerance changed: keep the current layout and solve again
|
function dataChanged() { // data or tolerance changed: re-score the current layout; only Solve moves the cities
|
||||||
|
if (!sv) return restart();
|
||||||
|
buildActive();
|
||||||
|
const asDrawn = sv.total === 0 && !sv.done && !sv.changed, watch = sv.watch, X = Float64Array.from(sv.X);
|
||||||
|
sv = makeSolver(2, X);
|
||||||
|
if (running) sv.watch = watch; // mid-solve: carry on with the new set
|
||||||
|
else { sv.watch = asDrawn; sv.changed = !asDrawn; $("#run").textContent = "Solve"; }
|
||||||
|
update(true);
|
||||||
|
}
|
||||||
|
function continueSolve() { // Solve pressed on a finished layout: solve again from it
|
||||||
if (!sv) return restart();
|
if (!sv) return restart();
|
||||||
buildActive(); const X = Float64Array.from(sv.X); sv = makeSolver(2, X); sv.watch = false; running = true; $("#run").textContent = "Pause"; update(true);
|
buildActive(); const X = Float64Array.from(sv.X); sv = makeSolver(2, X); sv.watch = false; running = true; $("#run").textContent = "Pause"; update(true);
|
||||||
}
|
}
|
||||||
@@ -499,7 +507,6 @@ function globeScore() {
|
|||||||
let SC = null, GS = null;
|
let SC = null, GS = null;
|
||||||
function update(full) {
|
function update(full) {
|
||||||
SC = score(sv.X, 2); if (full) GS = globeScore();
|
SC = score(sv.X, 2); if (full) GS = globeScore();
|
||||||
$("#flnote").hidden = S.fl;
|
|
||||||
const nAct = { rt: act.l.filter(l => l.kind === "rt").length, rr: act.l.filter(l => l.kind === "rail" || l.kind === "road").length, sat: act.l.filter(l => l.kind === "sat").length };
|
const nAct = { rt: act.l.filter(l => l.kind === "rt").length, rr: act.l.filter(l => l.kind === "rail" || l.kind === "road").length, sat: act.l.filter(l => l.kind === "sat").length };
|
||||||
const row = (label, a, b, n, cls) => `<tr><th scope="row">${label}</th><td class="${a ? cls : "ok"}">${fmt(a)}<span class="of"> / ${fmt(n)}</span></td><td class="${b ? cls : "ok"}">${fmt(b)}</td></tr>`;
|
const row = (label, a, b, n, cls) => `<tr><th scope="row">${label}</th><td class="${a ? cls : "ok"}">${fmt(a)}<span class="of"> / ${fmt(n)}</span></td><td class="${b ? cls : "ok"}">${fmt(b)}</td></tr>`;
|
||||||
const nf = S.fl ? FL.length : 0;
|
const nf = S.fl ? FL.length : 0;
|
||||||
@@ -699,13 +706,13 @@ $("#run").addEventListener("click", () => {
|
|||||||
running = !running; $("#run").textContent = running ? "Pause" : "Solve";
|
running = !running; $("#run").textContent = running ? "Pause" : "Solve";
|
||||||
});
|
});
|
||||||
$("#reset").addEventListener("click", () => restart());
|
$("#reset").addEventListener("click", () => restart());
|
||||||
for (const [id, key] of [["#d-fl", "fl"], ["#d-rt", "rt"], ["#d-rr", "rr"], ["#d-sat", "sat"]]) $(id).addEventListener("change", e => { S[key] = e.target.checked; continueSolve(); });
|
for (const [id, key] of [["#d-fl", "fl"], ["#d-rt", "rt"], ["#d-rr", "rr"], ["#d-sat", "sat"]]) $(id).addEventListener("change", e => { S[key] = e.target.checked; dataChanged(); });
|
||||||
$("#lo").addEventListener("input", e => { $("#lov").textContent = e.target.value + "%"; });
|
$("#lo").addEventListener("input", e => { $("#lov").textContent = e.target.value + "%"; });
|
||||||
$("#hi").addEventListener("input", e => { $("#hiv").textContent = e.target.value + "%"; });
|
$("#hi").addEventListener("input", e => { $("#hiv").textContent = e.target.value + "%"; });
|
||||||
$("#lo").addEventListener("change", e => { S.lo = 1 - e.target.value / 100; continueSolve(); });
|
$("#lo").addEventListener("change", e => { S.lo = 1 - e.target.value / 100; dataChanged(); });
|
||||||
$("#hi").addEventListener("change", e => { S.hi = e.target.value / 100 - 1; continueSolve(); });
|
$("#hi").addEventListener("change", e => { S.hi = e.target.value / 100 - 1; dataChanged(); });
|
||||||
for (const b of document.querySelectorAll("#light button")) b.addEventListener("click", () => {
|
for (const b of document.querySelectorAll("#light button")) b.addEventListener("click", () => {
|
||||||
S.vac = b.dataset.v === "vacuum"; for (const x of document.querySelectorAll("#light button")) x.setAttribute("aria-pressed", String(x === b)); continueSolve();
|
S.vac = b.dataset.v === "vacuum"; for (const x of document.querySelectorAll("#light button")) x.setAttribute("aria-pressed", String(x === b)); dataChanged();
|
||||||
});
|
});
|
||||||
$("#dl").addEventListener("click", () => {
|
$("#dl").addEventListener("click", () => {
|
||||||
const blob = new Blob([JSON.stringify(D)], { type: "application/json" }); const a = document.createElement("a");
|
const blob = new Blob([JSON.stringify(D)], { type: "application/json" }); const a = document.createElement("a");
|
||||||
@@ -790,10 +797,14 @@ const pace = sv => sv.total < 100 ? 0.35 : sv.stage === 0 ? (sv.total < 200 ? 1
|
|||||||
let frame = 0, capText = "";
|
let frame = 0, capText = "";
|
||||||
function caption(k) {
|
function caption(k) {
|
||||||
let t;
|
let t;
|
||||||
if (!sv.done && sv.total === 0 && !running)
|
if (sv.changed && sv.total === 0 && !running)
|
||||||
|
t = `<b>Measurements changed: ${fmt(k)} broken on this layout.</b> It was solved for the previous set. Press <b>Solve</b> to fit it to these.`;
|
||||||
|
else if (!sv.done && sv.total === 0 && !running)
|
||||||
t = `<b>${START_LABEL[S.start]}, as drawn: ${fmt(k)} measurements broken.</b> Red: a flight longer than its aircraft can fly, or (dashed) a ping faster than light. Amber: a flight far shorter than it takes to fly. Press <b>Solve</b> to let the cities move.`;
|
t = `<b>${START_LABEL[S.start]}, as drawn: ${fmt(k)} measurements broken.</b> Red: a flight longer than its aircraft can fly, or (dashed) a ping faster than light. Amber: a flight far shorter than it takes to fly. Press <b>Solve</b> to let the cities move.`;
|
||||||
else if (!sv.done)
|
else if (!sv.done)
|
||||||
t = `${running ? "Moving" : "Paused moving"} the cities to fit the measurements. Grey lines: each city's move from the starting map.`;
|
t = `${running ? "Moving" : "Paused moving"} the cities to fit the measurements. Grey lines: each city's move from the starting map.`;
|
||||||
|
else if (!S.fl)
|
||||||
|
t = `<b>Done: ${fmt(k)} measurements broken.</b> Without flight times, every measurement left is a maximum distance (no farther apart than this), and pulling cities closer always meets a maximum. These measurements can show that a map as drawn is wrong, but no flat map can fail them.`;
|
||||||
else if (sel >= 0) {
|
else if (sel >= 0) {
|
||||||
let n = 0, kept = 0, long = 0, short = 0;
|
let n = 0, kept = 0, long = 0, short = 0;
|
||||||
for (const f of act.f) if (f.a === sel || f.b === sel) { n++; const r = SC.ratio[f.i]; if (r > 1 + S.hi) long++; else if (r < 1 - S.lo) short++; else kept++; }
|
for (const f of act.f) if (f.a === sel || f.b === sel) { n++; const r = SC.ratio[f.i]; if (r > 1 + S.hi) long++; else if (r < 1 - S.lo) short++; else kept++; }
|
||||||
@@ -805,12 +816,12 @@ function loop() {
|
|||||||
frame++;
|
frame++;
|
||||||
if (sv && running && !sv.done) {
|
if (sv && running && !sv.done) {
|
||||||
if (sv.watch) { sv.acc += pace(sv); const n = Math.floor(sv.acc); sv.acc -= n; if (n) work(sv, 9, n); } else work(sv, 9, 400);
|
if (sv.watch) { sv.acc += pace(sv); const n = Math.floor(sv.acc); sv.acc -= n; if (n) work(sv, 9, n); } else work(sv, 9, 400);
|
||||||
if (sv.done) { running = false; $("#run").textContent = "Solve"; if (sv.watch && sel < 0) sel = SANTIAGO; }
|
if (sv.done) { running = false; $("#run").textContent = "Solve"; if (sv.watch && sel < 0 && S.fl) sel = SANTIAGO; }
|
||||||
}
|
}
|
||||||
if (sv && (running || frame % 6 === 0)) update(frame % 30 === 0);
|
if (sv && (running || frame % 6 === 0)) update(frame % 30 === 0);
|
||||||
if (sv) {
|
if (sv) {
|
||||||
const k = SC.long.length + SC.short.length + SC.rt.length + SC.rr.length + SC.sat.length;
|
const k = SC.long.length + SC.short.length + SC.rt.length + SC.rr.length + SC.sat.length;
|
||||||
const head = sv.done ? (sv.isBest ? `Best of ${D.stats.n_starts}` : "Done") : sv.total === 0 && !running ? `${START_LABEL[S.start]}, as drawn` : (running ? "Solving, " : "Paused, ") + STAGE(sv.stage);
|
const head = sv.done ? (sv.isBest ? `Best of ${D.stats.n_starts}` : "Done") : sv.total === 0 && !running ? (sv.changed ? "Measurements changed" : `${START_LABEL[S.start]}, as drawn`) : (running ? "Solving, " : "Paused, ") + STAGE(sv.stage);
|
||||||
$("#status").innerHTML = `${head} · broken <b>${fmt(k)}</b>`;
|
$("#status").innerHTML = `${head} · broken <b>${fmt(k)}</b>`;
|
||||||
caption(k);
|
caption(k);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -76,13 +76,17 @@ console.log('Best start: broken', kb, 'build says', want);
|
|||||||
if (kb !== want) fail('best layout count differs from the build', kb, want);
|
if (kb !== want) fail('best layout count differs from the build', kb, want);
|
||||||
// flights off: the plane can satisfy limits alone by shrinking
|
// flights off: the plane can satisfy limits alone by shrinking
|
||||||
await p.click('#d-fl');
|
await p.click('#d-fl');
|
||||||
|
await p.waitForTimeout(500);
|
||||||
|
const ch = await p.evaluate(() => [document.getElementById('status').textContent, sv.total, running]);
|
||||||
|
if (!/^Measurements changed/.test(ch[0]) || ch[1] !== 0 || ch[2]) fail('changing the data should re-score without moving the cities', ch);
|
||||||
|
await p.click('#run');
|
||||||
await p.waitForFunction(() => /^Done/.test(document.getElementById('status').textContent), null, { timeout: 120000 });
|
await p.waitForFunction(() => /^Done/.test(document.getElementById('status').textContent), null, { timeout: 120000 });
|
||||||
const kf = await broken();
|
const kf = await broken();
|
||||||
|
const capf = await p.evaluate(() => document.getElementById('mapcap').textContent);
|
||||||
|
if (!/no flat map can fail them/.test(capf)) fail('flight-times-off caption missing', capf);
|
||||||
console.log('Limits only: broken', kf);
|
console.log('Limits only: broken', kf);
|
||||||
if (kf > 5) fail('limits alone should be (nearly) satisfiable on a plane', kf);
|
if (kf > 5) fail('limits alone should be (nearly) satisfiable on a plane', kf);
|
||||||
if (await p.evaluate(() => document.getElementById('flnote').hidden)) fail('flight-times-off note not shown');
|
|
||||||
await p.click('#d-fl');
|
await p.click('#d-fl');
|
||||||
if (!(await p.evaluate(() => document.getElementById('flnote').hidden))) fail('flight-times-off note still shown');
|
|
||||||
await p.click('#starts button[data-start="best"]');
|
await p.click('#starts button[data-start="best"]');
|
||||||
await p.waitForTimeout(300);
|
await p.waitForTimeout(300);
|
||||||
// 3D release
|
// 3D release
|
||||||
|
|||||||
Reference in New Issue
Block a user