Skip to content
Open
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
135 changes: 135 additions & 0 deletions engine/src/main/battlecode/world/LiveMap.java
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,9 @@ public void assertIsValid() throws Exception {
throw new RuntimeException("MAP HEIGHT BENEATH GameConstants.MAP_MIN_HEIGHT");
}

// Validate map symmetry
assertSymmetryValid();

int initialBodyCountTeamA = 0;
int initialBodyCountTeamB = 0;

Expand Down Expand Up @@ -510,6 +513,138 @@ public void assertIsValid() throws Exception {
}
}

/**
* Validates that the map's terrain and initial bodies match the declared symmetry.
* Throws on the first error found.
*/
private void assertSymmetryValid() {
MapSymmetry sym = this.symmetry;

// Check terrain arrays
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
MapLocation loc = new MapLocation(x, y);
int curIdx = locationToIndex(loc);

int symX = symmetricX(x, sym);
int symY = symmetricY(y, sym);
MapLocation symLoc = new MapLocation(symX, symY);
int symIdx = locationToIndex(symLoc);

if (wallArray[curIdx] != wallArray[symIdx]) {
throw new RuntimeException("Wall mismatch for " + sym + " symmetry at (" + x + "," + y + ") vs (" + symX + "," + symY + ")");
}
if (dirtArray[curIdx] != dirtArray[symIdx]) {
throw new RuntimeException("Dirt mismatch for " + sym + " symmetry at (" + x + "," + y + ") vs (" + symX + "," + symY + ")");
}
if (cheeseMineArray[curIdx] != cheeseMineArray[symIdx]) {
throw new RuntimeException("Cheese mine mismatch for " + sym + " symmetry at (" + x + "," + y + ") vs (" + symX + "," + symY + ")");
}
if (cheeseArray[curIdx] != cheeseArray[symIdx]) {
throw new RuntimeException("Cheese mismatch for " + sym + " symmetry at (" + x + "," + y + "): " + cheeseArray[curIdx] + " vs " + cheeseArray[symIdx]);
}
}
}

// Check initial bodies (rat kings and cats)
Map<MapLocation, RobotInfo> robotsByLoc = new HashMap<>();
for (RobotInfo r : initialBodies) {
robotsByLoc.put(r.location, r);
}

for (RobotInfo robot : initialBodies) {
// Account for unit size when computing symmetric location
int unitSize = robot.type.getSize();
MapLocation symLoc = new MapLocation(
symmetricX(robot.location.x, sym, unitSize),
symmetricY(robot.location.y, sym, unitSize)
);
RobotInfo symRobot = robotsByLoc.get(symLoc);

if (symRobot == null) {
throw new RuntimeException("No robot at symmetric location " + symLoc + " for " + robot.type + " at " + robot.location);
}
if (robot.type != symRobot.type) {
throw new RuntimeException("Robot type mismatch: " + robot.type + " at " + robot.location + " vs " + symRobot.type + " at " + symLoc);
}
if (!areSymmetricTeams(robot.team, symRobot.team)) {
throw new RuntimeException("Robot team mismatch: " + robot.team + " at " + robot.location + " vs " + symRobot.team + " at " + symLoc);
}
}
}

/**
* Helper method to compute symmetric x coordinate for a given symmetry type.
* For terrain tiles (size 1).
*/
private int symmetricX(int x, MapSymmetry symmetry) {
return symmetricX(x, symmetry, 1);
}

/**
* Helper method to compute symmetric y coordinate for a given symmetry type.
* For terrain tiles (size 1).
*/
private int symmetricY(int y, MapSymmetry symmetry) {
return symmetricY(y, symmetry, 1);
}

/**
* Helper method to compute symmetric x coordinate for a given symmetry type,
* accounting for unit size.
* - Odd-sized units (RAT_KING=3): location is center, use standard formula
* - Even-sized units (CAT=2): location is bottom-left corner, offset by (size-1)
*/
private int symmetricX(int x, MapSymmetry symmetry, int unitSize) {
switch (symmetry) {
case HORIZONTAL:
return x;
case VERTICAL:
case ROTATIONAL:
if (unitSize % 2 == 0) {
// Even size: location is bottom-left corner, need offset
return width - unitSize - x;
} else {
// Odd size: location is center, standard formula
return width - 1 - x;
}
default:
throw new IllegalArgumentException("Unknown symmetry type: " + symmetry);
}
}

/**
* Helper method to compute symmetric y coordinate for a given symmetry type,
* accounting for unit size.
*/
private int symmetricY(int y, MapSymmetry symmetry, int unitSize) {
switch (symmetry) {
case VERTICAL:
return y;
case HORIZONTAL:
case ROTATIONAL:
if (unitSize % 2 == 0) {
// Even size: location is bottom-left corner, need offset
return height - unitSize - y;
} else {
// Odd size: location is center, standard formula
return height - 1 - y;
}
default:
throw new IllegalArgumentException("Unknown symmetry type: " + symmetry);
}
}

/**
* Helper method to check if two teams are symmetric to each other.
* Rat kings: A <-> B, Cats: NEUTRAL <-> NEUTRAL
*/
private boolean areSymmetricTeams(Team a, Team b) {
if (a == Team.A) return b == Team.B;
if (a == Team.B) return b == Team.A;
return a == Team.NEUTRAL && b == Team.NEUTRAL;
}

// private boolean isTeamNumber(int team) {
// return team == 1 || team == 2;
// }
Expand Down
22 changes: 19 additions & 3 deletions engine/src/main/battlecode/world/MapBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,25 @@ private int locationToIndex(int x, int y) {
// return loc.x + loc.y * width;
// }

// public void setWall(int x, int y, boolean value) {
// this.wallArray[locationToIndex(x, y)] = value;
// }
public void setWall(int x, int y, boolean value) {
this.wallArray[locationToIndex(x, y)] = value;
}

public void setDirt(int x, int y, boolean value) {
this.dirtArray[locationToIndex(x, y)] = value;
}

public void setCheeseMine(int x, int y, boolean value) {
this.cheeseMineArray[locationToIndex(x, y)] = value;
}

public void setCheese(int x, int y, int value) {
this.cheeseArray[locationToIndex(x, y)] = value;
}

public void addBody(RobotInfo body) {
this.bodies.add(body);
}

// public void setCloud(int x, int y, boolean value) {
// this.cloudArray[locationToIndex(x, y)] = value;
Expand Down
Loading