Skip to content

Commit b43ead4

Browse files
Digest CH-005: prove survivor death delivery
1 parent 2ae89f9 commit b43ead4

14 files changed

Lines changed: 167 additions & 68 deletions

File tree

.clue/id-ledger.yaml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ counters:
88
CH: "5"
99
CRIT: "8"
1010
DES: "8"
11-
EVT: "13"
11+
EVT: "14"
1212
G: "2"
13-
IDR: "3"
13+
IDR: "4"
1414
OQ: "2"
1515
P: "1"
1616
PDR: "2"
@@ -330,7 +330,7 @@ entries:
330330
component: "6"
331331
- id: EVT-007
332332
kind: numeric
333-
state: live
333+
state: retired
334334
prefix: EVT
335335
component: "7"
336336
- id: EVT-008
@@ -363,6 +363,11 @@ entries:
363363
state: live
364364
prefix: EVT
365365
component: "13"
366+
- id: EVT-014
367+
kind: numeric
368+
state: live
369+
prefix: EVT
370+
component: "14"
366371
- id: G-001
367372
kind: numeric
368373
state: live
@@ -388,6 +393,11 @@ entries:
388393
state: live
389394
prefix: IDR
390395
component: "3"
396+
- id: IDR-004
397+
kind: numeric
398+
state: live
399+
prefix: IDR
400+
component: "4"
391401
- id: OQ-001
392402
kind: numeric
393403
state: live

changes/CH-005-death-event-evidence-boundary/open-questions.md

Lines changed: 0 additions & 19 deletions
This file was deleted.

changes/CH-005-death-event-evidence-boundary/proposal.md

Lines changed: 0 additions & 23 deletions
This file was deleted.

changes/CH-005-death-event-evidence-boundary/tasks.md

Lines changed: 0 additions & 14 deletions
This file was deleted.

compat-test/compat_test.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,8 @@ def parse_args():
889889
conf.add_argument("--conformance", metavar="JAR",
890890
help="run one robot jar on one engine and print the result, "
891891
"including each participant's console output, as JSON")
892+
conf.add_argument("--conformance-source", type=Path,
893+
help="compile this bridge-owned robot source against classic before running it")
892894
conf.add_argument("--engine", choices=("rc", "tr"), default="rc",
893895
help="which engine --conformance drives")
894896
conf.add_argument("--robot-class",
@@ -1053,6 +1055,24 @@ def compile_trace_robot(opts):
10531055
return out_dir, None
10541056

10551057

1058+
def compile_conformance_robot(opts, source: Path):
1059+
"""Compiles a bridge-owned conformance probe against the classic API."""
1060+
out_dir = WORK_DIR / "conformance-probe-classes"
1061+
clean_dir(out_dir)
1062+
if not source.exists():
1063+
return None, f"conformance probe source not found: {source}"
1064+
1065+
classpath = str(Path(opts.robocode_home) / "libs" / "*")
1066+
cmd = [javac_beside(opts.rc_java_exe), "-cp", classpath, "-d", str(out_dir), str(source)]
1067+
try:
1068+
completed = subprocess.run(cmd, capture_output=True, text=True, timeout=180)
1069+
except (OSError, subprocess.SubprocessError) as e:
1070+
return None, f"could not compile conformance probe: {e}"
1071+
if completed.returncode != 0:
1072+
return None, f"conformance probe did not compile:\n{completed.stdout}\n{completed.stderr}"
1073+
return out_dir, None
1074+
1075+
10561076
def javac_beside(java_exe):
10571077
"""The javac that ships next to a given java executable.
10581078
@@ -1213,7 +1233,17 @@ def run_conformance(opts):
12131233
if opts.participants is not None:
12141234
setup["participants"] = opts.participants
12151235

1216-
if jar.is_dir():
1236+
if opts.conformance_source:
1237+
class_dir, error = compile_conformance_robot(opts, opts.conformance_source)
1238+
if error:
1239+
print(json.dumps({"ok": False, "fatal": error}))
1240+
return 2
1241+
packaged, error = package_test_robot_jar(class_dir, classname, WORK_DIR / "conformance")
1242+
if error:
1243+
print(json.dumps({"ok": False, "fatal": error}))
1244+
return 2
1245+
jar, version = packaged, "1.0"
1246+
elif jar.is_dir():
12171247
# Package the one robot under test, for both engines.
12181248
#
12191249
# The bridge side needs a real robot jar because the wrapper finds robots by their
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package conformance.probes;
2+
3+
import robocode.DeathEvent;
4+
import robocode.Robot;
5+
import robocode.RobotDeathEvent;
6+
import robocode.ScannedRobotEvent;
7+
8+
/** A two-instance probe that reports its own and another robot's death handlers. */
9+
public class DeathEventProbe extends Robot {
10+
11+
@Override
12+
public void run() {
13+
while (true) {
14+
ahead(100);
15+
turnGunRight(360);
16+
back(100);
17+
turnGunRight(360);
18+
}
19+
}
20+
21+
@Override
22+
public void onScannedRobot(ScannedRobotEvent event) {
23+
fire(2);
24+
}
25+
26+
@Override
27+
public void onRobotDeath(RobotDeathEvent event) {
28+
out.println("OtherDeath!");
29+
}
30+
31+
@Override
32+
public void onDeath(DeathEvent event) {
33+
out.println("OwnDeath!");
34+
}
35+
}

conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceHarness.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ private ConformanceHarness(String python, Path robocodeHome, Path testRobotClass
3939
this.rounds = rounds;
4040
}
4141

42+
/** Repository root, for bridge-owned probe sources passed to the Python harness. */
43+
static Path repoRoot() {
44+
return REPO_ROOT;
45+
}
46+
4247
/** The number of rounds every battle this harness runs is configured for. */
4348
int rounds() {
4449
return rounds;
@@ -89,7 +94,7 @@ static String missingEnvironment() {
8994
*
9095
* @param robotClass fully qualified, e.g. {@code tested.robots.InteruptibleEvent}
9196
*/
92-
BattleOutcome run(Engine engine, String robotClass) {
97+
BattleOutcome run(Engine engine, String robotClass, Path source) {
9398
List<String> command = new ArrayList<>(List.of(
9499
python,
95100
HARNESS.toString(),
@@ -98,6 +103,10 @@ BattleOutcome run(Engine engine, String robotClass) {
98103
"--engine", engine.harnessName(),
99104
"--rounds", String.valueOf(rounds),
100105
"--robocode-home", robocodeHome.toString()));
106+
if (source != null) {
107+
command.add("--conformance-source");
108+
command.add(source.toString());
109+
}
101110

102111
try {
103112
Process process = new ProcessBuilder(command)

conformance-test/src/test/java/dev/robocode/tankroyale/bridge/conformance/ConformanceTestBase.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.util.HashMap;
66
import java.util.Map;
7+
import java.nio.file.Path;
78

89
import static org.junit.jupiter.api.Assertions.assertTrue;
910
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@@ -43,8 +44,13 @@ interface Expectation {
4344
* Runs the robot on both engines and applies the same expectation to each.
4445
*/
4546
void assertOnBothEngines(String robotClass, Expectation expectation) {
47+
assertOnBothEngines(robotClass, null, expectation);
48+
}
49+
50+
/** Runs a locally held probe source on both engines after compiling it against classic. */
51+
void assertOnBothEngines(String robotClass, Path source, Expectation expectation) {
4652
for (Engine engine : Engine.values()) {
47-
BattleOutcome outcome = outcomeFor(engine, robotClass);
53+
BattleOutcome outcome = outcomeFor(engine, robotClass, source);
4854
assertTrue(outcome.completed(),
4955
() -> "the battle did not complete on " + engine + " (" + outcome.summary() + ")");
5056
expectation.check(outcome, engine);
@@ -59,8 +65,12 @@ void assertOnBothEngines(String robotClass, Expectation expectation) {
5965
* reason that has nothing to do with what it claims to check.
6066
*/
6167
BattleOutcome outcomeFor(Engine engine, String robotClass) {
68+
return outcomeFor(engine, robotClass, null);
69+
}
70+
71+
private BattleOutcome outcomeFor(Engine engine, String robotClass, Path source) {
6272
return ran.computeIfAbsent(engine.name() + " " + robotClass,
63-
key -> harness.run(engine, robotClass));
73+
key -> harness.run(engine, robotClass, source));
6474
}
6575

6676
/** The number of rounds every battle in this run is configured for. */
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package dev.robocode.tankroyale.bridge.conformance;
2+
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.nio.file.Path;
7+
8+
import static org.junit.jupiter.api.Assertions.assertTrue;
9+
10+
/** Acceptance evidence for EVT-014 — a survivor receives another robot's death. */
11+
class RobotDeathEventsConformanceTest extends ConformanceTestBase {
12+
13+
private static final String ROBOT = "conformance.probes.DeathEventProbe";
14+
private static final Path SOURCE = ConformanceHarness.repoRoot().resolve(Path.of("compat-test",
15+
"conformance-robots", "conformance", "probes", "DeathEventProbe.java"));
16+
private static final String OTHER_DEATH = "OtherDeath!";
17+
18+
@Test
19+
@DisplayName("EVT-014: a surviving robot reports another robot's death on both engines")
20+
void testEVT014_IntegrationPositive_SurvivorReceivesAnotherRobotsDeath() {
21+
assertOnBothEngines(ROBOT, SOURCE, (outcome, engine) ->
22+
assertTrue(outcome.anyConsoleContains(OTHER_DEATH),
23+
() -> "no survivor reported another robot's death on " + engine + " ("
24+
+ outcome.summary() + ")"));
25+
}
26+
}

docs/capabilities/CAP-001-event-dispatch-parity/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ The physics the events describe. That a `ScannedRobotEvent` arrives at the right
3535

3636
`draft`. The redesign that routed events through the Bot API's own event queue is implemented and believed correct, but it was verified by running battles and reading scores. Every criterion here is unproven in the sense that matters: nothing would tell us if it broke again. `M-001` is the plan door.
3737

38-
The conformance tier now reaches some of them. `EVT-004`, `EVT-011`, `EVT-012`, and `EVT-013` are active — the tests that already proved them were retagged after [`G-002`](../../goals/G-002-conformance-evidence-proves-the-criterion-it-names.md) found them mistagged, and `EVT-003` retired rather than be credited with evidence no available robot can produce ([`IDR-003`](../../decisions/IDR-003-evt-003-scoped-to-what-classic-actually-proves.md)). `EVT-004` is now proven with a locally built matched Tank Royale Bot API and runner pair under [`PDR-002`](../../decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md), which contains the server repair [`AN-009`](../../analysis/AN-009-the-server-never-sends-a-death-to-any-bot.md) identified. The capability still holds at `draft` because most criteria remain unproven, including `EVT-007`, which is blocked by the same original defect until the ported RobotDeathEvents evidence is wired.
38+
The conformance tier now reaches some of them. `EVT-004`, `EVT-011`, `EVT-012`, `EVT-013`, and `EVT-014` are active — the tests that already proved them were retagged after [`G-002`](../../goals/G-002-conformance-evidence-proves-the-criterion-it-names.md) found them mistagged, and `EVT-003`/`EVT-007` retired rather than be credited with evidence they cannot honestly claim ([`IDR-003`](../../decisions/IDR-003-evt-003-scoped-to-what-classic-actually-proves.md), [`IDR-004`](../../decisions/IDR-004-evt-007-scoped-to-observable-survivor-delivery.md)). `EVT-004` and `EVT-014` are proven with a locally built matched Tank Royale Bot API and runner pair under [`PDR-002`](../../decisions/PDR-002-locally-built-tank-royale-artifacts-for-conformance.md), which contains the server repair [`AN-009`](../../analysis/AN-009-the-server-never-sends-a-death-to-any-bot.md) identified. The capability still holds at `draft` because most criteria remain unproven.

0 commit comments

Comments
 (0)