Public
Edited
Oct 16, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
raw_progression
X
0
Y
1
Color
#ffff53
Size
Facet X
Facet Y
Mark
Auto
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
prog_fit = fit_all(_.slice(raw_progression, 1))
Insert cell
raw_progression
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Plot.plot({
marks: [
Plot.lineY(raw_progression, { x: "0", y: "1" }),
Plot.lineY(prog_fit.lin.points, { x: "0", y: "1", stroke: "red" }),
Plot.lineY(prog_fit.quad.points, { x: "0", y: "1", stroke: "green" }),
Plot.lineY(prog_fit.cub.points, { x: "0", y: "1", stroke: "blue" }),
Plot.lineY(prog_fit.exp.points, { x: "0", y: "1", stroke: "orange" }),
Plot.lineY(prog_fit.log.points, { x: "0", y: "1", stroke: "pink" }),
Plot.lineY(prog_fit.pow.points, { x: "0", y: "1", stroke: "purple" })
]
})
Insert cell
inv_prog = raw_progression.map(([x, y]) => [x, 1 / y])
Insert cell
inv_fit = fit_all(inv_prog)
Insert cell
inv_prog
X
0
Y
1
Color
Size
Facet X
Facet Y
Mark
Auto
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
Plot.lineY(
raw_progression1,
prog_fit.exp.points.map(([x, y]) => y)
).plot()
Insert cell
## The Treasure
Insert cell
Insert cell
fit_all(treasure_partial.attack.map(({ level, cost }) => [level, cost]))
Insert cell
fit_all(treasure_partial.haste.map(({ level, cost }) => [level, cost]))
Insert cell
treasure = {
const loot = [
{ type: "Loot", level: 0, cost: 132, value: 3 },
{ type: "Loot", level: 6, cost: 820, value: 4 },
{ type: "Loot", level: 17, cost: 1743, value: 5 },
{ type: "Loot", level: 28, cost: 2882, value: 6 },
{ type: "Loot", level: 39, cost: 4231, value: 7 },
{ type: "Loot", level: 50, value: 8 }
];
function process(list) {
for (let i = 0; i < list.length - 1; ++i) {
const it = list[i];
it.next = list[i + 1];
it.label = `${it.type} ${it.level}`;
it.benefit = list[i + 1].value / it.value;
it.cost_benefit = it.benefit / it.cost;
}
const last = list[list.length - 1];
last.cost_benefit = 0;
}

const attack = _.times(101, (x) => ({
type: "Attack",
level: x,
cost: _.round(0.06 * x ** 2 + 3 * x + 6.212),
value: _.round(1 + 0.1 * x, 2)
}));

const haste = _.times(21, (x) => ({
type: "Haste",
level: x,
cost: _.round(0.01 * x ** 2 + 6 * x + 16),
value: _.round(1 + 0.05 * x, 2)
}));

process(loot);
process(attack);
process(haste);

const combined_ah = _(attack)
.concat(haste)
.filter("next")
.sortBy("cost_benefit")
.reverse()
.value();
const combined_all = _(combined_ah)
.concat(loot)
.filter("next")
.sortBy("cost_benefit")
.reverse()
.value();

const start_state = { time: 0, income: loot[0].value };

return { loot, attack, haste, combined_ah, combined_all, start_state };
}
Insert cell
function* income_curve_gen(upgrades, state = treasure.start_state) {
let { time, income } = state;

for (const upgrade of upgrades) {
time += upgrade.cost / income;
income = _.round(income * upgrade.benefit, 5);
yield { time, income, upgrade: upgrade.next.label };
}
}
Insert cell
baseline = [treasure.start_state, ...income_curve_gen(treasure.combined_ah)]
Insert cell
baseline_with_loot = [
treasure.start_state,
...income_curve_gen(treasure.combined_all)
]
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.lineY(baseline, { x: "time", y: "income", tip: true }),
Plot.lineY(baseline_with_loot, { x: "time", y: "income", tip: true })
]
})
Insert cell
function graph_final_income(scenario) {
const last = scenario.at(-1);
return build_samples((x) => (x - last.time) * last.income, 0, 2000);
}
Insert cell
Plot.plot({
x: { domain: [0, 2000] },
marks: [
Plot.line(graph_final_income(baseline)),
Plot.line(graph_final_income(baseline_with_loot)),
Plot.ruleY([0])
]
})
Insert cell
Plot.plot({
x: { domain: [0, 2000] },
marks: [
Plot.link([baseline.at(-1), baseline_with_loot.at(-1)], {
x1: "time",
y1: 0,
x2: 2000,
y2: (p) => (2000 - p.time) * p.income
})
]
})
Insert cell
Insert cell
Insert cell
dl_sorted = _.sortBy(dark_lord_datapoints, "cave")
Insert cell
viewof table1 = Inputs.table(dl_sorted)
Insert cell
Plot.plot({
// y: { type: "log" },
marks: [
Plot.dot(dl_sorted, { x: "cave", y: "bonus", tip: true }),
plotFunction((x) => 1000 * 1.003 ** x - 900, { xmax: 850 }),
plotFunction((x) => 100 + 0.05 * x * x - 3 * x, { xmax: 850 })
]
})
Insert cell
dl_extended = dl_sorted.map((d) => ({ ...d, ratio: d.bonus / d.cave }))
Insert cell
dl_extended
X
cave
Y
ratio
Color
Size
Facet X
Facet Y
Mark
dot
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
dl_growth = dl_sorted.slice(0, -1).map((p, i) => {
const p2 = dl_sorted[i + 1],
dy = p2.bonus - p.bonus,
dx = p2.cave - p.cave;
return [p2.cave, dy / dx];
})
Insert cell
Plot.dot(dl_growth).plot()
Insert cell
dark_lord_datapoints_pct = dark_lord_datapoints.map(({ cave, bonus }) => ({
cave,
bonus: bonus / 100
}))
Insert cell
dark_lord_fit = fit_all1(
dark_lord_datapoints.map(({ cave, bonus }) => [cave, bonus])
)
Insert cell
dl_short = JSON.stringify(
dark_lord_datapoints.map(({ cave, bonus }) => [cave, bonus])
)
.replaceAll("[", "{")
.replaceAll("]", "}")
Insert cell
Insert cell
Insert cell
seq = (...args) => _(_.range(...args))
Insert cell
seq(5).value()
Insert cell
seq(1, 5).value()
Insert cell
Insert cell
Insert cell
function t(text) {
const {
groups: { m, s }
} = text[0].match(/^(?:(?<m>\d+)m)?\s*(?:(?<s>\d+)s)?$/);
return parseInt(m ?? 0) * 60 + parseInt(s ?? 0);
}
Insert cell
Insert cell
fit = require("regression")
Insert cell
fit_opt = ({ precision: 10 })
Insert cell
fit_all = (data) => ({
lin: fit.linear(data, fit_opt),
quad: fit.polynomial(data, fit_opt),
cub: fit.polynomial(data, { ...fit_opt, order: 3 }),
exp: fit.exponential(data, fit_opt),
log: fit.logarithmic(data, fit_opt),
pow: fit.power(data, fit_opt)
})
Insert cell
fit_all1 = (data) =>
_.sortBy(
[
fit.linear(data, fit_opt),
fit.polynomial(data, fit_opt),
fit.polynomial(data, { ...fit_opt, order: 3 }),
fit.exponential(data, fit_opt),
fit.logarithmic(data, fit_opt),
fit.power(data, fit_opt)
],
(it) => -it.r2
)
Insert cell
import { build_samples } from "@mcmcclur/adaptive-plotter"
Insert cell
levenbergMarquardt = {
const lib = await import(
"https://unpkg.com/ml-levenberg-marquardt@4.1.3/lib-esm/index.js?module"
);
return lib.levenbergMarquardt;
}
Insert cell
{
// Creates linear function using the provided slope and intercept parameters
function line([slope, intercept]) {
return (x) => slope * x + intercept;
}

// Input points (x,y)
const x = [0, 1, 2, 3, 4, 5, 6];
const y = [-2, 0, 2, 4, 6, 8, 10];

// Parameter values to use for first iteration
const initialValues = [1, 0]; // i.e., y = x

return levenbergMarquardt({ x, y }, line, { initialValues });
// {
// parameterValues: [1.9999986750084096, -1.9999943899435104]
// parameterError: 6.787132159723697e-11
// iterations: 2
// }
}
Insert cell
logisticRegression = {
const { default: d } = await import(
"https://unpkg.com/ml-logistic-regression@2.0.0/src/logreg.js?module"
);
return d; //.levenbergMarquardt;
}
Insert cell
new logisticRegression()
Insert cell
function plotFunction(f, { xmin = 1, xmax = 1000, dx = 1 } = {}) {
return Plot.line(_.range(xmin, xmax, dx).map((x) => [x, f(x)]));
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more