1 package nl.tudelft.jpacman.npc;
2
3 import nl.tudelft.jpacman.board.Direction;
4 import nl.tudelft.jpacman.board.Square;
5 import nl.tudelft.jpacman.board.Unit;
6 import nl.tudelft.jpacman.sprite.Sprite;
7
8 import java.util.ArrayList;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Optional;
12 import java.util.Random;
13
14 /**
15 * A non-player unit.
16 *
17 * @author Jeroen Roosen
18 */
19 public abstract class Ghost extends Unit {
20 /**
21 * The sprite map, one sprite for each direction.
22 */
23 private final Map<Direction, Sprite> sprites;
24
25 /**
26 * The base move interval of the ghost.
27 */
28 private final int moveInterval;
29
30 /**
31 * The random variation added to the {@link #moveInterval}.
32 */
33 private final int intervalVariation;
34
35 /**
36 * Calculates the next move for this unit and returns the direction to move
37 * in.
38 * <p>
39 * Precondition: The NPC occupies a square (hasSquare() holds).
40 *
41 * @return The direction to move in, or <code>null</code> if no move could
42 * be devised.
43 */
44 public Direction nextMove() {
45 return nextAiMove().orElseGet(this::randomMove);
46 }
47
48 /**
49 * Tries to calculate a move based on the behaviour of the npc.
50 *
51 * @return an optional containing the move or empty if the current state of the game
52 * makes the ai move impossible
53 */
54 public abstract Optional<Direction> nextAiMove();
55
56 /**
57 * Creates a new ghost.
58 *
59 * @param spriteMap The sprites for every direction.
60 * @param moveInterval The base interval of movement.
61 * @param intervalVariation The variation of the interval.
62 */
63 protected Ghost(Map<Direction, Sprite> spriteMap, int moveInterval, int intervalVariation) {
64 this.sprites = spriteMap;
65 this.intervalVariation = intervalVariation;
66 this.moveInterval = moveInterval;
67 }
68
69 @Override
70 public Sprite getSprite() {
71 return sprites.get(getDirection());
72 }
73
74 /**
75 * The time that should be taken between moves.
76 *
77 * @return The suggested delay between moves in milliseconds.
78 */
79 public long getInterval() {
80 return this.moveInterval + new Random().nextInt(this.intervalVariation);
81 }
82
83 /**
84 * Determines a possible move in a random direction.
85 *
86 * @return A direction in which the ghost can move, or <code>null</code> if
87 * the ghost is shut in by inaccessible squares.
88 */
89 protected Direction randomMove() {
90 Square square = getSquare();
91 List<Direction> directions = new ArrayList<>();
92 for (Direction direction : Direction.values()) {
93 if (square.getSquareAt(direction).isAccessibleTo(this)) {
94 directions.add(direction);
95 }
96 }
97 if (directions.isEmpty()) {
98 return null;
99 }
100 int i = new Random().nextInt(directions.size());
101 return directions.get(i);
102 }
103 }