101 lines
3.6 KiB
TypeScript
101 lines
3.6 KiB
TypeScript
|
|
import { describe, expect, test } from 'bun:test';
|
|
import { createSimulation, stepSimulation } from './simulation';
|
|
import { SIMULATION_CONFIG } from './types';
|
|
import { generateObservation, observationToInputs } from './sensors';
|
|
|
|
// Mock Genome that implements Perfect Tracking Logic
|
|
const perfectTrackerGenome = {
|
|
id: 9999,
|
|
nodes: [],
|
|
connections: [],
|
|
fitness: 0
|
|
};
|
|
|
|
// Strafer Bot (Same as in selfPlay.ts)
|
|
const straferGenome = {
|
|
id: -3,
|
|
nodes: [],
|
|
connections: [],
|
|
fitness: 0
|
|
};
|
|
|
|
describe('Aim Mechanics Verification', () => {
|
|
test('Perfect Tracker should defeat Strafer', () => {
|
|
// Setup Simulation
|
|
const sim = createSimulation(12345, 2); // Pair 2 (Strafer pair)
|
|
|
|
let trackerHits = 0;
|
|
let straferHits = 0;
|
|
|
|
// Run Match
|
|
let currentSim = sim;
|
|
const maxTicks = 300;
|
|
|
|
for (let t = 0; t < maxTicks; t++) {
|
|
const obsTracker = generateObservation(0, currentSim);
|
|
|
|
// --- PERFECT LOGIC ---
|
|
// 1. Get Target Relative Angle from Sensor (Index 54 in 0-based array of 56 inputs)
|
|
// But we can just read it from observation directly
|
|
const targetAngle = obsTracker.targetRelativeAngle; // [-1, 1]
|
|
const targetVisible = obsTracker.targetVisible;
|
|
|
|
// 2. Control Logic
|
|
// If angle > 0 (Left), Turn Left (-1). If angle < 0 (Right), Turn Right (1).
|
|
// P-Controller: turn = angle * K
|
|
const K = 5.0; // Strong gain
|
|
let turn = -targetAngle * K; // Note: Sign depends on coordinate system.
|
|
// In setup: Angle is Aim - Target.
|
|
// If Target is to Left (Positive relative?), we need to turn Left (Positive/Negative?)
|
|
|
|
// Let's verify sign:
|
|
// If target is at angle 0.1 (Left), we want to Increase Aim Angle?
|
|
// Usually turn +1 adds to angle.
|
|
// So turn = +1 * K.
|
|
|
|
// Note: targetRelativeAngle = (Target - Aim) / PI.
|
|
// If Target > Aim (Positive), we need to Turn Positive.
|
|
turn = targetAngle * 20.0; // Max turn
|
|
|
|
// Clamp
|
|
if (turn > 1) turn = 1;
|
|
if (turn < -1) turn = -1;
|
|
|
|
// Shoot if locked on
|
|
const shoot = (Math.abs(targetAngle) < 0.05 && targetVisible > 0.5) ? 1.0 : 0.0;
|
|
|
|
const actionTracker = {
|
|
moveX: 0,
|
|
moveY: 0,
|
|
turn: turn,
|
|
shoot: shoot
|
|
};
|
|
|
|
// --- STRAFER LOGIC ---
|
|
const straferMoveY = Math.sin(t * 0.2);
|
|
const actionStrafer = {
|
|
moveX: 0,
|
|
moveY: straferMoveY,
|
|
turn: 0,
|
|
shoot: 0 // Strafer is passive to isolate aim test
|
|
};
|
|
|
|
// Step
|
|
currentSim = stepSimulation(currentSim, [actionTracker, actionStrafer]);
|
|
|
|
// Count hits
|
|
if (currentSim.agents[1].hits > trackerHits) {
|
|
trackerHits = currentSim.agents[1].hits; // Agent 1 is Strafer
|
|
// console.log(`Hit at tick ${t}! Total: ${trackerHits}`);
|
|
}
|
|
}
|
|
|
|
console.log(`Perfect Tracker Result: ${trackerHits} Hits on Strafer in ${maxTicks} ticks.`);
|
|
|
|
// Assert Feasibility
|
|
// We expect at least 3-5 hits to prove it's possible.
|
|
expect(trackerHits).toBeGreaterThan(3);
|
|
});
|
|
});
|