Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .clue/id-ledger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,10 @@ events:
- {id: AN-705, kind: numeric, state: reserved, prefix: AN, component: "705"}
- {id: AN-704, kind: numeric, state: live, prefix: AN, component: "704"}
- {id: AN-705, kind: numeric, state: live, prefix: AN, component: "705"}
- {id: CH-020, kind: numeric, state: reserved, prefix: CH, component: "20"}
- {id: ROUTE-013, kind: numeric, state: reserved, prefix: ROUTE, component: "13"}
- {id: ROUTE-013, kind: numeric, state: live, prefix: ROUTE, component: "13"}
- {id: CH-020, kind: numeric, state: live, prefix: CH, component: "20"}
high-water:
- {id: ADR-002, kind: numeric, state: reserved, prefix: ADR, component: "2"}
- {id: AN-014, kind: numeric, state: reserved, prefix: AN, component: "14"}
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased

- Route legacy `Robot.stop(boolean)` overwrite calls to the matching Tank Royale API, preserving saved movement when the robot calls `stop(true)`.
- Truncate legacy robot and team author metadata to the Bot API's 50-character limit so long author names do not prevent wrapped bots from starting.
- Trim legacy robot and team author metadata in generated bot JSON so it matches the Bot API's canonical author identity and does not block bot startup.
- Copy a robot's bundled data file when `getDataFile()` resolves it, and create an empty file when no bundled resource exists, matching Classic's file initialization behavior.
Expand Down
388 changes: 386 additions & 2 deletions compat-test/parity-registry.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions compat-test/parity-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ This generated table is the reviewed index of the append-only observations in `p
| Status | Subjects |
|---|---:|
| CONFIRMED (score) | 233 |
| DISCREPANCY (errors) | 114 |
| DISCREPANCY (errors) | 115 |
| DISCREPANCY (no score) | 9 |
| DISCREPANCY (outcome) | 67 |
| DISCREPANCY (outcome) | 66 |
| MATCHED (failure) | 49 |
| MATCHED (score noise) | 139 |
| PASS | 712 |
Expand Down Expand Up @@ -168,7 +168,7 @@ This generated table is the reviewed index of the append-only observations in `p
| meleerumble/jab.DiamondStealer_5.jar | meleerumble | robot | DISCREPANCY (errors) | fae68a2757f1e40a | melee-opponent-pool-contamination | harness |
| meleerumble/jangs.ns51_1.0.jar | meleerumble | robot | DISCREPANCY (errors) | f6f5b19abc44f9b5 | melee-opponent-pool-contamination | harness |
| meleerumble/jaybot.adv.bots.JayBot_2.0.jar | meleerumble | robot | DISCREPANCY (errors) | 737e8e86d63ba6d5 | melee-opponent-pool-contamination | harness |
| meleerumble/jaybot.bots.Oddball_4.0.jar | meleerumble | robot | DISCREPANCY (outcome) | e15d719a76e461c3 | classic-stop-overwrite-explicitly-rejected-by-bridge | bridge |
| meleerumble/jaybot.bots.Oddball_4.0.jar | meleerumble | robot | DISCREPANCY (errors) | 50ea318f51f84718 | melee-opponent-pool-contamination | harness |
| meleerumble/jcs.Seth_1.8.jar | meleerumble | robot | DISCREPANCY (errors) | 9b9138a2f5fc680b | melee-opponent-pool-contamination | harness |
| meleerumble/jdw.Hurricane_1.4.jar | meleerumble | robot | DISCREPANCY (errors) | 0c2ae0cbac2e6851 | melee-opponent-pool-contamination | harness |
| meleerumble/jekl.StoneGhost_0.7.jar | meleerumble | robot | DISCREPANCY (errors) | 8de300b1a4e97d53 | melee-opponent-pool-contamination | harness |
Expand Down
10 changes: 9 additions & 1 deletion docs/capabilities/CAP-008-call-routing-fidelity/criteria.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,22 @@ Feature: Bot API call-routing fidelity
Then each value comes from the corresponding Bot API value, converted into Robocode's frame
And no getter returns a neighbouring component's value

@ROUTE-005
@ROUTE-005 @retired
Scenario: Stopping, resuming, and the independence flags route individually
Test-type: Unit
Given a robot that stops, resumes, or sets one independence flag
When the call is routed
Then it reaches that call alone, carrying its argument rather than an assumed value
And an unsupported variant is refused rather than silently downgraded

@ROUTE-013
Scenario: Stopping, resuming, overwrite, and independence flags route individually
Test-type: Unit
Given a robot that stops with an overwrite flag, resumes, or sets one independence flag
When the call is routed
Then it reaches the matching Bot API call alone, carrying its argument rather than an assumed value
And stop(overwrite) reaches IBot.stop(overwrite) with the boolean preserved

@ROUTE-006
Scenario: A queued command is queued, not executed
Test-type: Unit
Expand Down
2 changes: 2 additions & 0 deletions docs/capabilities/CAP-008-call-routing-fidelity/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Not "the call succeeded". Each names the Bot API method reached and the argument

That negative half is doing most of the work. A method that routes to the wrong neighbour still compiles, still runs, and still produces a battle; only an assertion that the neighbour was untouched distinguishes it.

`Robot.stop(boolean)` preserves its overwrite flag by calling the matching `IBot.stop(boolean)` overload. The routing tests check the `true` flag reaches that overload and that no neighbouring stop or resume call is made.

## Completeness, and why it is enforced rather than reviewed

`PeerSurfaceCoverageTest` walks the peer interfaces reflectively from `ITeamRobotPeer` and `IJuniorRobotPeer`, and fails naming any method no routing test calls.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -806,13 +806,7 @@ public void rescan() {
@Override
public void stop(boolean overwrite) {
log("stop()");
if (overwrite) {
// flemming-n-larsen: I don't expect any bots to use this functionality, and hence it is not supported (yet)
// in Robocode Tank Royale.
throw new UnsupportedOperationException(
"stop(overwrite=true) is unsupported. Contact Robocode Tank Royale author for support");
}
bot.stop();
bot.stop(overwrite);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
import static java.lang.Math.toRadians;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
* Acceptance evidence for ROUTE-005 — the {@code IStandardRobotPeer} surface: stopping,
* Acceptance evidence for ROUTE-013 — the {@code IStandardRobotPeer} surface: stopping,
* resuming, turning the radar, and the three independence flags.
*
* The flags are the interesting part. Each decouples one component from another's rotation,
Expand All @@ -33,37 +32,46 @@ void setUp() {
}

@Test
@DisplayName("ROUTE-005 positive: stopping and resuming route to their Bot API calls")
void testROUTE005_UnitPositive_RoutesStopAndResume() {
@DisplayName("ROUTE-013 positive: stopping and resuming route to their Bot API calls")
void testROUTE013_UnitPositive_RoutesStopAndResume() {
peer.stop(false);
assertTrue(bot.called("stop"), bot.names());
assertEquals(Boolean.FALSE, bot.onlyCall("stop").args[0]);

bot.clear();
peer.resume();
assertTrue(bot.called("resume"), bot.names());
}

@Test
@DisplayName("ROUTE-005 negative: stop with overwrite is refused rather than silently downgraded")
void testROUTE005_UnitNegative_RefusesStopWithOverwriteRatherThanIgnoringIt() {
// Tank Royale has no equivalent. Refusing is the honest answer: quietly treating it as
// an ordinary stop would discard the robot's saved movement state, and the robot would
// resume into a different action than the one it stopped.
assertThrows(UnsupportedOperationException.class, () -> peer.stop(true));
assertFalse(bot.called("stop"), "the unsupported call must not reach the bot: " + bot.names());
@DisplayName("ROUTE-013 positive: stop overwrite reaches the Bot API with its argument")
void testROUTE013_UnitPositive_RoutesStopOverwriteArgument() {
peer.stop(true);

assertEquals(Boolean.TRUE, bot.onlyCall("stop").args[0]);
}

@Test
@DisplayName("ROUTE-013 negative: stop overwrite does not call a neighboring Bot API method")
void testROUTE013_UnitNegative_DoesNotCallAnotherStopAction() {
peer.stop(true);

assertEquals(1, bot.calls().size(), bot.names());
assertEquals("stop", bot.calls().get(0).name);
assertEquals(Boolean.TRUE, bot.calls().get(0).args[0]);
}

@Test
@DisplayName("ROUTE-005 positive: turning the radar reaches the radar call in degrees")
void testROUTE005_UnitPositive_RoutesRadarTurnInDegrees() {
@DisplayName("ROUTE-013 positive: turning the radar reaches the radar call in degrees")
void testROUTE013_UnitPositive_RoutesRadarTurnInDegrees() {
peer.turnRadar(toRadians(120));

assertEquals(120.0, bot.onlyCall("turnRadarRight").doubleArg(0), EPSILON);
}

@Test
@DisplayName("ROUTE-005 positive: each independence flag reaches its own Bot API setter")
void testROUTE005_UnitPositive_RoutesEachIndependenceFlagToItsOwnSetter() {
@DisplayName("ROUTE-013 positive: each independence flag reaches its own Bot API setter")
void testROUTE013_UnitPositive_RoutesEachIndependenceFlagToItsOwnSetter() {
peer.setAdjustGunForBodyTurn(true);
assertEquals(true, bot.onlyCall("setAdjustGunForBodyTurn").args[0]);

Expand All @@ -77,17 +85,17 @@ void testROUTE005_UnitPositive_RoutesEachIndependenceFlagToItsOwnSetter() {
}

@Test
@DisplayName("ROUTE-005 negative: setting one independence flag sets no other")
void testROUTE005_UnitNegative_SettingOneIndependenceFlagLeavesTheOthersAlone() {
@DisplayName("ROUTE-013 negative: setting one independence flag sets no other")
void testROUTE013_UnitNegative_SettingOneIndependenceFlagLeavesTheOthersAlone() {
peer.setAdjustRadarForGunTurn(true);

assertFalse(bot.called("setAdjustGunForBodyTurn"), bot.names());
assertFalse(bot.called("setAdjustRadarForBodyTurn"), bot.names());
}

@Test
@DisplayName("ROUTE-005 positive: the flag value is passed through rather than assumed true")
void testROUTE005_UnitPositive_PassesTheFlagValueThroughRatherThanAssumingTrue() {
@DisplayName("ROUTE-013 positive: the flag value is passed through rather than assumed true")
void testROUTE013_UnitPositive_PassesTheFlagValueThroughRatherThanAssumingTrue() {
// A routing that ignored its argument would pass every test that only ever sets true,
// and would leave a robot unable to turn the independence off again.
peer.setAdjustGunForBodyTurn(false);
Expand All @@ -96,8 +104,8 @@ void testROUTE005_UnitPositive_PassesTheFlagValueThroughRatherThanAssumingTrue()
}

@Test
@DisplayName("ROUTE-005 positive: reading an independence flag reads the matching Bot API flag")
void testROUTE005_UnitPositive_ReadsEachIndependenceFlagFromItsOwnSource() {
@DisplayName("ROUTE-013 positive: reading an independence flag reads the matching Bot API flag")
void testROUTE013_UnitPositive_ReadsEachIndependenceFlagFromItsOwnSource() {
bot.returning("isAdjustGunForBodyTurn", true)
.returning("isAdjustRadarForGunTurn", false)
.returning("isAdjustRadarForBodyTurn", true);
Expand Down
Loading