From 4da9bdc4fa2f44eedba3dff29af7b0ce9180e442 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Tue, 24 Nov 2015 23:43:00 +0100 Subject: Refactor Ghosts --- src/ch/epfl/maze/physical/GhostPredator.java | 224 ------------------- src/ch/epfl/maze/physical/pacman/Blinky.java | 3 +- src/ch/epfl/maze/physical/pacman/Clyde.java | 3 +- src/ch/epfl/maze/physical/pacman/Ghost.java | 243 +++++++++++++++++++++ src/ch/epfl/maze/physical/pacman/Inky.java | 15 +- src/ch/epfl/maze/physical/pacman/Pinky.java | 3 +- .../physical/stragegies/reducer/CostReducer.java | 51 +++++ 7 files changed, 304 insertions(+), 238 deletions(-) delete mode 100644 src/ch/epfl/maze/physical/GhostPredator.java create mode 100644 src/ch/epfl/maze/physical/pacman/Ghost.java create mode 100644 src/ch/epfl/maze/physical/stragegies/reducer/CostReducer.java (limited to 'src/ch') diff --git a/src/ch/epfl/maze/physical/GhostPredator.java b/src/ch/epfl/maze/physical/GhostPredator.java deleted file mode 100644 index c1cfca8..0000000 --- a/src/ch/epfl/maze/physical/GhostPredator.java +++ /dev/null @@ -1,224 +0,0 @@ -package ch.epfl.maze.physical; - -import ch.epfl.maze.util.Direction; -import ch.epfl.maze.util.Vector2D; - -import java.util.EnumSet; -import java.util.Set; - -/** - * Predator ghost that have two different modes and a home position in the labyrinth. - * - * @author Pacien TRAN-GIRARD - */ -abstract public class GhostPredator extends Predator { - - public enum Mode { - CHASE(40), - SCATTER(14); - - public static final Mode DEFAULT = CHASE; - public final int duration; - - /** - * Constructs a new Mode with the given duration. - * - * @param duration The duration in cycles - */ - Mode(int duration) { - this.duration = duration; - } - - /** - * Returns the next Mode. - * - * @return The next Mode - */ - public Mode getNext() { - switch (this) { - case CHASE: - return SCATTER; - case SCATTER: - return CHASE; - default: - return DEFAULT; - } - } - } - - private static Prey commonPrey; - - private final Vector2D homePosition; - - private Mode mode; - private int modeCycle; - - /** - * Constructs a predator with a specified position. - * - * @param position Position of the predator in the labyrinth - */ - public GhostPredator(Vector2D position) { - super(position); - - this.homePosition = position; - - this.mode = Mode.DEFAULT; - this.modeCycle = 0; - } - - /** - * Selects a new random Prey to chase in the Daedalus. - * - * @param daedalus The Daedalus - * @return The Chosen One - */ - private static Prey selectRandomPrey(Daedalus daedalus) { - if (daedalus.getPreys().isEmpty()) return null; - - int randomPreyIndex = GhostPredator.RANDOM_SOURCE.nextInt(daedalus.getPreys().size()); - return daedalus.getPreys().get(randomPreyIndex); - } - - /** - * Returns the commonly targeted Prey. - * - * @param daedalus The Daedalus - * @return The common Prey - */ - private static Prey getPrey(Daedalus daedalus) { - if (GhostPredator.commonPrey == null || !daedalus.hasPrey(GhostPredator.commonPrey)) - GhostPredator.commonPrey = GhostPredator.selectRandomPrey(daedalus); - - return GhostPredator.commonPrey; - } - - /** - * Returns the commonly targeted Prey's position. - * - * @param daedalus The Daedalus - * @return The position of the Prey - */ - protected final Vector2D getPreyPosition(Daedalus daedalus) { - Prey prey = GhostPredator.getPrey(daedalus); - - if (prey == null) return this.homePosition; - return prey.getPosition(); - } - - /** - * Returns the commonly targeted Prey's Direction. - * - * @param daedalus The Daedalus - * @return The Direction the Prey is facing - */ - protected final Direction getPreyDirection(Daedalus daedalus) { - Prey prey = GhostPredator.getPrey(daedalus); - - if (prey == null) return Direction.NONE; - return prey.getDirection(); - } - - /** - * Calculates the Euclidean distance from the adjacent position at the given Direction to the target position. - * - * @param dir The adjacent Direction - * @param targetPosition The targeted position - * @return The Euclidean distance between the two positions - */ - private double calcDistanceFromAdjacentDirectionTo(Direction dir, Vector2D targetPosition) { - return this - .getPosition() - .addDirectionTo(dir) - .sub(targetPosition) - .dist(); - } - - /** - * Selects the best Direction in the given choices by minimizing the Euclidean distance to the targeted position. - * - * @param targetPosition The targeted position - * @param choices A set of Direction choices - * @return A set of optimal Direction choices - */ - private Set selectBestPaths(Vector2D targetPosition, Set choices) { - Set bestPaths = EnumSet.noneOf(Direction.class); - double minDist = Double.MAX_VALUE; - - for (Direction dir : choices) { - double dist = this.calcDistanceFromAdjacentDirectionTo(dir, targetPosition); - - if (dist < minDist) { - minDist = dist; - bestPaths.clear(); - } - - if (dist <= minDist) - bestPaths.add(dir); - } - - return bestPaths; - } - - /** - * Rotates to the next Mode. - */ - protected void rotateMode() { - this.mode = this.mode.getNext(); - this.modeCycle = 0; - } - - /** - * Increments the cycle counter and rotates to the next Mode if the Mode's duration has been reached. - */ - private void countCycle() { - this.modeCycle += 1; - - if (this.modeCycle >= this.mode.duration) - this.rotateMode(); - } - - @Override - public Direction move(Set choices, Daedalus daedalus) { - this.countCycle(); - - Set smartChoices = choices.size() > 1 ? this.excludeOrigin(choices) : choices; - Set bestPaths = this.selectBestPaths(this.getTargetPosition(daedalus), smartChoices); - return this.move(bestPaths); - } - - /** - * Returns the current Mode. - * - * @return The current Mode - */ - protected Mode getMode(Daedalus daedalus) { - return this.mode; - } - - /** - * Returns the position to target according to the current Mode. - * - * @param daedalus The Daedalus - * @return The position to target - */ - protected Vector2D getTargetPosition(Daedalus daedalus) { - switch (this.getMode(daedalus)) { - case CHASE: - return this.getPreyTargetPosition(daedalus); - case SCATTER: - return this.homePosition; - default: - return this.homePosition; - } - } - - /** - * Returns the Prey's projected targeted position. - * - * @param daedalus The Daedalus - * @return The projected position - */ - protected abstract Vector2D getPreyTargetPosition(Daedalus daedalus); - -} diff --git a/src/ch/epfl/maze/physical/pacman/Blinky.java b/src/ch/epfl/maze/physical/pacman/Blinky.java index 4e8c4a0..5f81d13 100644 --- a/src/ch/epfl/maze/physical/pacman/Blinky.java +++ b/src/ch/epfl/maze/physical/pacman/Blinky.java @@ -2,7 +2,6 @@ package ch.epfl.maze.physical.pacman; import ch.epfl.maze.physical.Animal; import ch.epfl.maze.physical.Daedalus; -import ch.epfl.maze.physical.GhostPredator; import ch.epfl.maze.util.Vector2D; /** @@ -11,7 +10,7 @@ import ch.epfl.maze.util.Vector2D; * @author EPFL * @author Pacien TRAN-GIRARD */ -public class Blinky extends GhostPredator { +public class Blinky extends Ghost { /** * Constructs a Blinky with a starting position. diff --git a/src/ch/epfl/maze/physical/pacman/Clyde.java b/src/ch/epfl/maze/physical/pacman/Clyde.java index 40089db..2d7e47a 100644 --- a/src/ch/epfl/maze/physical/pacman/Clyde.java +++ b/src/ch/epfl/maze/physical/pacman/Clyde.java @@ -2,7 +2,6 @@ package ch.epfl.maze.physical.pacman; import ch.epfl.maze.physical.Animal; import ch.epfl.maze.physical.Daedalus; -import ch.epfl.maze.physical.GhostPredator; import ch.epfl.maze.util.Vector2D; /** @@ -12,7 +11,7 @@ import ch.epfl.maze.util.Vector2D; * @author EPFL * @author Pacien TRAN-GIRARD */ -public class Clyde extends GhostPredator { +public class Clyde extends Ghost { private static double PROXIMITY_THRESHOLD = 4.0d; diff --git a/src/ch/epfl/maze/physical/pacman/Ghost.java b/src/ch/epfl/maze/physical/pacman/Ghost.java new file mode 100644 index 0000000..f8f511e --- /dev/null +++ b/src/ch/epfl/maze/physical/pacman/Ghost.java @@ -0,0 +1,243 @@ +package ch.epfl.maze.physical.pacman; + +import ch.epfl.maze.physical.Daedalus; +import ch.epfl.maze.physical.Predator; +import ch.epfl.maze.physical.Prey; +import ch.epfl.maze.physical.stragegies.picker.RandomPicker; +import ch.epfl.maze.physical.stragegies.reducer.BackwardReducer; +import ch.epfl.maze.physical.stragegies.reducer.CostReducer; +import ch.epfl.maze.util.Direction; +import ch.epfl.maze.util.Vector2D; + +import java.util.EnumSet; +import java.util.Set; + +/** + * Predator ghost that have two different modes and a home position in the labyrinth. + * + * @author Pacien TRAN-GIRARD + */ +abstract public class Ghost extends Predator implements BackwardReducer, CostReducer, RandomPicker { + + public enum Mode { + CHASE(40), + SCATTER(14); + + public static final Mode DEFAULT = CHASE; + public final int duration; + + /** + * Constructs a new Mode with the given duration. + * + * @param duration The duration in cycles + */ + Mode(int duration) { + this.duration = duration; + } + + /** + * Returns the next Mode. + * + * @return The next Mode + */ + public Mode getNext() { + switch (this) { + case CHASE: + return SCATTER; + case SCATTER: + return CHASE; + default: + return DEFAULT; + } + } + } + + private static Prey commonPrey; + + private final Vector2D homePosition; + + private Mode mode; + private int modeCycle; + + /** + * Constructs a predator with a specified position. + * + * @param position Position of the predator in the labyrinth + */ + public Ghost(Vector2D position) { + super(position); + + this.homePosition = position; + + this.mode = Mode.DEFAULT; + this.modeCycle = 0; + } + + /** + * Returns the cost to reach the target by choosing the given Direction by calculating the Euclidean distance. + * + * @param choice The Direction choice + * @param daedalus The Daedalus + * @return The Euclidean distance cost + */ + @Override + public int getChoiceCost(Direction choice, Daedalus daedalus) { + Vector2D target = this.getTargetPosition(daedalus); + return (int) (this.getDistanceTo(choice, target) * 100); + } + + @Override + public Set reduce(Set choices) { + return EnumSet.noneOf(Direction.class); + } + + /** + * Selects the best Direction in the given choices by minimizing the Euclidean distance to the targeted position + * after excluding the provenance if possible. + * + * @param choices A set of Direction choices + * @param daedalus The Daedalus + * @return A set of optimal Direction choices + */ + @Override + public Set reduce(Set choices, Daedalus daedalus) { + Set forwardChoices = choices.size() > 1 ? BackwardReducer.super.reduce(choices) : choices; + return CostReducer.super.reduce(forwardChoices, daedalus); + } + + @Override + public Direction move(Set choices, Daedalus daedalus) { + this.countCycle(); + + Set bestChoices = this.reduce(choices, daedalus); + return this.pick(bestChoices); + } + + /** + * Returns the Prey's projected targeted position. + * + * @param daedalus The Daedalus + * @return The projected position + */ + abstract protected Vector2D getPreyTargetPosition(Daedalus daedalus); + + /** + * Returns the current Mode. + * + * @param daedalus The Daedalus + * @return The current Mode + */ + protected Mode getMode(Daedalus daedalus) { + return this.mode; + } + + /** + * Returns the position to target according to the current Mode. + * + * @param daedalus The Daedalus + * @return The position to target + */ + protected Vector2D getTargetPosition(Daedalus daedalus) { + switch (this.getMode(daedalus)) { + case CHASE: + return this.getPreyTargetPosition(daedalus); + case SCATTER: + return this.homePosition; + default: + return this.homePosition; + } + } + + /** + * Selects a new random Prey to chase in the Daedalus. + * + * @param daedalus The Daedalus + * @return The Chosen One + */ + private static Prey selectAnyPrey(Daedalus daedalus) { + if (daedalus.getPreySet().isEmpty()) return null; + return daedalus.getPreySet().stream().findAny().get(); + } + + /** + * Sets a random Prey as the common Pre. + * + * @param daedalus The Daedalus + */ + private static synchronized void setAnyPrey(Daedalus daedalus) { + Ghost.commonPrey = Ghost.selectAnyPrey(daedalus); + } + + /** + * Returns the commonly targeted Prey. + * + * @param daedalus The Daedalus + * @return The common Prey + */ + private static Prey getPrey(Daedalus daedalus) { + if (Ghost.commonPrey == null || !daedalus.hasPrey(Ghost.commonPrey)) + Ghost.setAnyPrey(daedalus); + + return Ghost.commonPrey; + } + + /** + * Returns the commonly targeted Prey's position. + * + * @param daedalus The Daedalus + * @return The position of the Prey + */ + protected final Vector2D getPreyPosition(Daedalus daedalus) { + Prey prey = Ghost.getPrey(daedalus); + + if (prey == null) return this.homePosition; + return prey.getPosition(); + } + + /** + * Returns the commonly targeted Prey's Direction. + * + * @param daedalus The Daedalus + * @return The Direction the Prey is facing + */ + protected final Direction getPreyDirection(Daedalus daedalus) { + Prey prey = Ghost.getPrey(daedalus); + + if (prey == null) return Direction.NONE; + return prey.getDirection(); + } + + /** + * Calculates the Euclidean distance from the adjacent position at the given Direction to the target position. + * + * @param dir The adjacent Direction + * @param targetPosition The targeted position + * @return The Euclidean distance between the two positions + */ + private double getDistanceTo(Direction dir, Vector2D targetPosition) { + return this + .getPosition() + .addDirectionTo(dir) + .sub(targetPosition) + .dist(); + } + + /** + * Rotates to the next Mode. + */ + protected void rotateMode() { + this.mode = this.mode.getNext(); + this.modeCycle = 0; + } + + /** + * Increments the cycle counter and rotates to the next Mode if the Mode's duration has been reached. + */ + private void countCycle() { + this.modeCycle += 1; + + if (this.modeCycle >= this.mode.duration) + this.rotateMode(); + } + +} diff --git a/src/ch/epfl/maze/physical/pacman/Inky.java b/src/ch/epfl/maze/physical/pacman/Inky.java index 05c712c..c716058 100644 --- a/src/ch/epfl/maze/physical/pacman/Inky.java +++ b/src/ch/epfl/maze/physical/pacman/Inky.java @@ -2,7 +2,6 @@ package ch.epfl.maze.physical.pacman; import ch.epfl.maze.physical.Animal; import ch.epfl.maze.physical.Daedalus; -import ch.epfl.maze.physical.GhostPredator; import ch.epfl.maze.util.Vector2D; import java.util.NoSuchElementException; @@ -14,9 +13,9 @@ import java.util.NoSuchElementException; * @author EPFL * @author Pacien TRAN-GIRARD */ -public class Inky extends GhostPredator { +public class Inky extends Ghost { - private GhostPredator companion; + private Ghost companion; /** * Finds Inky's best friend (Blinky) in the Daedalus. @@ -24,10 +23,10 @@ public class Inky extends GhostPredator { * @param daedalus The Daedalus * @return The companion if found, null otherwise */ - private static GhostPredator findCompanion(Daedalus daedalus) { + private static Ghost findCompanion(Daedalus daedalus) { try { return (Blinky) daedalus - .getPredators() + .getPredatorSet() .stream() .filter(pred -> pred instanceof Blinky) .findFirst() @@ -43,7 +42,7 @@ public class Inky extends GhostPredator { * @param position Starting position of Inky in the labyrinth * @param companion Inky's accomplice */ - public Inky(Vector2D position, GhostPredator companion) { + public Inky(Vector2D position, Ghost companion) { super(position); this.companion = companion; } @@ -63,7 +62,7 @@ public class Inky extends GhostPredator { * @param daedalus The Daedalus * @return Inky's companion if present, null otherwise */ - private GhostPredator getCompanion(Daedalus daedalus) { + private Ghost getCompanion(Daedalus daedalus) { if (this.companion == null) this.companion = Inky.findCompanion(daedalus); @@ -77,7 +76,7 @@ public class Inky extends GhostPredator { * @return Inky's companion's position if present, null otherwise */ private Vector2D getCompanionPosition(Daedalus daedalus) { - GhostPredator companion = this.getCompanion(daedalus); + Ghost companion = this.getCompanion(daedalus); if (companion == null) return new Vector2D(); return companion.getPosition(); } diff --git a/src/ch/epfl/maze/physical/pacman/Pinky.java b/src/ch/epfl/maze/physical/pacman/Pinky.java index f3e145d..344f55a 100644 --- a/src/ch/epfl/maze/physical/pacman/Pinky.java +++ b/src/ch/epfl/maze/physical/pacman/Pinky.java @@ -2,7 +2,6 @@ package ch.epfl.maze.physical.pacman; import ch.epfl.maze.physical.Animal; import ch.epfl.maze.physical.Daedalus; -import ch.epfl.maze.physical.GhostPredator; import ch.epfl.maze.util.Direction; import ch.epfl.maze.util.Vector2D; @@ -12,7 +11,7 @@ import ch.epfl.maze.util.Vector2D; * @author EPFL * @author Pacien TRAN-GIRARD */ -public class Pinky extends GhostPredator { +public class Pinky extends Ghost { private static final int TARGET_OFFSET = 4; diff --git a/src/ch/epfl/maze/physical/stragegies/reducer/CostReducer.java b/src/ch/epfl/maze/physical/stragegies/reducer/CostReducer.java new file mode 100644 index 0000000..bfa5191 --- /dev/null +++ b/src/ch/epfl/maze/physical/stragegies/reducer/CostReducer.java @@ -0,0 +1,51 @@ +package ch.epfl.maze.physical.stragegies.reducer; + +import ch.epfl.maze.physical.Daedalus; +import ch.epfl.maze.util.Direction; + +import java.util.EnumSet; +import java.util.Set; + +/** + * A filter keeping only the cheapest choices. + * + * @author Pacien TRAN-GIRARD + */ +public interface CostReducer extends ChoiceReducer { + + /** + * Returns the cost of a choice. + * + * @param choice The Direction choice + * @param daedalus The Daedalus + * @return The cost of the given choice + */ + int getChoiceCost(Direction choice, Daedalus daedalus); + + @Override + default Set reduce(Set choices) { + return this.reduce(choices, null); + } + + @Override + default Set reduce(Set choices, Daedalus daedalus) { + Set cheaperChoices = EnumSet.noneOf(Direction.class); + int minimalCost = Integer.MAX_VALUE; + + for (Direction choice : choices) { + int choiceCost = this.getChoiceCost(choice, daedalus); + + if (choiceCost < minimalCost) { + cheaperChoices.clear(); + minimalCost = choiceCost; + } + + if (choiceCost <= minimalCost) { + cheaperChoices.add(choice); + } + } + + return cheaperChoices; + } + +} -- cgit v1.2.3