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);
|
||||
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() {
|
||||
for (const sel of turnSelects) {
|
||||
if (!sel.isConnected) {turnSelects.delete(sel); continue;}
|
||||
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 ---
|
||||
|
|
@ -562,7 +624,16 @@
|
|||
placeholder: "Bearhearth, Kingsland"
|
||||
});
|
||||
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
|
||||
// type-specific upgrade. State persists across type changes (hidden boxes
|
||||
|
|
@ -616,8 +687,14 @@
|
|||
if (adj) o.adjacent = adj;
|
||||
const fa = parsePairs(forced.value);
|
||||
if (Object.keys(fa).length) o.forced_action = fa;
|
||||
const at = parseInts(avail.value);
|
||||
if (at) o.available_turns = at;
|
||||
const a = arrival.value, d = departure.value;
|
||||
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;
|
||||
};
|
||||
card.append(
|
||||
|
|
@ -630,7 +707,8 @@
|
|||
field("Upgrades (already installed)", upWrap),
|
||||
field("Adjacent cities (csv of names)", adjacent),
|
||||
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,
|
||||
el("div", {class: "card-actions"}, removeBtn(card)));
|
||||
document.getElementById("cities").append(card);
|
||||
|
|
@ -798,15 +876,21 @@
|
|||
const to = selectEl(RESOURCES, c.to || "capital");
|
||||
const toAmt = num(c.to_amount ?? c.amount ?? 1, {min: 0});
|
||||
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 = () => {
|
||||
const o = {
|
||||
const base = {
|
||||
from: from.value, to: to.value,
|
||||
from_amount: +fromAmt.value, to_amount: +toAmt.value,
|
||||
max_count: +maxCount.value,
|
||||
};
|
||||
if (turn.value !== "") o.turn = +turn.value;
|
||||
return o;
|
||||
const sel = turns._selected();
|
||||
return (sel.length ? sel : [""]).map(tv => {
|
||||
const o = {...base};
|
||||
if (tv !== "") o.turn = +tv;
|
||||
return o;
|
||||
});
|
||||
};
|
||||
card.append(
|
||||
field("From", from),
|
||||
|
|
@ -814,7 +898,7 @@
|
|||
field("To", to),
|
||||
field("To amount", toAmt),
|
||||
field("Max count", maxCount),
|
||||
field("Turn (blank=final)", turn),
|
||||
field("Turns (none=final)", turns),
|
||||
el("div", {class: "card-actions"}, removeBtn(card)));
|
||||
document.getElementById("optional_conversions").append(card);
|
||||
}
|
||||
|
|
@ -858,7 +942,7 @@
|
|||
objective: {terms: collect("terms")},
|
||||
resource_constraints: collect("constraints"),
|
||||
conversions: collect("conversions"),
|
||||
optional_conversions: collect("optional_conversions"),
|
||||
optional_conversions: collect("optional_conversions").flat(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue