Metabolism M2.9: flora islands, gardens, walls, farmer strain, aimed work (VM AIM actions), food-limited ecology rebalance
This commit is contained in:
+142
-29
@@ -29,7 +29,8 @@ export const S_TOTAL = 9;
|
||||
export const S_WELL = 10; // nearest living well's pull
|
||||
export const S_PEACE = 11; // section's peace operator (war ↔ truce)
|
||||
export const S_ROCK = 12; // rock under this block: the quarriable ground
|
||||
export const SENSE_COUNT = 13;
|
||||
export const S_GARDEN = 13; // cultivated ground here: is this stone worked yet?
|
||||
export const SENSE_COUNT = 14;
|
||||
|
||||
// Gradient fields, two slots each (x, y): food, own strain, kin, foe, well.
|
||||
export const G_FOOD = 0;
|
||||
@@ -37,7 +38,9 @@ export const G_SELF = 1;
|
||||
export const G_KIN = 2;
|
||||
export const G_FOE = 3;
|
||||
export const G_WELL = 4;
|
||||
export const G_FIELDS = 5;
|
||||
export const G_ROCK = 5;
|
||||
export const G_GARDEN = 6;
|
||||
export const G_FIELDS = 7;
|
||||
export const GRAD_COUNT = G_FIELDS * 2;
|
||||
|
||||
export const A_MOVE_X = 0;
|
||||
@@ -50,11 +53,15 @@ export const A_DRIFT = 6; // gradual direction change following a gradien
|
||||
export const A_ACCELERATE = 7; // speed up / slow down
|
||||
export const A_DIG = 8; // quarry rock here (rock → carried → soil)
|
||||
export const A_BUILD = 9; // deposit carried rock as a wall
|
||||
export const ACTION_COUNT = 10;
|
||||
export const A_TILL = 10; // work stone into a garden (a food generator)
|
||||
export const A_TEND = 11; // tend a garden: it yields far more while tended
|
||||
export const A_AIM_X = 12; // aim the work: act ahead along x (set, not add)
|
||||
export const A_AIM_Y = 13; // aim the work: act ahead along y
|
||||
export const ACTION_COUNT = 14;
|
||||
export const REGS = 6;
|
||||
|
||||
export const MAX_INSTR = 32;
|
||||
export const MAX_STEPS = 48;
|
||||
export const MAX_INSTR = 56;
|
||||
export const MAX_STEPS = 64;
|
||||
|
||||
export const OP = {
|
||||
SET: 0, ADD: 1, MUL: 2, SUB: 3, CLAMP: 4, LERP: 5,
|
||||
@@ -93,23 +100,27 @@ class Asm {
|
||||
const ROLES = {
|
||||
builder: {
|
||||
base: { speed: 0.038, deposit: 1.50, cap: 1.25, growth: 0.90, eat: 0.032 },
|
||||
hungryAt: 0.50, spawnW: 0.30,
|
||||
hungryAt: 0.70, spawnW: 0.30,
|
||||
},
|
||||
grazer: {
|
||||
base: { speed: 0.095, deposit: 0.75, cap: 1.00, growth: 1.00, eat: 0.042 },
|
||||
hungryAt: 0.42, spawnW: 0.45,
|
||||
hungryAt: 0.60, spawnW: 0.45,
|
||||
},
|
||||
scout: {
|
||||
base: { speed: 0.155, deposit: 0.18, cap: 0.80, growth: 1.05, eat: 0.046 },
|
||||
hungryAt: 0.62, spawnW: 0.60,
|
||||
hungryAt: 0.75, spawnW: 0.60,
|
||||
},
|
||||
sentinel: {
|
||||
base: { speed: 0.070, deposit: 0.55, cap: 1.00, growth: 0.85, eat: 0.040 },
|
||||
hungryAt: 0.50, spawnW: 0.25, foeAt: 0.10,
|
||||
hungryAt: 0.70, spawnW: 0.25, foeAt: 0.10,
|
||||
},
|
||||
breeder: {
|
||||
base: { speed: 0.062, deposit: 0.50, cap: 0.90, growth: 1.25, eat: 0.036 },
|
||||
hungryAt: 0.55, spawnW: 0.85, kinAt: 0.12,
|
||||
hungryAt: 0.70, spawnW: 0.85, kinAt: 0.12,
|
||||
},
|
||||
farmer: {
|
||||
base: { speed: 0.055, deposit: 0.40, cap: 0.95, growth: 1.05, eat: 0.038 },
|
||||
hungryAt: 0.65, spawnW: 0.45, gardenAt: 0.15,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -178,38 +189,99 @@ export function compileStrains(summary, genome, rng) {
|
||||
kinPatch = asm.ifgt(4, spec.kinAt * (0.8 + 0.4 * rng.next()));
|
||||
}
|
||||
|
||||
// ---- hungry: follow the smell of food. A starving district trusts
|
||||
// the gradient; the world's well anchor blends in on top.
|
||||
const hungryPatch = asm.iflt(0, spec.hungryAt * (0.85 + 0.3 * noise));
|
||||
// ---- hungry: follow the smell of food — and WORK on the way,
|
||||
// because for a mason or a husbandman work IS foraging: the aimed
|
||||
// ground pays wages in food.
|
||||
const hungryPatch = asm.iflt(0, spec.hungryAt);
|
||||
asm.grad(4, G_FOOD, 0, 1);
|
||||
asm.grad(5, G_FOOD, 1, 1);
|
||||
asm.act(A_MOVE_X, 4, 1);
|
||||
asm.act(A_MOVE_Y, 5, 1);
|
||||
asm.act(A_AIM_X, 4, 1);
|
||||
asm.act(A_AIM_Y, 5, 1);
|
||||
asm.act(A_EAT, 0, 1);
|
||||
asm.halt();
|
||||
if (role === 'builder' || role === 'farmer') {
|
||||
// The husbandman tends first (a tended bed pays best), the
|
||||
// mason breaks stone first; both eat their wages.
|
||||
if (role === 'farmer') {
|
||||
asm.sense(5, S_GARDEN, 1);
|
||||
const bedH = asm.ifgt(5, 0.05);
|
||||
asm.sense(5, S_ROCK, 1);
|
||||
const seamH = asm.ifgt(5, 0.06);
|
||||
asm.halt();
|
||||
const tendH = asm.label();
|
||||
asm.jump(bedH, tendH);
|
||||
asm.act(A_TEND, 5, 1);
|
||||
asm.act(A_EAT, 0, 1);
|
||||
asm.halt();
|
||||
const tillH = asm.label();
|
||||
asm.jump(seamH, tillH);
|
||||
asm.act(A_TILL, 5, 1.2);
|
||||
asm.act(A_EAT, 0, 1);
|
||||
asm.halt();
|
||||
} else {
|
||||
asm.sense(5, S_ROCK, 1);
|
||||
const seamH = asm.ifgt(5, 0.06);
|
||||
asm.sense(5, S_GARDEN, 1);
|
||||
const spentH = asm.ifgt(5, 0.15);
|
||||
asm.halt();
|
||||
const tillH = asm.label();
|
||||
asm.jump(seamH, tillH);
|
||||
asm.act(A_TILL, 5, 1.2);
|
||||
asm.act(A_EAT, 0, 1);
|
||||
asm.halt();
|
||||
const digH = asm.label();
|
||||
asm.jump(spentH, digH);
|
||||
asm.act(A_DIG, 5, 1.2);
|
||||
asm.act(A_EAT, 0, 1);
|
||||
asm.halt();
|
||||
}
|
||||
} else {
|
||||
asm.halt();
|
||||
}
|
||||
|
||||
// ---- fed: the role's own work.
|
||||
const fed = asm.label();
|
||||
asm.jump(hungryPatch, fed);
|
||||
if (role === 'builder') {
|
||||
// The mason: where rock lies underfoot it quarries (rock becomes
|
||||
// carried burden and, ground down, soil); where the ground is
|
||||
// clear it walls — the carried rock rises as the colony's
|
||||
// structure. It never quite stops orbiting its ground.
|
||||
// The mason: it smells stone and walks to it; its WORK lands
|
||||
// AHEAD of it, along its aim — it carves the face of the wall,
|
||||
// not the ground beneath its feet. On fresh stone ahead it
|
||||
// tills (a garden: food for a while, then stone again); on
|
||||
// spent ground it quarries (burden for the walls); on clear
|
||||
// ground it builds what the burden brought.
|
||||
asm.sense(4, S_ROCK, 1);
|
||||
const quarry = asm.ifgt(4, 0.12);
|
||||
asm.act(A_BUILD, 1, 1.2);
|
||||
asm.act(A_EXCRETE, 3, 1.1);
|
||||
asm.grad(4, G_SELF, 0, 0.4);
|
||||
asm.grad(5, G_SELF, 1, 0.4);
|
||||
asm.act(A_DRIFT, 4, 0.8);
|
||||
const quarry = asm.ifgt(4, 0.06);
|
||||
// Clear ground: toward the stone.
|
||||
asm.grad(4, G_ROCK, 0, 1);
|
||||
asm.grad(5, G_ROCK, 1, 1);
|
||||
asm.act(A_MOVE_X, 4, 0.6);
|
||||
asm.act(A_MOVE_Y, 5, 0.6);
|
||||
asm.act(A_AIM_X, 4, 1);
|
||||
asm.act(A_AIM_Y, 5, 1);
|
||||
asm.sense(5, S_FOOD, 1);
|
||||
asm.act(A_BUILD, 5, 1.5);
|
||||
asm.act(A_EXCRETE, 3, 1.0);
|
||||
asm.act(A_SPAWN, 1, spec.spawnW);
|
||||
asm.halt();
|
||||
const onRock = asm.label();
|
||||
asm.jump(quarry, onRock);
|
||||
// Aim INTO the stone face ahead.
|
||||
asm.grad(4, G_ROCK, 0, 1);
|
||||
asm.grad(5, G_ROCK, 1, 1);
|
||||
asm.act(A_AIM_X, 4, 1);
|
||||
asm.act(A_AIM_Y, 5, 1);
|
||||
// Fresh stone becomes a garden: worked, it will feed the body
|
||||
// for a while — and then petrify back to rock.
|
||||
asm.sense(5, S_GARDEN, 1);
|
||||
const spent = asm.ifgt(5, 0.15);
|
||||
asm.act(A_TILL, 4, 1.6);
|
||||
asm.halt();
|
||||
const dig = asm.label();
|
||||
asm.jump(quarry, dig);
|
||||
asm.act(A_DIG, 4, 1);
|
||||
asm.grad(4, G_FOOD, 0, 0.3);
|
||||
asm.act(A_MOVE_X, 4, 0.3);
|
||||
asm.jump(spent, dig);
|
||||
// Worked-out ground: quarry what is left for the walls.
|
||||
asm.act(A_DIG, 4, 1.6);
|
||||
asm.act(A_EXCRETE, 3, 0.3);
|
||||
asm.halt();
|
||||
} else if (role === 'grazer') {
|
||||
// Commutes: follows the food edge, leaves a road behind.
|
||||
@@ -238,6 +310,42 @@ export function compileStrains(summary, genome, rng) {
|
||||
asm.act(A_EXCRETE, 3, 0.5);
|
||||
asm.act(A_SPAWN, 1, spec.spawnW);
|
||||
asm.halt();
|
||||
} else if (role === 'farmer') {
|
||||
// The husbandman: walks the field row, its work ahead of it —
|
||||
// tending the bed it is walking toward (a tended bed yields
|
||||
// far more), opening new beds into fresh stone.
|
||||
asm.sense(4, S_GARDEN, 1);
|
||||
const field = asm.ifgt(4, spec.gardenAt * (0.8 + 0.4 * rng.next()));
|
||||
asm.sense(5, S_ROCK, 1);
|
||||
const seam = asm.ifgt(5, 0.06);
|
||||
// roam toward the fields, aiming where we walk
|
||||
asm.grad(4, G_GARDEN, 0, 1);
|
||||
asm.grad(5, G_GARDEN, 1, 1);
|
||||
asm.act(A_MOVE_X, 4, 0.8);
|
||||
asm.act(A_MOVE_Y, 5, 0.8);
|
||||
asm.act(A_AIM_X, 4, 1);
|
||||
asm.act(A_AIM_Y, 5, 1);
|
||||
asm.act(A_TEND, 4, 1);
|
||||
asm.act(A_SPAWN, 1, spec.spawnW * 0.5);
|
||||
asm.halt();
|
||||
const tillNew = asm.label();
|
||||
asm.jump(seam, tillNew);
|
||||
asm.grad(4, G_ROCK, 0, 1);
|
||||
asm.grad(5, G_ROCK, 1, 1);
|
||||
asm.act(A_AIM_X, 4, 1);
|
||||
asm.act(A_AIM_Y, 5, 1);
|
||||
asm.act(A_TILL, 5, 1);
|
||||
asm.halt();
|
||||
const tend = asm.label();
|
||||
asm.jump(field, tend);
|
||||
asm.grad(4, G_GARDEN, 0, 1);
|
||||
asm.grad(5, G_GARDEN, 1, 1);
|
||||
asm.act(A_AIM_X, 4, 1);
|
||||
asm.act(A_AIM_Y, 5, 1);
|
||||
asm.act(A_TEND, 4, 1);
|
||||
asm.act(A_EXCRETE, 3, 0.4);
|
||||
asm.act(A_SPAWN, 1, spec.spawnW);
|
||||
asm.halt();
|
||||
} else { // breeder
|
||||
// Follows the crowd it feeds on; spawns when the law allows.
|
||||
asm.grad(4, G_KIN, 0, 1);
|
||||
@@ -311,7 +419,8 @@ export function runProgram(memBuf, off, senses, grads, act, code) {
|
||||
let steps = 0;
|
||||
const n = code.length;
|
||||
act[0] = 0; act[1] = 0; act[2] = 0; act[3] = 1; act[4] = 0; act[5] = 0;
|
||||
act[6] = 0; act[7] = 0;
|
||||
act[6] = 0; act[7] = 0; act[8] = 0; act[9] = 0;
|
||||
act[12] = 0; act[13] = 0;
|
||||
const mem = memBuf;
|
||||
while (steps++ < MAX_STEPS && pc < n) {
|
||||
const ins = code[pc];
|
||||
@@ -351,6 +460,10 @@ export function runProgram(memBuf, off, senses, grads, act, code) {
|
||||
case A_ACCELERATE: act[3] *= drive * w + 1; break;
|
||||
case A_DIG: act[6] += Math.max(0, drive) * w; break;
|
||||
case A_BUILD: act[7] += Math.max(0, drive) * w; break;
|
||||
case A_TILL: act[8] += Math.max(0, drive) * w; break;
|
||||
case A_TEND: act[9] += Math.max(0, drive) * w; break;
|
||||
case A_AIM_X: act[12] = drive; break; // aim is SET, not summed
|
||||
case A_AIM_Y: act[13] = drive; break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user