feat: add shared integration test framework for garages (#126)
Implements: - BattleResult type and JSON-lines parser (#127) - TR server lifecycle manager (#128) - Bot compiler using nim c (#129) - runBattle() orchestrator (#130) - Example test in OscillatorBot_garage (#131) - Framework usage guide (#132) - TestBattleRunner.java for external server (#133) - BattleRunner process lifecycle (#134) Fix: runner_process.nim was redefining TimeoutError locally; now uses std/net.TimeoutError consistently with server_manager.nim.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import dev.robocode.tankroyale.runner.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Generic battle runner that connects to an external Tank Royale server and
|
||||
* emits JSON-lines to stdout for downstream parsing.
|
||||
*
|
||||
* Usage:
|
||||
* ./run_test_battle.sh --server-url ws://localhost:7654 --rounds 5 --bot-dirs /path/bot1,/path/bot2
|
||||
*/
|
||||
public class TestBattleRunner {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Logger.getLogger("dev.robocode.tankroyale").setLevel(Level.WARNING);
|
||||
|
||||
String serverUrl = null;
|
||||
int rounds = 1;
|
||||
List<String> botDirs = List.of();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
switch (args[i]) {
|
||||
case "--server-url" -> serverUrl = args[++i];
|
||||
case "--rounds" -> rounds = Integer.parseInt(args[++i]);
|
||||
case "--bot-dirs" -> botDirs = Arrays.asList(args[++i].split(","));
|
||||
default -> { System.err.println("Unknown arg: " + args[i]); System.exit(1); }
|
||||
}
|
||||
}
|
||||
|
||||
if (serverUrl == null) { System.err.println("--server-url required"); System.exit(1); }
|
||||
if (botDirs.isEmpty()) { System.err.println("--bot-dirs required"); System.exit(1); }
|
||||
|
||||
final String url = serverUrl;
|
||||
final List<BotEntry> bots = botDirs.stream().map(BotEntry::of).collect(Collectors.toList());
|
||||
final int numRounds = rounds;
|
||||
final var setup = BattleSetup.classic(s -> s.setNumberOfRounds(numRounds));
|
||||
|
||||
try (var runner = BattleRunner.create(b -> b.externalServer(url).suppressServerOutput())) {
|
||||
var owner = new Object();
|
||||
try (var handle = runner.startBattleAsync(setup, bots)) {
|
||||
|
||||
handle.getOnGameStarted().on(owner, event -> {
|
||||
var names = event.getParticipants().stream()
|
||||
.map(p -> "\"" + escape(p.getName()) + "\"")
|
||||
.collect(Collectors.joining(","));
|
||||
System.out.println("{\"event\":\"game_started\",\"bots\":[" + names + "]}");
|
||||
System.out.flush();
|
||||
});
|
||||
|
||||
handle.getOnRoundEnded().on(owner, event -> {
|
||||
var results = event.getResults().stream().map(r ->
|
||||
"{\"name\":\"" + escape(r.getName()) + "\"" +
|
||||
",\"score\":" + r.getTotalScore() +
|
||||
",\"rank\":" + r.getRank() +
|
||||
",\"survived\":" + (r.getSurvival() > 0) + "}"
|
||||
).collect(Collectors.joining(","));
|
||||
System.out.println("{\"event\":\"round_ended\",\"round\":" + event.getRoundNumber() + ",\"results\":[" + results + "]}");
|
||||
System.out.flush();
|
||||
});
|
||||
|
||||
var final_ = handle.awaitResults();
|
||||
var results = final_.getResults().stream().map(r ->
|
||||
"{\"name\":\"" + escape(r.getName()) + "\"" +
|
||||
",\"totalScore\":" + r.getTotalScore() +
|
||||
",\"rank\":" + r.getRank() +
|
||||
",\"firstPlaces\":" + r.getFirstPlaces() +
|
||||
",\"survivalCount\":" + r.getSurvival() + "}"
|
||||
).collect(Collectors.joining(","));
|
||||
System.out.println("{\"event\":\"battle_ended\",\"results\":[" + results + "]}");
|
||||
System.out.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("Battle failed: " + e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// ponytail: minimal JSON escaping — only backslash and double-quote; extend if bot names use control chars
|
||||
static String escape(String s) {
|
||||
return s.replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user