feat(ModularBot): pluggable bot with 4 guns, phantom meteor movement, radar harness

- Gun harness: virtual bullet tracker, rolling fitness, auto-selector
- Guns: head-on, linear (extrapolation), circular (integrated formula), tsetlin machine (learning)
- Movement: phantom meteor gravity engine (danger histograms, phantom bullets, fire detection)
- Radar: harness + radar_lock adapter
- Color-coded modules: turret/bullet color per gun, body per movement, scan per radar
- Beats Target, SpinBot, Crazy, TrackFire in 10-round battles
This commit is contained in:
2026-09-20 00:37:10 +02:00
parent c9191b7afb
commit 254c7dc997
39 changed files with 2630 additions and 30 deletions
+81
View File
@@ -0,0 +1,81 @@
import dev.robocode.tankroyale.runner.*;
import dev.robocode.tankroyale.client.model.*;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* BNNBot validation battle: BNNBot vs Target (1 round, embedded server).
*
* Env vars:
* BNN_BOT_DIR — path to BNNBot directory (must contain BNNBot.json + BNNBot.sh)
* SAMPLE_BOTS_DIR — path to sample bots archive
* BNNBOT_CSV — forwarded to bot process: "1" enables CSV output
* ENEMY_BOT — name of enemy bot subfolder under SAMPLE_BOTS_DIR (default: Target)
* NUM_ROUNDS — number of rounds to run (default: 5)
*
* Usage: java -cp ".:$JAR" RunBNNBattle
*/
public class RunBNNBattle {
static final Map<Integer, String> idToName = new ConcurrentHashMap<>();
public static void main(String[] args) {
Logger.getLogger("dev.robocode.tankroyale").setLevel(Level.WARNING);
String bnnBotDir = requireEnv("BNN_BOT_DIR");
String sampleBots = requireEnv("SAMPLE_BOTS_DIR");
String enemyBot = System.getenv().getOrDefault("ENEMY_BOT", "Target");
int numRounds = Integer.parseInt(System.getenv().getOrDefault("NUM_ROUNDS", "5"));
System.out.println("Enemy: " + enemyBot + " Rounds: " + numRounds);
try (var runner = BattleRunner.create(b -> b.embeddedServer().suppressServerOutput())) {
var setup = BattleSetup.classic(s -> s.setNumberOfRounds(numRounds));
var bots = List.of(
BotEntry.of(bnnBotDir),
BotEntry.of(sampleBots + "/" + enemyBot)
);
var owner = new Object();
try (var handle = runner.startBattleAsync(setup, bots)) {
handle.getOnGameStarted().on(owner, event -> {
System.out.println("=== GAME STARTED ===");
for (var p : event.getParticipants()) {
idToName.put(p.getId(), p.getName());
System.out.printf(" #%d %s%n", p.getId(), p.getName());
}
System.out.printf("%-6s %-12s %-10s%n", "Turn", "Name", "RadarDir");
System.out.println("-".repeat(40));
});
handle.getOnTickEvent().on(owner, event -> {
int turn = event.getTurnNumber();
for (var state : event.getBotStates())
if (state.getName() != null) idToName.putIfAbsent(state.getId(), state.getName());
});
handle.getOnRoundEnded().on(owner, event ->
System.out.printf("%n=== ROUND %d ENDED (turn %d) ===%n",
event.getRoundNumber(), event.getTurnNumber()));
var results = handle.awaitResults();
System.out.printf("%n=== RESULTS (%d rounds) ===%n", results.getNumberOfRounds());
for (var r : results.getResults())
System.out.printf(" #%d %-25s %d pts%n", r.getRank(), r.getName(), r.getTotalScore());
}
}
}
static String requireEnv(String name) {
var v = System.getenv(name);
if (v == null || v.isBlank()) {
System.err.println("Error: " + name + " env var not set");
System.exit(1);
}
return v;
}
}