diff --git a/java/src/s2/GenericFunc.java b/java/src/s2/GenericFunc.java new file mode 100644 index 0000000..fdf5950 --- /dev/null +++ b/java/src/s2/GenericFunc.java @@ -0,0 +1,8 @@ +package s2; + +import battlecode.common.*; + +@FunctionalInterface +public interface GenericFunc { + void p() throws GameActionException; +} \ No newline at end of file diff --git a/java/src/s2/GenericRobotContoller.java b/java/src/s2/GenericRobotContoller.java new file mode 100644 index 0000000..cf32e51 --- /dev/null +++ b/java/src/s2/GenericRobotContoller.java @@ -0,0 +1,7 @@ +package s2; + +import battlecode.common.*; + +public interface GenericRobotContoller { + void run() throws GameActionException; +} diff --git a/java/src/s2/Mopper.java b/java/src/s2/Mopper.java new file mode 100644 index 0000000..7e1fbda --- /dev/null +++ b/java/src/s2/Mopper.java @@ -0,0 +1,90 @@ +package s2; + +import battlecode.common.*; + +public class Mopper implements GenericRobotContoller { + + RobotController rc; + Pathing pathing_engine; + + public Mopper(RobotController handler) throws GameActionException{ + rc = handler; + pathing_engine = new Pathing(handler); + } + + public void run() throws GameActionException{ + System.out.println("Starting mopper logic..."); + + // Define all possible directions + Direction[] directions = Direction.values(); + + // Initialize variables to track the best direction + Direction bestDirection = null; + int maxEnemiesInDirection = 0; + + // Get the mopper's current location + MapLocation curLoc = rc.getLocation(); + + // Scan for all enemies within the circle of radius 2 * sqrt(2) + RobotInfo[] nearbyEnemies = rc.senseNearbyRobots(curLoc, 8, rc.getTeam().opponent()); + + // Count enemies in each direction based on their relative position + for (Direction dir : directions) { + int enemyCount = 0; + + for (RobotInfo enemy : nearbyEnemies) { + MapLocation enemyLoc = enemy.getLocation(); + + // Check if the enemy lies in the swing range for the current direction + if (isInSwingRange(curLoc, enemyLoc, dir)) { + enemyCount++; + } + } + + System.out.println("Direction: " + dir + ", Enemies: " + enemyCount); + + // Update the best direction if this one has more enemies + if (enemyCount > maxEnemiesInDirection) { + maxEnemiesInDirection = enemyCount; + bestDirection = dir; + } + } + + // Perform the mop swing in the best direction if enemies are found + if (bestDirection != null && maxEnemiesInDirection > 0) { + + rc.mopSwing(bestDirection); + } else { + + } + + // If no enemies to mop swing, move randomly + pathing_engine.Move(); + + // Try to paint beneath us as we walk to avoid paint penalties + MapInfo currentTile = rc.senseMapInfo(rc.getLocation()); + if (!currentTile.getPaint().isAlly() && rc.canAttack(rc.getLocation())) { + rc.attack(rc.getLocation()); + } + } + + private boolean isInSwingRange(MapLocation mopperLoc, MapLocation targetLoc, Direction swingDir) { + // Get the relative position of the target + int dx = targetLoc.x - mopperLoc.x; + int dy = targetLoc.y - mopperLoc.y; + + // Check based on direction and relative positions + switch (swingDir) { + case NORTH: + return dx >= -1 && dx <= 1 && dy < 0 && dy >= -2; + case SOUTH: + return dx >= -1 && dx <= 1 && dy > 0 && dy <= 2; + case EAST: + return dy >= -1 && dy <= 1 && dx > 0 && dx <= 2; + case WEST: + return dy >= -1 && dy <= 1 && dx < 0 && dx >= -2; + default: + return false; + } + } +} diff --git a/java/src/s2/Pathing.java b/java/src/s2/Pathing.java new file mode 100644 index 0000000..a132abc --- /dev/null +++ b/java/src/s2/Pathing.java @@ -0,0 +1,130 @@ +package s2; + +import battlecode.common.*; +import java.util.Random; + +public class Pathing { + static final Direction[] directions = { + Direction.NORTH, + Direction.NORTHEAST, + Direction.EAST, + Direction.SOUTHEAST, + Direction.SOUTH, + Direction.SOUTHWEST, + Direction.WEST, + Direction.NORTHWEST, + }; + + GenericFunc diffuse = Pathing::diffuse1; + + static final Random rng = new Random(6147); + + static int robot_dir_idx = -1; //also technically velocity + + static RobotController rc; + + static private int modulo(int x, int y) { + int temp = Math.floorDiv(x,y); + return x-temp*y; + } + + public Pathing(RobotController handler) throws GameActionException{ + rc = handler; + } + + public void Move() throws GameActionException{ + diffuse = Pathing::diffuse1; + int bias = rng.nextInt(5); + if (bias == 3) { + diffuse = Pathing::diffuse2; + } + check_dir(); + diffuse.p(); + } + + private void check_dir() { + if (robot_dir_idx == -1) { + robot_dir_idx = rng.nextInt(8); + } + } + + private static void diffuse2() throws GameActionException{ + MapLocation current_location = rc.getLocation(); + boolean CurrIsAlly = rc.senseMapInfo(current_location).getPaint().isAlly(); + for (int i = 0; i < 6; i++) { + Direction goal_dir = directions[robot_dir_idx]; + if (rc.canMove(goal_dir) ){ + boolean NextIsAlly = rc.senseMapInfo(current_location.add(goal_dir)).getPaint().isAlly(); + if (!(!CurrIsAlly && NextIsAlly)) { + rc.move(goal_dir); + return; + } + } + int adj1 = modulo(robot_dir_idx + 1, 8); + int adj2 = modulo(robot_dir_idx - 1, 8); + boolean adj1_canMove = rc.canMove(directions[adj1]); + boolean adj2_canMove = rc.canMove(directions[adj2]); + if (robot_dir_idx % 2 == 0 || (!adj1_canMove && !adj2_canMove)) { + robot_dir_idx = modulo(robot_dir_idx + 3 + rng.nextInt(3), 8); + } else { + if (adj1_canMove) { + robot_dir_idx = adj1; + } else { // no need for 2nd check because if both fail then it would have gone into the + // previous if statement + robot_dir_idx = adj2; + } + } + } + // if stuck in hole + for (int i = 0; i < 10; i++) { + robot_dir_idx = rng.nextInt(8); + Direction goal_dir = directions[robot_dir_idx]; + if (rc.canMove(goal_dir)) { + rc.move(goal_dir); + } + } + } + + private static void diffuse1() throws GameActionException{ + MapInfo[] surrounding = rc.senseNearbyMapInfos(2); + int surround_count = 0; + for (MapInfo mapInfo : surrounding) { + if (mapInfo.getPaint().isAlly()) { + surround_count++; + } + } + for (int i = 0; i < 8; i++) { + Direction goal_dir = directions[robot_dir_idx]; + if (rc.canMove(goal_dir)) { + rc.move(goal_dir); + break; + } + int adj1 = modulo(robot_dir_idx + 1, 8); + int adj2 = modulo(robot_dir_idx - 1, 8); + boolean adj1_canMove = rc.canMove(directions[adj1]); + boolean adj2_canMove = rc.canMove(directions[adj2]); + if (robot_dir_idx % 2 == 0 || (!adj1_canMove && !adj2_canMove)) { + robot_dir_idx = modulo(robot_dir_idx + 3 + rng.nextInt(3), 8); + } else { + if (adj1_canMove) { + robot_dir_idx = adj1; + } else { // no need for 2nd check because if both fail then it would have gone into the + // previous if statement + robot_dir_idx = adj2; + } + } + boolean next_is_ally = rc.canMove(directions[robot_dir_idx]) && rc.senseMapInfo(rc.getLocation().add(directions[robot_dir_idx])).getPaint().isAlly(); + boolean is_surrounded = surround_count > 3; + if (next_is_ally && !is_surrounded) { + int shift = rng.nextInt(7)+1; + int next = modulo( robot_dir_idx + shift ,8); + if (next == robot_dir_idx) { + next++; + } + robot_dir_idx = next; + } + } + } + + +} diff --git a/java/src/s2/RobotPlayer.java b/java/src/s2/RobotPlayer.java new file mode 100644 index 0000000..134b63b --- /dev/null +++ b/java/src/s2/RobotPlayer.java @@ -0,0 +1,41 @@ +package s2; + +import battlecode.common.*; +public class RobotPlayer { + public static void run(RobotController rc) throws GameActionException { + GenericRobotContoller processor; + switch (rc.getType()) { + case SOLDIER: + processor = new Solider(rc); + break; + case MOPPER: + processor = new Mopper(rc); + break; + case SPLASHER: + processor = new Splasher(rc) ; + break; // Consider upgrading examplefuncsplayer to use splashers! + default: + processor = new Tower(rc); + break; + } + + while (true) { + try { + processor.run(); + } catch (GameActionException e) { + System.out.println("GameActionException"); + e.printStackTrace(); + } catch (Exception e) { + System.out.println("Exception"); + e.printStackTrace(); + + + } finally { + // Signify we've done everything we want to do, thereby ending our turn. + // This will make our code wait until the next turn, and then perform this loop + // again. + Clock.yield(); + } + } + } +} diff --git a/java/src/s2/Solider.java b/java/src/s2/Solider.java new file mode 100644 index 0000000..c094d77 --- /dev/null +++ b/java/src/s2/Solider.java @@ -0,0 +1,187 @@ +package s2; + +import battlecode.common.*; + +public class Solider implements GenericRobotContoller { + boolean isBuildingRuin = false; + boolean isBuildingSRP = false; + boolean SRP_built = false; + int cant_find_tower_for = 0; + boolean[][] SRP_pattern; + RobotController rc; + Pathing pathing_engine; + boolean buildPaintTowerNext = false; + + public Solider(RobotController handler) throws GameActionException { + rc = handler; + pathing_engine = new Pathing(handler); + SRP_pattern = rc.getResourcePattern(); + } + + public void run() throws GameActionException { + if (SRP_built == false && cant_find_tower_for > 40) { + boolean early_exit = false; + if (!rc.canMarkResourcePattern(rc.getLocation())) { + early_exit = true; + } + if (rc.getChips() < 700) { + early_exit = true; + } + if (!isBuildingSRP && early_exit == false) { + MapInfo[] info = rc.senseNearbyMapInfos(); + for (MapInfo mapInfo : info) { + if (mapInfo.isResourcePatternCenter()) { + early_exit = true; + break; + } + + // Locations within SRP range + boolean noRobot = rc.senseRobotAtLocation(mapInfo.getMapLocation()) == null; + boolean in_range = mapInfo.getMapLocation().isWithinDistanceSquared(rc.getLocation(), 1); + if (in_range) { + // boolean isPaintedbyAlly =mapInfo.getPaint().isAlly(); + boolean hasTower = mapInfo.hasRuin() && !noRobot; + if (mapInfo.isWall() || hasTower) { + early_exit = true; + break; + } + } + + boolean noTower = mapInfo.hasRuin() && noRobot; + if (noTower) { + early_exit = true; + break; + } + } + } + if (!early_exit && rc.isActionReady()) { + isBuildingSRP = true; + MapLocation curr_loc = rc.getLocation(); + MapInfo[] key_squares = rc.senseNearbyMapInfos(8); + if (rc.senseMapInfo(rc.getLocation()).getPaint() != PaintType.ALLY_SECONDARY) { + rc.attack(curr_loc, true); + }else{ + for (MapInfo mapInfo : key_squares) { + MapLocation relative_loc = mapInfo.getMapLocation().translate(-curr_loc.x, -curr_loc.y); + rc.setIndicatorDot(curr_loc, 0,0,255); + // System.out.println(relative_loc); + boolean color = SRP_pattern[relative_loc.x+2][-(relative_loc.y-2)]; + PaintType correct_paint = PaintType.ALLY_PRIMARY; + if (color) { + correct_paint = PaintType.ALLY_SECONDARY; + } + if (mapInfo.getPaint() != correct_paint) { + rc.attack(mapInfo.getMapLocation(),color); + break; + } + } + } + if (rc.canCompleteResourcePattern(curr_loc)) { + rc.completeResourcePattern(curr_loc); + rc.setTimelineMarker("Built SRP", 255,0,0); + SRP_built = true; + isBuildingSRP = false; + } + } + } + boolean found = false; + if (!isBuildingSRP) { + found = buildRuins(); + if (!found) { + cant_find_tower_for++; + } + } + // Move and attack randomly if no objective. + if (!found && !isBuildingSRP) { + pathing_engine.Move(); + } + //catch all + if (rc.isActionReady()) { + pathing_engine.Move(); + } + // Try to paint beneath us as we walk to avoid paint penalties. + // Avoiding wasting paint by re-painting our own tiles. + if (!isBuildingSRP) { + MapInfo[] infos = rc.senseNearbyMapInfos(9); + for (MapInfo mapInfo : infos) { + MapLocation T = mapInfo.getMapLocation(); + if (rc.canAttack(T)&& !mapInfo.getPaint().isAlly() && mapInfo.isPassable()) { + rc.attack(T); + break; + } + } + } + } + + + private boolean buildRuins() throws GameActionException { + isBuildingRuin = false; + // Sense information about all visible nearby tiles. + MapInfo[] nearbyTiles = rc.senseNearbyMapInfos(); + // Search for a nearby ruin to complete. + MapInfo curRuin = null; + int curDist = 100000000; + for (MapInfo tile : nearbyTiles) { + // Make sure the ruin is not already complete (has no tower on it) + if (tile.hasRuin() && rc.senseRobotAtLocation(tile.getMapLocation()) == null) { + int checkDist = tile.getMapLocation().distanceSquaredTo(rc.getLocation()); + if (checkDist < curDist) { + curDist = checkDist; + curRuin = tile; + } + } + } + + + if (curRuin != null) { + MapLocation targetLoc = curRuin.getMapLocation(); + Direction dir = rc.getLocation().directionTo(targetLoc); + if (rc.canMove(dir)) + rc.move(dir); + // Mark the pattern we need to draw to build a tower here if we haven't already. + // Decide tower type based on logic + UnitType towerToBuild; + + // If chips exceed 3000, always build paint towers + if (rc.getChips() > 3000) { + towerToBuild = UnitType.LEVEL_ONE_PAINT_TOWER; + } else { + // Alternate between paint and money towers + if (buildPaintTowerNext) { + towerToBuild = UnitType.LEVEL_ONE_PAINT_TOWER; + } else { + towerToBuild = UnitType.LEVEL_ONE_MONEY_TOWER; + } + buildPaintTowerNext = !buildPaintTowerNext; // Toggle for the next tower + } + + MapLocation shouldBeMarked = curRuin.getMapLocation().subtract(dir); + if (rc.senseMapInfo(shouldBeMarked).getMark() == PaintType.EMPTY && rc.canMarkTowerPattern(towerToBuild, targetLoc)) { + rc.markTowerPattern(towerToBuild, targetLoc); + System.out.println("Trying to build a tower at " + targetLoc); + } + // Fill in any spots in the pattern with the appropriate paint. + for (MapInfo patternTile : rc.senseNearbyMapInfos(targetLoc, 8)) { + if (patternTile.getMark() != patternTile.getPaint()) { + if (patternTile.getMark() != PaintType.EMPTY) { + boolean useSecondaryColor = patternTile.getMark() == PaintType.ALLY_SECONDARY; + if (rc.canAttack(patternTile.getMapLocation())) + rc.attack(patternTile.getMapLocation(), useSecondaryColor); + } + } + } + // Complete the ruin if we can. + if (rc.canCompleteTowerPattern(towerToBuild, targetLoc)) { + rc.completeTowerPattern(towerToBuild, targetLoc); + rc.setTimelineMarker("Tower built", 0, 255, 0); + System.out.println("Built a tower at " + targetLoc + "!"); + }else{ + isBuildingRuin = true; + return false; + } + return true; + } + return false; + } + +} diff --git a/java/src/s2/Splasher.java b/java/src/s2/Splasher.java new file mode 100644 index 0000000..fd14716 --- /dev/null +++ b/java/src/s2/Splasher.java @@ -0,0 +1,30 @@ +package s2; + +import battlecode.common.*; + +public class Splasher implements GenericRobotContoller { + RobotController rc; + Pathing pathing_engine; + public Splasher(RobotController handler) throws GameActionException{ + rc = handler; + pathing_engine = new Pathing(handler); + } + + public void run() throws GameActionException{ + // Sense information about all visible nearby tiles. + // Move and attack randomly if no objective. + pathing_engine.Move(); + // Try to paint beneath us as we walk to avoid paint penalties. + // Avoiding wasting paint by re-painting our own tiles. + // MapInfo[] info = rc.senseNearbyMapInfos(3); + // for (MapInfo mapInfo : info) { + // if(mapInfo.getPaint().isAlly()) { + // return; + // } + // } + MapInfo currentTile = rc.senseMapInfo(rc.getLocation()); + if (!currentTile.getPaint().isAlly() && rc.canAttack(rc.getLocation())) { + rc.attack(rc.getLocation()); + } + } +} diff --git a/java/src/s2/Tower.java b/java/src/s2/Tower.java new file mode 100644 index 0000000..66b826a --- /dev/null +++ b/java/src/s2/Tower.java @@ -0,0 +1,131 @@ +package s2; +import java.util.Random; +import battlecode.common.*; + +public class Tower implements GenericRobotContoller { + + final Direction[] directions = { + Direction.NORTH, + Direction.NORTHEAST, + Direction.EAST, + Direction.SOUTHEAST, + Direction.SOUTH, + Direction.SOUTHWEST, + Direction.WEST, + Direction.NORTHWEST, + }; + + final Random rng = new Random(6147); + + RobotController rc; + + int rtype = 0; + + int[] spawn_count = {0,0,0}; + int[] target_count = {3,1,1}; + + public Tower(RobotController handler) throws GameActionException{ + rc = handler; + } + + + public void run() throws GameActionException { + // Check for surplus and spawn soldier if conditions are met + // System.out.println("This is the money: " + rc.getMoney()); + // System.out.println("This is the paint: " + rc.getPaint()); + + // if (rc.getMoney() > 5000 && rc.getPaint() > 300) { + // for (Direction dir : directions) { + // MapLocation spawnLoc = rc.getLocation().add(dir); + // if (rc.canBuildRobot(UnitType.SOLDIER, spawnLoc)) { + // rc.buildRobot(UnitType.SOLDIER, spawnLoc); + // System.out.println("Surplus soldier spawned at: " + spawnLoc); + // break; // Spawn only one soldier + // } + // } + + // Pick a direction to build in. + Direction dir = directions[rng.nextInt(directions.length)]; + MapLocation nextLoc = rc.getLocation().add(dir); + // Pick a random robot type to build. + int robotType = rtype; + if (robotType == 0 && rc.canBuildRobot(UnitType.SOLDIER, nextLoc)) { + rc.buildRobot(UnitType.SOLDIER, nextLoc); + spawn_count[0]++; + } else if (robotType == 1 && rc.canBuildRobot(UnitType.SPLASHER, nextLoc)) { + rc.buildRobot(UnitType.SPLASHER, nextLoc); + spawn_count[1]++; + } else if (robotType == 2 && rc.canBuildRobot(UnitType.MOPPER, nextLoc)) { + rc.buildRobot(UnitType.MOPPER, nextLoc); + spawn_count[2]++; + } + // management of rtype relative to chip count; + if (spawn_count[rtype] >= target_count[rtype]) { + rtype++; + } + if (rtype == 2) { + rtype = 0; + spawn_count[0] = 0; + spawn_count[1] = 0; + spawn_count[2] = 0; + } + int chipCount = rc.getChips(); + if (chipCount > 10_000) { + target_count[0] = 5; + target_count[1] = 2; + } + if (chipCount < 650) { + target_count[0] = 3; + target_count[1] = 1; + // target_count[2] = 1; + } + + // Attack logic for Tower + RobotInfo[] nearbyEnemies = rc.senseNearbyRobots(-1, rc.getTeam().opponent()); + + // Perform Single Target Attack on the lowest HP priority target + if (nearbyEnemies.length > 0) { + RobotInfo target = null; + + // Find the lowest HP priority target + for (RobotInfo enemy : nearbyEnemies) { + if (target == null || enemy.getHealth() < target.getHealth() || + (enemy.getHealth() == target.getHealth() && isHigherPriority(enemy, target))) { + target = enemy; + } + } + + if (target != null && rc.canAttack(target.getLocation())) { + rc.attack(target.getLocation()); + } + } + + // Perform AoE Attack if any enemies are in range + if (rc.canAttack(null)) { + rc.attack(null); + } + + // Read incoming messages + Message[] messages = rc.readMessages(-1); + for (Message m : messages) { + System.out.println("Tower received message: '#" + m.getSenderID() + " " + m.getBytes()); + } + } + + // Helper method to determine if one robot is a higher priority target than another + private boolean isHigherPriority(RobotInfo current, RobotInfo target) { + UnitType currentType = current.getType(); + UnitType targetType = target.getType(); + + // Prioritize splashers first, then soldiers, and lastly moppers + if (currentType == UnitType.SPLASHER && targetType != UnitType.SPLASHER) { + return true; + } else if (currentType == UnitType.SOLDIER && targetType == UnitType.MOPPER) { + return true; + } + + return false; + } + + +}