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
1 change: 1 addition & 0 deletions assets/saves/oct26-2/best-calculator-2.json
Original file line number Diff line number Diff line change
@@ -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}]}]}
1 change: 1 addition & 0 deletions assets/saves/oct26-8/best-calculator-50.json
Original file line number Diff line number Diff line change
@@ -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}]}]}
8 changes: 4 additions & 4 deletions core/src/main/java/com/buaisociety/pacman/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -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-2" + File.separator + "generation-2.json");
// load exactFile contents to string
String json;
try {
Expand All @@ -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(12, 4); // Add 4 new inputs
//impl.updateClients(200); // have 200 pacman games at once
return impl;
} else {
Expand All @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,15 @@ private SpecialTrainingConditions() {

public static @NotNull EventListener<EntityPreSpawnEvent> onEntityPreSpawn() {
return event -> {
// Prevent ghosts from spawning during training
if (event.getEntityType() == 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);
// }
};
}

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/com/buaisociety/pacman/Tournament.java
Original file line number Diff line number Diff line change
Expand Up @@ -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-8" + File.separator + "best-calculator-50.json");
if (!file.exists()) {
System.err.println("Could not find the file: " + file.getAbsolutePath());
return null;
Expand Down
48 changes: 45 additions & 3 deletions core/src/main/java/com/buaisociety/pacman/entity/PacmanEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
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 kotlin.Pair;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector2d;
import org.joml.Vector2i;
Expand All @@ -23,6 +22,10 @@ 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;

public PacmanEntity(@NotNull Maze maze, @NotNull Config config) {
super(maze, EntityType.PACMAN);

Expand All @@ -33,6 +36,36 @@ 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 int getDistanceToNearestPellet(Direction direction) {
Tile startTile = maze.getTile(getTilePosition());
Tile neighborTile = startTile.getNeighbor(direction);
if (!neighborTile.getState().isPassable()) {
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);
}

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
Expand Down Expand Up @@ -75,6 +108,15 @@ public double getSpeed() {
}
}

public Pair<Integer, Direction> getDistanceAndDirectionToNearestPelletAndGhost() {
Tile startTile = maze.getTile(getTilePosition());
return pathFinder.getDistanceAndDirectionToNearestPowerPellet(startTile);
}

public boolean isInSuperMode() {
return maze.getFrightenedTimer() > 0;
}

@Override
public @NotNull Behavior getBehavior() {
return behavior;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
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;
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;
import org.joml.Vector2i;

public class NeatPacmanBehavior implements Behavior {

Expand All @@ -21,6 +24,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;
}
Expand All @@ -42,6 +48,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();
Expand All @@ -54,13 +73,92 @@ public Direction getDirection(@NotNull Entity entity) {
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);

// Nearest distance to ghost:
int distanceToGhostForward = pacman.getDistanceToNearestGhost(forward);
int distanceToGhostLeft = pacman.getDistanceToNearestGhost(left);
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 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);

// Get the current score and number of pellets left
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<Integer, Direction> 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,
canMoveRight ? 1f : 0f,
canMoveBehind ? 1f : 0f,
closestPalletIsForward ? 1f : 0f,
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,
// 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++) {
Expand Down
Loading