View Javadoc

1   /**
2    * Emohawk Bot, an implementation of the environment interface standard that 
3    * facilitates the connection between GOAL and Emohawk. 
4    * 
5    * Copyright (C) 2012 Emohawk Bot authors.
6    * 
7    * This program is free software: you can redistribute it and/or modify it under
8    * the terms of the GNU General Public License as published by the Free Software
9    * Foundation, either version 3 of the License, or (at your option) any later
10   * version.
11   * 
12   * This program is distributed in the hope that it will be useful, but WITHOUT
13   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15   * details.
16   * 
17   * You should have received a copy of the GNU General Public License along with
18   * this program. If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package nl.tudelft.goal.emohawk.agent;
22  
23  import java.lang.reflect.Method;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.concurrent.ConcurrentLinkedQueue;
30  import java.util.concurrent.Semaphore;
31  
32  import nl.tudelft.goal.emohawk.messages.Action;
33  import nl.tudelft.goal.emohawk.messages.Percept;
34  import nl.tudelft.goal.emohawk.messages.UnrealIdOrLocation;
35  import nl.tudelft.goal.unreal.messages.BotParameters;
36  import nl.tudelft.goal.unreal.messages.Parameters;
37  import SteeringProperties.ObstacleAvoidanceProperties;
38  import SteeringProperties.WalkAlongProperties;
39  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
40  import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
41  import cz.cuni.amis.pogamut.emohawk.agent.module.sensomotoric.EmoticonType;
42  import cz.cuni.amis.pogamut.emohawk.agent.module.sensomotoric.Place;
43  import cz.cuni.amis.pogamut.emohawk.bot.impl.EmohawkBotController;
44  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.AgentInfo;
45  import cz.cuni.amis.pogamut.ut2004.agent.params.UT2004AgentParameters;
46  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
47  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Initialize;
48  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
49  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
50  import cz.cuni.amis.pogamut.ut2004.utils.UT2004BotRunner;
51  import cz.cuni.amis.utils.exception.PogamutException;
52  import eis.eis2java.annotation.AsAction;
53  import eis.eis2java.annotation.AsPercept;
54  import eis.eis2java.translation.Filter.Type;
55  import eis.eis2java.util.AllPerceptsModule;
56  import eis.eis2java.util.AllPerceptsProvider;
57  import eis.exceptions.ActException;
58  import eis.exceptions.EntityException;
59  import eis.exceptions.PerceiveException;
60  
61  public class EmohawkBotBehavior extends EmohawkBotController<UT2004Bot> implements AllPerceptsProvider {
62  
63  	private Semaphore logic = new Semaphore(0, true);
64  
65  	private WalkAlongProperties walkAlongProperties;
66  
67  	private BotParameters parameters;
68  
69  	protected AllPerceptsModule percepts;
70  
71  	/**
72  	 * Queued up actions.
73  	 */
74  	private ConcurrentLinkedQueue<Action> actions = new ConcurrentLinkedQueue<Action>();
75  
76  	@SuppressWarnings("rawtypes")
77  	public void initializeController(UT2004Bot bot) {
78  		super.initializeController(bot);
79  
80  		// Setup parameters
81  		IAgentLogger logger = bot.getLogger();
82  		UT2004AgentParameters parameters = bot.getParams();
83  		if ((parameters instanceof BotParameters)) {
84  			this.parameters = (BotParameters) parameters;
85  		} else {
86  			log.warning("Provided parameters were not a subclass of UnrealGoalParameters, using defaults.");
87  			this.parameters = new BotParameters(logger);
88  		}
89  		Parameters defaults = BotParameters.getDefaults(logger);
90  		this.parameters.assignDefaults(defaults);
91  	}
92  
93  	protected void initializeModules(UT2004Bot bot) {
94  		super.initializeModules(bot);
95  
96  		steering.addObstacleAvoidanceSteering(new ObstacleAvoidanceProperties());
97  		walkAlongProperties = new WalkAlongProperties();
98  		walkAlongProperties.setDistanceFromThePartner(200);
99  		walkAlongProperties.setGiveWayToPartner(false);
100 		steering.addWalkAlongSteering(new WalkAlongProperties());
101 
102 		// Setup percept module.
103 		try {
104 			percepts = new AllPerceptsModule(this);
105 		} catch (EntityException e) {
106 			throw new PogamutException("Could not create percept module", e);
107 		}
108 
109 	}
110 
111 	/**
112 	 * Prepares the initialization message for Gamebots using the
113 	 * {@link BotParameters} provided to the {@link UT2004BotRunner}.
114 	 * 
115 	 */
116 	@Override
117 	public Initialize getInitializeCommand() {
118 		assert parameters != null;
119 
120 		// Prepare init command
121 		Initialize init = super.getInitializeCommand();
122 		init.setDesiredSkill(parameters.getSkill());
123 		init.setSkin(parameters.getSkin().getUnrealName());
124 		init.setTeam(parameters.getTeam());
125 		init.setShouldLeadTarget(parameters.shouldLeadTarget());
126 		init.setLocation(parameters.getInitialLocation());
127 		init.setRotation(parameters.getInitialRotation());
128 		// Set log level.
129 		bot.getLogger().setLevel(this.parameters.getLogLevel());
130 
131 		return init;
132 	}
133 
134 	@Override
135 	public void logic() throws PogamutException {
136 		super.logic();
137 
138 		// Mark that another logic iteration has began
139 		log.fine("--- Logic iteration ---");
140 
141 		// 0. Execute all outstanding actions.
142 		while (!actions.isEmpty()) {
143 			actions.remove().execute();
144 		}
145 
146 		// 1. Prepare new batch of percepts
147 		try {
148 			percepts.updatePercepts();
149 		} catch (PerceiveException e) {
150 			throw new PogamutException("Could not update percepts", e);
151 		}
152 	}
153 
154 	@Override
155 	public void botShutdown() {
156 		super.botShutdown();
157 
158 		/*
159 		 * The bot has stopped so we can release a permit.
160 		 * 
161 		 * TODO: This should not be required as it implies that actions and
162 		 * percepts are requested after the environment has been killed. However
163 		 * EIS currently allows different threads to manipulate the environment
164 		 * at the same time. Thus it may be possible for one thread to be
165 		 * waiting to acquire this agent while another shuts down the
166 		 * environment.
167 		 */
168 		logic.release();
169 	}
170 
171 	//
172 	// ============
173 	// EIS Percepts
174 	// ============
175 	//
176 
177 	@AsPercept(name = "navigation")
178 	public String navigation() {
179 
180 		if (steering.isNavigating()) {
181 			return "following";
182 			// FIXME: Would like to uss navigator.isExecuting here but it does
183 			// not consider all it's components. traveling.
184 		} else if (pathExecutor.isExecuting() || getBackToNavGraph.isExecuting() || runStraight.isExecuting()) {
185 			return "traveling";
186 		} else if (pathExecutor.isStuck()) {
187 			return "stuck";
188 		} else if (pathExecutor.isPathUnavailable()) {
189 			return "path_unavailable";
190 		} else if (pathExecutor.isTargetReached()) {
191 			return "destination_reached";
192 
193 		} else {
194 			return "waiting";
195 		}
196 	}
197 
198 	@AsPercept(name = "navPoint", multiplePercepts = true, filter = Type.ONCE)
199 	public List<Percept> perceptNavPoints() {
200 		Collection<NavPoint> navPoints = world.getAll(NavPoint.class).values();
201 		List<Percept> percepts = new ArrayList<Percept>(navPoints.size());
202 
203 		for (NavPoint p : navPoints) {
204 			percepts.add(new Percept(p.getId(), p.getLocation(), p.getOutgoingEdges().keySet()));
205 		}
206 
207 		return percepts;
208 	}
209 
210 	// TODO: Because EIS2Java assumes objects returned are unchanging, which is
211 	// not the case with pogamut we can't use any other filters but ALWAYS
212 	// (default) and ONCE.
213 	@AsPercept(name = "person", multiplePercepts = true)
214 	public Collection<Percept> person() {
215 
216 		Collection<Percept> persons = new ArrayList<Percept>();
217 		for (Player p : getPlayers().getVisiblePlayers().values()) {
218 			persons.add(new Percept(p.getId(), p.getName(), p.getLocation(), p.getRotation(), p.getEmotLeft(), p.getEmotCenter(), p
219 					.getEmotRight()));
220 		}
221 		return persons;
222 	}
223 
224 	@AsPercept(name = "self")
225 	public Percept self() {
226 		AgentInfo info = getInfo();
227 		EmoticonType emoteLeft = EmoticonType.get(info.getSelf().getEmotLeft());
228 		EmoticonType emoteCenter = EmoticonType.get(info.getSelf().getEmotCenter());
229 		EmoticonType emoteRight = EmoticonType.get(info.getSelf().getEmotRight());
230 
231 		return new Percept(info.getId(), info.getName(), info.getLocation(), info.getRotation(), emoteLeft, emoteCenter, emoteRight);
232 	}
233 
234 	@AsPercept(name = "emoticon", multiplePercepts = true, filter = Type.ONCE)
235 	public Collection<EmoticonType> emoticon() {
236 
237 		return Arrays.asList(EmoticonType.values());
238 	}
239 
240 	// @AsPercept(name = "animation", multiplePercepts = true, filter =
241 	// Type.ONCE)
242 	// public Set<AnimType> animation() {
243 	// return animations.getAvailableAnimations();
244 	// }
245 
246 	@AsPercept(name = "place", multiplePercepts = true, filter = Type.ONCE)
247 	public Collection<Place> place() {
248 		return places.getPlaces();
249 	}
250 
251 	/**
252 	 * Returns a previously prepared batch of percepts.
253 	 * 
254 	 * @return a previously prepared batch of percepts.
255 	 */
256 	public Map<Method, Object> getAllPercepts() {
257 		return percepts.getAllPercepts();
258 	}
259 
260 	//
261 	// =============
262 	// EIS ACTIONS
263 	// =============
264 	//
265 	@AsAction(name = "stop")
266 	public void stop() {
267 		addAction(new Action() {
268 
269 			@Override
270 			public void execute() {
271 				steering.stopNavigation();
272 				navigation.stopNavigation();
273 
274 			}
275 		});
276 	}
277 
278 	@AsAction(name = "runTo")
279 	public void runTo(UnrealIdOrLocation destination) {
280 		final ILocated location = getLocation(destination);
281 
282 		addAction(new Action() {
283 
284 			@Override
285 			public void execute() {
286 
287 				steering.stopNavigation();
288 				if (!move.isRunning())
289 					move.setRun();
290 				navigation.navigate(location);
291 			}
292 		});
293 	}
294 
295 	@AsAction(name = "walkTo")
296 	public void walkTo(UnrealIdOrLocation destination) {
297 		final ILocated location = getLocation(destination);
298 
299 		addAction(new Action() {
300 
301 			@Override
302 			public void execute() {
303 				steering.stopNavigation();
304 				if (move.isRunning())
305 					move.setWalk();
306 				navigation.navigate(location);
307 			}
308 		});
309 	}
310 
311 	private ILocated getLocation(UnrealIdOrLocation destination) {
312 
313 		ILocated location;
314 		if (destination.isLocation()) {
315 			location = destination.getLocation();
316 		} else {
317 			// Assuming ID's were passed properly this must be a player or
318 			// navpoint.
319 			location = (ILocated) world.get(destination.getId());
320 		}
321 		return location;
322 	}
323 
324 	@AsAction(name = "walkAlong")
325 	public void walkAlong(final Player partner) throws ActException {
326 		addAction(new Action() {
327 
328 			@Override
329 			public void execute() {
330 				navigation.stopNavigation();
331 				if (!move.isRunning())
332 					move.setRun();
333 				walkAlongProperties.setPartnerName(partner.getName());
334 				steering.setWalkAlongSteering(walkAlongProperties);
335 				steering.startNavigation();
336 			}
337 		});
338 	}
339 
340 	@AsAction(name = "emote")
341 	public void emote(final EmoticonType left, final EmoticonType center, final EmoticonType right) throws ActException {
342 		addAction(new Action() {
343 
344 			@Override
345 			public void execute() {
346 				emoticons.clearEmoticons();
347 
348 				if (left == EmoticonType.NONE && center == EmoticonType.NONE && right == EmoticonType.NONE) {
349 					return;
350 				}
351 
352 				if (left == EmoticonType.NONE && center != EmoticonType.NONE && right == EmoticonType.NONE) {
353 					emoticons.setCenterEmoticonType(center);
354 					return;
355 				}
356 
357 				if (left != EmoticonType.NONE && center == EmoticonType.NONE && right != EmoticonType.NONE) {
358 					emoticons.setDoubleEmoticon(left, right);
359 					return;
360 				}
361 
362 				emoticons.setTripleEmoticon(left, center, right);
363 			}
364 		});
365 	}
366 
367 	// @AsAction(name = "animate")
368 	// public void animate(AnimType animation, boolean loop) throws ActException
369 	// {
370 	// animations.stopAnimation();
371 	// animations.playAnimation(animation, loop);
372 	// }
373 
374 	@AsAction(name = "turn")
375 	public void turn(final int amount) throws ActException {
376 		addAction(new Action() {
377 
378 			@Override
379 			public void execute() {
380 				move.turnHorizontal(amount);
381 
382 			}
383 		});
384 	}
385 
386 	@AsAction(name = "turnTo")
387 	public void turnTo(final ILocated location) throws ActException {
388 		addAction(new Action() {
389 
390 			@Override
391 			public void execute() {
392 				move.turnTo(location);
393 			}
394 		});
395 	}
396 
397 	@AsAction(name = "jump")
398 	public void jump() throws ActException {
399 		addAction(new Action() {
400 
401 			@Override
402 			public void execute() {
403 				move.jump();
404 			}
405 		});
406 	}
407 
408 	@AsAction(name = "skip")
409 	public void skip() throws ActException {
410 		// Does nothing.
411 	}
412 
413 	/**
414 	 * Queues up the action to be executed on the first evaluation of the logic
415 	 * cycle.
416 	 * 
417 	 * @param action
418 	 */
419 	public void addAction(Action action) {
420 		actions.add(action);
421 	}
422 }