Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
95 lines
2.9 KiB
Java
95 lines
2.9 KiB
Java
package workbench;
|
|
|
|
import java.util.List;
|
|
import java.util.Random;
|
|
|
|
/**
|
|
* Agent that walks the manifold adjacency graph, injecting heat.
|
|
*
|
|
* Walk policy: 85% greedy (hottest neighbor), 15% random exploration.
|
|
* Injects injectAmt heat at current vertex each step.
|
|
* Life decays 0.05/step; respawns at random vertex when life <= 0.
|
|
*/
|
|
public class Friend {
|
|
|
|
public static final String[] IDS = {"G","R","C","Y","V"};
|
|
public static final int[] COLORS = {0x68ff9a, 0xff3232, 0x00eeff, 0xffd700, 0x9400d3};
|
|
|
|
public final int idx;
|
|
public final String id;
|
|
|
|
public float life = 1000f;
|
|
public float sap = 0f;
|
|
public int blooms = 0;
|
|
|
|
public int vIdx; // current vertex index on manifold
|
|
|
|
// position in 3D (copied from manifold vertex each step)
|
|
public float x, y, z;
|
|
|
|
private final float injectAmt;
|
|
private final float explorePct;
|
|
private final Random rng;
|
|
private final Manifold manifold;
|
|
|
|
// callback for PCB log lines (set by Workbench)
|
|
public Runnable onRespawn;
|
|
public Runnable onBloom;
|
|
|
|
public Friend(int idx, Manifold manifold) {
|
|
this.idx = idx;
|
|
this.id = IDS[idx];
|
|
this.manifold = manifold;
|
|
this.injectAmt = 8.0f;
|
|
this.explorePct = 0.15f;
|
|
this.rng = new Random();
|
|
this.vIdx = rng.nextInt(Math.max(1, manifold.vertexCount));
|
|
syncPosition();
|
|
}
|
|
|
|
public void compute() {
|
|
List<Integer> neighbors = manifold.adjacency.get(vIdx);
|
|
if (!neighbors.isEmpty()) {
|
|
int best = neighbors.get(0);
|
|
for (int n : neighbors) {
|
|
if (manifold.heat[n] > manifold.heat[best]) best = n;
|
|
}
|
|
vIdx = (rng.nextFloat() < explorePct)
|
|
? neighbors.get(rng.nextInt(neighbors.size()))
|
|
: best;
|
|
}
|
|
syncPosition();
|
|
|
|
life -= 0.05f;
|
|
sap += 0.02f;
|
|
if (vIdx < manifold.heat.length) manifold.heat[vIdx] += injectAmt;
|
|
|
|
if (life <= 0) {
|
|
vIdx = rng.nextInt(Math.max(1, manifold.vertexCount));
|
|
life = 1000f; sap = 0f;
|
|
if (onRespawn != null) onRespawn.run();
|
|
}
|
|
if (sap > 20) {
|
|
blooms++; sap = 0f;
|
|
if (onBloom != null) onBloom.run();
|
|
}
|
|
}
|
|
|
|
private void syncPosition() {
|
|
if (manifold.built && vIdx < manifold.vertexCount) {
|
|
// x/y from originalPositions — avoids per-frame wobble noise jitter
|
|
// z from positions — includes oracle heat displacement
|
|
x = manifold.originalPositions[vIdx*3];
|
|
y = manifold.originalPositions[vIdx*3+1];
|
|
z = manifold.positions[vIdx*3+2];
|
|
}
|
|
}
|
|
|
|
/** Relocate to random vertex (call after manifold rebuild). */
|
|
public void relocate() {
|
|
if (manifold.vertexCount > 0)
|
|
vIdx = rng.nextInt(manifold.vertexCount);
|
|
life = 1000f; sap = 0f;
|
|
syncPosition();
|
|
}
|
|
}
|