From 1cefc1b68f3eac7f5806828b5833ef6bd4f0d27d Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Mon, 23 Nov 2015 11:42:48 +0100 Subject: Implement GhostPredator common behaviour --- src/ch/epfl/maze/physical/GhostPredator.java | 196 ++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 3 deletions(-) diff --git a/src/ch/epfl/maze/physical/GhostPredator.java b/src/ch/epfl/maze/physical/GhostPredator.java index 34b8c4f..44bb1f6 100644 --- a/src/ch/epfl/maze/physical/GhostPredator.java +++ b/src/ch/epfl/maze/physical/GhostPredator.java @@ -1,7 +1,11 @@ package ch.epfl.maze.physical; +import ch.epfl.maze.util.Direction; import ch.epfl.maze.util.Vector2D; +import java.util.ArrayList; +import java.util.List; + /** * Predator ghost that have two different modes and a home position in the labyrinth. * @@ -9,9 +13,45 @@ import ch.epfl.maze.util.Vector2D; */ abstract public class GhostPredator extends Predator { - /* constants relative to the Pac-Man game */ - public static final int SCATTER_DURATION = 14; - public static final int CHASE_DURATION = 40; + 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. @@ -20,6 +60,156 @@ abstract public class GhostPredator extends Predator { */ 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.getCurrentDirection(); + } + + /** + * 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 An array of Direction choices + * @return An array of optimal Direction choices + */ + private Direction[] selectBestPaths(Vector2D targetPosition, Direction[] choices) { + List bestPaths = new ArrayList<>(); + 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.toArray(new Direction[bestPaths.size()]); + } + + /** + * 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(Direction[] choices, Daedalus daedalus) { + this.countCycle(); + + Direction[] smartChoices = choices.length > 1 ? this.excludeOrigin(choices) : choices; + Direction[] bestPaths = this.selectBestPaths(this.getTargetPosition(daedalus), smartChoices); + return this.move(bestPaths); + } + + /** + * 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.mode) { + 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); + } -- cgit v1.2.3