Main Page   Namespace List   Class Hierarchy   Compound List   File List   Compound Members   File Members  

BasicPlayer.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright (c) 2000-2003, Jelle Kok, University of Amsterdam
00003 All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007 
00008 1. Redistributions of source code must retain the above copyright notice, this
00009 list of conditions and the following disclaimer.
00010 
00011 2. Redistributions in binary form must reproduce the above copyright notice,
00012 this list of conditions and the following disclaimer in the documentation
00013 and/or other materials provided with the distribution.
00014 
00015 3. Neither the name of the University of Amsterdam nor the names of its
00016 contributors may be used to endorse or promote products derived from this
00017 software without specific prior written permission.
00018 
00019 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00020 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
00023 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00024 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00025 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00026 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00027 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00028 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 */
00030 
00048 #include "BasicPlayer.h"
00049 #include "Parse.h"        // parseFirstInt
00050 
00051 /********************** LOW-LEVEL SKILLS *************************************/
00052 
00057 SoccerCommand BasicPlayer::alignNeckWithBody( )
00058 {
00059   return SoccerCommand( CMD_TURNNECK, WM->getAgentBodyAngleRelToNeck( ) );
00060 }
00061 
00080 SoccerCommand BasicPlayer::turnBodyToPoint( VecPosition pos, int iCycles )
00081 {
00082   VecPosition posGlobal = WM->predictAgentPos(iCycles, 0);
00083   AngDeg angTurn        = (pos - posGlobal).getDirection();
00084   angTurn              -= WM->getAgentGlobalBodyAngle();
00085   angTurn               = VecPosition::normalizeAngle( angTurn );
00086   angTurn               = WM->getAngleForTurn( angTurn, WM->getAgentSpeed(),
00087                                                WM->getAgentObjectType() );
00088 
00089   return SoccerCommand( CMD_TURN, angTurn );
00090 }
00091 
00104 SoccerCommand BasicPlayer::turnBackToPoint( VecPosition pos, int iCycles )
00105 {
00106   VecPosition posGlobal = WM->predictAgentPos(iCycles, 0);
00107   AngDeg angTurn        = (pos - posGlobal).getDirection();
00108   angTurn              -= (WM->getAgentGlobalBodyAngle() + 180);
00109   angTurn               = VecPosition::normalizeAngle( angTurn );
00110   angTurn               = WM->getAngleForTurn( angTurn, WM->getAgentSpeed(),
00111                                                WM->getAgentObjectType() );
00112 
00113   return SoccerCommand( CMD_TURN, angTurn );
00114 }
00115 
00137 SoccerCommand BasicPlayer::turnNeckToPoint(VecPosition pos, SoccerCommand soc)
00138 {
00139   VecPosition posMe,   velMe;
00140   AngDeg      angBody, angNeck, angActual;
00141   Stamina     sta;
00142 
00143   // predict agent information after command 'soc' is performed
00144   // calculate the desired global angle of the neck
00145   // calculate the desired angle of the neck relative to the body
00146   WM->predictAgentStateAfterCommand(soc,&posMe,&velMe,&angBody,&angNeck,&sta);
00147   AngDeg angDesGlobNeck  = (pos - posMe).getDirection();
00148   AngDeg angNeckRelToBody= VecPosition::normalizeAngle(angDesGlobNeck-angBody);
00149 
00150   // calculate the current angle of the body relative to the neck
00151   // check if the desired neck angle relative to the body is possible:
00152   // if angle is smaller than the minimum or larger than the maximum neck angle
00153   //  turn neck to the minimum or maximum neck angle + the current neck angle
00154   // else calculate the desired angle relative to the body
00155   AngDeg angBodyRelToNeck = VecPosition::normalizeAngle(angBody-angNeck);
00156   if( angNeckRelToBody < SS->getMinNeckAng() )
00157     angActual = SS->getMinNeckAng() + angBodyRelToNeck;
00158   else if( angNeckRelToBody > SS->getMaxNeckAng() )
00159     angActual = SS->getMaxNeckAng() + angBodyRelToNeck;
00160   else
00161     angActual = angNeckRelToBody + angBodyRelToNeck;
00162   return SoccerCommand( CMD_TURNNECK, angActual );
00163 }
00164 
00176 SoccerCommand BasicPlayer::searchBall()
00177 {
00178   static Time   timeLastSearch;
00179   static SoccerCommand soc;
00180   static int    iSign       = 1;
00181   VecPosition   posBall =WM->getBallPos();
00182   VecPosition   posAgent=WM->getAgentGlobalPosition();
00183   AngDeg        angBall =(posBall-WM->getAgentGlobalPosition()).getDirection();
00184   AngDeg        angBody =WM->getAgentGlobalBodyAngle();
00185 
00186   if( WM->getCurrentTime().getTime() == timeLastSearch.getTime()  )
00187     return soc;
00188 
00189   if( WM->getCurrentTime() - timeLastSearch > 3 )
00190     iSign = ( isAngInInterval( angBall, angBody, 
00191              VecPosition::normalizeAngle(angBody+180) ) ) 
00192       ? 1
00193       : -1  ;
00194    
00195   //  if( iSign == -1 )
00196   // angBall = VecPosition::normalizeAngle( angBall + 180 );
00197   
00198   soc = turnBodyToPoint( posAgent + VecPosition(1.0,
00199     VecPosition::normalizeAngle(angBody+60*iSign), POLAR ) );
00200   Log.log( 556, "search ball: turn to %f s %d t(%d %d) %f", angBall, iSign,
00201            WM->getCurrentTime().getTime(), timeLastSearch.getTime(),
00202      soc.dAngle );
00203   timeLastSearch = WM->getCurrentTime();
00204   return soc;
00205 }
00206 
00207 
00223 SoccerCommand BasicPlayer::dashToPoint( VecPosition pos, int iCycles )
00224 {
00225   double dDashPower = WM->getPowerForDash(
00226                                  pos - WM->getAgentGlobalPosition(),
00227                                  WM->getAgentGlobalBodyAngle(),
00228                                  WM->getAgentGlobalVelocity(),
00229                                  WM->getAgentEffort(),
00230                                  iCycles                              );
00231   return SoccerCommand( CMD_DASH, dDashPower );
00232 }
00233 
00246 SoccerCommand BasicPlayer::freezeBall( )
00247 {
00248   // determine power needed to kick the ball to compensate for current speed
00249   // get opposite direction (current direction + 180) relative to body
00250   // and make the kick command.
00251   VecPosition posAgentPred = WM->predictAgentPos( 1, 0 );
00252 
00253   double dPower = WM->getKickPowerForSpeed( WM->getBallSpeed() );
00254   if( dPower > SS->getMaxPower() )
00255   {
00256     Log.log( 552, "%d: freeze ball has to much power", WM->getCurrentCycle() );
00257     dPower = (double)SS->getMaxPower();
00258   }
00259   double dAngle = WM->getBallDirection() + 180 - WM->getAgentGlobalBodyAngle();
00260   dAngle        = VecPosition::normalizeAngle( dAngle );
00261   SoccerCommand soc( CMD_KICK, dPower, dAngle );
00262   VecPosition posBall, velBall;
00263   WM->predictBallInfoAfterCommand( soc, &posBall, &velBall );
00264   if( posBall.getDistanceTo( posAgentPred ) < 0.8 * SS->getMaximalKickDist() )
00265     return soc;
00266   Log.log( 101, "freeze ball will end up oustide -> accelerate" );
00267   posBall = WM->getBallPos();
00268   // kick ball to position inside to compensate when agent is moving
00269   VecPosition posTo   = posAgentPred + 
00270     VecPosition( min( 0.7 * SS->getMaximalKickDist(),
00271           posBall.getDistanceTo( posAgentPred ) - 0.1 ),
00272      (posBall-posAgentPred).getDirection(),
00273      POLAR );
00274   VecPosition velDes( posTo - posBall );
00275   return accelerateBallToVelocity( velDes );
00276 }
00277 
00298 SoccerCommand BasicPlayer::kickBallCloseToBody( AngDeg ang, double dKickRatio )
00299 {
00300   AngDeg      angBody    = WM->getAgentGlobalBodyAngle();
00301   VecPosition posAgent   = WM->predictAgentPos( 1, 0 );
00302   double      dDist      = SS->getPlayerSize() +
00303                            SS->getBallSize()   +
00304                            SS->getKickableMargin()*dKickRatio;
00305   AngDeg      angGlobal  = VecPosition::normalizeAngle( angBody + ang );
00306   VecPosition posDesBall = posAgent   + VecPosition( dDist, angGlobal, POLAR );
00307   if( fabs( posDesBall.getY() ) > PITCH_WIDTH/2.0 ||
00308       fabs( posDesBall.getX() ) > PITCH_LENGTH/2.0 )
00309   {
00310     Line lineBody = Line::makeLineFromPositionAndAngle( posAgent, angGlobal );
00311     Line lineSide(0,0,0);
00312     if( fabs( posDesBall.getY() ) > PITCH_WIDTH/2.0 )
00313       lineSide = Line::makeLineFromPositionAndAngle(
00314     VecPosition( 0, sign(posDesBall.getY() )* PITCH_WIDTH/2.0 ), 0 );
00315     else
00316       lineSide = Line::makeLineFromPositionAndAngle(
00317     VecPosition( 0, sign(posDesBall.getX() )* PITCH_LENGTH/2.0 ),  90 );
00318     VecPosition posIntersect = lineSide.getIntersection( lineBody );
00319     posDesBall = posAgent + 
00320       VecPosition( posIntersect.getDistanceTo( posAgent ) - 0.2, 
00321        angGlobal, POLAR );
00322   }
00323     
00324   VecPosition vecDesired = posDesBall - WM->getBallPos();
00325   VecPosition vecShoot   = vecDesired - WM->getGlobalVelocity( OBJECT_BALL );
00326   double      dPower     = WM->getKickPowerForSpeed( vecShoot.getMagnitude() );
00327   AngDeg      angActual  = vecShoot.getDirection() - angBody;
00328               angActual  = VecPosition::normalizeAngle( angActual );
00329 
00330   if( dPower > SS->getMaxPower() && WM->getBallSpeed() > 0.1 )
00331   {
00332     Log.log( 500, "kickBallCloseToBody: cannot compensate ball speed, freeze");
00333     Log.log( 101, "kickBallCloseToBody: cannot compensate ball speed, freeze");
00334     return freezeBall();
00335   }
00336   else if( dPower > SS->getMaxPower() )
00337   {
00338     if( WM->isDeadBallUs() )
00339     {
00340       if( WM->getRelativeAngle( OBJECT_BALL ) > 25 )
00341       {
00342   Log.log( 101, "dead ball situation, turn to ball" );
00343   return turnBodyToObject( OBJECT_BALL );
00344       }
00345     }
00346     else
00347     {
00348       Log.log( 101, "kickBallCloseToBody: ball has no speed, but far away" );
00349       dPower = 100;
00350     }
00351   }
00352   else
00353     Log.log( 101, "(kick %f %f), vecDesired (%f,%f) %f posDes(%f,%f)", 
00354        dPower, angActual,vecDesired.getX(), vecDesired.getY(),ang,
00355        posDesBall.getX(), posDesBall.getY() );
00356   return SoccerCommand( CMD_KICK, dPower, angActual );
00357 }
00358 
00377 SoccerCommand BasicPlayer::accelerateBallToVelocity( VecPosition velDes )
00378 {
00379   AngDeg      angBody  = WM->getAgentGlobalBodyAngle();
00380   VecPosition velBall  = WM->getGlobalVelocity( OBJECT_BALL );
00381   VecPosition accDes   = velDes - velBall;
00382   double      dPower;
00383   double      angActual;
00384 
00385   // if acceleration can be reached, create shooting vector
00386   if( accDes.getMagnitude() < SS->getBallAccelMax() )
00387   {
00388     dPower    = WM->getKickPowerForSpeed   ( accDes.getMagnitude() );
00389     angActual = VecPosition::normalizeAngle( accDes.getDirection() - angBody );
00390     if( dPower <= SS->getMaxPower() )
00391       return SoccerCommand( CMD_KICK, dPower, angActual );
00392   }
00393 
00394   // else determine vector that is in direction 'velDes' (magnitude is lower)
00395          dPower    = SS->getMaxPower();
00396   double dSpeed    = WM->getActualKickPowerRate() * dPower;
00397   double tmp       = velBall.rotate(-velDes.getDirection()).getY();
00398          angActual = velDes.getDirection() - asinDeg( tmp / dSpeed );
00399          angActual = VecPosition::normalizeAngle( angActual - angBody );
00400 
00401   return SoccerCommand( CMD_KICK, dPower, angActual );
00402 }
00403 
00412 SoccerCommand BasicPlayer::catchBall()
00413 {
00414   // true means returned angle is relative to body instead of neck
00415   return SoccerCommand( CMD_CATCH, WM->getRelativeAngle( OBJECT_BALL, true ));
00416 }
00417 
00418 
00424 SoccerCommand BasicPlayer::communicate( char *str )
00425 {
00426   return SoccerCommand( CMD_SAY, str );
00427 }
00428 
00433 SoccerCommand BasicPlayer::teleportToPos( VecPosition pos )
00434 {
00435   return SoccerCommand( CMD_MOVE, pos.getX(), pos.getY() );
00436 
00437 }
00438 
00441 SoccerCommand BasicPlayer::listenTo( ObjectT obj )
00442 {
00443   if( !SoccerTypes::isKnownPlayer( obj ) )
00444     return SoccerCommand( CMD_ATTENTIONTO, -1.0, -1.0 );
00445 
00446   return SoccerCommand( CMD_ATTENTIONTO,
00447      1.0,                                       // 1.0 denotes our team
00448      SoccerTypes::getIndex( obj ) + 1 );
00449 }
00450 
00452 SoccerCommand BasicPlayer::tackle( )
00453 {
00454   return SoccerCommand( CMD_TACKLE, 100.0 );
00455 }
00456 
00457 /********************** INTERMEDIATE LEVEL SKILLS ****************************/
00458 
00467 SoccerCommand BasicPlayer::turnBodyToObject( ObjectT o )
00468 {
00469   return turnBodyToPoint( WM->predictPosAfterNrCycles(o, 1) );
00470 }
00471 
00487 SoccerCommand BasicPlayer::turnNeckToObject( ObjectT o, SoccerCommand soc )
00488 {
00489   return turnNeckToPoint( WM->predictPosAfterNrCycles(o, 1), soc );
00490 }
00491 
00492 SoccerCommand BasicPlayer::directTowards( VecPosition posTurnTo,
00493    AngDeg angWhenToTurn, VecPosition *pos, VecPosition *vel, AngDeg *angBody  )
00494 {
00495 //  return turnBodyToPoint( posTurnTo );
00496   // copy values or initialize when not set
00497   VecPosition posAgent= (pos    ==NULL)?WM->getAgentGlobalPosition ():*pos;
00498   VecPosition velAgent= (vel    ==NULL)?WM->getAgentGlobalVelocity ():*vel;
00499   AngDeg  angBodyAgent= (angBody==NULL)?WM->getAgentGlobalBodyAngle():*angBody;
00500 
00501   // first predict what happens when the agents rolls out.
00502   VecPosition posPred    = WM->predictFinalAgentPos();
00503   AngDeg      angTo      = ( posTurnTo - posPred ).getDirection();
00504   AngDeg      ang        = VecPosition::normalizeAngle( angTo - angBodyAgent );
00505   AngDeg      angNeck = 0;
00506 
00507   int iTurn = 0;
00508   while( fabs( ang ) > angWhenToTurn && iTurn < 5 )
00509   {
00510     iTurn++;
00511     WM->predictStateAfterTurn(
00512           WM->getAngleForTurn( ang, velAgent.getMagnitude() ),
00513           &posAgent,
00514           &velAgent,
00515           &angBodyAgent,
00516           &angNeck );
00517     ang = VecPosition::normalizeAngle( angTo - angBodyAgent );
00518   }
00519   Log.log( 509, "direct towards: %d turns", iTurn );
00520   posAgent = (pos    ==NULL)?WM->getAgentGlobalPosition ():*pos;  
00521   velAgent = (vel    ==NULL)?WM->getAgentGlobalVelocity ():*vel;      
00522   angBodyAgent = (angBody==NULL)?WM->getAgentGlobalBodyAngle():*angBody;
00523 
00524   switch( iTurn )
00525   {
00526     case 0:   cerr << "direct towards: 0 turns" ;
00527               return SoccerCommand( CMD_ILLEGAL );
00528     case 1: 
00529     case 2:   return turnBodyToPoint( posTurnTo, 2 );
00530     default:  return dashToPoint( 
00531              (pos==NULL)?WM->getAgentGlobalPosition ():*pos   );  // stop
00532   }
00533 }
00534 
00571 SoccerCommand BasicPlayer::moveToPos( VecPosition posTo, AngDeg angWhenToTurn,
00572                             double dDistBack, bool bMoveBack, int iCycles )
00573 {
00574 // previously we only turned relative to position in next cycle, now take
00575 // angle relative to position when you're totally rolled out...
00576 //  VecPosition posPred   = WM->predictAgentPos( 1, 0 );
00577   VecPosition posAgent  = WM->getAgentGlobalPosition();
00578   VecPosition posPred   = WM->predictFinalAgentPos();
00579   
00580   AngDeg      angBody   = WM->getAgentGlobalBodyAngle();
00581   AngDeg      angTo     = ( posTo - posPred ).getDirection();
00582               angTo     = VecPosition::normalizeAngle( angTo - angBody );
00583   AngDeg      angBackTo = VecPosition::normalizeAngle( angTo + 180 );
00584   
00585   double      dDist     = posAgent.getDistanceTo( posTo );
00586 
00587   Log.log( 509, "moveToPos (%f,%f): body %f to %f diff %f now %f when %f", 
00588           posTo.getX(), posTo.getY(), angBody, 
00589            ( posTo - posPred ).getDirection(), angTo, 
00590            ( posTo -  WM->predictAgentPos( 1, 0 )).getDirection(),
00591            angWhenToTurn );
00592   if( bMoveBack )
00593   {
00594     if( fabs( angBackTo ) < angWhenToTurn )
00595       return dashToPoint( posTo, iCycles );
00596     else
00597       return turnBackToPoint( posTo );
00598   }
00599   else if(  fabs( angTo     ) < angWhenToTurn ||
00600            (fabs( angBackTo ) < angWhenToTurn && dDist < dDistBack ) )
00601     return dashToPoint( posTo, iCycles );
00602   else
00603     return directTowards( posTo, angWhenToTurn );
00604 //return turnBodyToPoint( posTo );
00605 }
00606 
00607 
00615 SoccerCommand BasicPlayer::collideWithBall( )
00616 {
00617   SoccerCommand soc( CMD_ILLEGAL );
00618   if( WM->getRelativeDistance( OBJECT_BALL ) > 
00619       WM->getBallSpeed() + SS->getPlayerSpeedMax() )
00620     return soc;
00621    
00622   VecPosition posBallPred  = WM->predictPosAfterNrCycles( OBJECT_BALL, 1 );
00623   
00624   // first try turn
00625   soc                      = turnBodyToPoint( WM->getAgentGlobalPosition() + 
00626                                               VecPosition( 1, 0, POLAR ) );
00627   VecPosition posAgentPred = WM->predictAgentPosAfterCommand( soc );
00628   if( posAgentPred.getDistanceTo( posBallPred ) < 
00629       SS->getBallSize() + SS->getPlayerSize() )
00630   {
00631     Log.log( 511, "can collide with ball by turning" );
00632     return soc;
00633   }
00634                                
00635   soc          = dashToPoint( posBallPred, 1 );
00636   posAgentPred = WM->predictAgentPosAfterCommand( soc );
00637   if( posAgentPred.getDistanceTo( posBallPred ) < 
00638       SS->getBallSize() + SS->getPlayerSize() )
00639   {
00640     Log.log( 511, "can collide with ball by dashing %f", soc.dPower );
00641     return soc;
00642   }
00643     
00644   return SoccerCommand( CMD_ILLEGAL );  
00645 }
00710 SoccerCommand BasicPlayer::interceptClose( )
00711 {
00712   FeatureT feature_type = FEATURE_INTERCEPT_CLOSE;
00713   if( WM->isFeatureRelevant( feature_type ) )
00714     return WM->getFeature( feature_type ).getCommand();
00715 
00716   SoccerCommand soc, socDash1, socFinal, socCollide, socTurn(CMD_ILLEGAL);
00717   double        dPower, dDist;
00718   AngDeg        ang,    ang2;
00719   VecPosition   s1,     s2;
00720   bool          bReady = false;
00721 
00722   // first determine whether the distance to the ball is not too large
00723   dDist = 3*SS->getPlayerSpeedMax()
00724           + (1.0 + SS->getBallDecay())*SS->getBallSpeedMax()
00725           + SS->getMaximalKickDist();
00726   if( WM->getRelativeDistance( OBJECT_BALL ) > dDist )
00727   {
00728     bReady = true;
00729     socFinal = SoccerCommand( CMD_ILLEGAL ); // do not quit, but log feature
00730   }
00731 
00732   socCollide = collideWithBall( );
00733   // initialize all variables with information from the worldmodel.
00734   VecPosition   posAgent = WM->getAgentGlobalPosition( );
00735   VecPosition   posPred  = WM->predictAgentPos( 1, 0 ), posDash1;
00736   VecPosition   posBall  = WM->predictPosAfterNrCycles( OBJECT_BALL, 1 );
00737   VecPosition   velMe    = WM->getAgentGlobalVelocity( );
00738   Stamina       sta      = WM->getAgentStamina( );
00739   AngDeg        angBody  = WM->getAgentGlobalBodyAngle( ), angTurn, angNeck=0;
00740   double        dDesBody = 0.0;
00741 
00742   // our desired heading after the intercept is 0 degrees, only when we
00743   // are far up front we want to be headed toward the opponent goal
00744   if( posAgent.getX() > PENALTY_X - 5.0 )
00745     dDesBody = (WM->getPosOpponentGoal()-posAgent).getDirection();
00746 
00747   // get the distance to the closest opponent
00748   double dDistOpp;
00749   ObjectT objOpp = WM->getClosestInSetTo( OBJECT_SET_OPPONENTS,
00750      WM->getAgentObjectType(), &dDistOpp, PS->getPlayerConfThr() );
00751   angTurn =VecPosition::normalizeAngle(dDesBody-WM->getAgentGlobalBodyAngle());
00752 
00753   // check the distance to the ball when we do not dash (e.g., and only turn)
00754   posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 1 );
00755   VecPosition posPred1 = WM->predictAgentPos( 1, 0 );
00756   double dDist1 = posPred1.getDistanceTo( posBall );
00757   posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 2 );
00758   VecPosition posPred2 = WM->predictAgentPos( 2, 0 );
00759   double dDist2 = posPred2.getDistanceTo( posBall );
00760   posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 3 );
00761   VecPosition posPred3 = WM->predictAgentPos( 3, 0 );
00762   double dDist3 = posPred3.getDistanceTo( posBall );
00763   Log.log( 508, "dist 1: %f, 2: %f 3: %f, 0.6: %f", dDist1, dDist2, dDist3,
00764                                         0.7*SS->getMaximalKickDist() );
00765 
00766   AngDeg angThreshold = 25;
00767   bool   bOppClose = ( objOpp != OBJECT_ILLEGAL && dDistOpp < 3.0 )  ;
00768 
00769   // make a line from center of body in next cycle with direction of body
00770   // use next cycle since current velocity is always propogated to position in
00771   // next cycle.  Make a circle around the ball with a radius equal to the
00772   // sum of your own body, the ball size and a small buffer. Then calculate
00773   // the intersection between the line and this circle. These are the (two)
00774   // points that denote the possible agent locations close to the ball
00775   // From these two points we take the point where the body direction of the
00776   // agent makes the smallest angle with the ball (with backward
00777   // dashing we sometime have to dash "over" the ball to face it up front)
00778   posAgent  = WM->getAgentGlobalPosition( );
00779   posBall   = WM->predictPosAfterNrCycles( OBJECT_BALL, 1 );
00780   angBody   = WM->getAgentGlobalBodyAngle();
00781   velMe     = WM->getAgentGlobalVelocity( );
00782   sta       = WM->getAgentStamina( );
00783   Line line = Line::makeLineFromPositionAndAngle(posPred1,angBody);
00784   dDist     = SS->getPlayerSize()+SS->getBallSize()+SS->getKickableMargin()/6;
00785   int  iSol = line.getCircleIntersectionPoints(
00786                                             Circle(posBall,dDist), &s1, &s2);
00787   if (iSol > 0)                                          // if a solution
00788   {
00789     if (iSol == 2)                                       // take the best one
00790     {
00791      ang = VecPosition::normalizeAngle((posBall - s1).getDirection() -angBody);
00792      ang2= VecPosition::normalizeAngle((posBall - s2).getDirection() -angBody);
00793 //     if ( fabs(ang2) < 90)
00794      if( s2.getX() > s1.getX() ) // move as much forward as possible
00795        s1 = s2;                                          // and put it in s1
00796     }
00797 
00798     // try one dash
00799     // now we have the interception point we try to reach in one cycle. We
00800     // calculate the needed dash power from the current position to this point,
00801     // predict were we will stand if we execute this command and check whether
00802     // we are in the kickable distance
00803     dPower = WM->getPowerForDash(s1-posAgent, angBody, velMe,sta.getEffort() );
00804     posDash1 = WM->predictAgentPos( 1, (int)dPower);
00805     if ( posDash1.getDistanceTo( posBall ) < 0.95*SS->getMaximalKickDist() )
00806     {
00807       Log.log( 508, "dash 1x possible at s1" );
00808       socDash1 = SoccerCommand( CMD_DASH, dPower );
00809     }
00810     else
00811     {
00812       dPower=WM->getPowerForDash(s2-posAgent, angBody, velMe,sta.getEffort() );
00813       posDash1 = WM->predictAgentPos( 1, (int)dPower);
00814       if ( posDash1.getDistanceTo( posBall ) < 0.95*SS->getMaximalKickDist() )
00815       {
00816         Log.log( 508, "dash 1x possible at s2" );
00817         socDash1 = SoccerCommand( CMD_DASH, dPower );
00818       }
00819     }
00820   }
00821 
00822   // try one dash by getting close to ball
00823   // this handles situation where ball cannot be reached within distance
00824   // SS->getKickableMargin()/6
00825   if( socDash1.commandType == CMD_ILLEGAL )
00826   {
00827     soc = dashToPoint( posBall );
00828     WM->predictAgentStateAfterCommand(soc,&posDash1,&velMe,
00829                                       &angBody,&ang,&sta );
00830     if ( posDash1.getDistanceTo( posBall ) < 0.95*SS->getMaximalKickDist() )
00831     {
00832       Log.log( 508, "dash 1x possible (special)" );
00833       socDash1 = soc;
00834     }
00835   }
00836 
00837   if( bReady != true )
00838   { 
00839     if( bOppClose && ! socDash1.isIllegal() )
00840     {
00841       Log.log( 508, "do dash 1x, opponent close" );
00842       WM->logCircle( 508, posDash1, SS->getMaximalKickDist(), true );
00843       socFinal = socDash1;
00844     }
00845     else
00846     {
00847       soc = turnBodyToPoint( posPred1 + VecPosition(1,dDesBody, POLAR), 1 );
00848       WM->predictAgentStateAfterCommand(soc, &posPred, &velMe,
00849                                         &angBody, &ang, &sta);
00850       posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 1 );    
00851       if( posPred.getDistanceTo( posBall ) < 0.8*SS->getMaximalKickDist() )
00852       {    
00853         socTurn = soc; // we can do turn and end up ok, but can maybe improve
00854         ang     = VecPosition::normalizeAngle(dDesBody-angBody);
00855         if( fabs(ang) < angThreshold )
00856         {
00857           socFinal = soc;
00858           Log.log( 508, "turn 1x, dist %f, angle %f, opp %f",
00859              dDist1, angTurn, dDistOpp );
00860           WM->logCircle( 508, posPred1, SS->getMaximalKickDist(), true );
00861         }
00862           
00863       }
00864       if( socFinal.isIllegal() ) 
00865       {
00866         ang     = VecPosition::normalizeAngle(dDesBody-angBody);      
00867         WM->predictStateAfterTurn( 
00868                    WM->getAngleForTurn(ang,velMe.getMagnitude()),
00869                    &posPred, &velMe, &angBody, &angNeck, 
00870                    WM->getAgentObjectType(),
00871                    &sta             );
00872         posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 2 );         
00873         if( posPred.getDistanceTo( posBall ) < 0.8*SS->getMaximalKickDist() )
00874         {
00875           socTurn = soc; // we can do turn and end up ok, but can maybe improve
00876           ang     = VecPosition::normalizeAngle(dDesBody-angBody);
00877           if( fabs(ang) < angThreshold )
00878           {
00879             Log.log( 508, "turn 2x, dist %f, angle %f, opp %f",
00880                   dDist2, angTurn, dDistOpp );
00881             WM->logCircle( 508, posPred2, SS->getMaximalKickDist(), true );
00882             socFinal = soc;
00883           }
00884         }
00885       }
00886       if( socFinal.isIllegal() && ! socCollide.isIllegal() &&
00887           fabs( angTurn ) > angThreshold ) 
00888       {
00889         Log.log( 508, "collide with ball on purpose" );
00890         posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 1 );            
00891         WM->logCircle( 508, posBall, SS->getMaximalKickDist(), true );
00892         socFinal = socCollide;
00893       }
00894       if( socFinal.isIllegal() && fabs( angTurn ) > angThreshold )
00895       {
00896         posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 2 );
00897         soc = dashToPoint( posAgent );
00898         WM->predictAgentStateAfterCommand(soc,
00899                                           &posPred,&velMe,&angBody,&ang,&sta );
00900         if( posPred.getDistanceTo( posBall ) < 0.8*SS->getMaximalKickDist() )
00901         {
00902           Log.log( 508, "dash 1x (stop), turn 1x, dist %f, angle %f, opp %f",
00903            dDist2, angTurn, dDistOpp );
00904           WM->logCircle( 508, posPred, SS->getMaximalKickDist(), true );
00905           socFinal = soc;
00906         }
00907       }
00908       if( socFinal.isIllegal()  && ! socTurn.isIllegal() )
00909       {
00910         Log.log( 508, "can do turn" );
00911         WM->logCircle( 508, posPred1, SS->getMaximalKickDist(), true );
00912         socFinal = socTurn;
00913       }    
00914       if( socFinal.isIllegal()  && ! socDash1.isIllegal() )
00915       {
00916         Log.log( 508, "do dash 1x" );
00917         WM->logCircle( 508, posDash1, SS->getMaximalKickDist(), true );
00918         socFinal = socDash1;
00919       }
00920     }
00921 
00922   }
00923   // if there are no opponents, we are wrongly directed, and we will be closely
00924   // to the ball, see whether we can first update our heading
00925   else if( fabs( angTurn ) > angThreshold && !bOppClose &&
00926       dDist1 < 0.7*SS->getMaximalKickDist() )
00927   {
00928     soc = turnBodyToPoint( posPred1 + VecPosition(1,dDesBody, POLAR), 1 );
00929     Log.log( 508, "turn 1x, dist %f, angle %f, opp %f",
00930       dDist1, angTurn, dDistOpp );
00931     WM->logCircle( 508, posPred1, SS->getMaximalKickDist(), true );
00932     socFinal = soc;
00933   }
00934   else if( // fabs( angTurn ) > angThreshold &&
00935       !bOppClose &&
00936       dDist2 < 0.7*SS->getMaximalKickDist() )
00937   {
00938     soc = turnBodyToPoint( posPred2 + VecPosition(1,dDesBody, POLAR), 2 );
00939     Log.log( 508, "turn 2x, dist %f, angle %f, opp %f",
00940        dDist2, angTurn, dDistOpp );
00941     WM->logCircle( 508, posPred2, SS->getMaximalKickDist(), true );
00942     socFinal = soc;
00943   }
00944 
00945 
00946   else if( socCollide.commandType != CMD_ILLEGAL && 
00947            fabs( angTurn ) > angThreshold )
00948   {
00949     Log.log( 508, "collide with ball on purpose" );
00950     WM->logCircle( 508, posDash1, SS->getMaximalKickDist(), true );
00951     socFinal = socCollide;
00952   }
00953   else if( socDash1.commandType != CMD_ILLEGAL )
00954   {
00955     Log.log( 508, "do dash 1x" );
00956     WM->logCircle( 508, posDash1, SS->getMaximalKickDist(), true );
00957     socFinal = socDash1;
00958   }
00959 
00960 
00961   // cannot intercept ball in three cycles
00962   WM->setFeature( feature_type,
00963               Feature( WM->getTimeLastSeeMessage(),
00964                        WM->getTimeLastSenseMessage(),
00965                        WM->getTimeLastHearMessage(),                       
00966                        OBJECT_ILLEGAL,
00967                        -1,
00968                        socFinal ) );
00969   return socFinal;
00970 }
00971 
01008 SoccerCommand BasicPlayer::interceptCloseGoalie( )
01009 {
01010   SoccerCommand soc;
01011   double        dPower, dDist;
01012   AngDeg        ang;
01013   VecPosition   posClosestToBall;
01014 
01015   // initialize all variables with information from worldmodel.
01016   VecPosition   posPred   = WM->predictAgentPos( 1, 0 );
01017   VecPosition   posBall   = WM->predictPosAfterNrCycles( OBJECT_BALL, 1 );
01018   VecPosition   velMe     = WM->getAgentGlobalVelocity( );
01019   Stamina       sta       = WM->getAgentStamina( );
01020   AngDeg        angBody   = WM->getAgentGlobalBodyAngle( );
01021   Line          lineGoalie=Line::makeLineFromPositionAndAngle(posPred,angBody);
01022 
01023   // when it is theoretical possible
01024   // try one dash and check whether ball is in catchable area
01025   dDist =SS->getBallSpeedMax()+SS->getPlayerSpeedMax()+SS->getCatchableAreaL();
01026   if( WM->getRelativeDistance( OBJECT_BALL ) < dDist )
01027   {
01028     posClosestToBall = lineGoalie.getPointOnLineClosestTo( posBall );
01029     dPower           = WM->getPowerForDash(
01030                           posClosestToBall-posPred,
01031                           angBody,
01032                           velMe,
01033                           sta.getEffort()           );
01034     posPred          = WM->predictAgentPos( 1, (int)dPower);
01035     if ( posPred.getDistanceTo( posBall ) < SS->getCatchableAreaL() )
01036       return SoccerCommand( CMD_DASH, dPower );
01037   }
01038 
01039   // when it is theoretical possible
01040   // try two dashes and check whether ball is in catchable area
01041   // otherwise try first two  dashes and check whether ball is in catchable
01042   // area, thereafter for turn and dash.
01043   dDist = SS->getBallSpeedMax()*(1.0+SS->getBallDecay())
01044            + 2*SS->getPlayerSpeedMax()
01045            + SS->getCatchableAreaL();
01046   if( WM->getRelativeDistance( OBJECT_BALL ) < dDist )
01047   {
01048     // try two dashes
01049     // first predict the position in the next cycle when dash with full power
01050     // is performed. Then calculate the dash power to reach the point where the
01051     // ball will be in two cycles and predict the global position of the agent
01052     // after a dash with this power. If the position is in the catchable area
01053     // return a dash with full power.
01054     posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 2 );
01055     soc     = dashToPoint( posBall );
01056     WM->predictAgentStateAfterCommand(soc,&posPred,&velMe,&angBody,&ang,&sta );
01057     dPower=WM->getPowerForDash(posBall-posPred,angBody,velMe,sta.getEffort());
01058     WM->predictStateAfterDash( dPower, &posPred, &velMe, &sta, angBody,
01059                                WM->getAgentObjectType() );
01060     if( posPred.getDistanceTo(posBall) < SS->getCatchableAreaL() )
01061       return soc;
01062 
01063     // try one turn and a dash
01064     posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 2 );
01065     posPred = WM->predictAgentPos( 2, 0 );
01066     ang     = (posBall - posPred).getDirection();
01067     ang     = VecPosition::normalizeAngle( ang - angBody );
01068     if (fabs( ang ) > PS->getPlayerWhenToTurnAngle() ) // if we want to turn
01069     {
01070       soc = turnBodyToPoint( posBall, 2 );             // perform turn
01071       WM->predictAgentStateAfterCommand(soc,&posPred,&velMe,&angBody,&ang,&sta
01072 );
01073       dPower=WM->getPowerForDash(posBall-posPred, angBody, 
01074                                  velMe, sta.getEffort());
01075       WM->predictStateAfterDash( dPower, &posPred, &velMe, &sta, angBody);
01076       if( posPred.getDistanceTo(posBall) < SS->getCatchableAreaL() )
01077         return soc;
01078     }
01079   }
01080 
01081   // did not succeed
01082   return SoccerCommand( CMD_ILLEGAL );
01083 }
01084 
01120 SoccerCommand BasicPlayer::kickTo( VecPosition posTarget, double dEndSpeed )
01121 {
01122   VecPosition posBall  = WM->getBallPos();
01123   VecPosition velBall  = WM->getGlobalVelocity(OBJECT_BALL);
01124   VecPosition posTraj  = posTarget - posBall;
01125   VecPosition posAgent = WM->getAgentGlobalPosition();
01126   VecPosition velDes   = VecPosition(
01127                WM->getKickSpeedToTravel( posTraj.getMagnitude(), dEndSpeed ),
01128                posTraj.getDirection(),
01129                POLAR                                                       ); 
01130   double      dPower;
01131   AngDeg      angActual;
01132 
01133   if( WM->predictAgentPos(1, 0 ).getDistanceTo( posBall + velDes ) < 
01134       SS->getBallSize() + SS->getPlayerSize() )
01135   {
01136     Line line = Line::makeLineFromTwoPoints( posBall, posBall + velDes );
01137     VecPosition posBodyProj = line.getPointOnLineClosestTo( posAgent );
01138     double dDist = posBall.getDistanceTo( posBodyProj );
01139     if( velDes.getMagnitude() < dDist )
01140       dDist -=  SS->getBallSize() + SS->getPlayerSize();
01141     else
01142       dDist +=  SS->getBallSize() + SS->getPlayerSize();
01143     Log.log( 101, "kick results in collision, change velDes from (%f,%f)",
01144        velDes.getX(), velDes.getY() );
01145     velDes.setVecPosition( dDist, velDes.getDirection(), POLAR );
01146   }
01147 
01148   Log.log( 101, "ball (%f,%f), agent (%f,%f), to (%f,%f) ang %f %f" ,
01149           WM->getBallPos().getX(), WM->getBallPos().getY(),
01150           WM->getAgentGlobalPosition().getX(),
01151           WM->getAgentGlobalPosition().getY(),
01152           posTarget.getX(), posTarget.getY(),
01153           WM->getAgentGlobalBodyAngle(),
01154           WM->getAgentGlobalNeckAngle() );
01155   Log.log( 101, "relpos body (%f,%f), vel. ball:(%f,%f) dist: %f (%f,%f,%f)" ,
01156           WM->getRelativeDistance( OBJECT_BALL ),
01157           WM->getRelativeAngle( OBJECT_BALL, true ),
01158            velBall.getX(), velBall.getY(),
01159            SS->getMaximalKickDist(), SS->getPlayerSize(), SS->getBallSize(),
01160              SS->getKickableMargin() );
01161 
01162 
01163   double   dDistOpp;
01164   ObjectT  objOpp     = WM->getClosestInSetTo( OBJECT_SET_OPPONENTS, 
01165                                                  OBJECT_BALL, &dDistOpp );
01166 
01167   if( velDes.getMagnitude() > SS->getBallSpeedMax() ) // can never reach point
01168   {
01169              dPower     = SS->getMaxPower();
01170     double   dSpeed     = WM->getActualKickPowerRate() * dPower;
01171     double   tmp        = velBall.rotate(-velDes.getDirection()).getY();
01172              angActual  = velDes.getDirection() - asinDeg( tmp / dSpeed );
01173     double   dSpeedPred = (WM->getGlobalVelocity(OBJECT_BALL)+
01174                            VecPosition(dSpeed,angActual, POLAR )
01175                           ).getMagnitude();
01176   
01177     // but ball acceleration in right direction is very high
01178     if( dSpeedPred > PS->getPlayerWhenToKick()*SS->getBallAccelMax() )
01179     {
01180       Log.log( 101, "pos (%f,%f) too far, but can acc ball good to %f k=%f,%f",
01181                velDes.getX(), velDes.getY(), dSpeedPred, dSpeed, tmp );
01182       return accelerateBallToVelocity( velDes );    // shoot nevertheless
01183     }
01184     else if( WM->getActualKickPowerRate() >
01185              PS->getPlayerWhenToKick() * SS->getKickPowerRate() )
01186     {
01187       Log.log( 101, "point too far, freeze ball" ); // ball well-positioned
01188       return freezeBall();                          // freeze ball
01189     }
01190     else
01191     {
01192       Log.log( 101, "point too far, reposition ball (k_r = %f)",
01193                WM->getActualKickPowerRate()/(SS->getKickPowerRate()) );
01194       return kickBallCloseToBody( 0 );            // else position ball better
01195     }
01196   }
01197   else                                            // can reach point
01198   {
01199     VecPosition accBallDes = velDes - velBall;
01200     dPower = WM->getKickPowerForSpeed(accBallDes.getMagnitude());
01201     if( dPower <= 1.05*SS->getMaxPower() || // with current ball speed
01202   (dDistOpp < 2.0 && dPower <= 1.30*SS->getMaxPower() ) )
01203     {                               // 1.05 since cannot get ball fully perfect
01204       Log.log( 101, "point good and can reach point %f", dPower );
01205       return accelerateBallToVelocity( velDes );  // perform shooting action
01206     }
01207     else
01208     {
01209       Log.log( 101, "point good, but reposition ball since need %f",dPower );
01210       SoccerCommand soc = kickBallCloseToBody( 0 );
01211       VecPosition   posPredBall;
01212       WM->predictBallInfoAfterCommand( soc, &posPredBall );
01213       dDistOpp = posPredBall.getDistanceTo( WM->getGlobalPosition( objOpp ) );
01214       return soc;            // position ball better
01215     }
01216   }
01217 }
01218 
01219 
01251 /*
01252 SoccerCommand BasicPlayer::turnWithBallTo( AngDeg ang, AngDeg angKickThr,
01253                                            double dFreezeThr )
01254 {
01255   // if opponent is close
01256   // if ball is located more than 'angKickThr' degrees from ang
01257   //  kick ball to point right in front of player in direction ang
01258   // else if ball has still speed higher than 'dFreezeThr'
01260   // else
01261   //  turn to direction 'ang'
01262   VecPosition posAgent = WM->getAgentGlobalPosition();
01263   VecPosition posBall  = WM->getBallPos();
01264   AngDeg      angBody  = WM->getAgentGlobalBodyAngle();
01265   AngDeg      angDiff  = (posBall-posAgent).getDirection() - ang;
01266               angDiff  = VecPosition::normalizeAngle( angDiff );
01267   double      dDist;
01268   ObjectT     objOpp   = WM->getClosestInSetTo( OBJECT_SET_OPPONENTS,
01269                            WM->getAgentObjectType(), &dDist );
01270   VecPosition posOpp   = WM->getGlobalPosition( objOpp );
01271 
01272   if( objOpp != OBJECT_ILLEGAL && dDist < 2.5 )
01273   {
01274     if( posBall.getDistanceTo( posOpp ) < posBall.getDistanceTo( posAgent ) )
01275     {
01276       ang = (posOpp - posAgent).getDirection() + 180;
01277       return kickBallCloseToBody( VecPosition::normalizeAngle( ang-angBody ));
01278     }
01279   }
01280   else if( fabs( angDiff ) > angKickThr )
01281   {
01282     Log.log( 101, "turnWithBall: kick ball close %f", ang );
01283     return kickBallCloseToBody( VecPosition::normalizeAngle( ang - angBody ) );
01284   }
01285 
01286   // hier niet altijd freezen -> kan dan niet meer goed liggen omdat je zelf
01287   // doorschiet.
01288   if( WM->getBallSpeed() > dFreezeThr )
01289   {
01290     Log.log( 101, "turnWithBall: freeze ball" );
01291     return freezeBall();
01292   }
01293 
01294   ACT->putCommandInQueue( alignNeckWithBody() );
01295   return turnBodyToPoint( posAgent + VecPosition(1.0, ang, POLAR ) );
01296 }
01297 */
01298 SoccerCommand BasicPlayer::turnWithBallTo( AngDeg ang, AngDeg, double )
01299 {
01300   // if opponent is closer to the ball than I am
01301   //  kick ball away from his direction
01302   // if the ball will be in my kick_range in the next cycle
01303   //  turn to direction ang
01304   // if ball will be in kick range if frozen
01305   //   freeze_ball
01306   // else
01307   //   kickBallCloseToBody(ang)
01308   VecPosition   posAgent= WM->getAgentGlobalPosition();
01309   VecPosition   posBall = WM->getBallPos();
01310   AngDeg        angBody = WM->getAgentGlobalBodyAngle();
01311   double        dDist;
01312   ObjectT       objOpp  = WM->getClosestInSetTo( OBJECT_SET_OPPONENTS,
01313                           WM->getAgentObjectType(), &dDist );
01314   VecPosition   posOpp  = WM->getGlobalPosition( objOpp );
01315   SoccerCommand soc     = turnBodyToPoint(posAgent+VecPosition(1.0,ang,POLAR));
01316 
01317   if( objOpp != OBJECT_ILLEGAL && dDist < 2.5 )
01318   {
01319     AngDeg angBall = (posBall-posAgent).getDirection();
01320     AngDeg angOpp  = (posOpp -posAgent).getDirection();
01321     if( fabs( VecPosition::normalizeAngle( angBall - angOpp ) ) < 90  )
01322     {
01323       ang = angOpp + 180;
01324       Log.log( 101, "turnWithBall: kick ball away from opp at ang %f", angOpp);
01325       return kickBallCloseToBody( VecPosition::normalizeAngle( ang-angBody ));
01326     }
01327   }
01328 
01329   VecPosition posAgentPred = WM->predictAgentPosAfterCommand( soc );
01330   VecPosition posBallPred  = WM->predictPosAfterNrCycles( OBJECT_BALL, 1 );
01331   if( posAgentPred.getDistanceTo( posBallPred ) < 0.8*SS->getMaximalKickDist()
01332    ||
01333    ( posAgentPred.getDistanceTo(posBallPred) < 0.9*SS->getMaximalKickDist()
01334      && WM->getBallSpeed() < 0.1 ))
01335   {
01336     Log.log( 101, "turnWithBall: turn since ball will be kickable in t+1" );
01337     return soc;
01338   }
01339 
01340   posAgentPred = WM->predictPosAfterNrCycles( WM->getAgentObjectType(), 1, 0 );
01341 
01342   // if ball will not be in kickable distance in next turn, we have to freeze
01343   // the ball. Do this only when current ball position (same as frozen ball
01344   // position) lies within kickable distance of predicted agent position in
01345   // next cycle. When ball lies at edge of the kickable distance, do not
01346   // freeze, since when vel.is not completely correct can move outside area
01347   // but first we try to collide with the ball, the ball will then lie still
01348   // and we can turn in one cycle to the desired angle in the next cycle
01349   SoccerCommand socCollide = collideWithBall( );
01350   if( ! socCollide.isIllegal( ) )
01351   {
01352     Log.log( 101, "turnWithBall: collide with ball" );  
01353     return socCollide;
01354   }
01355   else if( posAgentPred.getDistanceTo(posBall) < 0.8*SS->getMaximalKickDist() 
01356            &&
01357            WM->getBallSpeed() > 0.1  )
01358   {
01359     Log.log( 101, "turnWithBall: freeze ball" );
01360     return freezeBall();
01361   }
01362   else
01363   {
01364     Log.log( 101, "turnWithBall: kick ball close to desired turnangle %f",ang);
01365     return kickBallCloseToBody( VecPosition::normalizeAngle( ang - angBody ) );
01366   }
01367 }
01368 
01369 
01370 
01371 
01427 SoccerCommand BasicPlayer::moveToPosAlongLine( VecPosition pos, AngDeg ang,
01428     double dDistThr, int iSign, AngDeg angThr, AngDeg angCorr )
01429 {
01430   Line        l        = Line::makeLineFromPositionAndAngle( pos, ang );
01431   VecPosition posBall  = WM->getBallPos();
01432   VecPosition posAgent = WM->getAgentGlobalPosition();
01433   AngDeg      angBody  = WM->getAgentGlobalBodyAngle();
01434   VecPosition posProj  = l.getPointOnLineClosestTo( posAgent );
01435   double      dDist    = posAgent.getDistanceTo( posProj );
01436   double      dDiff    = pos.getDistanceTo     ( posProj );
01437 
01438   // if deviated too much from line, compensate
01439   if( dDist > dDistThr )
01440   {
01441     // check on which side of line agent is located
01442     VecPosition posOrg(0,0);
01443     Line        m            = Line::makeLineFromTwoPoints( posOrg, posAgent );
01444     VecPosition posIntersect = l.getIntersection( m );
01445     int         iSide;
01446     if( posAgent.getDistanceTo(posOrg) < posIntersect.getDistanceTo( posOrg ) )
01447       iSide = +1;
01448     else
01449       iSide = -1;
01450 
01451     // adjust desired turning angle to move back to line in coming cycles
01452     ang = ang + iSign * iSide * angCorr;
01453   }
01454 
01455   Log.log( 553, "y difference to defend point %f", dDiff );
01456   // if current body angle differs much from desired turning angle, turn body
01457   if( fabs( VecPosition::normalizeAngle( ang - angBody ) ) > angThr )
01458   {
01459     Log.log( 553, "angle differs too much body = %f, des = %f", angBody, ang );
01460     return turnBodyToPoint( posAgent + VecPosition( 1.0, ang, POLAR ) );
01461   }
01462   else if(  posBall.getDistanceTo( OBJECT_BALL ) < 60 && dDiff > 0.6 )
01463     return dashToPoint( pos );  // move later when ball is far from pen. area.
01464   else if( posBall.getDistanceTo( OBJECT_BALL ) < 30  && dDiff > 0.3 ) 
01465     return dashToPoint( pos );  // move earlier when is ball near pen. area.
01466   else
01467     return SoccerCommand( CMD_ILLEGAL );
01468 }
01469 
01470 
01471 /********************** HIGH LEVEL SKILLS ************************************/
01472 
01482 SoccerCommand BasicPlayer::intercept( bool isGoalie )
01483 {
01484   SoccerCommand soc = (isGoalie)? interceptCloseGoalie():interceptClose(),soc2;
01485   VecPosition   pos = WM->getAgentGlobalPosition();
01486 
01487   if( soc.commandType != CMD_ILLEGAL
01488     && isGoalie )
01489   {
01490     Log.log( 502, "intercept in two cycles" );
01491     return soc;
01492   }
01493   Log.log( 608, "start intercept, obj %d", WM->getAgentObjectType()  );
01494   soc2 = WM->predictCommandToInterceptBall( WM->getAgentObjectType(), soc );
01495   if( soc2.isIllegal() )
01496     return turnBodyToPoint( pos + VecPosition( 1.0, 0, POLAR ) );
01497   return soc2;
01498 }
01499 
01542 SoccerCommand BasicPlayer::dribble( AngDeg ang, DribbleT dribbleT )
01543 {
01544   double        dLength;
01545   AngDeg        angBody  = WM->getAgentGlobalBodyAngle();
01546   VecPosition   posAgent = WM->getAgentGlobalPosition();
01547   SoccerCommand soc;
01548 
01549   // if not turned into correct direction, turn with the ball to that angle
01550   AngDeg angDiff = VecPosition::normalizeAngle( ang - angBody );
01551   if( fabs( angDiff ) > PS->getDribbleAngThr() )
01552     return turnWithBallTo( ang, PS->getTurnWithBallAngThr(),
01553                                 PS->getTurnWithBallFreezeThr() );
01554 
01555   switch( dribbleT )
01556   {
01557     case DRIBBLE_WITHBALL:
01558       dLength = 4.0;
01559       break;
01560     case DRIBBLE_SLOW:
01561       dLength = 5.0;
01562       break;
01563     case DRIBBLE_FAST:
01564       dLength = 10.0;
01565       break;
01566     default:
01567       dLength = 0.0;
01568       break;
01569   }
01570 
01571   // determine shooting point, relative to agent since moving in that dir.
01572   VecPosition posDribble = posAgent + VecPosition( dLength, angBody, POLAR );
01573 
01574   // adjust when point lies outside side of field
01575   // to a point that lies at distance 2.0 from the side of the field
01576   if( fabs( posDribble.getY() ) > PITCH_WIDTH/2.0  - 3.0 ) 
01577     posDribble.setY( (PITCH_WIDTH/2.0  - 3.0)*sign(posDribble.getY()) );
01578   if( fabs( posDribble.getX() ) > PITCH_LENGTH/2.0 - 3.0 )
01579     posDribble.setX( (PITCH_LENGTH/2.0  - 3.0)*sign(posDribble.getX()) );  
01580 
01581   soc = kickTo( posDribble, 0.5 );
01582 
01583   // if ball is kickable but already heading in the right direction, so only
01584   // small correction term is necessary, start intercepting
01585   SoccerCommand soc2      = intercept( false );
01586   if( soc.dPower < 7  && WM->isCollisionAfterCommand( soc2 ) == false )
01587   {
01588     Log.log( 560, "wanted to dribble, but only small kick %f", soc.dPower );
01589     return soc2;
01590   }
01591   return soc;
01592 }
01593 
01603 SoccerCommand BasicPlayer::directPass( VecPosition pos, PassT passType)
01604 {
01605   if( passType == PASS_NORMAL )
01606     return kickTo( pos, PS->getPassEndSpeed() );
01607   else if( passType == PASS_FAST )
01608     return kickTo( pos, PS->getFastPassEndSpeed() );
01609   else
01610     return SoccerCommand( CMD_ILLEGAL );
01611 }
01612 
01629 SoccerCommand BasicPlayer::leadingPass( ObjectT o, double dDist, DirectionT dir)
01630 {
01631   VecPosition posShoot = WM->getGlobalPosition( o );
01632   if( dir != DIR_ILLEGAL && dir != DIR_CENTER )
01633     posShoot+=VecPosition(dDist,SoccerTypes::getAngleFromDirection(dir),POLAR);
01634   return kickTo( posShoot, PS->getPassEndSpeed() );
01635 }
01636 
01667 SoccerCommand BasicPlayer::throughPass( ObjectT o, VecPosition posEnd,
01668                                         AngDeg *angMax )
01669 {
01670   VecPosition posShoot = getThroughPassShootingPoint( o, posEnd, angMax );
01671   double      dEnd     = getEndSpeedForPass( o, posShoot );
01672 
01673   return kickTo( posShoot, dEnd );
01674 }
01675 
01757 SoccerCommand BasicPlayer::outplayOpponent( ObjectT o, VecPosition pos,
01758                                             VecPosition *posTo )
01759 {
01760   // future: take more than one opponent into account
01761   
01762   VecPosition posAgent   = WM->getAgentGlobalPosition();
01763   AngDeg      angBody    = WM->getAgentGlobalBodyAngle();
01764 
01765   double dMaxDist = 12.0;
01766   if( posAgent.getX() > PENALTY_X - 6.0 )
01767     dMaxDist = 10.0;
01768 
01769   AngDeg      ang        = (pos - posAgent).getDirection();
01770   // if not heading in the desired direction, first turn with the ball
01771   AngDeg angTmp = VecPosition::normalizeAngle( ang - angBody ) ;
01772   if( fabs( angTmp ) > PS->getDribbleAngThr() )
01773     return turnWithBallTo( ang, PS->getTurnWithBallAngThr(),
01774                                 PS->getTurnWithBallFreezeThr() );
01775   ang = WM->getAgentGlobalBodyAngle();
01776   Line        l          = Line::makeLineFromPositionAndAngle(posAgent,ang);
01777   VecPosition posObj     = WM->getGlobalPosition( o );
01778   VecPosition posProj    = l.getPointOnLineClosestTo( posObj );
01779   double      dDistOpp   = posProj.getDistanceTo( posObj );
01780   double      dDistAgent = posProj.getDistanceTo( posAgent );
01781   VecPosition posShoot;
01782 
01783   // we want to know when distance from ball to point p equals distance
01784   // from opp to point p :
01785   // d1 + d3 = sqrt(d2^2 + d3^2) > (d1+d3)^2 = d2^2 + d3^2 =>
01786   // d1^2 + 2*d1*d3 = d2^2 -> d3 = (d2^2 - d1^2 ) / 2*d1
01787   double dCalcDist;
01788   if( o != OBJECT_ILLEGAL )
01789   {
01790     dCalcDist = (dDistOpp*dDistOpp-dDistAgent*dDistAgent)/(2*dDistAgent);
01791     dCalcDist += dDistAgent;
01792   }
01793   else
01794     dCalcDist = dMaxDist;
01795 
01796   Log.log( 552, "outplay opponent %d, calc: %f, opp: %f, agent:  %f",
01797      SoccerTypes::getIndex( o ) + 1, dCalcDist, dDistOpp, dDistAgent );
01798   Log.log( 560, "outplay opponent %d, calc: %f, opp: %f, agent:  %f",
01799      SoccerTypes::getIndex( o ) + 1, dCalcDist, dDistOpp, dDistAgent );
01800 
01801   if( dCalcDist > 7.0 ) // if point far away, kick there
01802   {
01803     dCalcDist = min( posAgent.getDistanceTo( pos ), dCalcDist - 2.5 );
01804     dCalcDist = min( dMaxDist, dCalcDist );
01805     posShoot  = posAgent + VecPosition( dCalcDist, ang, POLAR );
01806   }
01807   else if( dDistAgent < dDistOpp - 0.3 ) // point close and well-positioned
01808   {                                      // shoot far away and outplay opp
01809     dCalcDist = min( posAgent.getDistanceTo( pos ), dMaxDist );
01810     posShoot = posAgent + VecPosition( dCalcDist, ang, POLAR );
01811   }
01812   else                                   // opponent stands in line
01813     return SoccerCommand( CMD_ILLEGAL );
01814 
01815   if( posShoot.getDistanceTo( WM->getAgentGlobalPosition() ) < 2.5 )
01816   {
01817     Log.log( 552, "calculated point too close" );
01818     Log.log( 560, "calculated point too close (%f,%f)", posShoot.getX(),
01819      posShoot.getY() );
01820     return SoccerCommand( CMD_ILLEGAL );
01821   }
01822   else if( WM->getNrInSetInCone( OBJECT_SET_OPPONENTS,PS->getConeWidth(),
01823     posAgent, posShoot ) != 0 )
01824   {
01825     Log.log( 552, "outplay: is opponent in cone" );
01826     Log.log( 560, "outplay: is opponent in cone (%f,%f)", posShoot.getX(),
01827      posShoot.getY() );
01828     return SoccerCommand( CMD_ILLEGAL );
01829   }
01830   else if( WM->getNrInSetInCircle( OBJECT_SET_OPPONENTS, 
01831               Circle( posShoot, posShoot.getDistanceTo( posAgent ) ) ) > 1 )
01832   {
01833     Log.log( 552, "outplay: nr of opp in circle > 1" );
01834     Log.log( 560, "outplay: nr of opp in circle > 1" );
01835     return SoccerCommand( CMD_ILLEGAL );
01836   }
01837   else if( WM->getCurrentTime() - WM->getTimeGlobalAngles(o) < 3 )
01838   {   
01839     double dDistOpp = posShoot.getDistanceTo( posObj ) ;
01840     if( fabs( VecPosition::normalizeAngle( (posShoot - posObj).getDirection() -
01841              WM->getGlobalBodyAngle(o) ) ) < 30 )
01842       dDistOpp -= 1.0;
01843     if( dDistOpp < posAgent.getDistanceTo( posShoot ) )
01844     {
01845       Log.log( 560, "outplay: opponent closer" );
01846       return SoccerCommand( CMD_ILLEGAL );
01847     }
01848   }
01849 
01850 
01851 
01852   if( posTo != NULL )
01853     *posTo = posShoot;
01854   return kickTo( posShoot, 0.5 );
01855 }
01856 
01886 SoccerCommand BasicPlayer::clearBall( ClearBallT type, SideT s,AngDeg *angMax )
01887 {
01888   VecPosition posBall = WM->getBallPos();
01889   VecPosition posLeft, posRight;
01890   double      clearDist = PS->getClearBallDist();
01891 
01892   double      dPitchY = PITCH_WIDTH / 2.0;
01893   if( type == CLEAR_BALL_DEFENSIVE )
01894   {
01895     posLeft.setVecPosition ( 0, - dPitchY + clearDist );
01896     posRight.setVecPosition( 0, + dPitchY - clearDist );
01897   }
01898   else if( type == CLEAR_BALL_OFFENSIVE )
01899   {
01900     posLeft.setVecPosition ( PENALTY_X - clearDist, - dPitchY + clearDist );
01901     posRight.setVecPosition( PENALTY_X - clearDist, + dPitchY - clearDist );
01902   }
01903   else if( type == CLEAR_BALL_OFFENSIVE_SIDE )
01904   {
01905     posLeft.setVecPosition ( PENALTY_X - clearDist, - dPitchY + 8 );
01906     posRight.setVecPosition( PENALTY_X - clearDist, + dPitchY - 8 );
01907   }
01908   else if( type == CLEAR_BALL_GOAL && posBall.getY() > 0 )
01909   {
01910     posLeft.setVecPosition ( max(PENALTY_X - 2.0, posBall.getX()-10),  0.0 );
01911     posRight.setVecPosition( PITCH_LENGTH/2.0 - 5.0, 0.0 );
01912   }
01913   else if( type == CLEAR_BALL_GOAL && posBall.getY() < 0 )
01914   {
01915     posLeft.setVecPosition ( PITCH_LENGTH/2.0 - 5.0, 0.0 );
01916     posRight.setVecPosition( max(PENALTY_X - 2.0, posBall.getX()-10),  0.0 );
01917   }
01918   else
01919     return SoccerCommand( CMD_ILLEGAL );
01920 
01921   if( type != CLEAR_BALL_GOAL && s == SIDE_RIGHT ) // take only right part of
01922   {
01923     if( type == CLEAR_BALL_OFFENSIVE_SIDE )
01924       posLeft.setY( 15.0 );
01925     else
01926       posLeft.setY ( 0.0 );                          // field into account
01927 
01928   }
01929   else if( type != CLEAR_BALL_GOAL && s == SIDE_LEFT )
01930   {
01931     if( type == CLEAR_BALL_OFFENSIVE_SIDE )
01932       posRight.setY( -15.0 );
01933     else
01934       posRight.setY( 0.0 );
01935   }
01936 
01937   // get angle of ball with left and right points
01938   // get the largest angle between these two angles
01939   AngDeg angLeft  = (posLeft  - posBall).getDirection();
01940   AngDeg angRight = (posRight - posBall).getDirection();
01941   double dDist;
01942   if( type != CLEAR_BALL_GOAL )
01943     dDist = PS->getClearBallOppMaxDist();
01944   else
01945     dDist = max( posBall.getDistanceTo(posLeft ),
01946                  posBall.getDistanceTo(posRight) );
01947   AngDeg ang      = WM->getDirectionOfWidestAngle( posBall, angLeft, angRight,
01948                                         angMax, dDist );
01949 
01950   Line l1 = Line::makeLineFromPositionAndAngle( posBall, ang );
01951   Line l2 = Line::makeLineFromTwoPoints( posLeft, posRight );
01952   VecPosition posShoot = l1.getIntersection( l2 );
01953   Log.log( 560, "angLeft %f, right %f, best %f point (%f,%f)",
01954   angLeft, angRight, ang, posShoot.getX(), posShoot.getY() );
01955   if( type == CLEAR_BALL_GOAL  )
01956     return kickTo( posShoot, SS->getBallSpeedMax() );
01957   else if( type == CLEAR_BALL_OFFENSIVE )
01958     return kickTo( posShoot, 0.25 );
01959   else if( type == CLEAR_BALL_OFFENSIVE_SIDE )
01960   {
01961     return kickTo( posShoot, (posBall.getX()>20) ? 1.2 : 2.7);
01962   }
01963   else
01964     return kickTo( posShoot, 0.5 );
01965 }
01966 
02010 SoccerCommand BasicPlayer::mark( ObjectT o, double dDist, MarkT mark )
02011 {
02012   VecPosition posMark  = getMarkingPosition( o, dDist, mark );
02013   VecPosition posAgent = WM->getAgentGlobalPosition();
02014   VecPosition posBall  = WM->getGlobalPosition( OBJECT_BALL );
02015 //  AngDeg      angBody  = WM->getAgentGlobalBodyAngle();
02016 
02017   if( o == OBJECT_BALL )
02018   {
02019     if( posMark.getDistanceTo( posAgent ) < 1.5 )
02020       return turnBodyToObject( OBJECT_BALL );
02021     else
02022      return moveToPos( posMark, 30.0, 3.0, false );
02023   }
02024 
02025   if( posAgent.getDistanceTo( posMark ) < 2.0 )
02026   {
02027     AngDeg angOpp = (WM->getGlobalPosition( o ) - posAgent).getDirection();
02028     AngDeg angBall = (posBall - posAgent).getDirection();
02029     if( isAngInInterval( angBall, angOpp, 
02030                          VecPosition::normalizeAngle( angOpp + 180 ) ) )
02031       angOpp += 80;
02032     else
02033       angOpp -= 80;       
02034     angOpp  = VecPosition::normalizeAngle( angOpp );
02035     Log.log( 513, "mark: turn body to ang %f", angOpp );
02036     return turnBodyToPoint( posAgent + VecPosition( 1.0, angOpp, POLAR )  );
02037   }
02038   Log.log( 513, "move to marking position" );
02039   
02040   return moveToPos( posMark, 25, 3.0, false );
02041 }
02042 
02043 
02088 SoccerCommand BasicPlayer::defendGoalLine( double dDist )
02089 {
02090   // determine defending point as intersection keeper line and line ball-goal
02091   VecPosition posBall    = WM->getBallPos();
02092   VecPosition posAgent   = WM->getAgentGlobalPosition();
02093   VecPosition posGoal    = WM->getPosOwnGoal( );
02094   VecPosition posCenter(sign(posGoal.getX())*(fabs(posGoal.getX())-dDist),0);
02095   Line        lineGoal   = Line::makeLineFromPositionAndAngle( posCenter, 90 );
02096 
02097   VecPosition posGoalLeft ( posGoal.getX(), -SS->getGoalWidth()/2.0 );
02098   VecPosition posGoalRight( posGoal.getX(),  SS->getGoalWidth()/2.0 );
02099   Line left    = Line::makeLineFromTwoPoints( posBall, posGoalLeft  );
02100   Line right   = Line::makeLineFromTwoPoints( posBall, posGoalRight );
02101   posGoalLeft  = left.getIntersection ( lineGoal );
02102   posGoalRight = right.getIntersection( lineGoal );
02103   double dDistLeft  = posGoalLeft.getDistanceTo( posBall );
02104   double dDistRight = posGoalRight.getDistanceTo( posBall );
02105   double dDistLine  = posGoalLeft.getDistanceTo( posGoalRight );
02106   VecPosition posDefend  = posGoalLeft+
02107       VecPosition( 0, (dDistLeft/(dDistLeft+dDistRight))*dDistLine);
02108 
02109   bool        bBallInPen = WM->isInOwnPenaltyArea( posBall );
02110 
02111   // do not stand further to side than goalpost
02112   if( fabs( posDefend.getY() ) > SS->getGoalWidth()/2.0 )
02113     posDefend.setY( sign(posDefend.getY())*SS->getGoalWidth()/2.0);
02114 
02115   // if too far away from line, move directly towards it
02116   double dDiff = ( bBallInPen == true ) ? 1.5 : 0.5;
02117   if( posDefend.getX() + dDiff < posAgent.getX()  )
02118   {
02119     Log.log( 553, "move backwards to guard point" );
02120     return moveToPos( posDefend, 30, -1.0, true ); // always backwards
02121   }
02122   else if( posDefend.getX() - dDiff > posAgent.getX() )
02123   {
02124     Log.log( 553, "move forward to guard point" );
02125     return moveToPos( posDefend, 30, -1.0 );       // always forward
02126   }
02127 
02128   // desired body angle is in direction of the ball
02129   // predicted movement direction in subsequent cycles is in moving dir. ball
02130   AngDeg  angDes;
02131   if( fabs( posBall.getY() - posDefend.getY() ) > 0.5 )
02132     angDes = sign( posBall.getY() - posDefend.getY() )*90.0;
02133   else
02134     angDes = sign( WM->getAgentGlobalBodyAngle() )*90.0;
02135   int     iSign     = sign( WM->getGlobalVelocity( OBJECT_BALL ).getY() );
02136 
02137   // move to position along line: when ball in penalty area, never adjust body
02138   // angle (value 3.0) and change trajectory when angle difference is > 7
02139   // when ball is outside pen. area, adjust angle to move to line when more
02140   // than 0.5 from desired line, adjust when angle diff > 2 with 12 degrees
02141   if( bBallInPen )
02142   {
02143     Log.log( 553, "move along line, with ball in penalty area x: %f ang %f", 
02144                   posDefend.getX(), angDes );
02145     return moveToPosAlongLine( posDefend, angDes, 3.0, iSign, 7.0, 12.0 );
02146   }
02147   else
02148   {
02149     Log.log( 553, "move along line, with ball out pen. area (%f,%f)(%f,%f) %f",
02150       WM->getAgentGlobalPosition().getX(),
02151       WM->getAgentGlobalPosition().getY(),
02152       posBall.getX(), posBall.getY(), WM->getConfidence( OBJECT_BALL) );
02153     Log.log( 553, "%s", WM->strLastSeeMessage );
02154     return moveToPosAlongLine( posDefend, angDes, 0.5, iSign, 2.0, 12.0 );
02155   }
02156 }
02157 
02163 SoccerCommand BasicPlayer::interceptScoringAttempt( )
02164 {
02165   int iSide = 1;
02166   if( WM->isPenaltyUs() || WM->isPenaltyThem() )
02167     iSide = ( WM->getSide() == WM->getSidePenalty() ) ? 1 : -1;
02168 
02169   SoccerCommand soc;
02170   VecPosition   posAgent = WM->getAgentGlobalPosition();
02171   VecPosition   posBall  = WM->getBallPos();
02172   Line          lineBall = Line::makeLineFromPositionAndAngle( posBall,
02173                                     WM->getBallDirection() );
02174   Line          lineKeeper = Line::makeLineFromPositionAndAngle( posAgent, 90);
02175   bool          bInterceptAtSide = false;
02176 
02177   // first create intersection point between ball trajectory and rectangle
02178   // that is located just in front of the goal mouth
02179   VecPosition   posIntersect = lineBall.getIntersection( lineKeeper );
02180   if( fabs( posIntersect.getY() ) > SS->getGoalWidth()/2.0  )
02181   {
02182      VecPosition posGoalPost( -iSide*PITCH_LENGTH/2.0,
02183                          sign(posBall.getY())*(SS->getGoalWidth()/2.0 + 0.5));
02184      Line l = Line::makeLineFromPositionAndAngle( posGoalPost, 0 );
02185      posIntersect = lineBall.getIntersection( l );
02186 
02187      // if intersection point does not cross rectangle between the agent and
02188      // the goalpost, we just move to edge of the rectangle
02189      if( fabs(posIntersect.getX()) > fabs(posGoalPost.getX())  ||
02190          fabs(posIntersect.getX()) < fabs(posAgent.getX()) )
02191        posIntersect.setVecPosition( posAgent.getX(), posGoalPost.getY() );
02192      else
02193        bInterceptAtSide = true; // interception point is at side of rectangle
02194   }
02195 
02196   // first try close intercept
02197   soc = interceptCloseGoalie();
02198   if( ! soc.isIllegal() )
02199   {
02200     Log.log( 553, "close intercept" );
02201     return soc;
02202   }
02203 
02204   // if far away from goal, just intercept ball
02205   if( PITCH_LENGTH/2.0 - fabs(posAgent.getX()) > 8.0 )
02206   {
02207     Log.log( 553, "I am far away from keeper line: %d %d %f %f",
02208     WM->getSide(), WM->getSidePenalty(), -iSide*PITCH_LENGTH/2.0 ,
02209     posAgent.getX() );
02210     return intercept( true );
02211   }
02212   else if( fabs(posBall.getX()) > fabs(posAgent.getX()) &&
02213            fabs( posBall.getY() ) < SS->getGoalWidth()/2.0 + 2.0 )
02214   {
02215     Log.log( 553, "ball heading and ball behind me" );
02216     return intercept( true );
02217   }
02218 
02219   // move to interception point
02220   if( posIntersect.getDistanceTo( posAgent ) < 0.5 )
02221   {
02222      Log.log( 553, "close to intersection point keeperline" );
02223      soc = turnBodyToObject( OBJECT_BALL );
02224   }
02225   else if( sign( (posIntersect - posAgent ).getDirection() ) ==
02226            sign( WM->getAgentGlobalBodyAngle()             ) ||
02227        bInterceptAtSide == true)
02228   {
02229     Log.log( 553, "move forward to intersection point keeperline" );
02230     soc = moveToPos( posIntersect, 20, SS->getGoalWidth() );
02231   }
02232   else
02233   {
02234     Log.log( 553, "move backward to intersection point keeperline" );
02235     soc = moveToPos( posIntersect, 20, SS->getGoalWidth(), true );
02236   }
02237 
02238   return soc;
02239 }
02240 
02245 SoccerCommand BasicPlayer::holdBall( )
02246 {
02247   double        dDist;
02248   VecPosition   posAgent = WM->getAgentGlobalPosition();
02249   ObjectT       objOpp   = WM->getClosestInSetTo( OBJECT_SET_OPPONENTS, 
02250             OBJECT_BALL, &dDist );
02251   VecPosition   posOpp   = WM->getGlobalPosition ( objOpp );
02252   AngDeg        angOpp   = WM->getGlobalBodyAngle( objOpp );
02253   AngDeg        ang      = 0.0;
02254 
02255   if( objOpp != OBJECT_ILLEGAL && dDist < 5 )
02256   {
02257     // get the angle between the object to the agent
02258     // check whether object is headed to the left or right of this line
02259     ang = ( posAgent - posOpp ).getDirection();
02260     int iSign = -sign(VecPosition::normalizeAngle( angOpp - ang ));
02261     ang +=  iSign*45 - WM->getAgentGlobalBodyAngle();
02262     ang = VecPosition::normalizeAngle( ang );
02263     Log.log( 512, "hold ball: opp close shoot to ang %f", ang );
02264   }
02265   else
02266     Log.log( 512, "hold ball: in direction body: %f", ang );
02267 
02268   if( WM->getBallPos().getDistanceTo( posAgent + VecPosition( 0.7, ang, POLAR ) )
02269       < 0.3 )
02270   {
02271     SoccerCommand soc          = turnBodyToPoint( WM->getPosOpponentGoal() );
02272     VecPosition   posBallPred = WM->predictPosAfterNrCycles( OBJECT_BALL, 1 );
02273     VecPosition   posPred     = WM->predictAgentPosAfterCommand( soc );  
02274     if( posPred.getDistanceTo( posBallPred ) < 0.85 * SS->getMaximalKickDist() )
02275     {
02276       Log.log( 512, "hold ball: turn body to goal, ball remains still" );
02277       return soc;
02278     }
02279   }
02280 
02281   return kickBallCloseToBody( ang, 0.7 ) ;
02282 }
02283 
02284 /********************** UTILITY METHODS **************************************/
02285 
02297 VecPosition BasicPlayer::getThroughPassShootingPoint( ObjectT objTeam,
02298      VecPosition posEnd, AngDeg  *angMax  )
02299 {
02300   VecPosition posTeam = WM->getGlobalPosition( objTeam );
02301   return getShootPositionOnLine( posTeam, posEnd, angMax );
02302 }
02303 
02319 VecPosition BasicPlayer::getInterceptionPointBall( int *iMinCyclesBall,
02320                                                    bool isGoalie )
02321 {
02322   static Time timeLastMinCycles(-1,0);
02323   static Time timeLastIntercepted(-1,0);
02324   VecPosition posPred  = WM->getAgentGlobalPosition();
02325   VecPosition velMe    = WM->getAgentGlobalVelocity();
02326   double      dSpeed, dDistExtra;
02327   VecPosition posMe, posBall;
02328   AngDeg      ang, angBody, angNeck;
02329   Stamina     sta;
02330   double      dMaxDist, dBestX = -100;
02331   int         iCyclesBall, iCyclesOpp, iFirstBall = -100;//, iCyclesOppPoint;
02332 
02333 
02334   if( (WM->getCurrentTime() - timeLastIntercepted) > 2 )
02335     timeLastMinCycles.updateTime( -1 );
02336   timeLastIntercepted = WM->getCurrentTime();
02337   *iMinCyclesBall = 100;
02338 
02339   if( isGoalie )
02340     return getActiveInterceptionPointBall( iMinCyclesBall, isGoalie );
02341 
02342   dMaxDist = (isGoalie) ? SS->getCatchableAreaL() : SS->getMaximalKickDist();
02343 
02344   // predict the position of the agent when current velocity is propogated
02345   dSpeed     = WM->getAgentSpeed();
02346   dDistExtra = Geometry::getSumInfGeomSeries( dSpeed, SS->getPlayerDecay() );
02347   posPred   += VecPosition( dDistExtra, velMe.getDirection(), POLAR );
02348 
02349   ObjectT objOpp = WM->getFastestInSetTo(OBJECT_SET_OPPONENTS,
02350                                          OBJECT_BALL, &iCyclesOpp);
02351   Log.log( 501, "closest opponent can get to ball in %d cycles", iCyclesOpp );
02352   if( WM->isBeforeGoal( WM->getAgentGlobalPosition() ) )
02353     iCyclesOpp = 20;
02354   int i = 2; // otherwise should be done by interceptClose
02355   // for each loop, check whether agent can reach ball in less cycles
02356   // do not look further than cycles that fastest opponent can reach the ball
02357   while( i <= PS->getPlayerWhenToIntercept() && i <= iCyclesOpp + 2 )
02358   {
02359     posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, i );
02360 
02361     // if too far away, try next cycle
02362     if( posPred.getDistanceTo(posBall)/SS->getPlayerSpeedMax() > i + dMaxDist 
02363         ||
02364         WM->isInField( posBall ) == false )
02365     {
02366       i++;
02367       continue;
02368     }
02369 
02370     // (re-)initialize all the needed variables
02371     // set ball prediction one further to get right in front of ball line
02372     posMe   = WM->getAgentGlobalPosition();
02373     velMe   = WM->getAgentGlobalVelocity();
02374     angBody = WM->getAgentGlobalBodyAngle();
02375     angNeck = WM->getAgentGlobalNeckAngle();
02376     ang     = (posBall - posPred).getDirection();
02377     ang     = VecPosition::normalizeAngle( ang - angBody );
02378     sta     = WM->getAgentStamina();
02379     int turn = 0;
02380 
02381     // as long as not correctly headed for point, simulate a turn command
02382     while (fabs(ang) > PS->getPlayerWhenToTurnAngle() && turn<5)
02383     {
02384       turn++;
02385       WM->predictStateAfterTurn( WM->getAngleForTurn(ang,velMe.getMagnitude()),
02386                    &posMe, &velMe, &angBody, &angNeck,WM->getAgentObjectType(),
02387                    &sta             );
02388       ang      = (posBall - posPred).getDirection();
02389       ang      = VecPosition::normalizeAngle( ang - angBody );
02390     }
02391 
02392     if( turn > 1 )
02393       Log.log( 501, "nr of turns needed: %d", turn );
02394     int iTurnTmp = turn;
02395     // for cycles that are left over after turn(s), execute full power dash
02396     for( ; turn < i; turn++ )
02397       WM->predictStateAfterDash(SS->getMaxPower(),&posMe,&velMe,&sta,angBody);
02398 
02399     // if in kickable distance or passed ball, we can reach the ball!
02400     if (posMe.getDistanceTo( posBall ) < dMaxDist  ||
02401        (posMe.getDistanceTo( posPred ) > posBall.getDistanceTo( posPred ) -
02402                                          dMaxDist) )
02403     {
02404       Log.log( 501, "can intercept in %d cycles %d turns, ball %f best %f", 
02405            i, iTurnTmp, posBall.getX(), dBestX );
02406         
02407       if( *iMinCyclesBall == 100 ) // log first possible interception point
02408       {
02409         *iMinCyclesBall = i;
02410         iFirstBall = i;
02411       }
02412 
02413       iCyclesBall = i;
02414       if( WM->getCurrentTime() + iCyclesBall == timeLastMinCycles )
02415       {
02416         Log.log( 501, "choose old interception point %d", iCyclesBall );
02417         return posBall;
02418       }
02419       if( objOpp == OBJECT_ILLEGAL || isGoalie == true )
02420       {
02421         // if no opponent, move to first reachable point
02422         if( *iMinCyclesBall == 100 )
02423         {
02424           *iMinCyclesBall = iCyclesBall;
02425           i = PS->getPlayerWhenToIntercept() + 1; // and quit
02426         }
02427       }
02428       else if( WM->isBeforeGoal( WM->getAgentGlobalPosition() ) )
02429       {
02430         if( fabs(posBall.getY()) < fabs(dBestX ) &&
02431             iCyclesBall < iFirstBall + 3   &&
02432             (iCyclesOpp - iCyclesBall)  >= 2  )
02433         {
02434           dBestX = fabs(posBall.getY());
02435           *iMinCyclesBall = iCyclesBall;
02436           Log.log( 501, "update best cycles in goalarea to %d, opp %d",
02437         *iMinCyclesBall, (iCyclesOpp - iCyclesBall) );
02438         }
02439 
02440       }
02441       else if( posBall.getX() >= dBestX &&
02442                fabs( posBall.getY() ) < 32.0 &&
02443                fabs( posBall.getX() ) < 50.0 &&
02444                ( posBall.getX() > - PITCH_LENGTH/4.0 ||
02445                  iCyclesBall < iFirstBall + 3 ) &&
02446                (iCyclesOpp - iCyclesBall)  >= 1 )
02447       {
02448         dBestX = posBall.getX();
02449         *iMinCyclesBall = iCyclesBall;
02450         Log.log( 501, "update best cycles to %d, opp diff %d", *iMinCyclesBall,
02451          iCyclesOpp - iCyclesBall );
02452       }
02453     }
02454     i++;
02455   }
02456 
02457   if( i == 2 || ( i == iCyclesOpp + 3 && *iMinCyclesBall > iCyclesOpp ))
02458   {
02459     Log.log( 501, "move to interception point closest opp." );
02460     *iMinCyclesBall = iCyclesOpp;
02461   }
02462 
02463   posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, *iMinCyclesBall );
02464   Log.log( 501, "choose: %d", *iMinCyclesBall );
02465   timeLastMinCycles = WM->getCurrentTime() + *iMinCyclesBall;
02466 
02467   return posBall;
02468 }
02469 
02470 
02475 VecPosition BasicPlayer::getActiveInterceptionPointBall( int *iCyclesBall,
02476                                                          bool isGoalie )
02477 {
02478   VecPosition posPred  = WM->getAgentGlobalPosition();
02479   VecPosition velMe    = WM->getAgentGlobalVelocity();
02480   double      dSpeed, dDistExtra;
02481   VecPosition posMe, posBall;
02482   AngDeg      ang, angBody, angNeck;
02483   Stamina     sta;
02484   double      dMaxDist;
02485 
02486   if( isGoalie &&
02487       !WM->isInOwnPenaltyArea(WM->predictPosAfterNrCycles( OBJECT_BALL, 45)))
02488   {
02489     *iCyclesBall = -1;
02490     return posBall;
02491   }
02492 
02493   dMaxDist = (isGoalie) ? SS->getCatchableAreaL() : SS->getMaximalKickDist();
02494 
02495   // predict the position of the agent when current velocity is propogated
02496   dSpeed     = WM->getAgentSpeed();
02497   dDistExtra = Geometry::getSumInfGeomSeries( dSpeed, SS->getPlayerDecay() );
02498   posPred   += VecPosition( dDistExtra, velMe.getDirection(), POLAR );
02499 
02500   // for each loop, check whether agent can reach ball in less cycles
02501   for ( int i = 0; i <= PS->getPlayerWhenToIntercept(); i++ )
02502   {
02503     // (re-)initialize all the needed variables
02504     // set ball prediction one further to get right in front of ball line
02505     velMe   = WM->getAgentGlobalVelocity();
02506     angBody = WM->getAgentGlobalBodyAngle();
02507     angNeck = WM->getAgentGlobalNeckAngle();
02508     posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, i + 1 );
02509     posMe   = WM->getAgentGlobalPosition();
02510     ang     = (posBall - posPred).getDirection();
02511     ang     = VecPosition::normalizeAngle( ang - angBody );
02512     sta     = WM->getAgentStamina();
02513     int turn = 0;
02514 
02515     // as long as not correctly headed for point, simulate a turn command
02516     while (fabs(ang) > PS->getPlayerWhenToTurnAngle() && turn<5)
02517     {
02518       turn++;
02519       WM->predictStateAfterTurn( WM->getAngleForTurn(ang,velMe.getMagnitude()),
02520                    &posMe, &velMe, &angBody, &angNeck,WM->getAgentObjectType(),
02521                    &sta             );
02522       ang      = (posBall - posPred).getDirection();
02523       ang      = VecPosition::normalizeAngle( ang - angBody );
02524     }
02525 
02526     if( turn > 1 )
02527     {
02528       Log.log( 502, "nr of turns needed: %d", turn );
02529     }
02530 
02531     // for cycles that are left over after turn(s), execute full power dash
02532     for( ; turn < i; turn++ )
02533       WM->predictStateAfterDash(SS->getMaxPower(),&posMe,&velMe,&sta,angBody);
02534 
02535     // if in kickable distance or passed ball, we can reach the ball!
02536     if (posMe.getDistanceTo( posBall ) < dMaxDist  ||
02537        (posMe.getDistanceTo( posPred ) > posBall.getDistanceTo( posPred ) +
02538                                          dMaxDist) )
02539     {
02540       WM->logCircle( 501, posBall, 2, true );
02541       WM->logCircle( 501, posBall, 1, true );
02542       *iCyclesBall = i;
02543       Log.log( 501, "intercept ball in %d cycles", *iCyclesBall );
02544       return posBall;
02545     }
02546   }
02547 
02548   *iCyclesBall = -1;
02549   return posBall;
02550 }
02551 
02552 VecPosition BasicPlayer::getDribblePoint( DribbleT dribble, double *dDist )
02553 {
02554   AngDeg        angBody = WM->getAgentGlobalBodyAngle() ;
02555   VecPosition   posAgent = WM->getAgentGlobalPosition() ;
02556   VecPosition   pos;
02557   double        dLength;
02558 
02559   switch( dribble )
02560   {
02561     case DRIBBLE_WITHBALL:
02562       dLength = 5.0;   // 6: max 4.58, average 2.66
02563       *dDist = 4;
02564       break;
02565     case DRIBBLE_SLOW:
02566       dLength = 8.0;   // 8: max 6.47, average 3.34
02567       *dDist = 3.8;
02568       break;         // 9: max 8.57, average 5.38
02569     case DRIBBLE_FAST:
02570       dLength = 14.0;  // 14; max: 15.93 average 11.71
02571       *dDist = 12;
02572       break;
02573     default:
02574       dLength = 0.0;
02575       break;
02576   }
02577 
02578   // get point to shoot ball to
02579   pos = posAgent + VecPosition( dLength, angBody, POLAR );
02580 
02581   // adjust when point lies outside side of field
02582   // to a point that lies at distance 2.0 from the side of the field
02583   if( ( fabs( pos.getY() ) > PITCH_WIDTH/2.0  - 3.0 && fabs(angBody) > 3) ||
02584       ( fabs( pos.getX() ) > PITCH_LENGTH/2.0 - 3.0 ) )
02585     pos = WM->getOuterPositionInField( WM->getAgentGlobalPosition(),
02586                                   WM->getAgentGlobalBodyAngle(), 2.0, false );
02587 
02588     return pos;
02589 }
02590 
02602 VecPosition BasicPlayer::getShootPositionOnLine( VecPosition p1,
02603                                       VecPosition  p2, AngDeg *angLargest )
02604 {
02605   VecPosition posBall  = WM->getBallPos();
02606   Line   line          = Line::makeLineFromTwoPoints( p1, p2 );
02607   double dRadius       = min( PS->getClearBallOppMaxDist(),
02608                               posBall.getDistanceTo( p2 )  );
02609   AngDeg angMin        = (p1 - posBall ).getDirection();
02610   AngDeg angMax        = (p2 - posBall ).getDirection();
02611   // not correct when line crosses -180 boundary, but will never happenk
02612   AngDeg angShoot      = WM->getDirectionOfWidestAngle(
02613                              posBall, min(angMin, angMax),
02614                                  max(angMin,angMax), angLargest, dRadius );
02615   Line   line2         = Line::makeLineFromPositionAndAngle( posBall,
02616                                                              angShoot );
02617   return line.getIntersection( line2 );
02618 }
02619 
02633 double BasicPlayer::getEndSpeedForPass( ObjectT o, VecPosition posPass )
02634 {
02635   // we want that the ball arrives at that point after length nr of cycles
02636   // where length is the nr of cycles it takes the player to get there.
02637   VecPosition posBall = WM->getBallPos();
02638   double      dDist   = posBall.getDistanceTo( posPass );
02639   double      dLength = WM->predictNrCyclesToPoint( o, posPass );
02640   double      dFirst  = WM->getFirstSpeedFromDist( dDist, dLength );
02641   if( dFirst > SS->getBallSpeedMax() )
02642     dFirst = SS->getBallSpeedMax();
02643   double dEnd         = WM->getEndSpeedFromFirstSpeed( dFirst, dLength );
02644   if( dEnd > PS->getPassEndSpeed() )
02645     dEnd = PS->getPassEndSpeed();
02646   else if( dEnd < 0.6 )
02647     dEnd = 0.6;
02648   else if( dLength > 10.0 )
02649     dEnd = 0.6;
02650 
02651   return dEnd;
02652 }
02653 
02677 VecPosition BasicPlayer::getMarkingPosition( ObjectT o, double dDist, 
02678                                              MarkT mark)
02679 {
02680   VecPosition pos      = WM->getGlobalPosition( o );
02681   // except on back line assume players is moving to goalline
02682   if( pos.getX() > - PITCH_LENGTH/2.0 + 4.0 )
02683     pos -= VecPosition( 1.0, 0.0 );
02684 
02685   return WM->getMarkingPosition( pos, dDist, mark );
02686 
02687 }
02688 
02689 
02690 /*
02691   // stop and then turn
02692   // this is stamina intensive??
02693   if( bReady == true )
02694      ;
02695   else if( fabs( angTurn ) > angThreshold && !bOppClose &&
02696       WM->getAgentSpeed() > 0.1 )
02697   {
02698     posBall = WM->predictPosAfterNrCycles( OBJECT_BALL, 2 );
02699     soc = dashToPoint( posAgent );
02700     WM->predictAgentStateAfterCommand(soc,&posPred,&velMe,&angBody,&ang,&sta );
02701     if( posPred.getDistanceTo( posBall ) < 0.8*SS->getMaximalKickDist() )
02702     {
02703       Log.log( 508, "dash 1x (stop), turn 1x, dist %f, angle %f, opp %f",
02704          dDist2, angTurn, dDistOpp );
02705       WM->logCircle( 508, posPred, SS->getMaximalKickDist(), true );
02706       socFinal = soc;
02707     }
02708   }
02709   // if there are no opponents, we are wrongly directed, and we will be closely
02710   // to the ball, see whether we can first update our heading
02711   else if( fabs( angTurn ) > angThreshold && !bOppClose &&
02712       dDist1 < 0.7*SS->getMaximalKickDist() )
02713   {
02714     soc = turnBodyToPoint( posPred1 + VecPosition(1,dDesBody, POLAR), 1 );
02715     Log.log( 508, "turn 1x, dist %f, angle %f, opp %f",
02716       dDist1, angTurn, dDistOpp );
02717     WM->logCircle( 508, posPred1, SS->getMaximalKickDist(), true );
02718     socFinal = soc;
02719   }
02720   else if( // fabs( angTurn ) > angThreshold &&
02721       !bOppClose &&
02722       dDist2 < 0.7*SS->getMaximalKickDist() )
02723   {
02724     soc = turnBodyToPoint( posPred2 + VecPosition(1,dDesBody, POLAR), 2 );
02725     Log.log( 508, "turn 2x, dist %f, angle %f, opp %f",
02726        dDist2, angTurn, dDistOpp );
02727     WM->logCircle( 508, posPred2, SS->getMaximalKickDist(), true );
02728     socFinal = soc;
02729   }
02730 
02731 
02732   else if( socCollide.commandType != CMD_ILLEGAL && 
02733            fabs( angTurn ) > angThreshold )
02734   {
02735     Log.log( 508, "collide with ball on purpose" );
02736     WM->logCircle( 508, posDash1, SS->getMaximalKickDist(), true );
02737     socFinal = socCollide;
02738   }
02739   else if( socDash1.commandType != CMD_ILLEGAL )
02740   {
02741     Log.log( 508, "do dash 1x" );
02742     WM->logCircle( 508, posDash1, SS->getMaximalKickDist(), true );
02743     socFinal = socDash1;
02744   }
02745 */

Generated on Fri Nov 7 11:45:39 2003 for UvA Trilearn 2003 Base Code by doxygen1.2.12 written by Dimitri van Heesch, © 1997-2001