001    package edu.cmu.gamebots;
002    
003    //  edu.isi.gamebots.examples.ExampleBot
004    //  Copyright 2000, University of Southern California,
005    //                  Information Science Institute
006    //
007    //  Personal and Educational use is hereby granted.
008    //  Permission required for commercial use and redistribution.
009    
010    // CMU Gamebots Team:
011    // Minor hacks added to make it play domination a little better...
012    // Mods by [srs] Steve Schaffer (srs3+@andrew.cmu.edu)
013    //       [chris] Chris Sollitto (cs3@andrew.cmu.edu)
014    
015    
016    import java.awt.*;
017    import java.awt.geom.*;
018    import java.lang.*;
019    import java.util.*;
020    
021    import javax.swing.*;
022    
023    import edu.isi.gamebots.client.*;
024    import edu.isi.gamebots.examples.*;
025    
026    
027    /**
028     *  This example implementation the Bot class shows basic message handling, Bot
029     *  control, and and uses {@link UTMapUI} for a map in its user interface.
030     *
031     *  @author <a href="mailto:amarshal+gamebots@isi.edu">Andrew n marshall</a>
032     *  @author <a href="mailto:srs3+@andrew.cmu.edu">Steve Schaffer</a>
033     *  @author <a href="mailto:cs3@andrew.cmu.edu'>Chris Sollitto</a>
034     */
035    public class CMU_JBot extends Bot {
036      // Private Static Data
037      ///////////////////////////////////////////////////////////////////////////
038      protected static final int MAX_OUTPUT = 100;
039      protected static final double PLAYER_RADIUS = 18.0d;
040      protected static final double THETA_DELTA = (2d*Math.PI)/60d;  // Angular Delta (6 deg)
041      protected static final int NONE = 0;
042    
043      // state
044      protected static final int EXPLORING = 1;
045      protected static final int HEALING = 2;
046      protected static final int HUNTING = 3;
047    
048      //[srs]
049      protected static final int DOMINATING = 4;
050      protected int shotTime = 0;
051      protected boolean gotWeapon = false;
052    
053      // interference
054      protected static final int INTERFERENCE_BUMP = 1;
055      protected static final int INTERFERENCE_WALL = 2;
056      protected static final int INTERFERENCE_DAMAGE = 3;
057    
058      // directional tendancy
059      protected static final int LEFT = 1;
060      protected static final int RIGHT = 2;
061    
062      /**
063       *  Compares Paths based on theta.
064       *
065       *  Note: this comparator imposes orderings that are inconsistent with equals(...).
066       */
067      protected static final Comparator pathComparator = new Comparator() {
068        public int compare( Object aObj, Object bObj ) {
069          Path a = (Path) aObj;
070          Path b = (Path) bObj;
071    
072          if( a.theta < b.theta )
073            return -1;
074          if( a.theta == b.theta )
075            return 0;
076          else // a.theta > b.theta
077            return 1;
078        }
079      };
080    
081      // Private Data
082      ///////////////////////////////////////////////////////////////////////
083      protected NodeMap map = new NodeMap();
084    
085      protected Random random = new Random();
086    
087      protected Thread runnerThread;
088    
089      protected boolean logIntents  = false;
090      protected boolean logMessages = false;
091      protected boolean logSelf     = false;
092    
093      protected boolean didInit = false;
094    
095      protected Object stateLock = new Object();
096      protected long stateChangeTime = 0;
097      protected int state = EXPLORING;
098      protected int direction = NONE;  // for rot/straft tendancies..
099    
100      /**
101       *  This lock is used to synchronized reads from/writes to the various state
102       *  variables.
103       */
104      protected final Object selfLock = new Object();
105      protected double x, y, z;  // in world coordinates
106      protected double yaw, pitch, roll;  // in Radians
107      protected int health, armor, ammo;
108      protected int team = 255;
109      protected String weapon;
110      protected Vector3D here;  // Location as of the last vision update
111    
112      protected boolean interfered = false;
113      protected long interferenceTime = 0;
114      protected int  interferenceType = NONE;
115    
116      protected long lastRotIncTime = 0;
117      protected double yawTarget;
118    
119      protected Object nodeInfoLock = new Object();
120      protected java.util.Map idToNode = new HashMap();  // Id String to Node object
121      protected Collection knownNodes = idToNode.values();  // automatic updates
122      protected Set exploredNodes = new HashSet();
123      protected Set visibleNodes = new HashSet();
124      protected Set reachableNodes = new HashSet();
125    
126      protected Node target, lastTarget;
127      protected long targetAcquiredTime = 0;
128      protected long targetLostTime = 0;
129    
130      protected long domTargetChangeTime = 0; //[srs]
131      protected long pathRelock = 0;
132      protected int domTarget = 1;
133      protected int domsfound = 0;
134      protected String dom1id = "-";
135      protected String dom2id = "-";
136      protected String dom3id = "-";
137      protected Vector3D dom1loc;
138      protected Vector3D dom2loc;
139      protected Vector3D dom3loc;
140      protected int dom1owner = TEAM_NONE;
141      protected int dom2owner = TEAM_NONE;
142      protected int dom3owner = TEAM_NONE;
143      protected Vector3D previousHere;
144      protected Message domPath;
145      protected int domPathStep = -1;
146      protected boolean askPath = false;
147    
148      //  Public Methods
149      ///////////////////////////////////////////////////////////////////////
150      public JComponent getView() {
151        return map;
152      }
153    
154      //  Protected Methods
155      ///////////////////////////////////////////////////////////////////////
156    
157      // runner
158      protected Runnable runner = new Runnable() {
159        public void run() {
160          Thread thread = Thread.currentThread();
161          while( thread == runnerThread ) {
162            try {
163              if( didInit ) {
164                synchronized( stateLock ) {
165                  switch( state ) {
166                  default:
167                  case EXPLORING:
168                    explore();
169                    break;
170    
171                  case HEALING:
172                    heal();
173                    break;
174    
175                  case HUNTING:
176                    hunt();
177                    break;
178    
179                  case DOMINATING: //[srs]
180                    dominate();
181                    break;
182                  }
183                } // end sync
184              } // end if
185    
186              // [srs] thread.sleep( 1000l );
187              //[srs] keep from shooting at walls, etc.
188              for( int i = 0; i<6; i++ )
189              {
190                 if(didInit && shotTime>0)
191                 {
192                   shotTime--;
193                   if(shotTime==0)
194                   {
195                    stopShooting();
196                   }
197                 }
198                 thread.sleep( 1000 );
199              }
200    
201            } catch( InterruptedException error ) {
202              // ignore
203            }
204          }
205        }
206      };
207    
208      // Event Handlers
209      protected void connected() {
210        super.connected();
211    
212        log.logNote( "Connected... ("+new Date()+")" );
213    
214        runnerThread = new Thread( runner );
215        runnerThread.start();
216    
217        map.repaint();
218      }
219    
220      protected void receivedAsyncMessage( Message message ) {
221        long now = System.currentTimeMillis();
222    //    log.logNote("[srs] --ASYNCH-- ("+message.getType()+") @"+now);
223        if( didInit ) {
224          if( message.getType().equals( BUMP ) ) {
225            interferenceTime = System.currentTimeMillis();
226            interferenceType = INTERFERENCE_BUMP;
227            interfered = true;
228          } else if( message.getType().equals( WALL ) ) {
229            interferenceTime = System.currentTimeMillis();
230            interferenceType = INTERFERENCE_WALL;
231            interfered = true;
232          } else if( message.getType().equals( DAMAGE ) ) {
233            interferenceTime = System.currentTimeMillis();
234            interferenceType = INTERFERENCE_DAMAGE;
235            interfered = true;
236          }
237          else if( message.getType().equals( DIE ) )
238          { //[srs]
239              log.logNote("[srs] DIED!");
240               gotWeapon = false; //flag need weapon
241               shotTime = 0; //stop shooting
242               domTargetChangeTime=now;
243               askPath = false;
244               domPath = null;
245               domPathStep = -1;
246          }
247          else if( message.getType().equals( SEE ) )
248          { //[srs]
249              if (Integer.parseInt(message.getProperty("Team"))!=team)
250                shootAt(message.getProperty( ACTOR_ID )); //shoot any enemies
251          }
252          else if( message.getType().equals( ITEM ) )
253          { //[srs] get item
254            String i = message.getProperty( ITEM_ID );
255            if(i.indexOf(WEAPON_1)!=-1 || i.indexOf(WEAPON_2)!=-1 )
256            {
257             gotWeapon = true; //flag we are armed
258            }
259          }
260        else if (message.getType().equals( PATH ) )
261          {
262              log.logNote( "[srs] Recieved Path ######  " );
263             if(askPath==true) domPath = message; //store it
264    
265          }
266        }else if( message.getType().equals( INFO ) ) {
267          // Init
268          //  Should check to make sure it is only the first...
269          Properties props = new Properties();
270            props.setProperty( client.PLAYER_NAME, getName() );
271            int team = getInitialTeam();
272            if( team != TEAM_ANY )
273              props.setProperty( client.PLAYER_TEAM, Integer.toString(team) );
274          client.sendMessage( client.INIT, props );
275          didInit = true;
276        }
277    
278        if( logMessages )
279          log.logNote( message );
280      }
281    
282      protected void receivedSyncMessage( MessageBlock block ) {
283        long now = System.currentTimeMillis();
284    //    log.logNote("[srs] SYNCH -- "+now );
285    
286        StringBuffer sb = new StringBuffer();
287        if( logMessages ) {
288          sb.append( block );
289        }
290    
291        synchronized( selfLock ) {
292          if( here == null ||
293              ( here.x != x ||
294                here.y != y ||
295                here.z != z ) )
296            here = new Vector3D( x, y, z );
297        }
298        Message message;
299        String type, value;
300        Node node;
301    
302        synchronized( nodeInfoLock ) {
303          visibleNodes.clear();
304          reachableNodes.clear();
305    
306          Iterator i = block.getMessages();
307          while( i.hasNext() ) {
308            message = (Message) i.next();
309            type = message.getType();
310            if( type.equals( END ) ) {
311              // ignore
312            } else if( message.getType().equals( SELF ) ) {
313              if( logSelf )
314                log.logNote( message );
315              updateSelf( message );
316              if( logSelf )
317                log.logNote( "DEBUG: Loc: "+x+" "+y+" "+z+";  Rot: "+pitch+" "+yaw+" "+roll );
318            } else if( type.equals( PLAYER ) )
319            {
320              /* ---- CHRIS added ---- */
321              if (Integer.parseInt(message.getProperty("Team"))!=team)
322                shootAt(message.getProperty( ACTOR_ID ));
323              /*  ------------------- */
324    
325          }
326          else if( type.equals( GAMESTATE ) )
327          { //[srs] get updated dom info
328    
329            if(!dom1id.equals("-"))
330            { String s = message.getProperty( dom1id ); //actually the parser is a bit broken on this issue... but we'll deal with it
331              if(s!=null) dom1owner = Integer.parseInt(s);
332            }
333            if(!dom2id.equals("-"))
334            { String s = message.getProperty( dom2id );
335              if(s!=null) dom2owner = Integer.parseInt(s);
336            }
337            if(!dom3id.equals("-"))
338            { String s = message.getProperty( dom3id );
339              if(s!=null) dom3owner = Integer.parseInt(s);
340            }
341    
342          }
343          else { //some kind of nav node (or dom, or mov) [srs]
344              node = (Node) idToNode.get( message.getProperty( ACTOR_ID ) );
345              if( node == null ) {
346                node = new Node( message );
347                if( node.id != null )
348                  idToNode.put( node.id, node );
349                else if( logMessages )
350                  sb.append( "\n * No ID: " );
351              }
352    
353              value = message.getProperty( ACTOR_REACHABLE );
354              if( value != null &&
355                  value.equals( TRUE ) ) {
356                node.reachableFrom( here );
357                reachableNodes.add( node );
358              }
359    
360              visibleNodes.add( node );
361    
362              if( type.equals( NAV )|| type.equals( MOV ) )
363              {
364                  node.type = NODE_NAVIGATION;
365                  node.owner = TEAM_NONE;
366              }
367              else if ( type.equals( DOM ) )
368              {
369                  node.type = NODE_DOMINATION;
370                  node.owner = Integer.parseInt( message.getProperty( OWNER ) );
371    
372                  //[srs] store away locs of all doms found
373                  if( domsfound < 3 ) //[srs] this might be a previous unknown one
374                  {
375                    String id = message.getProperty( ACTOR_ID );
376                    if( !id.equals(dom1id) && !id.equals(dom2id) && !id.equals(dom3id) ) //[srs] haven't seen before
377                    {
378                        value = message.getProperty( LOCATION );
379                        if( value != null ) {
380                            double vector[] = parseVector( value );
381    
382                            if(domsfound==0) {dom1id=id; dom1loc = new Vector3D(vector[0],vector[1],vector[2]);}
383                            if(domsfound==1) {dom2id=id; dom2loc = new Vector3D(vector[0],vector[1],vector[2]);}
384                            if(domsfound==2) {dom3id=id; dom3loc = new Vector3D(vector[0],vector[1],vector[2]);}
385                            domsfound++;
386                        }
387                    }
388                  }
389              }
390              else if ( type.equals( INV ) )
391              {
392                  node.type = NODE_INVENTORY;
393                  node.owner = TEAM_NONE;
394              }
395    
396    
397            }
398            if( logMessages ) {
399              sb.append('\n');
400              sb.append( message );
401            }
402          }
403    
404          runnerThread.interrupt();
405          if( logMessages )
406            log.logNote( sb.toString() );
407        }
408    
409        map.repaint();
410      }
411    
412      protected void disconnected() {
413        didInit = false;
414    
415        log.logNote( "Disconnected... ("+new Date()+")" );
416    
417        if( runnerThread != null ) {
418          Thread oldThread = runnerThread;
419          runner = null;
420          oldThread.interrupt();
421        }
422    
423        map.repaint();
424      }
425    
426      protected void updateSelf( Message message ) {
427        String value;
428        double vector[];
429    
430        synchronized( selfLock ) {
431          value = message.getProperty( PLAYER_NAME );
432          if( value != null )
433            setName( value );
434    
435          value = message.getProperty( PLAYER_TEAM );
436          if( value != null ) {
437            try {
438              team = Integer.parseInt( value );
439            } catch( NumberFormatException error ) {
440              // Log and ignore
441              log.logError( "Illegal \""+PLAYER_TEAM+"\" value: "+value, error );
442            }
443          }
444    
445          value = message.getProperty( LOCATION );
446          if( value != null ) {
447            vector = parseVector( value );
448            x = vector[0];
449            y = vector[1];
450            z = vector[2];
451          }
452    
453          value = message.getProperty( ROTATION );
454          if( value != null ) {
455            vector = parseVector( value );
456            pitch = vector[0] * 2 * Math.PI / 65535;
457            yaw = vector[1] * 2 * Math.PI / 65535;
458            roll = vector[2] * 2 * Math.PI /   65535;
459          }
460    
461          value = message.getProperty( PLAYER_AMMO );
462          if( value != null ) {
463            try {
464              ammo = Integer.parseInt( value );
465            } catch( NumberFormatException error ) {
466              // Log and ignore
467              log.logError( "Illegal \""+PLAYER_AMMO+"\" value: "+value, error );
468            }
469          }
470    
471          value = message.getProperty( PLAYER_ARMOR );
472          if( value != null ) {
473            try {
474              armor = Integer.parseInt( value );
475            } catch( NumberFormatException error ) {
476              // Log and ignore
477              log.logError( "Illegal \""+PLAYER_ARMOR+"\" value: "+value, error );
478            }
479          }
480    
481          value = message.getProperty( PLAYER_HEALTH );
482          if( value != null ) {
483            try {
484              health = Integer.parseInt( value );
485            } catch( NumberFormatException error ) {
486              // Log and ignore
487              log.logError( "Illegal \""+PLAYER_HEALTH+"\" value: "+value, error );
488            }
489          }
490    
491          value = message.getProperty( PLAYER_WEAPON );
492          if( value != null &&
493              value != weapon )
494            weapon = value;
495            gotWeapon = true; //[srs]
496        }
497    
498        synchronized( nodeInfoLock ) {
499          if( target != null &&
500              target.loc.near( x, y, z, PLAYER_RADIUS ) ) {
501            exploredNodes.add( target );
502            if( target != null )
503              lastTarget = target;
504            target = null;
505          }
506        }
507        map.repaint();
508      }
509    
510      protected void explore() {
511        long now = System.currentTimeMillis();
512        // Assumes synchronized on stateLock
513    
514        if( state != EXPLORING ) {
515          state = EXPLORING;
516          stateChangeTime = now;
517        }
518    
519    //    log.logNote( "[srs] Exploring... (found "+domsfound+" doms)" );
520    
521        if( domsfound>=3 && now-stateChangeTime>15000 )
522        { dominate(); return; //don't need to explore if we found all doms [srs]
523        }
524    
525        if( domsfound>0 && now-stateChangeTime>40000 )
526        {
527         dominate(); return; //if it's been a real long time exploring - start dom
528        }
529    
530        // Atomic copy (need a consistent set of variables, but won't write to them;
531        final double x, y, z, yaw, pitch, roll;
532        synchronized( selfLock ) {
533          x = CMU_JBot.this.x;
534          y = CMU_JBot.this.y;
535          z = CMU_JBot.this.z;
536          yaw = CMU_JBot.this.yaw;
537          pitch = CMU_JBot.this.pitch;
538        }
539    
540        synchronized( nodeInfoLock ) {
541          if( reachableNodes == null )
542          {
543    //[srs]            return;  // No game update yet.
544              findNewTarget();//[srs] try to find some reachable ones.
545          }
546    
547          if( target == null ) {
548            targetLostTime = now;
549          }
550          //[srs] //elseif..>
551          if( reachableNodes.contains( target ) ) {
552            long delay = interferenceTime - targetAcquiredTime;
553            if( ( interferenceType == INTERFERENCE_WALL &&
554                  delay > 1000 ) ||
555                ( interferenceType != NONE &&
556                  delay > 3000 ) ||
557                ( delay > 6000 ) ) {  // Give up after some time
558              if( logIntents )
559                log.logNote( "Giving up on target. Interference Type: "+interferenceType+", delay: "+delay );
560              findNewTarget();
561            } else {
562              // Otherwise continue persuing target.
563              runTo( target.id );
564            }
565            return;
566          }
567    
568          if( reachableNodes.isEmpty() ) {
569            if( logIntents )
570              log.logNote( "No visible nodes." );
571            findNewTarget();
572          } else {
573            Node node;
574    
575    //    log.logNote( "[srs]Looking for interesting..." );
576             boolean retargetNow = false;
577             Iterator t = reachableNodes.iterator();
578             while( t.hasNext() )
579             {
580                 node = (Node) t.next();
581                 if( node.type == NODE_DOMINATION && node.owner!=team )
582                 { //opponent's dom point - go for it!
583        log.logNote( "[srs] Opponent's DOM!" );
584                     retargetNow = true;
585                     //state = DOMINATING  -- to do
586                 }
587                 else if( gotWeapon = false && node.type == NODE_INVENTORY )
588                 { //need a weapon? (but only if there's not a dom)
589        log.logNote( "[srs]Want a weapon!" );
590                     retargetNow = true;
591                 }
592    
593                 if(retargetNow==true)
594                 { //lock on to the point
595                   direction = NONE; interferenceType = NONE; targetAcquiredTime = now;
596                   if( target != null ) lastTarget = target;
597                   target = node; runTo( target.id );
598                   return;      //[srs] if no retarget, just fall through. else end here.
599                 }
600             }
601    
602    
603             { // {} by [srs]  pick random node
604    //    log.logNote( "[srs]Generic explore...." );
605    
606             Set options = new HashSet( reachableNodes );
607             options.removeAll( exploredNodes );
608             if( options.isEmpty() ) {
609               if( random.nextBoolean() ) {
610                 if( logIntents )
611                   log.logNote( "No unexplored nodes." );
612                 findNewTarget();
613                 return;
614               }
615               options = reachableNodes;
616               if( lastTarget != null )
617                 reachableNodes.remove( lastTarget );
618             }
619    
620            Iterator i = options.iterator();
621             while( i.hasNext() ) {
622               node = (Node) i.next();
623               if( node.loc.near( x, y, z, PLAYER_RADIUS ) )
624                 i.remove();
625             }
626             if( options.isEmpty() ) {
627               if( logIntents )
628                 log.logNote( "No distant nodes." );
629               findNewTarget();
630               return;
631             }
632    
633            node = (Node) options.toArray()[ random.nextInt( options.size() ) ];
634             if( target == null ) {
635               if( lastTarget == node )
636               { if( logIntents )
637                   log.logNote( "No visible nodes." );
638                 findNewTarget();
639               } // {} by [srs]
640             } else {
641               if( target == node )
642                 findNewTarget();
643             }
644    
645             direction = NONE;
646             interferenceType = NONE;
647             targetAcquiredTime = now;
648             if( target != null )
649               lastTarget = target;
650             target = node;
651             runTo( target.id );
652    
653             log.logNote( "[srs] Exploring... (found "+domsfound+" doms) new targ:"+target );
654    
655            }
656    
657          }
658        }
659      }
660    
661      protected void heal() {
662      }
663    
664      protected void hunt() {
665      }
666    
667      protected void dominate() //[srs] go do dom points and take control of them
668      {
669         Thread thread = Thread.currentThread();
670         long now = System.currentTimeMillis();
671    
672        if( state != DOMINATING ) {
673          state = DOMINATING;
674          stateChangeTime = now;
675        }
676    
677    //    log.logNote( "[srs] -DOMINATING-! ("+domsfound+" doms found)" );
678    
679        if( domsfound==0 || (domsfound<3 && now-stateChangeTime>20000 ) )
680            //[srs] havent found all of them and within reasonable time (or don't know any)
681        {
682            log.logNote( "[srs] Not dominating ");
683    
684          explore(); return;
685        }
686    
687        if(askPath==false && (now-domTargetChangeTime)>3000)
688        {
689            domTarget = chooseDomTarget();
690    
691            log.logNote( "[srs] DomTarget : "+domTarget );
692        //    if(random.nextInt(100)>77) {    log.logNote( "[srs] not dominating: " ); explore();return;} //still explore with some prob.
693    
694    
695        // ask for a path to the doms
696            log.logNote( "[srs] Getting path... ");
697            askPath=true; domPathStep=-1;
698            if(domTarget==1) pathTo( dom1loc.x, dom1loc.y, dom1loc.z );
699            if(domTarget==2) pathTo( dom2loc.x, dom2loc.y, dom2loc.z );
700            if(domTarget==3) pathTo( dom3loc.x, dom3loc.y, dom3loc.z );
701    
702            try{thread.sleep(5000); }  //give server a chance to find us a path
703            catch( InterruptedException error ) {
704                // ignore + I HATE JAVA, BTFW
705            }
706        }
707    
708    
709        if(askPath==true && domPath!=null) //we got a path back - should be following it.
710        {
711           Vector3D loc;
712           if(domPathStep>=0) loc = parseStep(domPath,domPathStep);
713           else loc = parseStep(domPath,0);
714    
715           if(loc!=null)
716           {
717    //           log.logNote("[srs] Following Path to "+loc.x+","+loc.y+","+loc.z);
718    //           runTo(loc.x,loc.y,loc.z);
719           }
720    
721           if(loc==null) //if we've reached the end of the path
722           {
723               log.logNote("[srs] Path complete!");
724                askPath=false;
725                domPathStep=-1;
726                domPath=null;
727                return;
728           }
729           else if(domPathStep==-1)
730           {
731               if(loc!=null) {
732                   log.logNote("[srs] Starting path: runto("+loc.x+","+loc.y+","+loc.z+")");
733                    runTo(loc.x,loc.y,loc.z);
734                    pathRelock = now;
735               }
736              domPathStep++;
737           }
738           else if (loc.near( x, y, z, PLAYER_RADIUS ))
739           {
740               log.logNote("[srs] Path point complete: "+domPathStep);
741               domPathStep++;
742    
743               loc = parseStep(domPath,domPathStep);
744               if (loc!=null)
745               {    log.logNote("[srs] Continuing path: runto("+loc.x+","+loc.y+","+loc.z+")");
746                    runTo(loc.x,loc.y,loc.z);
747                    pathRelock = now;
748               }
749           }
750           else
751           {
752               if(previousHere==null) previousHere = new Vector3D(0,0,0);
753               if( previousHere.near(x,y,z, PLAYER_RADIUS) && now-pathRelock>10000 ) { //try and get out of stuck on ramp, etc
754                   log.logNote("[srs] Stuck on path.. jitter!"); jitter();
755                   try{thread.sleep(100000);} catch( InterruptedException error ) {}; pathRelock=0;
756                   askPath=false; dominate(); return;
757               }
758               previousHere.x = x; previousHere.y = y; previousHere.z = z;
759    
760           }
761    
762    
763        }
764    
765      }
766    
767      protected Vector3D parseStep(Message message, int step)
768      {
769          Vector3D temp = null;
770    
771          String s = message.getProperty( Integer.toString(step) );
772          if(s==null) return null;
773    
774          String loc = s.substring(s.indexOf(" "));
775          if(loc==null) return null;
776    
777          try { double[] vector = parseVector( loc );
778              temp = new Vector3D( vector[0], vector[1], vector[2] );
779            } catch( RuntimeException error ) {  }
780          return temp;
781    
782      }
783    
784      //[srs]
785      protected void pathTo( double x, double y, double z )
786      {
787        log.logNote( "[srs] Getting Path to: "+x+","+y+","+z );
788        Properties props = new Properties();
789          props.setProperty( LOCATION, x+" "+y+" "+z );
790          props.setProperty( ACTOR_ID, Integer.toString(111) ); //"unique" id
791        client.sendMessage( GETPATH, props );
792      }
793    
794      //[srs]
795     protected int chooseDomTarget()
796     {
797     //    log.logNote( "[srs]Choosing new dom: " );
798        if(domsfound>=1 && dom1owner!=team && random.nextBoolean() ) return 1;
799        if(domsfound>=3 && dom3owner!=team && random.nextBoolean() ) return 3;
800        if(domsfound>=2 && dom2owner!=team && random.nextBoolean() ) return 2;
801    
802        int v = random.nextInt(2)+1;
803        if( v<=domsfound ) return v;
804    
805        return 1;
806     }
807    
808      /* ---- CHRIS added ---- */
809      protected void shootAt( String target )
810      {
811        //[srs] shoot only one thing at a time
812        if(shotTime>0) stopShooting();
813    
814        if( target == null )
815          throw new IllegalArgumentException( "Target cannot be null." );
816    
817        Properties props = new Properties();
818          props.setProperty( ARG_TARGET, target );
819        client.sendMessage( SHOOT, props );
820        shotTime = 2; //[srs] shoot for 2 ticks
821        //log.logNote( "[srs]Shooting at: "+target );
822    
823      }
824      /* --------------------- */
825    
826      //[srs]
827      protected void stopShooting(  )
828      {
829         Properties props = new Properties();
830         client.sendMessage( STOP_SHOOT, props );
831      }
832    
833    
834      protected void findNewTarget() {
835    //    log.logNote( "[srs]Finding new target!" );
836    
837        interferenceType = NONE;
838        synchronized( nodeInfoLock ) {
839          if( target != null )
840            lastTarget = target;
841          target = null;
842        }
843        interfered = false;
844        incRotation();
845      }
846    
847      protected void incRotation() {
848        long now = System.currentTimeMillis();
849        if( now-lastRotIncTime < 200 ) {
850    //      log.logNote( "Don't turn yet." );
851          return;
852        }
853        if( direction == NONE ) {
854          if( random.nextBoolean() )
855            direction = LEFT;
856          else
857            direction = RIGHT;
858        }
859        double newYaw;
860        if( direction == LEFT )
861          newYaw = yaw+(Math.PI/6);  // 15 deg
862        else
863          newYaw = yaw-(Math.PI/6);  // 15 deg
864        if( newYaw == yawTarget ) {
865          if( logIntents )
866            log.logNote( "Stuck in rotation: Jittering." );
867          jitter();  // get unstuck
868        } else {
869          if( logIntents )
870            log.logNote( "Rotating to "+newYaw );
871          turnTo( pitch, newYaw, 0 );
872          yawTarget = newYaw;
873          lastRotIncTime = now;
874        }
875      }
876    
877      protected void jitter() {
878        switch( random.nextInt(8) ) {
879        case 0:
880          runTo( x+(PLAYER_RADIUS/2), y, z );
881          break;
882        case 1:
883          runTo( x+(PLAYER_RADIUS/2), y+(PLAYER_RADIUS/2), z );
884          break;
885        case 2:
886          runTo( x, y+(PLAYER_RADIUS/2), z );
887          break;
888        case 3:
889          runTo( x-(PLAYER_RADIUS/2), y+(PLAYER_RADIUS/2), z );
890          break;
891        case 4:
892          runTo( x-(PLAYER_RADIUS/2), y, z );
893          break;
894        case 5:
895          runTo( x-(PLAYER_RADIUS/2), y-(PLAYER_RADIUS/2), z );
896          break;
897        case 6:
898          runTo( x, y-(PLAYER_RADIUS/2), z );
899          break;
900        default:
901          runTo( x+(PLAYER_RADIUS/2), y-(PLAYER_RADIUS/2), z );
902          break;
903        }
904      }
905    
906      //  Inner Classes
907      ///////////////////////////////////////////////////////////////////////////
908      protected class Path {
909        public final Vector3D to;   // Usually the location of a node
910        public final Vector3D from;
911        public final double theta;  // Angle on the x-y plane, from x axis
912        public final double phi;    // Angle from the x-y plane
913        public final double distSquared;
914    
915        public final Line2D line;
916    
917        public Path( Vector3D to, Vector3D from ) {
918          this.to = to;
919          this.from = from;
920    
921          double dx = to.x-from.x;
922          double dy = to.y-from.y;
923          double dz = to.z-from.z;
924          theta = Math.atan2( dy, dx );
925          double xyRadiusSquared = dx*dx + dy*dy;
926          phi = Math.atan2( dz, Math.sqrt( xyRadiusSquared ) );
927          distSquared = xyRadiusSquared + dz*dz;
928    
929          line = new Line2D.Double( from.x, from.y, to.x, to.y );
930        }
931      }
932    
933      protected class Node {
934        // Public Data
935        ///////////////////////////////////////////////////////////////////////
936        public final String id;
937        public int type = NONE; // [srs] done.
938        public int owner = TEAM_NONE;  //[srs] if a dom node
939    
940        public final Vector3D loc;
941        public final SortedSet paths = new TreeSet( pathComparator );
942    
943        // Public Methods
944        ///////////////////////////////////////////////////////////////////////
945        public Node( Message message ) {
946          String value = message.getProperty( LOCATION );
947          Vector3D temp = null;
948          if( value != null ) {
949            try {
950              double[] vector = parseVector( value );
951              temp = new Vector3D( vector[0], vector[1], vector[2] );
952            } catch( RuntimeException error ) {
953              // temp
954            }
955          }
956          loc = temp;
957          id = message.getProperty( ACTOR_ID );
958        }
959    
960        public boolean equals( Object obj ) {
961          if( !(obj instanceof Node) )
962            return false;
963          Node node = (Node) obj;
964          if( id == null )
965            return node.id == null &&
966                   loc.near( node.loc, PLAYER_RADIUS );
967          return id.equals( node.id );
968        }
969    
970        public String toString() {
971          return "{Node "+ACTOR_ID+"="+id+", "+LOCATION+"="+loc+"}";
972        }
973    
974        public void reachableFrom( Vector3D from ) {
975          synchronized( paths ) {
976            Path path = new Path( loc, from );
977            if( !paths.contains( path ) ) {
978              SortedSet sub = paths.headSet( path );
979              if( !sub.isEmpty() ) {
980                Path prev = (Path) sub.last();
981                if( prev != null &&
982                    (path.theta - prev.theta < THETA_DELTA) &&
983                    (path.distSquared > prev.distSquared) )
984                  paths.remove( prev );
985              }
986              sub = paths.tailSet( path );
987              if( !sub.isEmpty() ) {
988                Path next = (Path) sub.first();
989                if( next != null &&
990                    (next.theta - path.theta < THETA_DELTA) &&
991                    (path.distSquared > next.distSquared) )
992                  paths.remove( next );
993              }
994              // TO-DO: special case theta's near +/-pi?
995              paths.add( path );
996            }
997          }
998        }
999      }
1000    
1001      protected class NodeMap extends JComponent {
1002        // Private Constants
1003        ///////////////////////////////////////////////////////////////////////
1004        protected final double minSymbolSize = 8d;
1005        protected final double symbolSize = 12d;
1006    
1007        protected final Color COLOR_TEAM_GOLD = new Color( 0xCC9900 );  // Gold
1008        protected final Color COLOR_TEAM_DEFAULT = Color.black;
1009    
1010        protected final Color COLOR_BACKGROUND = Color.white;
1011        protected final Color COLOR_AXIS = Color.lightGray;
1012    
1013        protected final Color COLOR_NODE = Color.gray;
1014        protected final Color COLOR_NODE_VISIBLE = Color.darkGray;
1015        protected final Color COLOR_NODE_TARGET = Color.pink;
1016        protected final Color COLOR_NODE_TARGET_VISIBLE = Color.magenta;
1017        protected final Color COLOR_PATH = new Color( 0xEEEEEE );  // Very light gray
1018    
1019        // Private Data
1020        ///////////////////////////////////////////////////////////////////////
1021        protected Object scaleLock = new Object();
1022        protected double scale, symbolScale;
1023    
1024        protected Line2D xAxis = new Line2D.Double( -Double.MAX_VALUE, 0, Double.MAX_VALUE, 0 );
1025        protected Line2D yAxis = new Line2D.Double( 0, -Double.MAX_VALUE, 0, Double.MAX_VALUE );
1026        protected Ellipse2D playerCircle = new Ellipse2D.Double( -symbolSize/2, -symbolSize/2,
1027                                                                 symbolSize,    symbolSize );
1028        protected Ellipse2D navPointCircle = new Ellipse2D.Double( -symbolSize/4, -symbolSize/4,
1029                                                                   symbolSize/2,  symbolSize/2 );
1030    
1031        //  Public Methods
1032        ///////////////////////////////////////////////////////////////////////
1033        public NodeMap() {
1034          setDoubleBuffered( true );
1035          setScale( minSymbolSize/(2d*PLAYER_RADIUS) );
1036        }
1037    
1038        public double getScale() {
1039          return scale;
1040        }
1041    
1042        public void setScale( double scale ) {
1043          synchronized( scaleLock ) {
1044            this.scale = scale;
1045            this.symbolScale = Math.max( 2*PLAYER_RADIUS, minSymbolSize/scale )/symbolSize;
1046          }
1047          repaint();
1048        }
1049    
1050        //  Private Methods
1051        ///////////////////////////////////////////////////////////////////////
1052        protected void paintComponent( Graphics g ) {
1053          Color oldColor = g.getColor();
1054          Font  oldFont  = g.getFont();
1055    
1056          Font font = getFont();
1057          g.setFont( font );
1058          FontMetrics fm = getFontMetrics( font );
1059          Dimension size = getSize();
1060    
1061          if( g instanceof Graphics2D ) {
1062            // Clear background
1063            g.setColor( COLOR_BACKGROUND );
1064            g.fillRect( 0, 0, size.width, size.height );
1065    
1066            if( paintComponent2D( (Graphics2D) g.create() ) )
1067              repaint();
1068          } else {
1069            // Clear background
1070            g.setColor( getBackground() );
1071            g.fillRect( 0, 0, size.width, size.height );
1072    
1073            g.setColor( getForeground() );
1074            g.drawString( "Requires Graphics2D", 2, fm.getAscent()+2 );
1075          }
1076    
1077          if( !client.isConnected() ) {
1078            final String message = "Disconnected";
1079            int width = fm.stringWidth( message );
1080    
1081            g.setColor( Color.red );
1082            g.drawString( message, size.width-width-2, size.height-fm.getDescent()-2 );
1083          }
1084    
1085    
1086          g.setColor( oldColor );
1087          g.setFont( oldFont );
1088        }
1089    
1090        protected boolean paintComponent2D( Graphics2D g ) {
1091          Graphics2D g2, g3;
1092          Iterator i, i2;
1093          Node node;
1094          Path path;
1095          boolean needRepaint = false;
1096    
1097          Dimension size = getSize();
1098          Rectangle2D.Double bounds = new Rectangle2D.Double( 0, 0, size.width, size.height );
1099    
1100          // Atomic copy (need a consistent set of variables, but won't write to them;
1101          final double scale, symbolScale;
1102          synchronized( scaleLock ) {
1103            scale = this.scale;
1104            symbolScale = this.symbolScale;
1105          }
1106          final double x, y, z, yaw, pitch;
1107          synchronized( selfLock ) {
1108            x = CMU_JBot.this.x;
1109            y = CMU_JBot.this.y;
1110            z = CMU_JBot.this.z;
1111            yaw = CMU_JBot.this.yaw;
1112            pitch = CMU_JBot.this.pitch;
1113          }
1114    
1115          // Scale && translate
1116          g.translate( bounds.width/2, bounds.height/2 );
1117          g.scale( scale, scale );
1118          g2 = (Graphics2D) g.create();
1119            g2.translate( -x, -y );
1120    
1121            // Draw Origin
1122            g2.setColor( COLOR_AXIS );
1123            g2.draw( xAxis );
1124            g2.draw( yAxis );
1125    
1126    //        synchronized( nodeInfoLock ) {
1127            try {
1128              // Draw Paths...
1129              i = knownNodes.iterator();
1130              while( i.hasNext() ) {
1131                node = (Node) i.next();
1132    
1133                i2 = node.paths.iterator();
1134                g2.setColor( COLOR_PATH );
1135                while( i2.hasNext() ) {
1136                  path = (Path) i2.next();
1137                  g2.draw( path.line );
1138                }
1139              }
1140    
1141              // Draw Nodes...
1142              i = knownNodes.iterator();
1143              while( i.hasNext() ) {
1144                node = (Node) i.next();
1145    
1146                g3 = (Graphics2D) g2.create();
1147                  g3.translate( node.loc.x, node.loc.y );
1148                  g3.scale( symbolScale, symbolScale );
1149                  drawNode( g3, node );
1150                g3.dispose();
1151              }
1152            } catch( RuntimeException error ) {
1153              // Probably just a problem with not blocking while accessing the node Info
1154              needRepaint = true;
1155            }
1156    
1157            // Draw Players...
1158    
1159          g2.dispose();
1160    
1161          // Draw Self
1162          g2 = (Graphics2D) g.create();
1163            g2.scale( symbolScale, symbolScale );
1164            drawPlayer( g2, team, yaw, pitch );
1165          g2.dispose();
1166    
1167          return needRepaint;
1168        }
1169    
1170        protected void drawPlayer( Graphics2D g, int team, double yaw, double pitch ) {
1171          switch( team ) {
1172          case TEAM_RED:
1173            g.setColor( Color.red );
1174            break;
1175          case TEAM_BLUE:
1176            g.setColor( Color.blue );
1177            break;
1178          case TEAM_GREEN:
1179            g.setColor( Color.green );
1180            break;
1181          case TEAM_GOLD:
1182            g.setColor( COLOR_TEAM_GOLD );
1183            break;
1184          default:
1185            g.setColor( COLOR_TEAM_DEFAULT );
1186            break;
1187          }
1188    
1189          double length = symbolSize*Math.cos( pitch );
1190          double x = length * Math.cos( yaw );
1191          double y = length * Math.sin( yaw );
1192    
1193          g.draw( playerCircle );
1194          g.draw( new Line2D.Double( 0d, 0d, x, y ) );
1195        }
1196    
1197        protected void drawNode( Graphics2D g, Node node ) {
1198          switch( node.type ) {
1199          default:
1200            if( visibleNodes.contains(node) ) {
1201              if( node == target )
1202                g.setColor( Color.magenta );
1203              else
1204                g.setColor( Color.darkGray );
1205            } else {
1206              if( node == target )
1207                g.setColor( Color.pink );
1208              else
1209                g.setColor( Color.lightGray );
1210            }
1211            g.draw( navPointCircle );
1212            break;
1213          }
1214        }
1215      }
1216    }