separation of script.js

This commit is contained in:
33333-33333 2026-05-14 20:15:23 +09:00
commit 5d09ad30d7
7 changed files with 1314 additions and 1307 deletions

116
utils.js Normal file
View file

@ -0,0 +1,116 @@
function clamp(v, min, max) {
return Math.max(min, Math.min(max, v));
}
function bestBy(items, score) {
let best = null;
let bestScore = -Infinity;
for (const item of items) {
const value = score(item);
if (value > bestScore) {
best = item;
bestScore = value;
}
}
return best;
}
function smoothstep(t) {
return t * t * (3 - 2 * t);
}
const traitFields = [
["mobility", traits => traits.mobility, 0.02, 1, 1, 1],
["resourceAttraction", traits => traits.resourceAttraction, 0.05, 1.2, 1, 1],
["assimilation", traits => traits.assimilation, 0, 0.75, 1, 1],
["ethnocentrism", traits => traits.ethnocentrism, 0, 1.2, 1, 1],
["reproductionThreshold", traits => traits.reproductionThreshold, 12, 48, 16, 1 / 48],
["sedentary", traits => traits.sedentary, 0, 1, 1, 1]
];
function mutateTraits(traits, rng, amount) {
return Object.fromEntries(traitFields.map(([key, get, min, max, mutationScale]) => [
key,
clamp(get(traits) + rng.range(-amount * mutationScale, amount * mutationScale), min, max)
]));
}
function blendTraits(a, b, t) {
return Object.fromEntries(traitFields.map(([key, get]) => [key, lerp(get(a), get(b), t)]));
}
function emptyTraitSums() {
return Object.fromEntries(traitFields.map(([key]) => [key, 0]));
}
function addTraits(sum, traits) {
for (const [key, get, , , , sumScale] of traitFields) sum[key] += get(traits) * sumScale;
}
function averageTraits(sum, count) {
return Object.fromEntries(traitFields.map(([key, , , , , sumScale]) => [key, sum[key] / count / sumScale]));
}
function compositionTotal(composition) {
return [...composition.values()].reduce((sum, value) => sum + value, 0);
}
function addBirthsToComposition(composition, births, rng = null) {
const entries = [...composition.entries()];
const total = entries.reduce((sum, [, value]) => sum + value, 0);
if (!total || births <= 0) return;
for (let n = 0; n < births; n++) {
let r = (rng ? rng.next() : Math.random()) * total;
for (const [id, count] of entries) {
r -= count;
if (r <= 0) {
composition.set(id, (composition.get(id) || 0) + 1);
break;
}
}
}
}
function removeFromComposition(composition, loss) {
let remaining = loss;
const total = compositionTotal(composition);
if (!total) return;
for (const [id, count] of [...composition]) {
const removed = Math.min(count, Math.ceil(loss * (count / total)));
composition.set(id, Math.max(0, count - removed));
remaining -= removed;
if (composition.get(id) <= 0) composition.delete(id);
if (remaining <= 0) break;
}
}
function dominantComposition(composition) {
let bestId = null;
let bestCount = 0;
for (const [id, count] of composition) {
if (count > bestCount) {
bestId = id;
bestCount = count;
}
}
return bestId;
}
function traitDistance(a, b) {
return traitFields.reduce((sum, [, get, , , , distanceScale]) => sum + Math.abs(get(a) - get(b)) * distanceScale, 0);
}
function hslToRgb(h, s, l) {
const hue = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
return [
Math.round(hue(p, q, h + 1 / 3) * 255),
Math.round(hue(p, q, h) * 255),
Math.round(hue(p, q, h - 1 / 3) * 255)
];
}
function mix(a, b, t) {
return [
Math.round(lerp(a[0], b[0], t)),
Math.round(lerp(a[1], b[1], t)),
Math.round(lerp(a[2], b[2], t))
];
}
function lerp(a, b, t) {
return a + (b - a) * t;
}