001         package com.croftsoft.apps.mars.ai;
002    
003         import java.util.*;
004    
005         import com.croftsoft.core.ai.astar.Cartographer;
006         import com.croftsoft.core.lang.NullArgumentException;
007         import com.croftsoft.core.math.geom.Point2DD;
008         import com.croftsoft.core.math.geom.PointXY;
009    
010         /*********************************************************************
011         * Used with the Tank A* implementation.
012         *
013         * @version
014         *   2003-05-10
015         * @since
016         *   2003-03-21
017         * @author
018         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
019         *********************************************************************/
020    
021         public final class  TankCartographer
022           implements Cartographer
023         //////////////////////////////////////////////////////////////////////
024         //////////////////////////////////////////////////////////////////////
025         {
026    
027         private final double          initStepSize;
028    
029         private final int             directions;
030    
031         private final StateSpaceNode  startStateSpaceNode;
032    
033         private final StateSpaceNode  goalStateSpaceNode;
034    
035         private final List            adjacentList;
036    
037         //
038    
039         private TankConsole  tankConsole;
040    
041         //////////////////////////////////////////////////////////////////////
042         //////////////////////////////////////////////////////////////////////
043    
044         public  TankCartographer (
045           double  initStepSize,
046           int     directions )
047         //////////////////////////////////////////////////////////////////////
048         {
049           this.initStepSize   = initStepSize;
050    
051           this.directions     = directions;
052    
053           startStateSpaceNode = new StateSpaceNode ( );
054    
055           goalStateSpaceNode  = new StateSpaceNode ( );
056    
057           adjacentList        = new ArrayList ( );
058         }
059    
060         //////////////////////////////////////////////////////////////////////
061         //////////////////////////////////////////////////////////////////////
062    
063         public void  setGoalPointXY ( PointXY  goalPointXY )
064         //////////////////////////////////////////////////////////////////////
065         {
066           goalStateSpaceNode.setPointXY ( goalPointXY );
067         }
068    
069         public void  setStartStateSpaceNode (
070           StateSpaceNode  startStateSpaceNode )
071         //////////////////////////////////////////////////////////////////////
072          {
073           this.startStateSpaceNode.set ( startStateSpaceNode );
074         }
075    
076         public void  setTankConsole ( TankConsole  tankConsole )
077         //////////////////////////////////////////////////////////////////////
078         {
079           NullArgumentException.check ( this.tankConsole = tankConsole );
080         }
081    
082         //////////////////////////////////////////////////////////////////////
083         // interface Cartographer methods
084         //////////////////////////////////////////////////////////////////////
085    
086         public double  estimateCostToGoal ( Object  node )
087         //////////////////////////////////////////////////////////////////////
088         {
089           return getCostToAdjacentNode ( node, goalStateSpaceNode );
090         }
091    
092         public Iterator  getAdjacentNodes ( Object  node )
093         //////////////////////////////////////////////////////////////////////
094         {
095           StateSpaceNode  stateSpaceNode = ( StateSpaceNode ) node;
096    
097           adjacentList.clear ( );
098    
099           double  distanceToGoal
100             = stateSpaceNode.distance ( goalStateSpaceNode  );
101    
102           double  distanceFromStart
103             = stateSpaceNode.distance ( startStateSpaceNode );
104    
105           double  stepSize
106             = ( ( int ) ( distanceFromStart / initStepSize ) ) * initStepSize;
107    
108           stepSize = Math.max ( stepSize, initStepSize );
109    
110           if ( distanceToGoal <= stepSize )
111           {
112             adjacentList.add ( goalStateSpaceNode );
113    
114             return adjacentList.iterator ( );
115           }
116    
117           PointXY  pointXY = stateSpaceNode.getPointXY ( );
118    
119           double  x = pointXY.getX ( );
120    
121           double  y = pointXY.getY ( );
122    
123           PointXY  goalPointXY = goalStateSpaceNode.getPointXY ( );
124    
125           double  headingToGoal = Math.atan2 (
126             goalPointXY.getY ( ) - y,
127             goalPointXY.getX ( ) - x );
128    
129           for ( int  i = 0; i < directions; i++ )
130           {
131             double  heading = headingToGoal + i * 2.0 * Math.PI / directions;
132    
133             StateSpaceNode  adjacentStateSpaceNode = new StateSpaceNode (
134               new Point2DD (
135                 x + stepSize * Math.cos ( heading ),
136                 y + stepSize * Math.sin ( heading ) ),
137               heading );
138    
139             if ( tankConsole.isSpaceAvailable (
140               adjacentStateSpaceNode.getPointXY ( ) ) )
141             {
142               adjacentList.add ( adjacentStateSpaceNode );
143             }
144           }
145           
146           return adjacentList.iterator ( );
147         }
148    
149         public double  getCostToAdjacentNode (
150           Object  fromNode,
151           Object  toNode )
152         //////////////////////////////////////////////////////////////////////
153         {
154           StateSpaceNode  fromStateSpaceNode = ( StateSpaceNode ) fromNode;
155    
156           StateSpaceNode  toStateSpaceNode   = ( StateSpaceNode ) toNode;
157    
158           double  rotation = fromStateSpaceNode.rotation ( toStateSpaceNode );
159    
160           rotation = Math.abs ( rotation );
161    
162           double  bodyRotationSpeed = tankConsole.getBodyRotationSpeed ( );
163    
164           double  rotationTime = rotation / bodyRotationSpeed;
165    
166           double  travelTime = calculateTravelTime ( fromNode, toNode );
167    
168           double  totalTime = travelTime + rotationTime;
169    
170           return totalTime;
171         }
172    
173         public boolean  isGoalNode ( Object  node )
174         //////////////////////////////////////////////////////////////////////
175         {
176           return 
177             goalStateSpaceNode.distance ( ( StateSpaceNode ) node ) == 0.0;
178         }
179    
180         //////////////////////////////////////////////////////////////////////
181         //////////////////////////////////////////////////////////////////////
182    
183         private double  calculateTravelTime (
184           Object  fromNode,
185           Object  toNode )
186         //////////////////////////////////////////////////////////////////////
187         {
188           StateSpaceNode  fromStateSpaceNode = ( StateSpaceNode ) fromNode;
189    
190           StateSpaceNode  toStateSpaceNode   = ( StateSpaceNode ) toNode;
191    
192           double  distance = fromStateSpaceNode.distance ( toStateSpaceNode );
193    
194           return distance / tankConsole.getTankSpeed ( );
195         }
196    
197         //////////////////////////////////////////////////////////////////////
198         //////////////////////////////////////////////////////////////////////
199         }