Skip to content

Commit d7a1ccc

Browse files
committed
feat(sdk): Game runner handles FAIL command and crashes cleanly
1 parent 20117a9 commit d7a1ccc

File tree

7 files changed

+59
-31
lines changed

7 files changed

+59
-31
lines changed

runner/src/main/java/com/codingame/gameengine/runner/Command.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ static enum OutputCommand implements CommandKey {
1515
}
1616

1717
static enum InputCommand implements CommandKey {
18-
VIEW, INFOS, NEXT_PLAYER_INPUT, NEXT_PLAYER_INFO, SCORES, UINPUT, TOOLTIP, SUMMARY, METADATA;
18+
VIEW, INFOS, NEXT_PLAYER_INPUT, NEXT_PLAYER_INFO, SCORES, UINPUT, TOOLTIP, SUMMARY, METADATA, FAIL;
1919
}
2020

2121
private List<String> lines;

runner/src/main/java/com/codingame/gameengine/runner/GameRunner.java

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ private void runAgents() {
127127
GameTurnInfo turnInfo = readGameInfo(round);
128128
boolean validTurn = turnInfo.isComplete();
129129

130+
gameResult.failCause = turnInfo.get(InputCommand.FAIL).orElse(null);
131+
130132
if (validTurn) {
131133
gameResult.outputs.get("referee").add(refereeStdout.toString());
132134
refereeStdout.reset();
@@ -283,7 +285,7 @@ private GameTurnInfo readGameInfo(int round) {
283285

284286
referee.sendInput(new Command(OutputCommand.GET_GAME_INFO).toString());
285287

286-
while (!turnInfo.isComplete()) {
288+
while (!turnInfo.isComplete() && !turnInfo.refereeHasFailed()) {
287289
Command command = readCommand(referee, round);
288290
if (command == null) {
289291
return turnInfo;
@@ -294,34 +296,39 @@ private GameTurnInfo readGameInfo(int round) {
294296
}
295297

296298
private Command readCommand(Agent agent, int round) {
297-
String output = agent.getOutput(1, 150_000);
298-
if (output != null) {
299-
output = output.replace('\r', '\n');
300-
}
301-
if (checkOutput(output, 1) != OutputResult.OK) {
302-
throw new RuntimeException("Invalid Referee command: " + output);
303-
}
299+
try {
300+
String output = agent.getOutput(1, 150_000);
301+
if (output != null) {
302+
output = output.replace('\r', '\n');
303+
}
304+
if (checkOutput(output, 1) != OutputResult.OK) {
305+
throw new RuntimeException("Invalid Referee command: " + output);
306+
}
304307

305-
Matcher m = COMMAND_HEADER_PATTERN.matcher(output.trim());
306-
if (m.matches()) {
307-
String command = m.group("cmd");
308-
int nbLinesToRead = Integer.parseInt(m.group("lineCount"));
308+
Matcher m = COMMAND_HEADER_PATTERN.matcher(output.trim());
309+
if (m.matches()) {
310+
String command = m.group("cmd");
311+
int nbLinesToRead = Integer.parseInt(m.group("lineCount"));
309312

310-
if (nbLinesToRead >= 0) {
311-
output = agent.getOutput(nbLinesToRead, 150_000, round == 0);
312-
output = output.replace('\r', '\n');
313+
if (nbLinesToRead >= 0) {
314+
output = agent.getOutput(nbLinesToRead, 150_000, round == 0);
315+
output = output.replace('\r', '\n');
316+
} else {
317+
throw new RuntimeException("Invalid Referee command line count: " + output);
318+
}
319+
if (checkOutput(output, nbLinesToRead) != OutputResult.OK) {
320+
throw new RuntimeException(
321+
"Error reading Referee command. Buffer capacity: " + output.length() + " / "
322+
+ (round == 0 ? RefereeAgent.REFEREE_MAX_BUFFER_SIZE_EXTRA : RefereeAgent.REFEREE_MAX_BUFFER_SIZE)
323+
);
324+
}
325+
return new Command(InputCommand.valueOf(command), output);
313326
} else {
314-
throw new RuntimeException("Invalid Referee command line count: " + output);
327+
throw new RuntimeException("Invalid referee command: " + output);
315328
}
316-
if (checkOutput(output, nbLinesToRead) != OutputResult.OK) {
317-
throw new RuntimeException(
318-
"Error reading Referee command. Buffer capacity: " + output.length() + " / "
319-
+ (round == 0 ? RefereeAgent.REFEREE_MAX_BUFFER_SIZE_EXTRA : RefereeAgent.REFEREE_MAX_BUFFER_SIZE)
320-
);
321-
}
322-
return new Command(InputCommand.valueOf(command), output);
323-
} else {
324-
throw new RuntimeException("Invalid referee command: " + output);
329+
} catch (RuntimeException err) {
330+
err.printStackTrace();
331+
return new Command(InputCommand.FAIL, err.toString());
325332
}
326333
}
327334

@@ -408,12 +415,15 @@ public void write(int b) throws IOException {
408415
public void write(int b) throws IOException {
409416
err.write(b);
410417
refereeStderr.write(b);
411-
}
418+
}
412419
}));
413420
requireGameNotEnded();
414421
Properties conf = new Properties();
415422
initialize(conf);
423+
416424
runAgents();
425+
426+
referee.destroy();
417427
destroyPlayers();
418428
gameEnded = true;
419429
System.setOut(out);

runner/src/main/java/com/codingame/gameengine/runner/GameTurnInfo.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ boolean isComplete() {
2626
boolean isEndTurn() {
2727
return isCompleteEndTurn();
2828
}
29+
30+
boolean refereeHasFailed() {
31+
return received.containsKey(InputCommand.FAIL);
32+
}
2933

3034
private boolean isCompleteEndTurn() {
3135
return received.containsKey(InputCommand.SCORES)

runner/src/main/java/com/codingame/gameengine/runner/JavaPlayerAgent.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ protected void runInputOutput() throws Exception {
8787

8888
@Override
8989
public void destroy() {
90-
// Does nothing
9190
if (javaRunnerThread != null) {
9291
javaRunnerThread.setStopping(true);
9392
javaRunnerThread.interrupt();

runner/src/main/java/com/codingame/gameengine/runner/RefereeAgent.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class RefereeAgent extends Agent {
2222
private OutputStream processStdin = null;
2323
private InputStream processStdout = null;
2424
private InputStream processStderr = null;
25+
26+
private Thread thread;
2527

2628
public RefereeAgent() {
2729
super();
@@ -34,6 +36,14 @@ public RefereeAgent() {
3436
throw new RuntimeException("Cannot initialize Referee Agent");
3537
}
3638
}
39+
40+
@Override
41+
public void destroy() {
42+
if (thread != null) {
43+
thread.interrupt();
44+
}
45+
}
46+
3747

3848
@Override
3949
protected OutputStream getInputStream() {
@@ -53,13 +63,12 @@ protected InputStream getErrorStream() {
5363
@Override
5464
protected void runInputOutput() throws Exception {
5565

56-
Thread t = new Thread() {
66+
thread = new Thread() {
5767
public void run() {
5868
RefereeMain.start(agentStdin, new PrintStream(agentStdout));
5969
}
6070
};
61-
62-
t.start();
71+
thread.start();
6372
}
6473

6574
@Override

runner/src/main/java/com/codingame/gameengine/runner/dto/GameResult.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ public class GameResult {
1616
public List<Tooltip> tooltips = new ArrayList<>();
1717
public Map<Integer, Integer> ids = new HashMap<>();
1818
public List<AgentDto> agents = new ArrayList<>();
19+
public String failCause = null;
1920
}

runner/src/main/resources/view/app.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ function PlayerCtrl ($scope, $timeout, $interval, $filter, drawerFactory, gameMa
6262
if ($scope.gameLoaded || !ctrl.data) {
6363
return
6464
}
65+
if (ctrl.data.failCause) {
66+
$scope.errors = ctrl.data.failCause
67+
return;
68+
}
69+
6570
$scope.gameLoaded = true
6671
$scope.uinput = ctrl.data.uinput
6772
ctrl.gameInfo = convertFrameFormat(ctrl.data)

0 commit comments

Comments
 (0)