View Javadoc

1   package cz.cuni.amis.pogamut.udk.agent.navigation.loquenavigator;
2   
3   import java.util.logging.Level;
4   import java.util.logging.Logger;
5   
6   import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
7   import cz.cuni.amis.pogamut.udk.agent.module.sensor.AgentInfo;
8   import cz.cuni.amis.pogamut.udk.agent.module.sensor.Players;
9   import cz.cuni.amis.pogamut.udk.agent.module.sensor.Senses;
10  import cz.cuni.amis.pogamut.udk.agent.navigation.IUDKPathRunner;
11  import cz.cuni.amis.pogamut.udk.bot.command.AdvancedLocomotion;
12  import cz.cuni.amis.pogamut.udk.bot.impl.UDKBot;
13  import cz.cuni.amis.pogamut.udk.communication.messages.gbcommands.Move;
14  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.NavPointNeighbourLink;
15  import cz.cuni.amis.utils.NullCheck;
16  
17  /**
18   * Responsible for direct running to location.
19   *
20   * <p>This class commands the agent directly to the given location. Silently
21   * tries to resolve incidental collisions, troubling pits, obstacles, etc.
22   * In other words, give me a destination and you'll be there in no time.</p>
23   *
24   * <h4>Precise jumper</h4>
25   *
26   * Most of the incident running problems and troubles can be solved by precise
27   * single-jumping or double-jumping. This class calculates the best spots for
28   * initiating such jumps and then follows jump sequences in order to nicely
29   * jump and then land exactly as it was desired.
30   *
31   * <h4>Pogamut troubles</h4>
32   *
33   * This class was supposed to use autotrace rays to scan the space and ground
34   * in from of the agent. However, results of depending on these traces were
35   * much worst than jumping whenever possible. Therefore, no autotrace is being
36   * used and the agent simply jumps a lot. Some human players do that as well.
37   * See {@link #runToLocation } for details.
38   *
39   * <h4>Speed</h4>
40   *
41   * The agent does not ever try to run faster than with speed of <i>1.0</i> as
42   * it is used by most of <i>body.runTo*()</i> methods. Anyway, speeding is not
43   * available to common players (AFAIK), so why should this agent cheat?
44   *
45   * <h4>Focus</h4>
46   *
47   * This class works with destination location as well as agent focal point.
48   * Since the agent can look at something else rather than the destination,
49   * this running API is also suitable for engaging in combat or escaping from
50   * battles.
51   *
52   * @author Juraj Simlovic [jsimlo@matfyz.cz]
53   */
54  public class LoqueRunner implements IUDKPathRunner {
55      /**
56       * Number of steps we have taken.
57       */
58      private int runnerStep = 0;
59  
60      /**
61       * Jumping sequence of a single-jumps.
62       */
63      private int runnerSingleJump = 0;
64      /**
65       * Jumping sequence of a double-jumps.
66       */
67      private int runnerDoubleJump = 0;
68  
69      /**
70       * Collision counter.
71       */
72      private int collisionCount = 0;
73      
74      /**
75       * Collision location.
76       */
77      private Location collisionSpot = null;
78  
79      /*========================================================================*/
80  
81      /**
82       * Initializes direct running to the given destination.
83       */
84      public void reset()
85      {
86          // reset working info
87          runnerStep = 0;
88          runnerSingleJump = 0;
89          runnerDoubleJump = 0;
90          collisionCount = 0;
91          collisionSpot = null;
92      }
93  
94      /*========================================================================*/
95      
96      /**
97       * Handles running directly to the specified location.
98       *
99       * <h4>Pogamut troubles</h4>
100      *
101      * <p>Reachchecks are buggy (they ignore most of the pits). Autotrace rays
102      * are buggy (they can not be used to scan the ground). Now, how's the agent
103      * supposed to travel along a map full of traps, when he is all blind, his
104      * guide-dogs are stupid and blind as well and his white walking stick is
105      * twisted?</p>
106      *
107      * <p>There is only one thing certain here (besides death and taxes): No
108      * navpoint is ever placed above a pit or inside map geometry. But, navpoint
109      * positions are usually the only places where we know the ground is safe.
110      * So, due to all this, the agent tries to jump whenever possible and still
111      * suitable for landing each jump on a navpoint. This still helps overcome
112      * most of the map troubles. Though it is counter-productive at times.</p>
113      *
114      * @param firstLocation Location to which to run.
115      * @param secondLocation Location where to continue (may be null).
116      * @param focus Location to which to look.
117      * @param reachable Whether the location is reachable.
118      * @return True, if no problem occured.
119      */
120     public boolean runToLocation (Location firstLocation, Location secondLocation, Location focus, NavPointNeighbourLink navPointsLink, boolean reachable)
121     {
122 
123         if (log != null && log.isLoggable(Level.FINER)) {            
124      	            log.finer(
125 	                "Runner.runToLocation(): runnerStep is "
126 	                + runnerStep + ", reachable is " + reachable + ",  navPointsLink is" + navPointsLink
127 	            );            
128         }
129         
130         // take another step
131         runnerStep++;
132 
133         // wait for delayed start: this is usually used for waiting
134         // in order to ensure the previous runner request completion
135         if (runnerStep <= 0) {
136         	// TODO: [Jimmy] what's this? It is never effective, as we're increasing runnerStep in the previous statement
137             return true;
138         }
139 
140         // are we just starting a new runner request? the first step should
141         // always be like this, in order to gain speed/direction before jumps
142         if (runnerStep <= 1)
143         {
144             // start running to that location..
145             bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
146 
147              // This heuristics works when the bot continues movement - e.g. when the jump is in the same direction as
148              // current velocity vector and we have already some speed
149              if ((navPointsLink != null) && (
150                     navPointsLink.isForceDoubleJump()
151                 || (navPointsLink.getNeededJump() != null)
152                 || (navPointsLink.getFlags() & 8) != 0
153                 )) {
154 
155                  // some jumplinks (R_Jump) are leading down, we jump only when we are going up or to approx. same level
156                  if (((navPointsLink.getFlags() & 8) == 0) || (memory.getLocation().z - 100 <= firstLocation.z)) { 
157                     Location direction = Location.sub(firstLocation, memory.getLocation()).getNormalized();
158                     Location velocityDir = new Location(memory.getVelocity().asVector3d()).getNormalized();
159                     Double result = Math.acos(direction.dot(velocityDir));
160 
161                     // we will jump if our speed is reasonable and our direction differs max. 20 degrees
162                     if (memory.getVelocity().size() > 200 && !result.isNaN() && result < (Math.PI / 9)) 
163                         return resolveJump (firstLocation, secondLocation, focus, navPointsLink, reachable);
164                  }
165              }                
166          
167             return true;
168         }
169 
170         // are we single-jumping already?
171         if (runnerSingleJump > 0)
172         {
173             // continue with the single-jump
174             return iterateSingleJumpSequence (firstLocation, secondLocation, focus, reachable);
175         }
176         // are we double-jumping already?
177         else if (runnerDoubleJump > 0)
178         {
179             // continue with the double-jump
180             return iterateDoubleJumpSequence (firstLocation, secondLocation, focus, reachable);
181         }
182         // collision experienced?
183         if (senses.isCollidingOnce())
184         {
185             // try to resolve it
186             return resolveCollision (firstLocation, secondLocation, focus, reachable);
187         }
188         // are we going to jump now?
189         else 
190         if 
191         (
192             // the agent is not jumping already
193             (runnerSingleJump == 0) && (runnerDoubleJump == 0)
194             &&
195             (
196                 // is the destination directly unreachable?
197                 !reachable
198                 // is there an unpleasant pit ahead?
199                 || (navPointsLink != null) &&
200                 (
201                     navPointsLink.isForceDoubleJump()
202                 || (navPointsLink.getNeededJump() != null)
203                 || (navPointsLink.getFlags() & 8) != 0
204                 )
205                 // note: see pogamut notes in javadoc above
206                 //|| true
207                 // is there an unpleasant wall ahead?
208                 // note: see pogamut notes in javadoc above
209                 //|| true
210                 // are we going to jump just because we want to show off?
211                 //|| (Math.random () < .03)
212             )
213         ) 
214         {
215             // try to start a jump
216             return resolveJump (firstLocation, secondLocation, focus, navPointsLink, reachable);
217         }
218 
219         // otherwise: just keep running to that location..
220         bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
221 
222         if (log != null && log.isLoggable(Level.FINER)) {
223      	            log.finer(
224 	                "Runner.runToLocation(): issuing default move command to: " + firstLocation
225 	            );
226         }
227         
228         return true;
229     }
230 
231     /*========================================================================*/
232 
233     /**
234      * Tries to resolve collisions.
235      *
236      * <p>Only continuous collisions are resolved, first by a double jump, then
237      * by a single-jump.</p>
238      *
239      * @param firstLocation Location to which to run.
240      * @param secondLocation Location where to continue (may be null).
241      * @param focus Location to which to look.
242      * @param reachable Whether the location is reachable.
243      * @return True, if no problem occured.
244      */
245     private boolean resolveCollision (Location firstLocation, Location secondLocation, Location focus, boolean reachable)
246     {
247         // are we colliding at a new spot?
248         if (
249             // no collision yet
250             (collisionSpot == null)
251             // or the last collision is far away
252             || (memory.getLocation().getDistance2D(collisionSpot) > 120)
253         ) {
254             // setup new collision spot info
255         	if (log != null && log.isLoggable(Level.FINER)) 
256 	            log.finer(
257 	                "Runner.resolveCollision(): collision at "
258 	                + (int) memory.getLocation().getDistance2D(firstLocation)
259 	            );
260             collisionSpot = memory.getLocation();
261             collisionCount = 1;
262             // meanwhile: keep running to the location..
263             bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
264             return true;
265         }
266         // so, we were already colliding here before..
267         // try to solve the problem according to how long we're here..
268         else 
269         	switch (collisionCount++ % 2) {
270             case 0:
271                 // ..first by a double jump sequnce
272             	if (log != null && log.isLoggable(Level.FINER)) 
273 	                log.finer(
274 	                    "Runner.resolveCollision(): repeated collision (" + collisionCount + "):"
275 	                    + " double-jumping at " + (int) memory.getLocation().getDistance2D(firstLocation)
276 	                );
277                 return initDoubleJumpSequence (firstLocation, secondLocation, focus, reachable);
278 
279             default:
280                 // ..then by a single-jump sequence
281             	if (log != null && log.isLoggable(Level.FINER)) 
282 	                log.finer(
283 	                    "Runner.resolveCollision(): repeated collision (" + collisionCount + "):"
284 	                    + " single-jumping at " + (int) memory.getLocation().getDistance2D(firstLocation)
285 	                );
286                 return initSingleJumpSequence (firstLocation, secondLocation, focus, reachable);
287         	}
288     }
289 
290     /*========================================================================*/
291 
292     /**
293      * Starts a new (single or double)-jump sequence based on the distance.
294      *
295      * <p>Due to inevitability of ensuring of landing on destination locations,
296      * jumps may only be started, when it is appropriate. This method decides,
297      * whether and which jump would be appropriate and the initiates jumping
298      * sequence.</p>
299      *
300      * @param firstLocation Location to which to run.
301      * @param secondLocation Location where to continue (may be null).
302      * @param focus Location to which to look.
303      * @param reachable Whether the location is reachable.
304      * @return True, if no problem occured.
305      */
306     private boolean resolveJump (Location firstLocation, Location secondLocation, Location focus, NavPointNeighbourLink navPointsLink, boolean reachable)
307     {    		
308         // get the distance of the target location
309         int distance = (int) memory.getLocation().getDistance2D(firstLocation);
310         // get the agent overall velocity
311         int velocity = (int) memory.getVelocity().size();
312 
313         // cut the jumping distance of the next jump.. this is to allow to
314         // jump more than once per one runner request, while ensuring that
315         // the last jump will always land exactly on the destination..
316         int jumpDistance = distance % 1000;
317 
318         // get the agent z-distance (e.g. is the destination above/below?)..     
319         int zDistance = (int) firstLocation.getDistanceZ(memory.getLocation());
320         // adjust jumping distance for jumps into lower/higher positions
321         jumpDistance += Math.min (200, Math.max (-200, zDistance));
322         
323         log.finer("Runner.resolveJump: distance = " + distance + ", velocity = " + velocity + ", jumpDistance = " + jumpDistance + ", zDistance = " + zDistance);        
324         
325         // TODO: [Jimmy] test it!
326         boolean enforceDoubleJump = false;       
327         if ((navPointsLink != null) 
328         	 && (
329         	 	  (    navPointsLink.getNeededJump() != null 
330         	 	   || (navPointsLink.getFlags() & 8 ) != 0
331         	      ) 
332         		  && zDistance > 60 
333         	    )
334            ){
335         	// we should jump && the zDistance between our position and the target is more than 60units
336         	// we won't ever make it with ordinary jump
337         	enforceDoubleJump = true;
338         	log.finest("Runner.resolveJump(): double jump indicated");
339         }
340 
341         // we already missed all jumping opportunities
342         if (jumpDistance < 370)
343         {
344             //TODO: test - michal bida
345             //We force jump when needed jump is true or when jump flag is set
346             if (navPointsLink != null) {
347                 if (navPointsLink.getNeededJump() != null || (navPointsLink.getFlags() & 8 ) != 0)
348                 	// TODO: [Jimmy] test it!
349                 	if (enforceDoubleJump) return initDoubleJumpSequence(firstLocation, secondLocation, focus, reachable);
350                     return initSingleJumpSequence (firstLocation, secondLocation, focus, reachable);
351             }
352             
353             // if it's reachable, don't worry, we'll make it no matter what
354             // if it's unreachable: well, are we waiting for the next jump?
355             if (reachable || (distance >= 1000))
356             {
357                 // just keep running to that location..
358                 bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
359                 return true;
360             }
361             // otherwise: we should try to solve the situation here, since
362             // the destination is not reachable (i.e. there is an obstacle
363             // or a pit ahead).. however, the reachability checks does not
364             // work very well, and raycasting is broken too.. well, trying
365             // to resolve this situation by a random choice does not work
366             // either.. therefore, just keep running to that location and
367             // wait for success or timeout, whatever comes first..
368             bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
369             return true;
370         }
371         // this is the right space for a single-jump
372         else if (jumpDistance < 470)
373         {
374         	// is double jump enforced?
375         	if (enforceDoubleJump) return initDoubleJumpSequence(firstLocation, secondLocation, focus, reachable);
376             // start a single-jump sequences        	
377             return initSingleJumpSequence (firstLocation, secondLocation, focus, reachable);
378         }
379         // we already missed the double-jump opportunity
380         // this is the space for waiting for a single-jump
381         else if (jumpDistance < 600)
382         {
383         	// but if the double jump is enforced... we should try that!
384         	if (enforceDoubleJump) {
385         		// even though we've missed the opportunity, the ordinary jump won't help us!
386         		// TODO: [Jimmy] may be we should fail?
387         		return initDoubleJumpSequence(firstLocation, secondLocation, focus, reachable);
388         	}
389 
390             // meanwhile: keep running to the location..
391             bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
392             return true;
393         }
394         // this is the right space for double-jumping
395         // but only, if we have the necessary speed
396         else if ((jumpDistance < 700) && (velocity > 300))
397         {
398         	if (!enforceDoubleJump) {
399         		// we do not need double jump
400         		// so... don't use double jump when link is R_Jump (for that we need just single jump)
401 	            if (navPointsLink != null && (navPointsLink.getFlags() & 8 ) != 0) {
402 	                // meanwhile: keep running to the location..
403 	                bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
404 	                return true;
405 	            }
406         	}
407         	// we truly need the double jump! single jump won't take us too the desired target!
408 	           
409             // start double-jump sequence by double-jump command
410             return initDoubleJumpSequence (firstLocation, secondLocation, focus, reachable);
411         }
412         // otherwise, wait for the right double-jump distance
413         // meanwhile: keep running to the location..
414         bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
415         return true;
416     }
417 
418     /*========================================================================*/
419 
420     /**
421      * Initiates new single-jump sequence.
422      *
423      * <p>Single-jump sequences are used to ensure that no single-jump is ever
424      * turned accidentally into a semi-double-jump. Such mishaps could lead to
425      * overjumping the desired landing location.</p>
426      *
427      * @param firstLocation Location to which to run.
428      * @param secondLocation Location where to continue (may be null).
429      * @param focus Location to which to look.
430      * @param reachable Whether the location is reachable.
431      * @return True, if no problem occured.
432      */
433     private boolean initSingleJumpSequence (Location firstLocation, Location secondLocation, Location focus, boolean reachable)
434     {
435         // do not allow two jumping sequences
436         if ((runnerSingleJump > 0) || (runnerDoubleJump > 0))
437             throw new RuntimeException ("jumping sequence aleady started");
438 
439         log.finer("Runner.initSingleJumpSequence() !");
440         
441         // point to the destination
442         bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
443         // issue jump command
444         body.jump ();
445         // and setup sequence
446         runnerSingleJump = 1;
447         return true;
448     }
449 
450     /**
451      * Follows single-jump sequence steps.
452      * @param firstLocation Location to which to run.
453      * @param secondLocation Location where to continue (may be null).
454      * @param focus Location to which to look.
455      * @param reachable Whether the location is reachable.
456      * @return True, if no problem occured.
457      */
458     private boolean iterateSingleJumpSequence (Location firstLocation, Location secondLocation, Location focus, boolean reachable)
459     {
460         // get the distance of the target location
461         int distance = (int) memory.getLocation().getDistance2D(firstLocation);
462         // get the agent vertical velocity (e.g. is the agent jumping/falling?)..
463         int zVelocity = (int) memory.getVelocity().z;
464 
465         // what phase of the single-jump sequence?
466         switch (runnerSingleJump)
467         {
468             // the first phase: wait for the jump
469             case 1:
470                 // did the agent started the jump already?
471                 if (zVelocity > 100)
472                 {
473                     // continue the jump sequence by waiting for a peak
474                 	if (log != null && log.isLoggable(Level.FINER)) log.finer("Runner.iterateSingleJumpSequence(): single-jump registered at " + distance + ", z-velo " + zVelocity);
475                     runnerSingleJump++;
476                 }
477                 // meanwhile: just wait for the jump to start
478                 bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
479                 return true;
480 
481             //  the last phase: finish the jump
482             default:
483                 // did the agent started to fall already
484                 if (zVelocity <= 0)
485                 {
486                     // kill the single-jump sequence
487                 	if (log != null && log.isLoggable(Level.FINER)) log.finer("Runner.iterateSingleJumpSequence(): single-jump completed at " + distance + ", z-velo " + zVelocity);
488                     runnerSingleJump = 0;
489                 }
490                 // meanwhile: just wait for the jump to start
491                 bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
492                 return true;
493         }
494     }
495 
496     /*========================================================================*/
497 
498     /**
499      * Initiates new double-jump sequence.
500      *
501      * <p>Double-jump sequences are used to ensure that the agent correctly
502      * claims the double-jump boost at the jump peak.</p>
503      *
504      * @param firstLocation Location to which to run.
505      * @param secondLocation Location where to continue (may be null).
506      * @param focus Location to which to look.
507      * @param reachable Whether the location is reachable.
508      * @return True, if no problem occured.
509      */
510     private boolean initDoubleJumpSequence (Location firstLocation, Location secondLocation, Location focus, boolean reachable)
511     {
512         // do not allow two jumping sequences
513         if ((runnerSingleJump > 0) || (runnerDoubleJump > 0))
514             throw new RuntimeException ("jumping sequence aleady started");
515 
516         // point to the destination
517         //body.strafeTo(firstLocation, focus);
518 
519         double delay = 0.39;
520         double jumpZ = 680;        
521         
522         // 70 -> delay 0.39 / jumpZ 680
523         double distanceZ = firstLocation.getDistanceZ(memory.getLocation());
524         if (distanceZ > 0) {
525         	jumpZ = 680 * distanceZ / 70;
526         }
527         if (jumpZ < 480) jumpZ = 480;
528         
529         log.finer("Runner.initDoubleJumpSequence(): double jump delay = " + delay + ", jumpZ = " + jumpZ);
530         
531         // make some inferation about the doubleJump parameters 
532         
533         // issue jump command
534         body.doubleJump(delay, jumpZ);
535         // and setup sequence
536         runnerDoubleJump = 1;
537         return true;
538     }
539 
540     /**
541      * Follows double-jump sequence steps.
542      * @param firstLocation Location to which to run.
543      * @param secondLocation Location where to continue (may be null).
544      * @param focus Location to which to look.
545      * @param reachable Whether the location is reachable.
546      * @return True, if no problem occured.
547      */
548     private boolean iterateDoubleJumpSequence (Location firstLocation, Location secondLocation, Location focus, boolean reachable)
549     {
550         // get the distance of the target location
551         int distance = (int) memory.getLocation().getDistance2D(firstLocation);
552         // get the agent vertical velocity (e.g. is the agent jumping/falling?)..
553         int zVelocity = (int) memory.getVelocity().z;
554 
555         // what phase of the double-jump sequence?
556         switch (runnerDoubleJump)
557         {
558             // the first phase: wait for the jump
559             case 1:
560                 // did the agent started the jump already?
561                 if (zVelocity > 100)
562                 {
563                     // continue the double-jump sequence by waiting for a peak
564                     if (log != null && log.isLoggable(Level.FINER)) log.finer("Runner.iterateDoubleJumpSequence(): double-jump registered at " + distance + ", z-velo " + zVelocity);
565                     runnerDoubleJump++;
566                 }
567                 // meanwhile: just wait for the jump to start
568                 bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
569                 return true;
570 
571             // the second phase: claim the extra boost at jump peak..
572             case 2:
573                 // is this the awaited jump peak?
574                 if (zVelocity < 150)
575                 {
576                     // continue the double-jump sequence by a single jump boost
577                     if (log != null && log.isLoggable(Level.FINER)) log.finer("Runner.iterateDoubleJumpSequence(): double-jump boost at " + distance + ", z-velo " + zVelocity);
578                     body.jump ();
579                     runnerDoubleJump++;
580                     return true;
581                 }
582                 // meanwhile: just wait for the jump peak
583                 bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
584                 return true;
585 
586             // the last phase:  finish the double-jump
587             default:
588                 // did the agent started to fall already
589                 if (zVelocity <= 0)
590                 {
591                     // kill the doule-jump sequence
592                     runnerDoubleJump = 0;
593                 }
594                 // meanwhile: just wait for the agent to start falling
595                 bot.getAct().act(new Move().setFirstLocation(firstLocation).setSecondLocation(secondLocation).setFocusLocation(focus));
596                 return true;
597         }
598     }
599 
600     /*========================================================================*/
601 
602     /** Agent's bot. */
603     protected UDKBot bot;
604     /** Loque memory. */
605     protected AgentInfo memory;
606     /** Agent's body. */
607     protected AdvancedLocomotion body;
608     /** Agent's log. */
609     protected Logger log;
610     /** Base agent's senses. */
611 	protected Senses senses;
612 
613     /*========================================================================*/
614 
615     /**
616      * Constructor.
617      * @param bot Agent's bot.
618      * @param memory Loque memory.
619      */
620     public LoqueRunner (UDKBot bot, AgentInfo agentInfo, AdvancedLocomotion locomotion, Logger log) {
621         // setup reference to agent
622     	NullCheck.check(bot, "bot");
623     	this.bot = bot;
624         NullCheck.check(agentInfo, "agentInfo");
625         this.memory = agentInfo;
626         NullCheck.check(locomotion, "locomotion");
627         this.body = new AdvancedLocomotion(bot, log);
628         this.senses = new Senses(bot, memory, new Players(bot), log);
629         this.log = log;
630     }
631     
632 }