-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSandbox.as
More file actions
296 lines (265 loc) · 7.5 KB
/
Sandbox.as
File metadata and controls
296 lines (265 loc) · 7.5 KB
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
//
// Copyright 2010, Darren Lafreniere
// <http://www.lafarren.com/theseus-minotaur-solver/>
//
// This file is part of lafarren.com's Theseus and Minotaur Maze Solver,
// or tmmaze-solver.
//
// tmmaze-solver is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// tmmaze-solver is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with tmmaze-solver, named License.txt. If not, see
// <http://www.gnu.org/licenses/>.
//
package com.lafarren.tmmaze.solver
{
import com.lafarren.tmmaze.core.EntityType;
import com.lafarren.tmmaze.core.Game;
import com.lafarren.tmmaze.core.Maze;
import com.lafarren.tmmaze.core.MazeDefinition;
import com.lafarren.tmmaze.core.MoveType;
import com.lafarren.tmmaze.core.PointInt;
import flash.events.EventDispatcher;
import flash.geom.Point;
/**
* INTERNAL PACKAGE CLASS
*
* Implements a sandbox game and maze space for the solver to work in.
* Implements a sandbox game and maze space for the solver to work in.
* For performance reasons, this class breaks encapsulation a bit and
* both extends the Game class and implements the Maze interface. This
* allows the class to cache the often-queried agent success states
* after an agent position changes.
*
* The sandbox wraps a Maze instance and delegates to it for all static
* maze data related methods, and stores its own agent position data,
* allowing the solver to directly change those positions as needed.
* The wrapped Maze's data will never be modified by this class.
*/
internal class Sandbox extends Game implements Maze
{
private const ENABLE_ASSERTIONS:Boolean = false;
private var m_wrappedMaze:Maze;
private var m_entityPositions:Vector.<PointInt>;
// Game game ended and agent success for fast lookups.
private var m_hasEndedCached:Boolean;
private var m_hasSucceededTheseusCached:Boolean;
private var m_hasSucceededMinotaurCached:Boolean;
public function Sandbox(gameSource:Game)
{
super(this);
m_wrappedMaze = gameSource.maze;
if (!gameSource.copyStateTo(this, true))
{
throw new Error("Could not copy game state into sandbox!");
}
const entityCount:int = EntityType.length;
m_entityPositions = new Vector.<PointInt>(entityCount, true);
for (var i:int = 0; i < entityCount; ++i)
{
m_entityPositions[i] = m_wrappedMaze.getEntityPosition(EntityType.fromOrdinal(i));
}
updateOutcomeCache();
}
/**
* @inheritDoc
*/
// Game override
public override function hasEnded():Boolean
{
return m_hasEndedCached;
}
/**
* @inheritDoc
*/
// Game override
public override function hasSucceeded(agent:EntityType):Boolean
{
return (agent == EntityType.THESEUS)
? m_hasSucceededTheseusCached
: m_hasSucceededMinotaurCached;
}
/**
* @inheritDoc
*/
// Maze method
public function get eventDispatcher():EventDispatcher
{
// Does not dispatch events.
return new EventDispatcher();
}
/**
* @inheritDoc
*/
// Maze method
public function cloneMazeDefinition():MazeDefinition
{
return m_wrappedMaze.cloneMazeDefinition();
}
/**
* @inheritDoc
*/
// Maze method
public function get name():String
{
return m_wrappedMaze.name;
}
/**
* @inheritDoc
*/
// Maze method
public function get numCols():int
{
return m_wrappedMaze.numCols;
}
/**
* @inheritDoc
*/
// Maze method
public function get numRows():int
{
return m_wrappedMaze.numRows;
}
/**
* @inheritDoc
*/
// Maze method
public function isTileInside(col:int, row:int):Boolean
{
return m_wrappedMaze.isTileInside(col, row);
}
/**
* @inheritDoc
*/
// Maze method
public function getHorizontalWall(col:int, row:int):Boolean
{
return m_wrappedMaze.getHorizontalWall(col, row);
}
/**
* @inheritDoc
*/
// Maze method
public function getVerticalWall(col:int, row:int):Boolean
{
return m_wrappedMaze.getVerticalWall(col, row);
}
/**
* @inheritDoc
*/
// Maze method
public function getEntityPosition(entityType:EntityType):PointInt
{
return m_entityPositions[entityType.ordinal].clone();
}
/**
* @inheritDoc
*/
// Maze method
public function getEntityPositionFloat(entityType:EntityType):Point
{
return m_entityPositions[entityType.ordinal].toPoint();
}
/**
* @inheritDoc
*/
// Maze method
public function areEntitiesAtSamePosition(entityTypeA:EntityType, entityTypeB:EntityType):Boolean
{
return m_entityPositions[entityTypeA.ordinal].equals(m_entityPositions[entityTypeB.ordinal]);
}
/**
* @inheritDoc
*/
// Maze method
public function isMoveOpen(col:int, row:int, moveType:MoveType):Boolean
{
return m_wrappedMaze.isMoveOpen(col, row, moveType);
}
/**
* @inheritDoc
*/
// Maze method
public function move(agent:EntityType, moveType:MoveType):Boolean
{
var result:Boolean = false;
if (ENABLE_ASSERTIONS)
{
if (!agent.isAgent)
{
throw new Error("Unexpected: Sandbox.move called for a non-agent entity!");
}
if (hasEnded())
{
throw new Error("Unexpected: Sandbox.move called for an ended game!");
}
}
{
// OPTIMIZATION: assumes that isMoveOpen won't modify position
var position:PointInt = m_entityPositions[agent.ordinal];
if (m_wrappedMaze.isMoveOpen(position.x, position.y, moveType))
{
position.x += moveType.offsetX;
position.y += moveType.offsetY;
updateOutcomeCache();
result = true;
}
}
return result;
}
/**
* Returns a direct reference to the internal entity PointInt position object.
* @param entityType An EntityType reference.
* @return a direct reference to the internal entity PointInt position object.
*/
public function getEntityPositionRef(entityType:EntityType):PointInt
{
return m_entityPositions[entityType.ordinal];
}
/**
* Updates the cached return values for the hasEnded and hasSucceeded
* methods. This method MUST be called after positions are manually
* updated. This does not happen automatically for the sake of
* performance.
*/
public function updateOutcomeCache():void
{
// Slight optimization: mazes have many more successful Minotaur
// paths than successful Theseus paths, so test Minotaur success
// first.
m_hasSucceededMinotaurCached = super.hasSucceeded(EntityType.MINOTAUR);
m_hasSucceededTheseusCached = m_hasSucceededMinotaurCached
? false
: super.hasSucceeded(EntityType.THESEUS);
m_hasEndedCached = m_hasSucceededTheseusCached || m_hasSucceededMinotaurCached;
}
/**
* Convenience method. If the sandbox's current agent is not Theseus,
* it will be ticked until Theseus is current.
*/
public function tickUntilTheseusIsCurrent():void
{
while (!hasEnded() && !isCurrentAgent(EntityType.THESEUS))
{
tick();
}
}
/**
* Provides a public means of calling setCurrentAgentProtected.
*
* @see Game.setCurrentAgentProtected
*/
public function setCurrentAgent(agent:EntityType, numMovesThisTurn:int = 0):void
{
super.setCurrentAgentProtected(agent, numMovesThisTurn);
}
}
}