1 | package jpacman.model; |
2 | |
3 | import java.util.Observable; |
4 | import java.util.Vector; |
5 | |
6 | /** |
7 | * The top level state machine, which also acts as interface to the viewer |
8 | * (which observes the state machine) and the controller (which triggers the |
9 | * state machine events). |
10 | * |
11 | * @author Arie van Deursen; Aug 1, 2003 |
12 | * @version $Id: Engine.java,v 1.6 2008/02/04 11:00:59 arie Exp $ |
13 | */ |
14 | public class Engine extends Observable { |
15 | |
16 | /** |
17 | * The underlying pacman game, on top of which this engine runs. |
18 | */ |
19 | private Game theGame; |
20 | |
21 | /** |
22 | * The starting state is special, since within the starting state the rest |
23 | * of the game is being constructed. So other state inspection methods |
24 | * should each check that they are not in the starting state first, and only |
25 | * then their inspection can yield a meaningful result. |
26 | */ |
27 | private boolean starting = false; |
28 | |
29 | /** |
30 | * True iff the game is being suspended. |
31 | */ |
32 | private boolean halted = false; |
33 | |
34 | /** |
35 | * The game has been set up, and is just waiting to get started. |
36 | * @return true iff game is starting. |
37 | */ |
38 | public synchronized boolean inStartingState() { |
39 | return starting && !(theGame.playerDied() || theGame.playerWon()); |
40 | } |
41 | |
42 | /** |
43 | * We're actually playing a game. |
44 | * @return true iff game is still on. |
45 | */ |
46 | public synchronized boolean inPlayingState() { |
47 | return !starting && !halted && theGame.initialized() |
48 | && !theGame.playerDied() && !theGame.playerWon(); |
49 | } |
50 | |
51 | /** |
52 | * Alas, we died after playing. |
53 | * @return true iff player died. |
54 | */ |
55 | public synchronized boolean inDiedState() { |
56 | return !starting && theGame.playerDied(); |
57 | } |
58 | |
59 | /** |
60 | * Yes, we have been lucky and we won! |
61 | * @return true iff player has won. |
62 | */ |
63 | public synchronized boolean inWonState() { |
64 | return !starting && theGame.playerWon(); |
65 | } |
66 | |
67 | /** |
68 | * We're pausing a little and halted the game. |
69 | * @return true iff we're halted. |
70 | */ |
71 | public synchronized boolean inHaltedState() { |
72 | return halted; |
73 | } |
74 | |
75 | /** |
76 | * A superstate representing having died or won. |
77 | * @return true iff the game is over. |
78 | */ |
79 | public synchronized boolean inGameOverState() { |
80 | return inDiedState() || inWonState(); |
81 | } |
82 | |
83 | /** |
84 | * We can be in at most one of the Engine's states. |
85 | * |
86 | * @return True if we in exactly one state. |
87 | */ |
88 | protected boolean invariant() { |
89 | // beware of the xor: |
90 | // xor on odd nr of args also permits all args to be true. |
91 | // (see in3420 exam in 2003) |
92 | boolean oneStateOnly = inStartingState() |
93 | ^ inPlayingState() |
94 | ^ inHaltedState() |
95 | ^ inDiedState() |
96 | ^ inWonState() |
97 | && !(inStartingState() && inPlayingState() && inHaltedState() |
98 | && inDiedState() && inWonState()); |
99 | return oneStateOnly && theGame != null; |
100 | } |
101 | |
102 | /** |
103 | * Start a new engine with a default game using a default map. |
104 | * Postcondition: the game is ready to be played. |
105 | */ |
106 | public Engine() { |
107 | theGame = new Game(); |
108 | initialize(theGame); |
109 | assert inStartingState(); |
110 | assert invariant(); |
111 | } |
112 | |
113 | /** |
114 | * Start a new engine with a specific game. |
115 | * |
116 | * @param g |
117 | * Game to be used. |
118 | */ |
119 | public Engine(Game g) { |
120 | assert g != null; |
121 | assert g.initialized(); |
122 | initialize(g); |
123 | } |
124 | |
125 | /** |
126 | * Reset the game. |
127 | * @param g The underlying game. |
128 | */ |
129 | private void initialize(Game g) { |
130 | assert g != null; |
131 | assert g.initialized(); |
132 | theGame = g; |
133 | starting = true; |
134 | assert inStartingState(); |
135 | assert invariant(); |
136 | } |
137 | |
138 | /** |
139 | * Stop the game if it is in a playing state. |
140 | */ |
141 | public synchronized void quit() { |
142 | assert invariant(); |
143 | if (inPlayingState()) { |
144 | halted = true; |
145 | notifyViewers(); |
146 | } |
147 | assert invariant(); |
148 | } |
149 | |
150 | /** |
151 | * (Re)start the game, unless it is playing. |
152 | */ |
153 | public synchronized void start() { |
154 | assert invariant(); |
155 | if (inHaltedState()) { |
156 | assert halted; |
157 | halted = false; |
158 | } else { |
159 | if (inStartingState()) { |
160 | assert starting; |
161 | starting = false; |
162 | } else { |
163 | if (inGameOverState()) { |
164 | assert !starting; |
165 | theGame.initialize(); |
166 | starting = true; |
167 | } |
168 | } |
169 | } |
170 | notifyViewers(); |
171 | assert invariant() : "Invariant invalid after updating the viewers"; |
172 | } |
173 | |
174 | /** |
175 | * Try to move the player along a given offset. |
176 | * |
177 | * @param dx |
178 | * Horizontal offset |
179 | * @param dy |
180 | * Vertical offset. |
181 | */ |
182 | public synchronized void movePlayer(int dx, int dy) { |
183 | assert invariant(); |
184 | if (inPlayingState()) { |
185 | theGame.movePlayer(dx, dy); |
186 | notifyViewers(); |
187 | } |
188 | assert invariant(); |
189 | } |
190 | |
191 | /** |
192 | * Try to move the given monster along a given offset. |
193 | * |
194 | * @param monster |
195 | * The monster to be moved |
196 | * @param dx |
197 | * Horizontal offset |
198 | * @param dy |
199 | * Vertical offset |
200 | */ |
201 | public synchronized void moveMonster(Monster monster, int dx, int dy) { |
202 | // TODO monster moves not yet supported. |
203 | |
204 | } |
205 | |
206 | |
207 | |
208 | /** |
209 | * Warn the observers that the state has changed. |
210 | */ |
211 | private void notifyViewers() { |
212 | setChanged(); |
213 | notifyObservers(); |
214 | } |
215 | |
216 | /** |
217 | * The game itself. Package visible, used for testing purposes. |
218 | * |
219 | * @return the full game. |
220 | */ |
221 | Game getGame() { |
222 | return theGame; |
223 | } |
224 | |
225 | /** |
226 | * The guest code at position x, y. Offered for viewers who would like to |
227 | * draw the board. |
228 | * |
229 | * @param x |
230 | * X coordinate |
231 | * @param y |
232 | * Y coordinate |
233 | * @return Type code of the guest. |
234 | */ |
235 | public synchronized char getGuestCode(int x, int y) { |
236 | return getGame().getGuestCode(x, y); |
237 | } |
238 | |
239 | /** |
240 | * Width of the board, offered for clients wishing to draw the board. |
241 | * |
242 | * @return the board's width. |
243 | */ |
244 | public synchronized int boardWidth() { |
245 | return getGame().boardWidth(); |
246 | } |
247 | |
248 | /** |
249 | * Height of the board, offered for clients wishing to draw the board. |
250 | * |
251 | * @return the board's height. |
252 | */ |
253 | public synchronized int boardHeight() { |
254 | assert invariant(); |
255 | return getGame().boardHeight(); |
256 | } |
257 | |
258 | /** |
259 | * The amount of food eaten so far. |
260 | * |
261 | * @return Amount of food eaten. |
262 | */ |
263 | public synchronized int getFoodEaten() { |
264 | assert invariant(); |
265 | return getPlayer().getPointsEaten(); |
266 | } |
267 | |
268 | /** |
269 | * Return a fresh vector containing all the monsters in the game. |
270 | * |
271 | * @return All monsters. |
272 | */ |
273 | public synchronized Vector<Monster> getMonsters() { |
274 | assert invariant(); |
275 | Vector<Monster> result = theGame.getMonsters(); |
276 | assert result != null; |
277 | return result; |
278 | } |
279 | |
280 | /** |
281 | * Return the player of the game. |
282 | * |
283 | * @return The game's player. |
284 | */ |
285 | public synchronized Player getPlayer() { |
286 | assert invariant(); |
287 | return theGame.getPlayer(); |
288 | } |
289 | |
290 | /** |
291 | * @return Returns the most recent advancement of the player in the |
292 | * horizontal direction. |
293 | */ |
294 | public synchronized int getPlayerLastDx() { |
295 | return getGame().getPlayerLastDx(); |
296 | } |
297 | |
298 | /** |
299 | * @return Returns the most recent advancement of the player in the vertical |
300 | * direction. |
301 | */ |
302 | public synchronized int getPlayerLastDy() { |
303 | return getGame().getPlayerLastDy(); |
304 | } |
305 | } |