mildly better turn input
This commit is contained in:
parent
f52871318e
commit
2207cf2fbc
1 changed files with 94 additions and 10 deletions
104
index.html
104
index.html
|
|
@ -518,11 +518,73 @@
|
||||||
if (value !== undefined && value !== null && value !== "") sel.value = String(value);
|
if (value !== undefined && value !== null && value !== "") sel.value = String(value);
|
||||||
return sel;
|
return sel;
|
||||||
}
|
}
|
||||||
|
// Plain turn-number selects (no "final" entry): a blank option (value "")
|
||||||
|
// labeled however the caller likes, then turn 0..count-1. Used for the
|
||||||
|
// cities' Arrival/Departure turns where blank means "unbounded" rather
|
||||||
|
// than "final".
|
||||||
|
const plainTurnSelects = new Set();
|
||||||
|
function fillPlainTurnOptions(sel) {
|
||||||
|
const prev = sel.value;
|
||||||
|
sel.innerHTML = "";
|
||||||
|
sel.append(el("option", {value: ""}, sel._blankLabel || ""));
|
||||||
|
for (let t = 0; t < turnsCount(); t++)
|
||||||
|
sel.append(el("option", {value: String(t)}, "turn " + t));
|
||||||
|
sel.value = [...sel.options].some(o => o.value === prev) ? prev : "";
|
||||||
|
}
|
||||||
|
function plainTurnSelect(value, blankLabel) {
|
||||||
|
const sel = el("select");
|
||||||
|
sel._blankLabel = blankLabel;
|
||||||
|
plainTurnSelects.add(sel);
|
||||||
|
fillPlainTurnOptions(sel);
|
||||||
|
if (value !== undefined && value !== null && value !== "") sel.value = String(value);
|
||||||
|
return sel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multi-turn picker: a row of checkboxes ("final" + turn 0..count-1).
|
||||||
|
// _selected() returns the checked values as strings ("" = final). Registers
|
||||||
|
// for refresh so its options track the Turns count, preserving selections.
|
||||||
|
const multiTurnGroups = new Set();
|
||||||
|
function fillMultiTurnOptions(group) {
|
||||||
|
const prev = new Set((group._boxes || []).filter(b => b.checked).map(b => b.value));
|
||||||
|
group._boxes = [];
|
||||||
|
group.innerHTML = "";
|
||||||
|
const mk = (val, label) => {
|
||||||
|
const cb = el("input", {type: "checkbox"});
|
||||||
|
cb.value = val;
|
||||||
|
if (prev.has(val)) cb.checked = true;
|
||||||
|
group._boxes.push(cb);
|
||||||
|
return el("label", {style: "display:inline-block;margin-right:.6rem;font-size:.85rem"},
|
||||||
|
[cb, " " + label]);
|
||||||
|
};
|
||||||
|
group.append(mk("", "final"));
|
||||||
|
for (let t = 0; t < turnsCount(); t++) group.append(mk(String(t), "turn " + t));
|
||||||
|
}
|
||||||
|
function multiTurnSelect(values) {
|
||||||
|
const group = el("div", {class: "multi-turn"});
|
||||||
|
group._boxes = [];
|
||||||
|
group._selected = () => group._boxes.filter(b => b.checked).map(b => b.value);
|
||||||
|
multiTurnGroups.add(group);
|
||||||
|
fillMultiTurnOptions(group);
|
||||||
|
if (values && values.length) {
|
||||||
|
const want = new Set(values.map(v => (v === "" || v == null) ? "" : String(v)));
|
||||||
|
for (const b of group._boxes) if (want.has(b.value)) b.checked = true;
|
||||||
|
}
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
function refreshTurnSelects() {
|
function refreshTurnSelects() {
|
||||||
for (const sel of turnSelects) {
|
for (const sel of turnSelects) {
|
||||||
if (!sel.isConnected) {turnSelects.delete(sel); continue;}
|
if (!sel.isConnected) {turnSelects.delete(sel); continue;}
|
||||||
fillTurnOptions(sel);
|
fillTurnOptions(sel);
|
||||||
}
|
}
|
||||||
|
for (const sel of plainTurnSelects) {
|
||||||
|
if (!sel.isConnected) {plainTurnSelects.delete(sel); continue;}
|
||||||
|
fillPlainTurnOptions(sel);
|
||||||
|
}
|
||||||
|
for (const group of multiTurnGroups) {
|
||||||
|
if (!group.isConnected) {multiTurnGroups.delete(group); continue;}
|
||||||
|
fillMultiTurnOptions(group);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- starting resources & tradeable ---
|
// --- starting resources & tradeable ---
|
||||||
|
|
@ -562,7 +624,16 @@
|
||||||
placeholder: "Bearhearth, Kingsland"
|
placeholder: "Bearhearth, Kingsland"
|
||||||
});
|
});
|
||||||
const forced = el("input", {value: "", placeholder: "0:upgrade"});
|
const forced = el("input", {value: "", placeholder: "0:upgrade"});
|
||||||
const avail = el("input", {value: "", placeholder: ""});
|
// Available turns are given as an inclusive arrival..departure range and
|
||||||
|
// expanded into the explicit list the solver expects. Blank arrival means
|
||||||
|
// "from turn 0", blank departure means "through the last turn"; both blank
|
||||||
|
// means available on every turn.
|
||||||
|
const arrival = plainTurnSelect(undefined, "start");
|
||||||
|
const departure = plainTurnSelect(undefined, "end");
|
||||||
|
if (c.available_turns && c.available_turns.length) {
|
||||||
|
arrival.value = String(Math.min(...c.available_turns));
|
||||||
|
departure.value = String(Math.max(...c.available_turns));
|
||||||
|
}
|
||||||
|
|
||||||
// Upgrade checkboxes: the 3 universal upgrades plus the current type's
|
// Upgrade checkboxes: the 3 universal upgrades plus the current type's
|
||||||
// type-specific upgrade. State persists across type changes (hidden boxes
|
// type-specific upgrade. State persists across type changes (hidden boxes
|
||||||
|
|
@ -616,8 +687,14 @@
|
||||||
if (adj) o.adjacent = adj;
|
if (adj) o.adjacent = adj;
|
||||||
const fa = parsePairs(forced.value);
|
const fa = parsePairs(forced.value);
|
||||||
if (Object.keys(fa).length) o.forced_action = fa;
|
if (Object.keys(fa).length) o.forced_action = fa;
|
||||||
const at = parseInts(avail.value);
|
const a = arrival.value, d = departure.value;
|
||||||
if (at) o.available_turns = at;
|
if (a !== "" || d !== "") {
|
||||||
|
const lo = a !== "" ? +a : 0;
|
||||||
|
const hi = d !== "" ? +d : Math.max(0, turnsCount() - 1);
|
||||||
|
const at = [];
|
||||||
|
for (let t = lo; t <= hi; t++) at.push(t);
|
||||||
|
if (at.length) o.available_turns = at;
|
||||||
|
}
|
||||||
return o;
|
return o;
|
||||||
};
|
};
|
||||||
card.append(
|
card.append(
|
||||||
|
|
@ -630,7 +707,8 @@
|
||||||
field("Upgrades (already installed)", upWrap),
|
field("Upgrades (already installed)", upWrap),
|
||||||
field("Adjacent cities (csv of names)", adjacent),
|
field("Adjacent cities (csv of names)", adjacent),
|
||||||
field("Forced actions (turn:action, csv)", forced),
|
field("Forced actions (turn:action, csv)", forced),
|
||||||
field("Avail turns (csv, blank=all)", avail),
|
field("Arrival turn (blank=start)", arrival),
|
||||||
|
field("Departure turn (blank=end)", departure),
|
||||||
vatGroup,
|
vatGroup,
|
||||||
el("div", {class: "card-actions"}, removeBtn(card)));
|
el("div", {class: "card-actions"}, removeBtn(card)));
|
||||||
document.getElementById("cities").append(card);
|
document.getElementById("cities").append(card);
|
||||||
|
|
@ -798,15 +876,21 @@
|
||||||
const to = selectEl(RESOURCES, c.to || "capital");
|
const to = selectEl(RESOURCES, c.to || "capital");
|
||||||
const toAmt = num(c.to_amount ?? c.amount ?? 1, {min: 0});
|
const toAmt = num(c.to_amount ?? c.amount ?? 1, {min: 0});
|
||||||
const maxCount = num(c.max_count ?? 1, {min: 1});
|
const maxCount = num(c.max_count ?? 1, {min: 1});
|
||||||
const turn = turnSelect(c.turn);
|
// Multiple turns may be selected; each chosen turn yields its own copy of
|
||||||
|
// this conversion in the problem JSON (one entry per turn).
|
||||||
|
const turns = multiTurnSelect(c.turn != null ? [c.turn] : []);
|
||||||
card._get = () => {
|
card._get = () => {
|
||||||
const o = {
|
const base = {
|
||||||
from: from.value, to: to.value,
|
from: from.value, to: to.value,
|
||||||
from_amount: +fromAmt.value, to_amount: +toAmt.value,
|
from_amount: +fromAmt.value, to_amount: +toAmt.value,
|
||||||
max_count: +maxCount.value,
|
max_count: +maxCount.value,
|
||||||
};
|
};
|
||||||
if (turn.value !== "") o.turn = +turn.value;
|
const sel = turns._selected();
|
||||||
return o;
|
return (sel.length ? sel : [""]).map(tv => {
|
||||||
|
const o = {...base};
|
||||||
|
if (tv !== "") o.turn = +tv;
|
||||||
|
return o;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
card.append(
|
card.append(
|
||||||
field("From", from),
|
field("From", from),
|
||||||
|
|
@ -814,7 +898,7 @@
|
||||||
field("To", to),
|
field("To", to),
|
||||||
field("To amount", toAmt),
|
field("To amount", toAmt),
|
||||||
field("Max count", maxCount),
|
field("Max count", maxCount),
|
||||||
field("Turn (blank=final)", turn),
|
field("Turns (none=final)", turns),
|
||||||
el("div", {class: "card-actions"}, removeBtn(card)));
|
el("div", {class: "card-actions"}, removeBtn(card)));
|
||||||
document.getElementById("optional_conversions").append(card);
|
document.getElementById("optional_conversions").append(card);
|
||||||
}
|
}
|
||||||
|
|
@ -858,7 +942,7 @@
|
||||||
objective: {terms: collect("terms")},
|
objective: {terms: collect("terms")},
|
||||||
resource_constraints: collect("constraints"),
|
resource_constraints: collect("constraints"),
|
||||||
conversions: collect("conversions"),
|
conversions: collect("conversions"),
|
||||||
optional_conversions: collect("optional_conversions"),
|
optional_conversions: collect("optional_conversions").flat(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue