function sim(opts) {
const W = opts.GENERATIONS + 1;
const M = 0.01 * opts.MUTATION_PERCENT;
const S = 0.01 * opts.SYNONYMOUS_PERCENT;
let counts = new Float32Array(W * W);
counts[0] = 100;
const data = [];
for (let generation = 1; generation <= opts.GENERATIONS; ++generation) {
const next = new Float32Array(W * W);
const stats = {};
for (let s = 0; s < W; ++s) {
for (let n = 0; n < W; ++n) {
const c = counts[s + n * W];
{
const k = opts.fn(s, n);
stats[k] = (stats[k] || 0) + c;
}
next[s + n * W] += c * (1 - M);
next[s + n * W + 1] += c * M * S;
next[s + n * W + W] += c * M * (1 - S);
}
}
for (let s = 0; s < W; ++s) {
for (let n = 0; n < W; ++n) {
next[s + n * W] *= 0.01 * (100 + n * opts.PRESSURE);
}
}
const total = next.reduce((a, b) => a + b);
const CAP = POPULATION_MODE == 'Scale to unit population' ? 100 : 10**30;
if (POPULATION_MODE == 'Scale to unit population' || total > CAP) {
for (let i = 0; i < W * W; ++i) {
next[i] *= CAP / total;
if (next[i] > CAP) next[i] = CAP;
}
}
counts = next;
for (const k in stats) {
data.push({
generation,
population: stats[k],
value: parseFloat(k),
});
}
}
return data;
}