summaryrefslogtreecommitdiff
path: root/src/ch/epfl/maze/physical/World.java
blob: 257fa65b4da46d1e6583de41e1a088b21171d114 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package ch.epfl.maze.physical;

import ch.epfl.maze.util.Direction;
import ch.epfl.maze.util.Vector2D;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;

/**
 * World that is represented by a labyrinth of tiles in which an {@code Animal}
 * can move.
 *
 * @author EPFL
 * @author Pacien TRAN-GIRARD
 */
public abstract class World {

    /* tiles constants */
    public static final int FREE = 0;
    public static final int WALL = 1;
    public static final int START = 2;
    public static final int EXIT = 3;
    public static final int NOTHING = -1;

    /**
     * Structure of the labyrinth, an NxM array of tiles
     */
    private final int[][] labyrinth;

    private final Vector2D start;
    private final Vector2D exit;

    /**
     * Constructs a new world with a labyrinth. The labyrinth must be rectangle.
     *
     * @param labyrinth Structure of the labyrinth, an NxM array of tiles
     */
    public World(int[][] labyrinth) {
        this.labyrinth = labyrinth;

        this.start = this.findFirstTileOfType(World.START);
        this.exit = this.findFirstTileOfType(World.EXIT);
    }

    /**
     * Finds the coordinates of the first occurrence of the given tile type.
     *
     * @param tileType Type of the tile
     * @return A Vector2D of the first occurrence of the given tile type
     */
    private Vector2D findFirstTileOfType(int tileType) {
        for (int x = 0; x < this.getWidth(); ++x)
            for (int y = 0; y < this.getHeight(); ++y)
                if (this.getTile(x, y) == tileType)
                    return new Vector2D(x, y);

        return null;
    }

    /**
     * Determines whether the labyrinth has been solved by every animal.
     *
     * @return <b>true</b> if no more moves can be made, <b>false</b> otherwise
     */
    abstract public boolean isSolved();

    /**
     * Resets the world as when it was instantiated.
     */
    abstract public void reset();

    /**
     * Returns a copy of the set of all current animals in the world.
     *
     * @return A set of all animals in the world
     */
    public Set<Animal> getAnimalSet() {
        return null;
    }

    /**
     * Returns a copy of the list of all current animals in the world.
     *
     * @return A list of all animals in the world
     * @implNote Not abstract for compatibility purpose (in order not to break tests)
     * @deprecated Use getAnimalSet() instead
     */
    public List<Animal> getAnimals() {
        return new ArrayList<>(this.getAnimalSet());
    }

    /**
     * Checks in a safe way the tile number at position (x, y) in the labyrinth.
     *
     * @param x Horizontal coordinate
     * @param y Vertical coordinate
     * @return The tile number at position (x, y), or the NOTHING tile if x or y is
     * incorrect.
     */
    public final int getTile(int x, int y) {
        if (x < 0 || x >= this.getWidth()) return World.NOTHING;
        if (y < 0 || y >= this.getHeight()) return World.NOTHING;
        return this.labyrinth[y][x];
    }

    /**
     * Determines if coordinates are free to walk on.
     *
     * @param x Horizontal coordinate
     * @param y Vertical coordinate
     * @return <b>true</b> if an animal can walk on tile, <b>false</b> otherwise
     */
    public final boolean isFree(int x, int y) {
        int tile = this.getTile(x, y);
        return !(tile == World.WALL || tile == World.NOTHING);
    }

    /**
     * Determines if coordinates are free to walk on.
     *
     * @param position The position vector
     * @return <b>true</b> if an animal can walk on tile, <b>false</b> otherwise
     */
    public final boolean isFree(Vector2D position) {
        return this.isFree(position.getX(), position.getY());
    }

    /**
     * Computes and returns the available choices for a position in the
     * labyrinth. The result will be typically used by {@code Animal} in
     * {@link ch.epfl.maze.physical.Animal#move(Set) move(Set<Direction>)}
     *
     * @param position A position in the maze
     * @return A set of all available choices at a position
     */
    public final Set<Direction> getChoiceSet(Vector2D position) {
        Set<Direction> choices = EnumSet.noneOf(Direction.class);

        for (Direction dir : Direction.POSSIBLE_DIRECTIONS)
            if (this.isFree(position.addDirectionTo(dir)))
                choices.add(dir);

        return choices.isEmpty() ? EnumSet.of(Direction.NONE) : choices;
    }

    /**
     * Computes and returns the available choices for a position in the
     * labyrinth. The result will be typically used by {@code Animal} in
     * {@link ch.epfl.maze.physical.Animal#move(Direction[]) move(Direction[])}
     *
     * @param position A position in the maze
     * @return An array of all available choices at a position
     * @deprecated Use @code{Set<Direction> getChoiceSet(Vector2D position)} instead
     */
    public final Direction[] getChoices(Vector2D position) {
        Set<Direction> choiceSet = this.getChoiceSet(position);
        return choiceSet.toArray(new Direction[choiceSet.size()]);
    }

    /**
     * Returns horizontal length of labyrinth.
     *
     * @return The horizontal length of the labyrinth
     */
    public final int getWidth() {
        return this.labyrinth[0].length;
    }

    /**
     * Returns vertical length of labyrinth.
     *
     * @return The vertical length of the labyrinth
     */
    public final int getHeight() {
        return this.labyrinth.length;
    }

    /**
     * Returns the entrance of the labyrinth at which animals should begin when
     * added.
     *
     * @return Start position of the labyrinth, null if none.
     */
    public final Vector2D getStart() {
        return this.start;
    }

    /**
     * Returns the exit of the labyrinth at which animals should be removed.
     *
     * @return Exit position of the labyrinth, null if none.
     */
    public final Vector2D getExit() {
        return this.exit;
    }

}