From 78f7cbb98c4f56d3694c39c9f6f31c0e141a0856 Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 10:34:30 -0400 Subject: [PATCH 01/10] Start training + kill pacman if no longer collect anything for 10 seconds --- .../entity/behavior/NeatPacmanBehavior.java | 16 ++++++++++++++++ .../pacman/lwjgl3/Lwjgl3Launcher.java | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java index d3fd62f..63d896e 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java @@ -21,6 +21,9 @@ public class NeatPacmanBehavior implements Behavior { // specific pools of points instead of subtracting from all. private int scoreModifier = 0; + private int numberUpdatesSincelastscone = 0; + private int lastScore = 0; + public NeatPacmanBehavior(@NotNull Client client) { this.client = client; } @@ -42,6 +45,19 @@ public Direction getDirection(@NotNull Entity entity) { // TODO: Make changes here to help with your training... // END OF SPECIAL TRAINING CONDITIONS + // If pacman got stuck (not collecting anything anymore), kill it + int newScore = pacman.getMaze().getLevelManager().getScore(); + if (newScore > lastScore) { + lastScore = newScore; + numberUpdatesSincelastscone = 0; + } + + // 60 updates per seconds * 10 seconds + if (numberUpdatesSincelastscone++ > 60 * 10) { + pacman.kill(); + return Direction.UP; + } + // We are going to use these directions a lot for different inputs. Get them all once for clarity and brevity Direction forward = pacman.getDirection(); Direction left = pacman.getDirection().left(); diff --git a/lwjgl3/src/main/java/com/buaisociety/pacman/lwjgl3/Lwjgl3Launcher.java b/lwjgl3/src/main/java/com/buaisociety/pacman/lwjgl3/Lwjgl3Launcher.java index 0e83fca..ec719a4 100644 --- a/lwjgl3/src/main/java/com/buaisociety/pacman/lwjgl3/Lwjgl3Launcher.java +++ b/lwjgl3/src/main/java/com/buaisociety/pacman/lwjgl3/Lwjgl3Launcher.java @@ -13,7 +13,7 @@ public static void main(String[] args) { } private static Lwjgl3Application createApplication() { - boolean isTraining = false; // set this as false to try out the tournament settings + boolean isTraining = true; // set this as false to try out the tournament settings if (isTraining) { Lwjgl3ApplicationConfiguration config = getDefaultConfiguration(); // disable vsync to run the game as fast as possible From 562c54364d45e7f187159cd1a2317005125029d8 Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 12:02:10 -0400 Subject: [PATCH 02/10] 8 inputs: nearestPalletToLeft,Right,Up,Down --- .../java/com/buaisociety/pacman/Main.java | 4 +- .../pacman/entity/PacmanEntity.java | 19 +++++++-- .../entity/behavior/NeatPacmanBehavior.java | 10 +++++ .../buaisociety/pacman/maze/MazeGraph.java | 31 ++++++++++++++ .../pacman/maze/ShortestPathFinder.java | 40 +++++++++++++++++++ 5 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 core/src/main/java/com/buaisociety/pacman/maze/MazeGraph.java create mode 100644 core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java diff --git a/core/src/main/java/com/buaisociety/pacman/Main.java b/core/src/main/java/com/buaisociety/pacman/Main.java index 29113e7..19a6fad 100644 --- a/core/src/main/java/com/buaisociety/pacman/Main.java +++ b/core/src/main/java/com/buaisociety/pacman/Main.java @@ -115,7 +115,7 @@ public void create() { } NeatImpl impl = NeatImpl.fromJson(json); // modify this as needed - //impl.updateNodeCounts(8, 4); // Add 4 new inputs + impl.updateNodeCounts(8, 4); // Add 4 new inputs //impl.updateClients(200); // have 200 pacman games at once return impl; } else { @@ -125,7 +125,7 @@ public void create() { neatParameters.setTargetClientsPerSpecies(12); // targeting ~12 clients per species neatParameters.setStagnationLimit(10); // lower stagnation limit neatParameters.setUseBiasNode(true); // use bias node - return new NeatImpl(4, 4, totalGames, neatParameters); + return new NeatImpl(8, 4, totalGames, neatParameters); } } diff --git a/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java b/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java index f09755c..a22031d 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java @@ -5,9 +5,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.buaisociety.pacman.entity.behavior.AggressiveChaseBehavior; import com.buaisociety.pacman.entity.behavior.Behavior; -import com.buaisociety.pacman.maze.Maze; -import com.buaisociety.pacman.maze.Tile; -import com.buaisociety.pacman.maze.TileState; +import com.buaisociety.pacman.maze.*; import com.buaisociety.pacman.sprite.GrayscaleSpriteSheet; import org.jetbrains.annotations.NotNull; import org.joml.Vector2d; @@ -23,6 +21,9 @@ public class PacmanEntity extends Entity { private int freezeTicks; private boolean isAlive = true; + private final MazeGraph graph; + private final ShortestPathFinder pathFinder; + public PacmanEntity(@NotNull Maze maze, @NotNull Config config) { super(maze, EntityType.PACMAN); @@ -33,6 +34,18 @@ public PacmanEntity(@NotNull Maze maze, @NotNull Config config) { // This sprite sheet is 3x4 tiled sprite sheet, each tile is 20x20 pixels this.spriteSheet = config.spriteSheet; this.spriteSheet.setColors(Color.CLEAR, Color.YELLOW); + + this.graph = new MazeGraph(maze); + this.pathFinder = new ShortestPathFinder(graph); + } + + public float getDistanceToNearestPellet(Direction direction) { + Tile startTile = maze.getTile(getTilePosition()); + Tile neighborTile = startTile.getNeighbor(direction); + if (!neighborTile.getState().isPassable()) { + return Float.MAX_VALUE; + } + return pathFinder.getDistanceToNearestPellet(neighborTile); } @Override diff --git a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java index 63d896e..0e8ac19 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java @@ -3,6 +3,7 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.buaisociety.pacman.maze.Maze; +import com.buaisociety.pacman.maze.Tile; import com.buaisociety.pacman.sprite.DebugDrawing; import com.cjcrafter.neat.Client; import com.buaisociety.pacman.entity.Direction; @@ -70,11 +71,20 @@ public Direction getDirection(@NotNull Entity entity) { boolean canMoveRight = pacman.canMove(right); boolean canMoveBehind = pacman.canMove(behind); + float distanceToNearestPelletForward = pacman.getDistanceToNearestPellet(forward); + float distanceToNearestPelletLeft = pacman.getDistanceToNearestPellet(left); + float distanceToNearestPelletRight = pacman.getDistanceToNearestPellet(right); + float distanceToNearestPelletBehind = pacman.getDistanceToNearestPellet(behind); + float[] outputs = client.getCalculator().calculate(new float[]{ canMoveForward ? 1f : 0f, canMoveLeft ? 1f : 0f, canMoveRight ? 1f : 0f, canMoveBehind ? 1f : 0f, + distanceToNearestPelletForward, + distanceToNearestPelletLeft, + distanceToNearestPelletRight, + distanceToNearestPelletBehind, }).join(); int index = 0; diff --git a/core/src/main/java/com/buaisociety/pacman/maze/MazeGraph.java b/core/src/main/java/com/buaisociety/pacman/maze/MazeGraph.java new file mode 100644 index 0000000..19e2778 --- /dev/null +++ b/core/src/main/java/com/buaisociety/pacman/maze/MazeGraph.java @@ -0,0 +1,31 @@ +package com.buaisociety.pacman.maze; + +import com.buaisociety.pacman.entity.Direction; + +import java.util.*; + +public class MazeGraph { + private final Map> adjList = new HashMap<>(); + + public MazeGraph(Maze maze) { + for (int y = 0; y < maze.getDimensions().y(); y++) { + for (int x = 0; x < maze.getDimensions().x(); x++) { + Tile tile = maze.getTile(x, y); + if (tile.getState().isPassable()) { + List neighbors = new ArrayList<>(); + for (Direction direction : Direction.values()) { + Tile neighbor = tile.getNeighbor(direction); + if (neighbor.getState().isPassable()) { + neighbors.add(neighbor); + } + } + adjList.put(tile, neighbors); + } + } + } + } + + public Map> getAdjList() { + return adjList; + } +} diff --git a/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java b/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java new file mode 100644 index 0000000..7f90273 --- /dev/null +++ b/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java @@ -0,0 +1,40 @@ +package com.buaisociety.pacman.maze; + +import java.util.*; + +public class ShortestPathFinder { + private final MazeGraph mazeGraph; + + public ShortestPathFinder(MazeGraph mazeGraph) { + this.mazeGraph = mazeGraph; + } + + public int getDistanceToNearestPellet(Tile startTile) { + Queue queue = new LinkedList<>(); + Set visited = new HashSet<>(); + Map distance = new HashMap<>(); + + queue.add(startTile); + visited.add(startTile); + distance.put(startTile, 0); + + while (!queue.isEmpty()) { + Tile current = queue.poll(); + int currentDistance = distance.get(current); + + if (current.getState() == TileState.PELLET || current.getState() == TileState.POWER_PELLET) { + return currentDistance; + } + + for (Tile neighbor : mazeGraph.getAdjList().get(current)) { + if (!visited.contains(neighbor)) { + queue.add(neighbor); + visited.add(neighbor); + distance.put(neighbor, currentDistance + 1); + } + } + } + + return Integer.MAX_VALUE; // No pellet found + } +} From 0194441c8a78e8841358db611c2b507228c326dc Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 12:41:43 -0400 Subject: [PATCH 03/10] GhostDetection + currentScore + number of Pellets left --- .../java/com/buaisociety/pacman/Main.java | 6 ++-- .../pacman/entity/PacmanEntity.java | 14 ++++++-- .../entity/behavior/NeatPacmanBehavior.java | 25 ++++++++++--- .../pacman/maze/ShortestPathFinder.java | 36 +++++++++++++++++++ 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/buaisociety/pacman/Main.java b/core/src/main/java/com/buaisociety/pacman/Main.java index 19a6fad..58e9062 100644 --- a/core/src/main/java/com/buaisociety/pacman/Main.java +++ b/core/src/main/java/com/buaisociety/pacman/Main.java @@ -103,9 +103,9 @@ public void create() { public @NotNull Neat createNeat() { // Change this to true/false as needed, if you want to load from file - if (false) { + if (true) { // TODO: Change this to the exact file you want to load - File exactFile = new File("saves" + File.separator + "oct26-4" + File.separator + "generation-51.json"); + File exactFile = new File("saves" + File.separator + "oct26-6" + File.separator + "generation-15.json"); // load exactFile contents to string String json; try { @@ -115,7 +115,7 @@ public void create() { } NeatImpl impl = NeatImpl.fromJson(json); // modify this as needed - impl.updateNodeCounts(8, 4); // Add 4 new inputs + impl.updateNodeCounts(14, 4); // Add 4 new inputs //impl.updateClients(200); // have 200 pacman games at once return impl; } else { diff --git a/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java b/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java index a22031d..7aaf8b1 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java @@ -21,6 +21,7 @@ public class PacmanEntity extends Entity { private int freezeTicks; private boolean isAlive = true; + // For graph traversal: the maze graph and the shortest pathfinder private final MazeGraph graph; private final ShortestPathFinder pathFinder; @@ -39,15 +40,24 @@ public PacmanEntity(@NotNull Maze maze, @NotNull Config config) { this.pathFinder = new ShortestPathFinder(graph); } - public float getDistanceToNearestPellet(Direction direction) { + public int getDistanceToNearestPellet(Direction direction) { Tile startTile = maze.getTile(getTilePosition()); Tile neighborTile = startTile.getNeighbor(direction); if (!neighborTile.getState().isPassable()) { - return Float.MAX_VALUE; + return Integer.MAX_VALUE; } return pathFinder.getDistanceToNearestPellet(neighborTile); } + public int getDistanceToNearestGhost(Direction direction) { + Tile startTile = maze.getTile(getTilePosition()); + Tile neighborTile = startTile.getNeighbor(direction); + if (!neighborTile.getState().isPassable()) { + return Integer.MAX_VALUE; + } + return pathFinder.getDistanceToNearestGhost(neighborTile); + } + @Override public void reset() { animationFrame = 0; diff --git a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java index 0e8ac19..bff8e04 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java @@ -71,10 +71,21 @@ public Direction getDirection(@NotNull Entity entity) { boolean canMoveRight = pacman.canMove(right); boolean canMoveBehind = pacman.canMove(behind); - float distanceToNearestPelletForward = pacman.getDistanceToNearestPellet(forward); - float distanceToNearestPelletLeft = pacman.getDistanceToNearestPellet(left); - float distanceToNearestPelletRight = pacman.getDistanceToNearestPellet(right); - float distanceToNearestPelletBehind = pacman.getDistanceToNearestPellet(behind); + // Nearest distance to Pallet: + int distanceToNearestPelletForward = pacman.getDistanceToNearestPellet(forward); + int distanceToNearestPelletLeft = pacman.getDistanceToNearestPellet(left); + int distanceToNearestPelletRight = pacman.getDistanceToNearestPellet(right); + int distanceToNearestPelletBehind = pacman.getDistanceToNearestPellet(behind); + + // Nearest distance to ghost: + int distanceToGhostForward = pacman.getDistanceToNearestGhost(forward); + int distanceToGhostLeft = pacman.getDistanceToNearestGhost(left); + int distanceToGhostRight = pacman.getDistanceToNearestGhost(right); + int distanceToGhostBehind = pacman.getDistanceToNearestGhost(behind); + + // Get the current score and number of pellets left + int currentScore = pacman.getMaze().getLevelManager().getScore(); + int pelletsLeft = pacman.getMaze().getPelletsRemaining(); float[] outputs = client.getCalculator().calculate(new float[]{ canMoveForward ? 1f : 0f, @@ -85,6 +96,12 @@ public Direction getDirection(@NotNull Entity entity) { distanceToNearestPelletLeft, distanceToNearestPelletRight, distanceToNearestPelletBehind, + distanceToGhostForward, + distanceToGhostLeft, + distanceToGhostRight, + distanceToGhostBehind, + currentScore, + pelletsLeft, }).join(); int index = 0; diff --git a/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java b/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java index 7f90273..346922e 100644 --- a/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java +++ b/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java @@ -1,5 +1,9 @@ +// ShortestPathFinder.java package com.buaisociety.pacman.maze; +import com.buaisociety.pacman.entity.Entity; +import com.buaisociety.pacman.entity.GhostEntity; + import java.util.*; public class ShortestPathFinder { @@ -37,4 +41,36 @@ public int getDistanceToNearestPellet(Tile startTile) { return Integer.MAX_VALUE; // No pellet found } + + public int getDistanceToNearestGhost(Tile startTile) { + Queue queue = new LinkedList<>(); + Set visited = new HashSet<>(); + Map distance = new HashMap<>(); + + queue.add(startTile); + visited.add(startTile); + distance.put(startTile, 0); + + while (!queue.isEmpty()) { + Tile current = queue.poll(); + int currentDistance = distance.get(current); + + // Check if the current tile has a ghost + for (Entity entity : current.getMaze().getEntities()) { + if (entity instanceof GhostEntity && entity.getTilePosition().equals(current.getPosition())) { + return currentDistance; + } + } + + for (Tile neighbor : mazeGraph.getAdjList().get(current)) { + if (!visited.contains(neighbor)) { + queue.add(neighbor); + visited.add(neighbor); + distance.put(neighbor, currentDistance + 1); + } + } + } + + return Integer.MAX_VALUE; // No ghost found + } } From 11b0eb5fbbd8d603ad9a99e68f36015dcf427bc8 Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 12:41:43 -0400 Subject: [PATCH 04/10] GhostDetection + currentScore + number of Pellets left From 058d68541e100e9d392e91038514c76be07aad46 Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 15:33:35 -0400 Subject: [PATCH 05/10] Distance to the Power Pellet --- .../java/com/buaisociety/pacman/Main.java | 4 +- .../pacman/SpecialTrainingConditions.java | 7 +++- .../pacman/entity/PacmanEntity.java | 10 +++++ .../entity/behavior/NeatPacmanBehavior.java | 20 +++++++++ .../pacman/maze/ShortestPathFinder.java | 42 +++++++++++++++++++ 5 files changed, 79 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/buaisociety/pacman/Main.java b/core/src/main/java/com/buaisociety/pacman/Main.java index 58e9062..1abfcf8 100644 --- a/core/src/main/java/com/buaisociety/pacman/Main.java +++ b/core/src/main/java/com/buaisociety/pacman/Main.java @@ -115,7 +115,7 @@ public void create() { } NeatImpl impl = NeatImpl.fromJson(json); // modify this as needed - impl.updateNodeCounts(14, 4); // Add 4 new inputs + impl.updateNodeCounts(21, 4); // Add 4 new inputs //impl.updateClients(200); // have 200 pacman games at once return impl; } else { @@ -125,7 +125,7 @@ public void create() { neatParameters.setTargetClientsPerSpecies(12); // targeting ~12 clients per species neatParameters.setStagnationLimit(10); // lower stagnation limit neatParameters.setUseBiasNode(true); // use bias node - return new NeatImpl(8, 4, totalGames, neatParameters); + return new NeatImpl(21, 4, totalGames, neatParameters); } } diff --git a/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java b/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java index d9d5684..6f0a106 100644 --- a/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java +++ b/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java @@ -28,10 +28,13 @@ private SpecialTrainingConditions() { public static @NotNull EventListener onEntityPreSpawn() { return event -> { - // Prevent ghosts from spawning during training - if (event.getEntityType() == EntityType.GHOST) { + // Prevent ghosts from spawning during training if there is already a ghost + if (event.getEntityType() == EntityType.GHOST && event.getMaze().getEntities().stream().anyMatch(e -> e.getType() == EntityType.GHOST)) { event.setCancelled(true); } + + + }; } diff --git a/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java b/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java index 7aaf8b1..e1a25a2 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java @@ -7,6 +7,7 @@ import com.buaisociety.pacman.entity.behavior.Behavior; import com.buaisociety.pacman.maze.*; import com.buaisociety.pacman.sprite.GrayscaleSpriteSheet; +import kotlin.Pair; import org.jetbrains.annotations.NotNull; import org.joml.Vector2d; import org.joml.Vector2i; @@ -98,6 +99,15 @@ public double getSpeed() { } } + public Pair getDistanceAndDirectionToNearestPelletAndGhost() { + Tile startTile = maze.getTile(getTilePosition()); + return pathFinder.getDistanceAndDirectionToNearestPowerPellet(startTile); + } + + public boolean isInSuperMode() { + return maze.getFrightenedTimer() > 0; + } + @Override public @NotNull Behavior getBehavior() { return behavior; diff --git a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java index bff8e04..4c6d253 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java @@ -9,6 +9,7 @@ import com.buaisociety.pacman.entity.Direction; import com.buaisociety.pacman.entity.Entity; import com.buaisociety.pacman.entity.PacmanEntity; +import kotlin.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -87,6 +88,18 @@ public Direction getDirection(@NotNull Entity entity) { int currentScore = pacman.getMaze().getLevelManager().getScore(); int pelletsLeft = pacman.getMaze().getPelletsRemaining(); + // Check if Pacman should eat a PowerPellet + boolean shouldEatPowerPellet = (distanceToGhostForward < 5 || distanceToGhostLeft < 5 || distanceToGhostRight < 5 || distanceToGhostBehind < 5); + // Get closest power pellet direction and distance + Pair closestPowerPellet = pacman.getDistanceAndDirectionToNearestPelletAndGhost(); + int distanceToClosestPowerPellet = closestPowerPellet.getFirst(); + Direction directionToClosestPowerPellet = closestPowerPellet.getSecond(); + boolean closestPowerPelletIsForward = directionToClosestPowerPellet == forward; + boolean closestPowerPelletIsLeft = directionToClosestPowerPellet == left; + boolean closestPowerPelletIsRight = directionToClosestPowerPellet == right; + boolean closestPowerPelletIsBehind = directionToClosestPowerPellet == behind; + boolean isInSuperMode = pacman.isInSuperMode(); + float[] outputs = client.getCalculator().calculate(new float[]{ canMoveForward ? 1f : 0f, canMoveLeft ? 1f : 0f, @@ -102,6 +115,13 @@ public Direction getDirection(@NotNull Entity entity) { distanceToGhostBehind, currentScore, pelletsLeft, + shouldEatPowerPellet ? 1f : 0f, + distanceToClosestPowerPellet, + closestPowerPelletIsForward ? 1f : 0f, + closestPowerPelletIsLeft ? 1f : 0f, + closestPowerPelletIsRight ? 1f : 0f, + closestPowerPelletIsBehind ? 1f : 0f, + isInSuperMode ? 1f : 0f, }).join(); int index = 0; diff --git a/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java b/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java index 346922e..1a0b765 100644 --- a/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java +++ b/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java @@ -1,8 +1,10 @@ // ShortestPathFinder.java package com.buaisociety.pacman.maze; +import com.buaisociety.pacman.entity.Direction; import com.buaisociety.pacman.entity.Entity; import com.buaisociety.pacman.entity.GhostEntity; +import kotlin.Pair; import java.util.*; @@ -73,4 +75,44 @@ public int getDistanceToNearestGhost(Tile startTile) { return Integer.MAX_VALUE; // No ghost found } + + private List getAdjacentTiles(Tile startTile) { + return new ArrayList<>(mazeGraph.getAdjList().get(startTile)); + } + + public Pair getDistanceAndDirectionToNearestPowerPellet(Tile startTile) { + Queue queue = new LinkedList<>(); + Set visited = new HashSet<>(); + Map distance = new HashMap<>(); + Map directionMap = new HashMap<>(); + queue.add(startTile); + visited.add(startTile); + distance.put(startTile, 0); + for (Direction direction : Direction.values()) { + Tile neighbor = startTile.getNeighbor(direction); + if (neighbor.getState().isPassable()) { + queue.add(neighbor); + visited.add(neighbor); + distance.put(neighbor, 1); + directionMap.put(neighbor, direction); + } + } + while (!queue.isEmpty()) { + Tile current = queue.poll(); + int currentDistance = distance.get(current); + if (current.getState() == TileState.POWER_PELLET) { + Direction initialDirection = directionMap.get(current); + return new Pair<>(currentDistance, initialDirection); + } + for (Tile neighbor : getAdjacentTiles(current)) { + if (!visited.contains(neighbor) && neighbor.getState().isPassable()) { + queue.add(neighbor); + visited.add(neighbor); + distance.put(neighbor, currentDistance + 1); + directionMap.put(neighbor, directionMap.getOrDefault(current, null)); + } + } + } + return new Pair<>(Integer.MAX_VALUE, null); // No PowerPellet found + } } From a862bdadbe25693856b67ecde1dcec010b51beef Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 17:02:12 -0400 Subject: [PATCH 06/10] Level 1 passed --- .../java/com/buaisociety/pacman/Main.java | 8 ++-- .../pacman/SpecialTrainingConditions.java | 12 +++-- .../entity/behavior/NeatPacmanBehavior.java | 46 +++++++++++-------- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/com/buaisociety/pacman/Main.java b/core/src/main/java/com/buaisociety/pacman/Main.java index 1abfcf8..bf11c71 100644 --- a/core/src/main/java/com/buaisociety/pacman/Main.java +++ b/core/src/main/java/com/buaisociety/pacman/Main.java @@ -103,9 +103,9 @@ public void create() { public @NotNull Neat createNeat() { // Change this to true/false as needed, if you want to load from file - if (true) { + if (false) { // TODO: Change this to the exact file you want to load - File exactFile = new File("saves" + File.separator + "oct26-6" + File.separator + "generation-15.json"); + File exactFile = new File("saves" + File.separator + "oct26-1" + File.separator + "generation-2.json"); // load exactFile contents to string String json; try { @@ -115,7 +115,7 @@ public void create() { } NeatImpl impl = NeatImpl.fromJson(json); // modify this as needed - impl.updateNodeCounts(21, 4); // Add 4 new inputs + impl.updateNodeCounts(6, 4); // Add 4 new inputs //impl.updateClients(200); // have 200 pacman games at once return impl; } else { @@ -125,7 +125,7 @@ public void create() { neatParameters.setTargetClientsPerSpecies(12); // targeting ~12 clients per species neatParameters.setStagnationLimit(10); // lower stagnation limit neatParameters.setUseBiasNode(true); // use bias node - return new NeatImpl(21, 4, totalGames, neatParameters); + return new NeatImpl(8, 4, totalGames, neatParameters); } } diff --git a/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java b/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java index 6f0a106..2b8e1ba 100644 --- a/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java +++ b/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java @@ -28,13 +28,15 @@ private SpecialTrainingConditions() { public static @NotNull EventListener onEntityPreSpawn() { return event -> { - // Prevent ghosts from spawning during training if there is already a ghost - if (event.getEntityType() == EntityType.GHOST && event.getMaze().getEntities().stream().anyMatch(e -> e.getType() == EntityType.GHOST)) { + // Only 1 ghost +// if (event.getEntityType() == EntityType.GHOST && event.getMaze().getEntities().stream().anyMatch(e -> e.getType() == EntityType.GHOST)) { +// event.setCancelled(true); +// } + + // Prevent all ghosts from spawning during training + if (event.getEntityType() == EntityType.GHOST) { event.setCancelled(true); } - - - }; } diff --git a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java index 4c6d253..64487d1 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java @@ -84,8 +84,14 @@ public Direction getDirection(@NotNull Entity entity) { int distanceToGhostRight = pacman.getDistanceToNearestGhost(right); int distanceToGhostBehind = pacman.getDistanceToNearestGhost(behind); + // One hot encoding for the direction of the closest power pellet + int minDistance = Math.min(distanceToNearestPelletForward, Math.min(distanceToNearestPelletLeft, Math.min(distanceToNearestPelletRight, distanceToNearestPelletBehind))); + boolean closestPalletIsForward = distanceToNearestPelletForward == minDistance; + boolean closestPalletIsLeft = distanceToNearestPelletLeft == minDistance; + boolean closestPalletIsRight = distanceToNearestPelletRight == minDistance; + boolean closestPalletIsBehind = distanceToNearestPelletBehind == minDistance; + // Get the current score and number of pellets left - int currentScore = pacman.getMaze().getLevelManager().getScore(); int pelletsLeft = pacman.getMaze().getPelletsRemaining(); // Check if Pacman should eat a PowerPellet @@ -105,25 +111,29 @@ public Direction getDirection(@NotNull Entity entity) { canMoveLeft ? 1f : 0f, canMoveRight ? 1f : 0f, canMoveBehind ? 1f : 0f, - distanceToNearestPelletForward, - distanceToNearestPelletLeft, - distanceToNearestPelletRight, - distanceToNearestPelletBehind, - distanceToGhostForward, - distanceToGhostLeft, - distanceToGhostRight, - distanceToGhostBehind, - currentScore, - pelletsLeft, - shouldEatPowerPellet ? 1f : 0f, - distanceToClosestPowerPellet, - closestPowerPelletIsForward ? 1f : 0f, - closestPowerPelletIsLeft ? 1f : 0f, - closestPowerPelletIsRight ? 1f : 0f, - closestPowerPelletIsBehind ? 1f : 0f, - isInSuperMode ? 1f : 0f, + closestPalletIsForward ? 1f : 0f, + closestPalletIsLeft ? 1f : 0f, + closestPalletIsRight ? 1f : 0f, + closestPalletIsBehind ? 1f : 0f, }).join(); + // closestPalletIsForward ? 1f : 0f, + // closestPalletIsLeft ? 1f : 0f, + // closestPalletIsRight ? 1f : 0f, + // closestPalletIsBehind ? 1f : 0f, + +// distanceToGhostForward, +// distanceToGhostLeft, +// distanceToGhostRight, +// distanceToGhostBehind, +// shouldEatPowerPellet ? 1f : 0f, +// distanceToClosestPowerPellet, +// closestPowerPelletIsForward ? 1f : 0f, +// closestPowerPelletIsLeft ? 1f : 0f, +// closestPowerPelletIsRight ? 1f : 0f, +// closestPowerPelletIsBehind ? 1f : 0f, +// isInSuperMode ? 1f : 0f, + int index = 0; float max = outputs[0]; for (int i = 1; i < outputs.length; i++) { From 2c88657cabb08adfa6d9ad36b7fdb5b149ef9129 Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 17:55:48 -0400 Subject: [PATCH 07/10] Level 1 submission --- .../java/com/buaisociety/pacman/Main.java | 6 ++-- .../pacman/SpecialTrainingConditions.java | 12 +++---- .../com/buaisociety/pacman/Tournament.java | 2 +- .../pacman/entity/PacmanEntity.java | 9 +++++ .../entity/behavior/NeatPacmanBehavior.java | 27 ++++++++++++++ .../entity/behavior/TournamentBehavior.java | 36 +++++++++++++++++++ .../com/buaisociety/pacman/maze/Maze.java | 19 ++++++++++ .../com/buaisociety/pacman/maze/Tile.java | 11 ++++++ .../pacman/lwjgl3/Lwjgl3Launcher.java | 2 +- 9 files changed, 113 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/buaisociety/pacman/Main.java b/core/src/main/java/com/buaisociety/pacman/Main.java index bf11c71..d5a0f8e 100644 --- a/core/src/main/java/com/buaisociety/pacman/Main.java +++ b/core/src/main/java/com/buaisociety/pacman/Main.java @@ -103,9 +103,9 @@ public void create() { public @NotNull Neat createNeat() { // Change this to true/false as needed, if you want to load from file - if (false) { + if (true) { // TODO: Change this to the exact file you want to load - File exactFile = new File("saves" + File.separator + "oct26-1" + File.separator + "generation-2.json"); + File exactFile = new File("saves" + File.separator + "oct26-2" + File.separator + "generation-2.json"); // load exactFile contents to string String json; try { @@ -115,7 +115,7 @@ public void create() { } NeatImpl impl = NeatImpl.fromJson(json); // modify this as needed - impl.updateNodeCounts(6, 4); // Add 4 new inputs + impl.updateNodeCounts(12, 4); // Add 4 new inputs //impl.updateClients(200); // have 200 pacman games at once return impl; } else { diff --git a/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java b/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java index 2b8e1ba..61a6834 100644 --- a/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java +++ b/core/src/main/java/com/buaisociety/pacman/SpecialTrainingConditions.java @@ -29,14 +29,14 @@ private SpecialTrainingConditions() { public static @NotNull EventListener onEntityPreSpawn() { return event -> { // Only 1 ghost -// if (event.getEntityType() == EntityType.GHOST && event.getMaze().getEntities().stream().anyMatch(e -> e.getType() == EntityType.GHOST)) { -// event.setCancelled(true); -// } - - // Prevent all ghosts from spawning during training - if (event.getEntityType() == EntityType.GHOST) { + if (event.getEntityType() == EntityType.GHOST && event.getMaze().getEntities().stream().anyMatch(e -> e.getType() == EntityType.GHOST)) { event.setCancelled(true); } + +// // Prevent all ghosts from spawning during training +// if (event.getEntityType() == EntityType.GHOST) { +// event.setCancelled(true); +// } }; } diff --git a/core/src/main/java/com/buaisociety/pacman/Tournament.java b/core/src/main/java/com/buaisociety/pacman/Tournament.java index 697fa74..6e7021a 100644 --- a/core/src/main/java/com/buaisociety/pacman/Tournament.java +++ b/core/src/main/java/com/buaisociety/pacman/Tournament.java @@ -47,7 +47,7 @@ public class Tournament extends ApplicationAdapter { */ public Behavior setupBehavior() { // TODO: Choose your best client here - File file = new File("saves" + File.separator + "oct20-4" + File.separator + "best-calculator-127.json"); + File file = new File("saves" + File.separator + "oct26-2" + File.separator + "best-calculator-2.json"); if (!file.exists()) { System.err.println("Could not find the file: " + file.getAbsolutePath()); return null; diff --git a/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java b/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java index e1a25a2..71f976e 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java @@ -59,6 +59,15 @@ public int getDistanceToNearestGhost(Direction direction) { return pathFinder.getDistanceToNearestGhost(neighborTile); } + public boolean dfsCheckForGhost(Direction direction) { + Tile startTile = maze.getTile(getTilePosition()); + Tile neighborTile = startTile.getNeighbor(direction); + if (!neighborTile.getState().isPassable()) { + return false; + } + return pathFinder.dfsCheckForGhost(startTile, neighborTile); + } + @Override public void reset() { animationFrame = 0; diff --git a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java index 64487d1..15bec6d 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java @@ -12,6 +12,7 @@ import kotlin.Pair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.joml.Vector2i; public class NeatPacmanBehavior implements Behavior { @@ -91,6 +92,28 @@ public Direction getDirection(@NotNull Entity entity) { boolean closestPalletIsRight = distanceToNearestPelletRight == minDistance; boolean closestPalletIsBehind = distanceToNearestPelletBehind == minDistance; + // Get the fruit position + Vector2i fruitPosition = pacman.getMaze().getFruitPosition(); + if (fruitPosition != null) { + // Calculate direction to the fruit + Tile pacmanTile = pacman.getMaze().getTile(pacman.getTilePosition()); + Tile fruitTile = pacman.getMaze().getTile(fruitPosition); + Direction directionToFruit = pacmanTile.getDirectionTo(fruitTile); + + // Use the direction to the fruit in your NEAT algorithm inputs + boolean fruitIsForward = directionToFruit == pacman.getDirection(); + boolean fruitIsLeft = directionToFruit == pacman.getDirection().left(); + boolean fruitIsRight = directionToFruit == pacman.getDirection().right(); + boolean fruitIsBehind = directionToFruit == pacman.getDirection().behind(); + } + + boolean ghostLeft = pacman.dfsCheckForGhost(left); + boolean ghostRight = pacman.dfsCheckForGhost(right); + boolean ghostForward = pacman.dfsCheckForGhost(forward); + boolean ghostBehind = pacman.dfsCheckForGhost(behind); + + System.out.printf("%b %b %b %b \n", ghostLeft, ghostRight, ghostForward, ghostBehind); + // Get the current score and number of pellets left int pelletsLeft = pacman.getMaze().getPelletsRemaining(); @@ -115,6 +138,10 @@ public Direction getDirection(@NotNull Entity entity) { closestPalletIsLeft ? 1f : 0f, closestPalletIsRight ? 1f : 0f, closestPalletIsBehind ? 1f : 0f, + ghostLeft ? 1f : 0f, + ghostRight ? 1f : 0f, + ghostForward ? 1f : 0f, + ghostBehind ? 1f : 0f, }).join(); // closestPalletIsForward ? 1f : 0f, diff --git a/core/src/main/java/com/buaisociety/pacman/entity/behavior/TournamentBehavior.java b/core/src/main/java/com/buaisociety/pacman/entity/behavior/TournamentBehavior.java index 22cab50..0cdea1b 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/behavior/TournamentBehavior.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/behavior/TournamentBehavior.java @@ -15,6 +15,9 @@ public class TournamentBehavior implements Behavior { private int previousScore = 0; private int framesSinceScoreUpdate = 0; + private int numberUpdatesSincelastscone = 0; + private int lastScore = 0; + public TournamentBehavior(Calculator calculator) { this.calculator = calculator; } @@ -49,8 +52,41 @@ public Direction getDirection(@NotNull Entity entity) { // TODO: Put all your code for info into the neural network here + // We are going to use these directions a lot for different inputs. Get them all once for clarity and brevity + Direction forward = pacman.getDirection(); + Direction left = pacman.getDirection().left(); + Direction right = pacman.getDirection().right(); + Direction behind = pacman.getDirection().behind(); + + // Input nodes 1, 2, 3, and 4 show if the pacman can move in the forward, left, right, and behind directions + boolean canMoveForward = pacman.canMove(forward); + boolean canMoveLeft = pacman.canMove(left); + boolean canMoveRight = pacman.canMove(right); + boolean canMoveBehind = pacman.canMove(behind); + + // Nearest distance to Pallet: + int distanceToNearestPelletForward = pacman.getDistanceToNearestPellet(forward); + int distanceToNearestPelletLeft = pacman.getDistanceToNearestPellet(left); + int distanceToNearestPelletRight = pacman.getDistanceToNearestPellet(right); + int distanceToNearestPelletBehind = pacman.getDistanceToNearestPellet(behind); + + // One hot encoding for the direction of the closest power pellet + int minDistance = Math.min(distanceToNearestPelletForward, Math.min(distanceToNearestPelletLeft, Math.min(distanceToNearestPelletRight, distanceToNearestPelletBehind))); + boolean closestPalletIsForward = distanceToNearestPelletForward == minDistance; + boolean closestPalletIsLeft = distanceToNearestPelletLeft == minDistance; + boolean closestPalletIsRight = distanceToNearestPelletRight == minDistance; + boolean closestPalletIsBehind = distanceToNearestPelletBehind == minDistance; + float[] inputs = new float[] { // TODO: Add your inputs here + canMoveForward ? 1f : 0f, + canMoveLeft ? 1f : 0f, + canMoveRight ? 1f : 0f, + canMoveBehind ? 1f : 0f, + closestPalletIsForward ? 1f : 0f, + closestPalletIsLeft ? 1f : 0f, + closestPalletIsRight ? 1f : 0f, + closestPalletIsBehind ? 1f : 0f, }; float[] outputs = calculator.calculate(inputs).join(); diff --git a/core/src/main/java/com/buaisociety/pacman/maze/Maze.java b/core/src/main/java/com/buaisociety/pacman/maze/Maze.java index 1e31fb5..8e960a9 100644 --- a/core/src/main/java/com/buaisociety/pacman/maze/Maze.java +++ b/core/src/main/java/com/buaisociety/pacman/maze/Maze.java @@ -414,6 +414,25 @@ public void spawnFruit() { entities.add(fruit); } + public boolean isFruitPresent() { + for (Entity entity : entities) { + if (entity instanceof FruitEntity) { + return true; + } + } + return false; + } + + // Maze.java + public @Nullable Vector2i getFruitPosition() { + for (Entity entity : entities) { + if (entity instanceof FruitEntity) { + return entity.getTilePosition(); + } + } + return null; + } + public void update() { ticks++; if (freezeTicks > 0) { diff --git a/core/src/main/java/com/buaisociety/pacman/maze/Tile.java b/core/src/main/java/com/buaisociety/pacman/maze/Tile.java index 1de2ede..a49babd 100644 --- a/core/src/main/java/com/buaisociety/pacman/maze/Tile.java +++ b/core/src/main/java/com/buaisociety/pacman/maze/Tile.java @@ -96,6 +96,17 @@ public boolean equals(Object o) { return Objects.equals(maze, tile.maze) && Objects.equals(position, tile.position); } + public @NotNull Direction getDirectionTo(@NotNull Tile targetTile) { + int dx = targetTile.getPosition().x() - this.position.x; + int dy = targetTile.getPosition().y() - this.position.y; + + if (Math.abs(dx) > Math.abs(dy)) { + return dx > 0 ? Direction.RIGHT : Direction.LEFT; + } else { + return dy > 0 ? Direction.UP : Direction.DOWN; + } + } + @Override public int hashCode() { return Objects.hash(maze, position); diff --git a/lwjgl3/src/main/java/com/buaisociety/pacman/lwjgl3/Lwjgl3Launcher.java b/lwjgl3/src/main/java/com/buaisociety/pacman/lwjgl3/Lwjgl3Launcher.java index ec719a4..0e83fca 100644 --- a/lwjgl3/src/main/java/com/buaisociety/pacman/lwjgl3/Lwjgl3Launcher.java +++ b/lwjgl3/src/main/java/com/buaisociety/pacman/lwjgl3/Lwjgl3Launcher.java @@ -13,7 +13,7 @@ public static void main(String[] args) { } private static Lwjgl3Application createApplication() { - boolean isTraining = true; // set this as false to try out the tournament settings + boolean isTraining = false; // set this as false to try out the tournament settings if (isTraining) { Lwjgl3ApplicationConfiguration config = getDefaultConfiguration(); // disable vsync to run the game as fast as possible From 476f7663b55aac42d58e22ecef61390a4d43d372 Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 17:58:37 -0400 Subject: [PATCH 08/10] add weight --- assets/saves/oct26-2/best-calculator-2.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 assets/saves/oct26-2/best-calculator-2.json diff --git a/assets/saves/oct26-2/best-calculator-2.json b/assets/saves/oct26-2/best-calculator-2.json new file mode 100644 index 0000000..5dbd566 --- /dev/null +++ b/assets/saves/oct26-2/best-calculator-2.json @@ -0,0 +1 @@ +{"isAddBias":true,"inputs":[{"id":0,"x":0.1,"incoming":[]},{"id":1,"x":0.1,"incoming":[]},{"id":2,"x":0.1,"incoming":[]},{"id":3,"x":0.1,"incoming":[]},{"id":4,"x":0.1,"incoming":[]},{"id":5,"x":0.1,"incoming":[]},{"id":6,"x":0.1,"incoming":[]},{"id":7,"x":0.1,"incoming":[]},{"id":8,"x":0.1,"incoming":[]}],"hidden":[],"outputs":[{"id":9,"x":0.9,"incoming":[{"fromId":0,"weight":1.8324667,"enabled":true},{"fromId":1,"weight":3.3833725,"enabled":true},{"fromId":2,"weight":-0.054615706,"enabled":true},{"fromId":3,"weight":-1.6720431,"enabled":true},{"fromId":4,"weight":0.10172549,"enabled":true},{"fromId":5,"weight":0.020776946,"enabled":true},{"fromId":6,"weight":-2.0471458,"enabled":true},{"fromId":7,"weight":-1.5629632,"enabled":true},{"fromId":8,"weight":0.13760364,"enabled":true}]},{"id":10,"x":0.9,"incoming":[{"fromId":0,"weight":0.85197365,"enabled":true},{"fromId":1,"weight":0.9681164,"enabled":true},{"fromId":2,"weight":0.29334953,"enabled":true},{"fromId":3,"weight":-0.54721165,"enabled":true},{"fromId":4,"weight":0.027339846,"enabled":true},{"fromId":5,"weight":1.4024067,"enabled":true},{"fromId":6,"weight":1.2905818,"enabled":true},{"fromId":7,"weight":-1.8324815,"enabled":true},{"fromId":8,"weight":-0.5099077,"enabled":true}]},{"id":11,"x":0.9,"incoming":[{"fromId":0,"weight":-0.50607824,"enabled":true},{"fromId":1,"weight":-1.0208844,"enabled":true},{"fromId":2,"weight":0.44424167,"enabled":true},{"fromId":3,"weight":-1.2910391,"enabled":true},{"fromId":4,"weight":0.034571037,"enabled":true},{"fromId":5,"weight":1.2249593,"enabled":true},{"fromId":6,"weight":-0.15091224,"enabled":true},{"fromId":7,"weight":0.64671206,"enabled":true},{"fromId":8,"weight":-0.106307045,"enabled":true}]},{"id":12,"x":0.9,"incoming":[{"fromId":0,"weight":-0.4848314,"enabled":true},{"fromId":1,"weight":-0.02907367,"enabled":true},{"fromId":2,"weight":0.04155484,"enabled":true},{"fromId":3,"weight":0.7356783,"enabled":true},{"fromId":4,"weight":-0.65222055,"enabled":true},{"fromId":5,"weight":1.0584612,"enabled":true},{"fromId":6,"weight":-1.0657367,"enabled":true},{"fromId":7,"weight":-1.2688935,"enabled":true},{"fromId":8,"weight":-0.32647762,"enabled":true}]}]} \ No newline at end of file From b6c2a3097ef59206ca4b31d15d62ffde7b245e9d Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 18:43:25 -0400 Subject: [PATCH 09/10] Trying for Level 2 --- .../com/buaisociety/pacman/Tournament.java | 2 +- .../entity/behavior/NeatPacmanBehavior.java | 2 - .../entity/behavior/TournamentBehavior.java | 9 ++ .../pacman/maze/ShortestPathFinder.java | 89 +++++++++++++++++++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/buaisociety/pacman/Tournament.java b/core/src/main/java/com/buaisociety/pacman/Tournament.java index 6e7021a..1c652a6 100644 --- a/core/src/main/java/com/buaisociety/pacman/Tournament.java +++ b/core/src/main/java/com/buaisociety/pacman/Tournament.java @@ -47,7 +47,7 @@ public class Tournament extends ApplicationAdapter { */ public Behavior setupBehavior() { // TODO: Choose your best client here - File file = new File("saves" + File.separator + "oct26-2" + File.separator + "best-calculator-2.json"); + File file = new File("saves" + File.separator + "oct26-8" + File.separator + "best-calculator-50.json"); if (!file.exists()) { System.err.println("Could not find the file: " + file.getAbsolutePath()); return null; diff --git a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java index 15bec6d..eff4cdb 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/behavior/NeatPacmanBehavior.java @@ -112,8 +112,6 @@ public Direction getDirection(@NotNull Entity entity) { boolean ghostForward = pacman.dfsCheckForGhost(forward); boolean ghostBehind = pacman.dfsCheckForGhost(behind); - System.out.printf("%b %b %b %b \n", ghostLeft, ghostRight, ghostForward, ghostBehind); - // Get the current score and number of pellets left int pelletsLeft = pacman.getMaze().getPelletsRemaining(); diff --git a/core/src/main/java/com/buaisociety/pacman/entity/behavior/TournamentBehavior.java b/core/src/main/java/com/buaisociety/pacman/entity/behavior/TournamentBehavior.java index 0cdea1b..0ac5c40 100644 --- a/core/src/main/java/com/buaisociety/pacman/entity/behavior/TournamentBehavior.java +++ b/core/src/main/java/com/buaisociety/pacman/entity/behavior/TournamentBehavior.java @@ -77,6 +77,11 @@ public Direction getDirection(@NotNull Entity entity) { boolean closestPalletIsRight = distanceToNearestPelletRight == minDistance; boolean closestPalletIsBehind = distanceToNearestPelletBehind == minDistance; + boolean ghostLeft = pacman.dfsCheckForGhost(left); + boolean ghostRight = pacman.dfsCheckForGhost(right); + boolean ghostForward = pacman.dfsCheckForGhost(forward); + boolean ghostBehind = pacman.dfsCheckForGhost(behind); + float[] inputs = new float[] { // TODO: Add your inputs here canMoveForward ? 1f : 0f, @@ -87,6 +92,10 @@ public Direction getDirection(@NotNull Entity entity) { closestPalletIsLeft ? 1f : 0f, closestPalletIsRight ? 1f : 0f, closestPalletIsBehind ? 1f : 0f, + ghostLeft ? 1f : 0f, + ghostRight ? 1f : 0f, + ghostForward ? 1f : 0f, + ghostBehind ? 1f : 0f, }; float[] outputs = calculator.calculate(inputs).join(); diff --git a/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java b/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java index 1a0b765..22585a3 100644 --- a/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java +++ b/core/src/main/java/com/buaisociety/pacman/maze/ShortestPathFinder.java @@ -3,6 +3,7 @@ import com.buaisociety.pacman.entity.Direction; import com.buaisociety.pacman.entity.Entity; +import com.buaisociety.pacman.entity.EntityType; import com.buaisociety.pacman.entity.GhostEntity; import kotlin.Pair; @@ -11,6 +12,7 @@ public class ShortestPathFinder { private final MazeGraph mazeGraph; + public ShortestPathFinder(MazeGraph mazeGraph) { this.mazeGraph = mazeGraph; } @@ -115,4 +117,91 @@ public Pair getDistanceAndDirectionToNearestPowerPellet(Tile } return new Pair<>(Integer.MAX_VALUE, null); // No PowerPellet found } + + public boolean dfsCheckForGhost(Tile startTile, Tile directionTile) { + Set visited = new HashSet<>(); + visited.add(startTile); // Mark Pacman's current position as visited + Tile current = directionTile; // Start from the tile in the specified direction + + int counter = 0; + + // Traverse through the hallway until a root is found + while (getUnvisitedNeighborCount(current, visited) == 1) { + counter++; + // Check for a ghost immediately upon visiting each tile + if (containsGhost(current)) { + System.out.printf("Ghost found at %s\n", current.getPosition()); + return true; + } + + // Move to the next tile in the hallway + for (Tile neighbor : mazeGraph.getAdjList().get(current)) { + if (!visited.contains(neighbor)) { + visited.add(neighbor); + current = neighbor; + break; + } + } + } + + if (counter > 8) { + return false; // No ghost found within 8 tiles + } + + // Check the current tile (root) for a ghost + if (containsGhost(current)) { + System.out.printf("Ghost found at %s\n", current.getPosition()); + return true; + } + + // Start a countdown when a root is reached + int countdown = 2; + Stack stack = new Stack<>(); + stack.push(current); + + while (!stack.isEmpty() && countdown > 0) { + Tile tile = stack.pop(); + visited.add(tile); + + // Check for a ghost on every visited tile during countdown + if (containsGhost(tile)) { + System.out.printf("Ghost found at %s\n", tile.getPosition()); + return true; + } + + // Add unvisited neighbors to the stack + for (Tile neighbor : mazeGraph.getAdjList().get(tile)) { + if (!visited.contains(neighbor)) { + stack.push(neighbor); + visited.add(neighbor); + } + } + + // Decrease the countdown after expanding one depth level + countdown--; + } + + return false; // No ghost found within two tiles from the root + } + + + + private boolean containsGhost(Tile t){ + for (Entity entity : t.getMaze().getEntities()) { + if (entity instanceof GhostEntity && entity.getTilePosition().equals(t.getPosition())) { + return true; + } + } + return false; + } + + private int getUnvisitedNeighborCount(Tile tile, Set visited) { + int count = 0; + for (Tile neighbor : mazeGraph.getAdjList().get(tile)) { + if (!visited.contains(neighbor)) { + count++; + } + } + return count; + } } From d67c3c880d14687838772817f36c62eba6ce54fa Mon Sep 17 00:00:00 2001 From: Ruihang Date: Sat, 26 Oct 2024 18:45:08 -0400 Subject: [PATCH 10/10] level 2 json --- assets/saves/oct26-8/best-calculator-50.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 assets/saves/oct26-8/best-calculator-50.json diff --git a/assets/saves/oct26-8/best-calculator-50.json b/assets/saves/oct26-8/best-calculator-50.json new file mode 100644 index 0000000..a7e8c79 --- /dev/null +++ b/assets/saves/oct26-8/best-calculator-50.json @@ -0,0 +1 @@ +{"isAddBias":true,"inputs":[{"id":0,"x":0.1,"incoming":[]},{"id":1,"x":0.1,"incoming":[]},{"id":2,"x":0.1,"incoming":[]},{"id":3,"x":0.1,"incoming":[]},{"id":4,"x":0.1,"incoming":[]},{"id":5,"x":0.1,"incoming":[]},{"id":6,"x":0.1,"incoming":[]},{"id":7,"x":0.1,"incoming":[]},{"id":8,"x":0.1,"incoming":[]},{"id":9,"x":0.1,"incoming":[]},{"id":10,"x":0.1,"incoming":[]},{"id":11,"x":0.1,"incoming":[]},{"id":12,"x":0.1,"incoming":[]}],"hidden":[{"id":52,"x":0.29999998,"incoming":[{"fromId":7,"weight":0.55496633,"enabled":true},{"fromId":0,"weight":-2.228508,"enabled":true}]},{"id":73,"x":0.29999998,"incoming":[{"fromId":6,"weight":0.48558712,"enabled":true},{"fromId":0,"weight":0.4345972,"enabled":true}]},{"id":19,"x":0.49999997,"incoming":[{"fromId":7,"weight":1.0295924,"enabled":false},{"fromId":0,"weight":2.6991332,"enabled":true},{"fromId":52,"weight":0.30984354,"enabled":true}]},{"id":30,"x":0.49999997,"incoming":[{"fromId":8,"weight":0.02581057,"enabled":true},{"fromId":0,"weight":-0.5465857,"enabled":true},{"fromId":11,"weight":0.46978152,"enabled":true},{"fromId":9,"weight":-0.007936746,"enabled":true}]},{"id":37,"x":0.49999997,"incoming":[{"fromId":6,"weight":0.12942661,"enabled":true},{"fromId":0,"weight":1.7212927,"enabled":true},{"fromId":11,"weight":0.21053489,"enabled":true},{"fromId":1,"weight":1.7341816,"enabled":true},{"fromId":73,"weight":1.3575654,"enabled":true},{"fromId":9,"weight":-0.24852258,"enabled":true}]},{"id":46,"x":0.49999997,"incoming":[{"fromId":6,"weight":-0.6546517,"enabled":true},{"fromId":0,"weight":-0.20992944,"enabled":true}]},{"id":77,"x":0.49999997,"incoming":[{"fromId":4,"weight":-0.21262969,"enabled":true},{"fromId":0,"weight":0.6063913,"enabled":true}]},{"id":84,"x":0.7,"incoming":[{"fromId":19,"weight":2.1982403,"enabled":true},{"fromId":0,"weight":0.38986376,"enabled":true}]},{"id":85,"x":0.7,"incoming":[{"fromId":77,"weight":0.42196923,"enabled":true},{"fromId":0,"weight":-0.2050695,"enabled":true}]}],"outputs":[{"id":13,"x":0.9,"incoming":[{"fromId":0,"weight":-0.86397016,"enabled":true},{"fromId":1,"weight":0.16719003,"enabled":true},{"fromId":2,"weight":-1.3761001,"enabled":true},{"fromId":3,"weight":1.1805954,"enabled":true},{"fromId":4,"weight":-0.6630107,"enabled":true},{"fromId":5,"weight":5.701949,"enabled":true},{"fromId":6,"weight":0.44960445,"enabled":true},{"fromId":7,"weight":0.021090955,"enabled":true},{"fromId":8,"weight":-0.48344734,"enabled":true},{"fromId":30,"weight":-0.35903573,"enabled":true},{"fromId":37,"weight":1.3333621,"enabled":true},{"fromId":19,"weight":-0.32492244,"enabled":false},{"fromId":9,"weight":1.0683477,"enabled":true},{"fromId":77,"weight":0.14141536,"enabled":true},{"fromId":84,"weight":0.8991051,"enabled":true},{"fromId":85,"weight":-0.6737307,"enabled":true}]},{"id":14,"x":0.9,"incoming":[{"fromId":0,"weight":0.9966605,"enabled":true},{"fromId":1,"weight":0.98815656,"enabled":true},{"fromId":2,"weight":-0.14601178,"enabled":true},{"fromId":3,"weight":-2.0109222,"enabled":true},{"fromId":4,"weight":0.67859244,"enabled":true},{"fromId":5,"weight":-0.28100336,"enabled":true},{"fromId":6,"weight":0.03473513,"enabled":true},{"fromId":7,"weight":0.6009386,"enabled":true},{"fromId":8,"weight":0.35581443,"enabled":true},{"fromId":12,"weight":-0.078998834,"enabled":true},{"fromId":10,"weight":0.2730469,"enabled":true},{"fromId":30,"weight":0.6095357,"enabled":true},{"fromId":19,"weight":-0.65000033,"enabled":true},{"fromId":9,"weight":0.5810351,"enabled":true},{"fromId":37,"weight":0.451707,"enabled":true}]},{"id":15,"x":0.9,"incoming":[{"fromId":0,"weight":-1.0039276,"enabled":true},{"fromId":1,"weight":0.42218173,"enabled":true},{"fromId":2,"weight":-0.05447316,"enabled":true},{"fromId":3,"weight":1.2558259,"enabled":true},{"fromId":4,"weight":0.7104747,"enabled":true},{"fromId":5,"weight":-1.2626678,"enabled":true},{"fromId":6,"weight":-0.7866507,"enabled":true},{"fromId":7,"weight":1.1627291,"enabled":true},{"fromId":8,"weight":-1.5199153,"enabled":true},{"fromId":19,"weight":1.1756326,"enabled":true},{"fromId":46,"weight":0.4242193,"enabled":true},{"fromId":9,"weight":0.0042515565,"enabled":true}]},{"id":16,"x":0.9,"incoming":[{"fromId":0,"weight":1.1559062,"enabled":true},{"fromId":1,"weight":0.53096974,"enabled":true},{"fromId":2,"weight":-0.26391035,"enabled":true},{"fromId":3,"weight":0.66762894,"enabled":true},{"fromId":4,"weight":-2.4051938,"enabled":true},{"fromId":5,"weight":-1.5032532,"enabled":true},{"fromId":6,"weight":-0.16034475,"enabled":true},{"fromId":7,"weight":-0.40630278,"enabled":true},{"fromId":8,"weight":0.37963352,"enabled":true},{"fromId":10,"weight":0.5831176,"enabled":true}]}]} \ No newline at end of file