more frontend refactoring
This commit is contained in:
parent
dd99ed8a23
commit
2e50f61e57
3 changed files with 26 additions and 18 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
__pycache__
|
__pycache__
|
||||||
.venv
|
.venv
|
||||||
|
*.pdf
|
||||||
|
|
|
||||||
39
index.html
39
index.html
|
|
@ -162,10 +162,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card.log-on .linear-only {
|
|
||||||
opacity: .4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Output tables: a CSS grid kept inside a scroll container so it never
|
/* Output tables: a CSS grid kept inside a scroll container so it never
|
||||||
stretches the page wider than it should. */
|
stretches the page wider than it should. */
|
||||||
.gtable-wrap {
|
.gtable-wrap {
|
||||||
|
|
@ -406,13 +402,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cities ---
|
// --- cities ---
|
||||||
|
// Pick a name not already used by an existing city: the lowest non-negative
|
||||||
|
// integer (as a string) that's free, matching the seeded "0", "1", … names.
|
||||||
|
function uniqueCityName() {
|
||||||
|
const taken = new Set(
|
||||||
|
[...document.getElementById("cities").children].map(r => r._get().name));
|
||||||
|
for (let n = 0; ; n++) if (!taken.has(String(n))) return String(n);
|
||||||
|
}
|
||||||
function addCity(c = {}) {
|
function addCity(c = {}) {
|
||||||
const card = el("div", {class: "card"});
|
const card = el("div", {class: "card"});
|
||||||
const name = el("input", {value: c.name || ""});
|
const name = el("input", {value: c.name || uniqueCityName()});
|
||||||
const type = selectEl(CITY_TYPES, c.type || "hub");
|
const type = selectEl(CITY_TYPES, c.type || "hub");
|
||||||
const renown = num(c.renown ?? "", {min: 1, placeholder: "auto"});
|
const renown = num(c.renown ?? "", {min: 1, placeholder: "auto"});
|
||||||
const vs = num(c.vat_steel || 0, {min: 0}), vb = num(c.vat_brass || 0, {min: 0}),
|
const vs = num(c.vat_steel || 1, {min: 0}), vb = num(c.vat_brass || 1, {min: 0}),
|
||||||
ve = num(c.vat_electrum || 0, {min: 0});
|
ve = num(c.vat_electrum || 1, {min: 0});
|
||||||
const reno = el("input", {type: "checkbox"}); reno.checked = c.can_renovate !== false;
|
const reno = el("input", {type: "checkbox"}); reno.checked = c.can_renovate !== false;
|
||||||
const adjacent = el("input", {
|
const adjacent = el("input", {
|
||||||
value: (c.adjacent || []).join(", "),
|
value: (c.adjacent || []).join(", "),
|
||||||
|
|
@ -515,7 +518,6 @@
|
||||||
};
|
};
|
||||||
card.append(
|
card.append(
|
||||||
field("Agent", type),
|
field("Agent", type),
|
||||||
field("Name", name),
|
|
||||||
field("Effect", desc),
|
field("Effect", desc),
|
||||||
field("Bastions (Baron only)", bastions),
|
field("Bastions (Baron only)", bastions),
|
||||||
field("Forced city (turn:city, csv)", forced),
|
field("Forced city (turn:city, csv)", forced),
|
||||||
|
|
@ -535,6 +537,7 @@
|
||||||
const toggle = () => card.classList.toggle("log-on", isLog.checked);
|
const toggle = () => card.classList.toggle("log-on", isLog.checked);
|
||||||
isLog.onchange = toggle;
|
isLog.onchange = toggle;
|
||||||
const expr = el("textarea", {rows: 1, placeholder: "(x) => Math.log2(x + 1)"});
|
const expr = el("textarea", {rows: 1, placeholder: "(x) => Math.log2(x + 1)"});
|
||||||
|
expr.innerText = "(x) => Math.log2(x + 1)";
|
||||||
if (t._expr) expr.value = t._expr;
|
if (t._expr) expr.value = t._expr;
|
||||||
card._get = () => {
|
card._get = () => {
|
||||||
const o = {resource: res.value, scalar: +scalar.value};
|
const o = {resource: res.value, scalar: +scalar.value};
|
||||||
|
|
@ -555,6 +558,10 @@
|
||||||
document.getElementById("terms").append(card);
|
document.getElementById("terms").append(card);
|
||||||
toggle();
|
toggle();
|
||||||
}
|
}
|
||||||
|
function addTerms(terms = {}) {
|
||||||
|
Object.entries(terms).forEach(([resource, scalar]) =>
|
||||||
|
addTerm({resource, scalar}));
|
||||||
|
}
|
||||||
|
|
||||||
// Eval the expression ONCE into a function, then call it over the amounts
|
// Eval the expression ONCE into a function, then call it over the amounts
|
||||||
// the lookup table needs (0..max_resource).
|
// the lookup table needs (0..max_resource).
|
||||||
|
|
@ -666,7 +673,7 @@
|
||||||
out.append(el("h2", {}, "Solution"));
|
out.append(el("h2", {}, "Solution"));
|
||||||
out.append(el("p", {
|
out.append(el("p", {
|
||||||
html:
|
html:
|
||||||
`<b>Status:</b> ${s.status} <b>Objective:</b> ${s.objective_value} ` +
|
`<b>Status:</b> ${s.status} <b>Objective:</b> ${s.objective_value ?? "—"} ` +
|
||||||
`<b>Final renown total:</b> ${s.final_renown_total}`
|
`<b>Final renown total:</b> ${s.final_renown_total}`
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -711,19 +718,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- seed with the example problem ---
|
// --- seed with the example problem ---
|
||||||
addCity({name: "Aridias", type: "hub", renown: 2, adjacent: ["Bearhearth"]});
|
addCity({name: "0", type: "hub", renown: 2});
|
||||||
addCity({name: "Bearhearth", type: "foundry", renown: 2, vat_steel: 3, vat_brass: 2, vat_electrum: 1, adjacent: ["Kingsland"]});
|
addCity({name: "1", type: "foundry", renown: 2, vat_steel: 1, vat_brass: 1, vat_electrum: 1});
|
||||||
addCity({name: "Kingsland", type: "metropolis", renown: 4, can_renovate: false, adjacent: ["Roseward"]});
|
addCity({name: "2", type: "hub", renown: 2});
|
||||||
addCity({name: "Roseward", type: "monument", renown: 2});
|
addCity({name: "3", type: "foundry", renown: 2});
|
||||||
|
addCity({name: "4", type: "monument", renown: 2});
|
||||||
addAgent({kind: "Planner"});
|
addAgent({kind: "Planner"});
|
||||||
Object.entries({
|
addTerms({
|
||||||
"renown": 0,
|
"renown": 0,
|
||||||
"luxuries": 1,
|
"luxuries": 1,
|
||||||
"steel": 2,
|
"steel": 2,
|
||||||
"brass": 1,
|
"brass": 1,
|
||||||
"electrum": 2
|
"electrum": 2
|
||||||
}).forEach(([resource, scalar]) =>
|
})
|
||||||
addTerm({resource, scalar}));
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
4
solve.py
4
solve.py
|
|
@ -394,7 +394,7 @@ class CityTurnPlan:
|
||||||
@dataclass
|
@dataclass
|
||||||
class Solution:
|
class Solution:
|
||||||
status: str
|
status: str
|
||||||
objective_value: float
|
objective_value: float | None
|
||||||
final_resources: dict[str, float]
|
final_resources: dict[str, float]
|
||||||
final_renown_total: int
|
final_renown_total: int
|
||||||
plan: list[CityTurnPlan] = field(default_factory=list)
|
plan: list[CityTurnPlan] = field(default_factory=list)
|
||||||
|
|
@ -1277,7 +1277,7 @@ def solve(problem: Problem, max_time_seconds: float = 30.0,
|
||||||
|
|
||||||
if status not in (cp_model.OPTIMAL, cp_model.FEASIBLE):
|
if status not in (cp_model.OPTIMAL, cp_model.FEASIBLE):
|
||||||
return Solution(
|
return Solution(
|
||||||
status=status_name, objective_value=float("nan"),
|
status=status_name, objective_value=None,
|
||||||
final_resources={}, final_renown_total=0, plan=[],
|
final_resources={}, final_renown_total=0, plan=[],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue