1 package nl.tudelft.jpacman.npc.ghost;
2
3 import java.util.EnumMap;
4 import java.util.List;
5 import java.util.Map;
6 import java.util.Optional;
7
8 import nl.tudelft.jpacman.board.Direction;
9 import nl.tudelft.jpacman.board.Square;
10 import nl.tudelft.jpacman.board.Unit;
11 import nl.tudelft.jpacman.level.Player;
12 import nl.tudelft.jpacman.npc.Ghost;
13 import nl.tudelft.jpacman.sprite.Sprite;
14
15 /**
16 * <p>
17 * An implementation of the classic Pac-Man ghost Clyde.
18 * </p>
19 * <p>
20 * Pokey needs a new nickname because out of all the ghosts,
21 * Clyde is the least likely to "C'lyde" with Pac-Man. Clyde is always the last
22 * ghost out of the regenerator, and the loner of the gang, usually off doing
23 * his own thing when not patrolling the bottom-left corner of the maze. His
24 * behavior is very random, so while he's not likely to be following you in hot
25 * pursuit with the other ghosts, he is a little less predictable, and still a
26 * danger.
27 * </p>
28 * <p>
29 * <b>AI:</b> Clyde has two basic AIs, one for when he's far from Pac-Man, and
30 * one for when he is near to Pac-Man.
31 * When Clyde is far away from Pac-Man (beyond eight grid spaces),
32 * Clyde behaves very much like Blinky, trying to move to Pac-Man's exact
33 * location. However, when Clyde gets within eight grid spaces of Pac-Man, he
34 * automatically changes his behavior and runs away.
35 * </p>
36 * <p>
37 * Source: http://strategywiki.org/wiki/Pac-Man/Getting_Started
38 * </p>
39 *
40 * @author Jeroen Roosen
41 */
42 public class Clyde extends Ghost {
43
44 /**
45 * The amount of cells Clyde wants to stay away from Pac Man.
46 */
47 private static final int SHYNESS = 8;
48
49 /**
50 * The variation in intervals, this makes the ghosts look more dynamic and
51 * less predictable.
52 */
53 private static final int INTERVAL_VARIATION = 50;
54
55 /**
56 * The base movement interval.
57 */
58 private static final int MOVE_INTERVAL = 250;
59
60 /**
61 * A map of opposite directions.
62 */
63 private static final Map<Direction, Direction> OPPOSITES = new EnumMap<>(Direction.class);
64
65 static {
66 OPPOSITES.put(Direction.NORTH, Direction.SOUTH);
67 OPPOSITES.put(Direction.SOUTH, Direction.NORTH);
68 OPPOSITES.put(Direction.WEST, Direction.EAST);
69 OPPOSITES.put(Direction.EAST, Direction.WEST);
70 }
71
72 /**
73 * Creates a new "Clyde", a.k.a. "Pokey".
74 *
75 * @param spriteMap The sprites for this ghost.
76 */
77 public Clyde(Map<Direction, Sprite> spriteMap) {
78 super(spriteMap, MOVE_INTERVAL, INTERVAL_VARIATION);
79 }
80
81 /**
82 * {@inheritDoc}
83 *
84 * <p>
85 * Clyde has two basic AIs, one for when he's far from Pac-Man, and one for
86 * when he is near to Pac-Man.
87 * When Clyde is far away from Pac-Man (beyond eight grid spaces),
88 * Clyde behaves very much like Blinky, trying to move to Pac-Man's exact
89 * location. However, when Clyde gets within eight grid spaces of Pac-Man,
90 * he automatically changes his behavior and runs away
91 * </p>
92 */
93 @Override
94 public Optional<Direction> nextAiMove() {
95 assert hasSquare();
96
97 Unit nearest = Navigation.findNearest(Player.class, getSquare());
98 if (nearest == null) {
99 return Optional.empty();
100 }
101 assert nearest.hasSquare();
102 Square target = nearest.getSquare();
103
104 List<Direction> path = Navigation.shortestPath(getSquare(), target, this);
105 if (path != null && !path.isEmpty()) {
106 Direction direction = path.get(0);
107 if (path.size() <= SHYNESS) {
108 return Optional.ofNullable(OPPOSITES.get(direction));
109 }
110 return Optional.of(direction);
111 }
112 return Optional.empty();
113 }
114 }