tactics.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           tactics.cpp  -  description
00003                              -------------------
00004     begin                : Fri Mar 30 2001
00005     copyright            : (C) 2001 by Martin Bickel
00006     email                : bickel@asc-hq.org
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "ai_common.h"
00019 #include "../actions/attackcommand.h"
00020 #include "../actions/moveunitcommand.h"
00021 
00022 const int attack_unitdestroyed_bonus = 90;
00023 
00024 
00025 void AI :: searchTargets ( Vehicle* veh, const MapCoordinate3D& pos, TargetVector& tl, int moveDist, AStar3D& vm, int hemmingBonus )
00026 {
00027 
00028    npush ( veh->xpos );
00029    npush ( veh->ypos );
00030    npush ( veh->height );
00031 
00032    bool unitRadarActivated = getMap()->getField(veh->getPosition())->unitHere(veh);
00033    if ( unitRadarActivated )
00034       veh->removeview();
00035    // int fieldsWithChangedVisibility = evaluateviewcalculation ( getMap(), veh->xpos, veh->ypos, veh->typ->view, 0xff );
00036    veh->xpos = pos.x;
00037    veh->ypos = pos.y;
00038    veh->height = pos.getBitmappedHeight();
00039    veh->addview();
00040    int fieldsWithChangedVisibility = evaluateviewcalculation ( getMap(), veh->getPosition(), veh->view, 0xff );
00041 
00042 
00043    if ( AttackCommand::avail ( veh )) {
00044       AttackCommand va ( veh );
00045       va.searchTargets();
00046       for ( AttackCommand::FieldList::const_iterator i = va.getAttackableUnits().begin(); i != va.getAttackableUnits().end(); ++i ) {
00047          MapCoordinate targ = i->first;
00048          const AttackWeap* aw = &(i->second);
00049 
00050          int bestweap = -1;
00051          int targdamage = -1;
00052          int weapstrength = -1;
00053          for ( int w = 0; w < aw->count; w++ ) {
00054             tunitattacksunit uau ( veh, getMap()->getField (targ)->vehicle, 1, aw->num[w] );
00055             uau.calc();
00056             if ( uau.dv.damage > targdamage ) {
00057                bestweap = aw->num[w];
00058                targdamage = uau.dv.damage;
00059                weapstrength = aw->strength[w];
00060             } else
00061                if ( uau.dv.damage == 100 )
00062                   if ( weapstrength == -1 || weapstrength > aw->strength[w] ) {
00063                      bestweap = aw->num[w];
00064                      targdamage = uau.dv.damage;
00065                      weapstrength = aw->strength[w];
00066                   }
00067          }
00068 
00069          if ( bestweap == -1 )
00070             displaymessage ( "inconsistency in AI :: searchTarget", 1 );
00071 
00072 
00073          MoveVariant* mv = new MoveVariant;
00074 
00075          tunitattacksunit uau ( veh, getMap()->getField(targ)->vehicle, 1, bestweap );
00076          mv->orgDamage = uau.av.damage;
00077          mv->damageAfterMove = uau.av.damage;
00078          mv->enemyOrgDamage = uau.dv.damage;
00079          uau.calc();
00080 
00081 
00082          mv->damageAfterAttack = uau.av.damage;
00083          mv->enemyDamage = uau.dv.damage;
00084          mv->enemy = getMap()->getField(targ)->vehicle;
00085          mv->movePos = pos;
00086          mv->attackx = targ.x;
00087          mv->attacky = targ.y;
00088          mv->weapNum = bestweap;
00089          mv->moveDist = moveDist;
00090          mv->attacker = veh;
00091 
00092          mv->positionThreat = getFieldThreat ( pos.x, pos.y ).threat[veh->getValueType()];
00093 
00094 
00095          for ( int nf = 0; nf < sidenum; nf++ ) {
00096             MapCoordinate mc = getNeighbouringFieldCoordinate ( MapCoordinate ( mv->attackx, mv->attacky), nf );
00097             MapField* fld = getMap()->getField(mc);
00098             if ( fld && !veh->typ->wait)
00099                mv->neighbouringFieldsReachable[nf] = (vm.fieldVisited( MapCoordinate3D(mc.x, mc.y, pos.getBitmappedHeight()) ) || ( veh->xpos == mc.x && veh->ypos == mc.y )) && !fld->building && (!fld->vehicle || fld->unitHere(veh));
00100             else
00101                mv->neighbouringFieldsReachable[nf] = false;
00102 
00103          }
00104 
00105          int attackerDirection = getdirection ( targ.x, targ.y, pos.x, pos.y );
00106          float hemmingFactor = 1;
00107          for ( int nf = 0; nf < sidenum-1 && nf < hemmingBonus; nf++ ) {
00108             // we are starting opposite the attacker, since this is the highest hemming bonus
00109             int checkDir;
00110             if ( nf == 0 )
00111                checkDir = attackerDirection+sidenum/2;
00112             else {
00113                if ( nf & 1 )
00114                   checkDir = attackerDirection+sidenum/2 + (nf+1)/2;
00115                else
00116                   checkDir = attackerDirection+sidenum/2 - (nf+1)/2;
00117             }
00118 
00119             MapCoordinate mc = getNeighbouringFieldCoordinate ( MapCoordinate ( mv->attackx, mv->attacky), checkDir%sidenum );
00120             MapField* fld = getMap()->getField(mc);
00121             if ( fld && !fld->building && !fld->vehicle )
00122                hemmingFactor += AttackFormula::getHemmingFactor ( nf );
00123          }
00124 
00125          mv->result = getAttackValue ( uau, mv->attacker, mv->enemy, hemmingFactor );
00126 
00127          if ( mv->result > 0 )
00128             tl.push_back ( mv );
00129 
00130       }
00131    }
00132 
00133    // if ( fieldsWithChangedVisibility )
00134    //   evaluateviewcalculation ( getMap(), veh->xpos, veh->ypos, veh->typ->view, 0xff );
00135    veh->removeview();
00136    npop ( veh->height );
00137    npop ( veh->ypos );
00138    npop ( veh->xpos );
00139 
00140    if ( unitRadarActivated )
00141       veh->addview();
00142 
00143    if ( fieldsWithChangedVisibility || 1 )  // viewbug.sav !!!!!
00144       evaluateviewcalculation ( getMap(), veh->getPosition(), veh->view, 0xff );
00145 }
00146 
00147 bool AI::MoveVariant::operator> ( const AI::MoveVariant& mv2 ) const
00148 {
00149    return (     result > mv2.result
00150             || (result == mv2.result && positionThreat < mv2.positionThreat  )
00151             || (result == mv2.result && positionThreat == mv2.positionThreat && moveDist < mv2.moveDist) );  // mv1.moveDist < mv2.moveDist
00152 }
00153 
00154 
00155 bool AI::MoveVariant::operator< ( const AI::MoveVariant& mv2 ) const
00156 {
00157    return (     result < mv2.result
00158             || (result == mv2.result && positionThreat > mv2.positionThreat )
00159             || (result == mv2.result && positionThreat == mv2.positionThreat && moveDist > mv2.moveDist) );  // mv1.moveDist < mv2.moveDist
00160 }
00161 
00162 /*
00163 bool operator> ( const AI::MoveVariant& mv1, const AI::MoveVariant& mv2 ) 
00164 {
00165    return (     mv1.result > mv2.result
00166             || (mv1.result == mv2.result && mv1.positionThreat < mv2.positionThreat  )
00167             || (mv1.result == mv2.result && mv1.positionThreat == mv2.positionThreat && mv1.moveDist < mv2.moveDist) );  // mv1.moveDist < mv2.moveDist
00168 }
00169 
00170 bool operator< ( const AI::MoveVariant& mv1, const AI::MoveVariant& mv2 )
00171 {
00172    return (     mv1.result < mv2.result
00173             || (mv1.result == mv2.result && mv1.positionThreat > mv2.positionThreat )
00174             || (mv1.result == mv2.result && mv1.positionThreat == mv2.positionThreat && mv1.moveDist > mv2.moveDist) );  // mv1.moveDist < mv2.moveDist
00175 }
00176 
00177 */
00178 bool AI::moveVariantComp ( const AI::MoveVariant* mv1, const AI::MoveVariant* mv2 )
00179 {
00180    return *mv1 < *mv2;
00181    // return ( mv1->result < mv2->result || (mv1->result == mv2->result && mv1->moveDist > mv2->moveDist ));
00182 }
00183 
00184 void AI::getAttacks ( AStar3D& vm, Vehicle* veh, TargetVector& tv, int hemmingBonus, bool justOne, bool executeService )
00185 {
00186 
00188 
00189    int x1 = veh->xpos;
00190    int y1 = veh->ypos;
00191    int x2 = veh->xpos;
00192    int y2 = veh->ypos;
00193 
00194    for ( AStar3D::Container::iterator ff = vm.visited.begin(); ff != vm.visited.end(); ++ff ) {
00195       x1 = min ( x1, ff->h.x );
00196       y1 = min ( y1, ff->h.y );
00197       x2 = max ( x2, ff->h.x );
00198       y2 = max ( y2, ff->h.y );
00199    }
00200 
00201    int maxrange = 0;
00202    for ( int i = 0; i < veh->typ->weapons.count; ++i )
00203       maxrange = max ( maxrange, veh->typ->weapons.weapon[i].maxdistance );
00204 
00205    maxrange += 30;
00206 
00207    x1 = max ( 0, x1 - maxrange/10 );
00208    y1 = max ( 0, y1 - maxrange*2/10 );
00209    x2 = min ( getMap()->xsize, x2 + maxrange/10 );
00210    y2 = min ( getMap()->ysize, y2 + maxrange*2/10 );
00211 
00212    int enemycount = 0;
00213    for ( int y = y1; y <= y2 && !enemycount; ++y)
00214       for ( int x = x1; x <= x2; ++x) {
00215          MapField* fld = getMap()->getField(x,y);
00216          if ( fld && fld->vehicle)
00217             if ( getPlayer(fld->vehicle->getOwner()).diplomacy.isHostile( getPlayerNum() )  )
00218                enemycount++;
00219       }
00220 
00221    if( !enemycount )
00222       return;
00223 
00224 
00225 
00226    int orgxpos = veh->xpos ;
00227    int orgypos = veh->ypos ;
00228 
00229    if ( getMap()->getField ( veh->xpos, veh->ypos )->unitHere ( veh ) )  // unit not inside a building or transport
00230       searchTargets ( veh, veh->getPosition3D(), tv, 0, vm, hemmingBonus );
00231 
00232    if ( tv.size() && justOne )
00233       return;
00234 
00235    // Now we cycle through all fields that are reachable...
00236    if ( !veh->typ->wait ) {
00237 
00238       RefuelConstraint* apl = NULL;
00239       if ( RefuelConstraint::necessary ( veh, *this ))
00240         apl = new RefuelConstraint( *this, veh );
00241 
00242       int fuelLacking = 0;
00243       for ( AStar3D::Container::iterator ff = vm.visited.begin(); ff != vm.visited.end(); ++ff )
00244          if ( !ff->hasAttacked ) {
00245             MapField* fld = getMap()->getField (ff->h);
00246             if ( !fld->vehicle && !fld->building ) {
00247                 if ( !apl || apl->returnFromPositionPossible ( ff->h )) {
00248                    searchTargets ( veh, ff->h, tv, beeline ( ff->h.x, ff->h.y, orgxpos, orgypos ), vm, hemmingBonus );
00249                    if ( tv.size() && justOne )
00250                       return;
00251                 } else
00252                    fuelLacking++;
00253              }
00254          }
00255 
00256       if ( apl ) {
00257          delete apl;
00258          apl = NULL;
00259       }
00260 
00261       if ( !tv.size() && fuelLacking && executeService)
00262          issueRefuelOrder( veh, true );
00263    }
00264 }
00265 
00266 AI::AiResult AI::executeMoveAttack ( Vehicle* veh, TargetVector& tv )
00267 {
00268    int unitNetworkID = veh->networkid;
00269    AiResult result;
00270 
00271    MoveVariant* mv = *max_element( tv.begin(), tv.end(), moveVariantComp );
00272 
00273    if ( mv->movePos != veh->getPosition3D() ) {
00274       VisibilityStates org_vision =  _vision ;
00275       _vision = visible_now;
00276       
00277       auto_ptr<MoveUnitCommand> muc ( new MoveUnitCommand( veh ));
00278       muc->setFlags ( MoveUnitCommand::NoInterrupt );
00279       muc->setDestination( mv->movePos );
00280       ActionResult res = muc->execute( getContext () );
00281       if ( res.successful() )
00282          muc.release();
00283       else {
00284          displaymessage ( "AI :: executeMoveAttack \n error in movement with unit %d", 1, veh->networkid );
00285       }
00286 
00287       result.unitsMoved ++;
00288       _vision = org_vision;
00289    }
00290 
00291    // the unit may have been shot down due to reactionfire during movement
00292    if ( !getMap()->getUnit(unitNetworkID)) {
00293       result.unitsDestroyed++;
00294       return result;
00295    }
00296 
00297    if ( veh->attacked )
00298       return result;
00299 
00300    auto_ptr<AttackCommand> va (new AttackCommand( veh ));
00301    ActionResult res = va->searchTargets();
00302    if ( !res.successful() )
00303       displaymessage ( "AI :: executeMoveAttack \n error in attack step 2 with unit %d", 1, veh->networkid );
00304 
00305    VehicleTypeEfficiencyCalculator vtec( *this, veh, mv->enemy );
00306 
00307    va->setTarget( MapCoordinate( mv->attackx, mv->attacky), mv->weapNum );
00308    res = va->execute( getContext() );
00309    if ( !res.successful() )
00310       displaymessage ( "AI :: executeMoveAttack \n error in attack step 3 with unit %d", 1, veh->networkid );
00311    else
00312       va.release();
00313 
00314    vtec.calc();
00315 
00316    result.unitsMoved ++;
00317 
00318    // the unit may have been shot in the attack
00319    if ( !getMap()->getUnit(unitNetworkID)) {
00320       result.unitsDestroyed++;
00321       return result;
00322    }
00323 
00324 
00325    if ( MoveUnitCommand::avail( veh ))
00326       result += moveToSavePlace ( veh );
00327 
00328    return result;
00329 }
00330 
00331 bool AI::targetsNear( Vehicle* veh )
00332 {
00333    AStar3D ast ( getMap(), veh, false, veh->getMovement() );
00334    ast.findAllAccessibleFields ();
00335    TargetVector tv;
00336    getAttacks ( ast, veh, tv, 0, true, false );
00337    if ( tv.size() )
00338       return true;
00339    else
00340       return false;
00341 }
00342 
00343 
00344 int AI::getDirForBestTacticsMove ( const Vehicle* veh, TargetVector& tv )
00345 {
00346    if ( tv.size() <= 0 )
00347       return -1;
00348 
00349    MoveVariant* mv = *max_element( tv.begin(), tv.end(), moveVariantComp );
00350    return getdirection ( veh->xpos, veh->ypos, mv->movePos.x, mv->movePos.y );
00351 }
00352 
00353 MapCoordinate AI::getDestination ( Vehicle* veh )
00354 {
00355    AiParameter::Task task = veh->aiparam[ getPlayerNum() ]->getTask();
00356    if ( task == AiParameter::tsk_nothing || task == AiParameter::tsk_tactics ) {
00357       TargetVector tv;
00358       AStar3D ast ( getMap(), veh, false, veh->getMovement() );
00359       ast.findAllAccessibleFields ();
00360       getAttacks ( ast, veh, tv, 0 );
00361 
00362       if ( tv.size() > 0 ) {
00363 
00364          MoveVariant* mv = *max_element( tv.begin(), tv.end(), moveVariantComp );
00365          return MapCoordinate ( mv->movePos.x, mv->movePos.y );
00366       } else
00367          task = AiParameter::tsk_strategy;
00368    }
00369 
00370    if ( task == AiParameter::tsk_strategy ) {
00371       Section* sec = sections.getBest ( 0, veh );
00372       if ( sec )
00373          return MapCoordinate ( sec->centerx, sec->centery );
00374    }
00375 
00376    return MapCoordinate ( veh->xpos, veh->ypos );
00377 }
00378 
00379 
00380 AI::AiResult AI::moveToSavePlace ( Vehicle* veh, int preferredHeight )
00381 {
00382    int unitNetworkID = veh->networkid;
00383 
00384    AiResult result;
00385 
00386    auto_ptr<MoveUnitCommand> muc( new MoveUnitCommand( veh ));
00387 
00388    ActionResult res = muc->searchFields( preferredHeight );
00389    if ( !res.successful() )
00390       return result;
00391    
00392    const set<MapCoordinate3D>& fields = muc->getReachableFields();
00393             
00394    if ( fields.empty() )
00395       return result;
00396 
00397 
00398    int xtogo = veh->xpos;
00399    int ytogo = veh->ypos;
00400    int threat = maxint;
00401    int dist = maxint;
00402 
00403    if ( getMap()->getField ( veh->xpos, veh->ypos)->unitHere ( veh ) ) {  // vehicle not in building / transport
00404       threat = int( getFieldThreat ( veh->xpos, veh->ypos).threat[ veh->aiparam[ getPlayerNum()]->valueType] * 1.5 + 1);
00405        // multiplying with 1.5 to make this field a bit unattractive, to allow other units (for example buggies) to attack from this field to, since it is probably quite a good position (else it would not have been chosen)
00406    }
00407 
00408    for ( set<MapCoordinate3D>::const_iterator i = fields.begin(); i != fields.end(); ++i ) {
00409       MapField* fld = getMap()->getField( *i );
00410       if ( !fld->vehicle && !fld->building ) {
00411          AiThreat& ait = getFieldThreat ( i->x, i->y );
00412          int _dist = beeline ( i->x, i->y, veh->xpos, veh->ypos);
00413 
00414             // make fields far away a bit unattractive; we don't want to move the whole distance back again next turn
00415          int t = int( ait.threat[ veh->aiparam[ getPlayerNum()]->valueType ] * log ( double(_dist) )/log(double(10)) );
00416 
00417          if ( t < threat || ( t == threat && (_dist < dist ))) {
00418             threat = t;
00419             xtogo = i->x;
00420             ytogo = i->y;
00421             dist = _dist;
00422          }
00423       }
00424    }
00425 
00426    if ( veh->xpos != xtogo || veh->ypos != ytogo ) {
00427       
00428       muc->setDestination( MapCoordinate(xtogo, ytogo ));
00429       
00430       res = muc->execute( getContext() );
00431       if ( res.successful() )
00432          muc.release();
00433       else
00434          displaymessage ( "AI :: moveToSavePlace \n error in movement3 step 3 with unit %d", 1, veh->networkid );
00435 
00436       result.unitsMoved++;
00437    }
00438 
00439    if ( !getMap()->getUnit( unitNetworkID ))
00440       result.unitsDestroyed++;
00441 
00442    return result;
00443 }
00444 
00445 
00446 int AI::changeVehicleHeight ( Vehicle* veh, int preferredDirection )
00447 {
00448 #if 0  // ####
00449    int bh = getBestHeight ( veh );
00450    if ( bh != veh->height && bh != -1 ) {
00451       ChangeVehicleHeight* cvh;
00452       int newheight;
00453       if ( bh > veh->height ) {
00454          cvh = new IncreaseVehicleHeight ( mapDisplay, NULL );
00455          newheight = veh->height << 1;
00456       } else {
00457          cvh = new DecreaseVehicleHeight ( mapDisplay, NULL );
00458          newheight = veh->height >> 1;
00459       }
00460       auto_ptr<ChangeVehicleHeight> acvh ( cvh );
00461 
00462       if ( newheight & veh->typ->height ) {
00463          if ( cvh->available ( veh ) ) {
00464 
00465             int stat = cvh->execute ( veh, -1, -1, 0, newheight, 1 );
00466             if ( stat == 2 ) {   // if the unit could change its height vertically, or the height change is not available, skip this block
00467 
00468                int bestx = -1;
00469                int besty = -1;
00470                int moveremain = minint;
00471 
00472                if ( preferredDirection == -1 ) {
00473                  /*
00474                   // making a backup
00475                   TemporaryContainerStorage tus ( veh );
00476                   veh->height = newheight;
00477                   veh->resetMovement ( );
00478                   MapCoordinate mc = getDestination ( veh );
00479                   preferredDirection = getdirection ( veh->xpos, veh->ypos, mc.x, mc.y );
00480                   tus.restore();
00481                   */
00482                   preferredDirection = getdirection ( veh->xpos, veh->ypos, getMap()->xsize/2, getMap()->ysize/2 );
00483                }
00484 
00485                for ( int i = 0; i < cvh->reachableFields.getFieldNum(); i++ ) {
00486                   int newMoveRemain = cvh->reachableFields.getData ( i ).dist;
00487 
00488                   // check if we move in the direction that should be moved to
00489                   if ( preferredDirection != -1 ) {
00490                      int xp, yp;
00491                      cvh->reachableFields.getFieldCoordinates ( i, &xp, &yp );
00492 
00493                      int dir = preferredDirection - getdirection ( veh->xpos, veh->ypos, xp, yp );
00494                      if ( dir > sidenum/2 )
00495                         dir -= sidenum;
00496                      if ( dir < -sidenum/2 )
00497                         dir += sidenum;
00498 
00499                      dir = abs ( dir );
00500                      // ok, now we have the directional difference in dir, with a range of 0 .. 3 ; 0 meaning moving in the preferred direction
00501 
00502 
00503                      newMoveRemain -= (dir*2-3)* veh->typ->steigung*maxmalq / 3;
00504                      // if dir==0, we subtract -steigung   => newMoveRemain is getting larger because we move in the right direction
00505                      // if dir==3, we subtract +steigung
00506                   }
00507 
00508                   if ( newMoveRemain > moveremain ) {
00509                      cvh->reachableFields.getFieldCoordinates ( i, &bestx, &besty );
00510                      moveremain = newMoveRemain;
00511                   }
00512                }
00513 
00514                if ( bestx != -1 && besty != -1 ) {
00515                   cvh->execute ( NULL, bestx, besty, 2, -1, -1 );
00516                   if ( cvh->getStatus() == 1000 )
00517                      return 1;
00518 
00519                   if ( cvh->getStatus() != 3 )
00520                      displaymessage ( "AI :: changeVehicleHeight \n error in changeHeight step 2 with unit %d", 1, veh->networkid );
00521 
00522                   cvh->execute ( NULL, bestx, besty, 3, -1, -1 );
00523                   if ( cvh->getStatus() != 1000 )
00524                      displaymessage ( "AI :: changeVehicleHeight \n error in changeHeight step 3 with unit %d", 1, veh->networkid );
00525 
00526                   return 1;
00527                } else
00528                   return -1;
00529             } else {
00530                if ( stat == 1000 )
00531                  return 1;
00532 
00533                if ( veh->typ->steigung == 0 )
00534                  return -2;
00535 
00536                return -1;
00537             }
00538          } else {  // cvh->available
00539             return -2;
00540          }
00541       }
00542 
00543    }
00544    #endif
00545    return 0;
00546 }
00547 
00548 
00549 AI::AiResult AI::tactics( void )
00550 {
00551    const int tsk_num = 3;
00552    AiParameter::Task tasks[tsk_num] = { AiParameter::tsk_nothing,
00553                                         AiParameter::tsk_tactics,
00554                                         AiParameter::tsk_tactwait
00555                                       };
00556    AiResult result;
00557 
00558    displaymessage2("starting tactics ... ");
00559 
00560    typedef list<int> TactVehicles;
00561    TactVehicles tactVehicles;
00562 
00563    for ( Player::VehicleList::iterator vi = getPlayer().vehicleList.begin(); vi != getPlayer().vehicleList.end(); vi++ ) {
00564       Vehicle* veh = *vi;
00565 
00566       if ( !veh->aiparam[ getPlayerNum() ] )
00567           calculateThreat( veh );
00568       
00569       bool unitUsable = false;
00570       if ( veh->aiparam[ getPlayerNum() ]->getJob() == AiParameter::job_fight
00571         || veh->aiparam[ getPlayerNum() ]->getJob() == AiParameter::job_undefined
00572         || (veh->aiparam[ getPlayerNum() ]->getJob() == AiParameter::job_guard && veh->aiparam[ getPlayerNum() ]->dest_nwid == -1 && veh->aiparam[ getPlayerNum() ]->dest.x == -1 ))
00573          for ( int j = 0; j < tsk_num; j++ )
00574             if ( veh->aiparam[ getPlayerNum() ]->getTask() == tasks[j] )
00575                unitUsable = true;
00576 
00577       if ( getMap()->getField(veh->getPosition())->vehicle != veh ) {
00578          Vehicle* transport = getMap()->getField(veh->getPosition())->vehicle;
00579          if ( transport ) {
00580             const ContainerBaseType::TransportationIO* unloadSystem = transport->vehicleUnloadSystem( veh->typ, -1 );
00581             if ( unloadSystem && unloadSystem->disableAttack )
00582                continue;
00583          }
00584       }
00585 
00586 
00587       int maxWeapDist = minint;
00588       for ( int w = 0; w < veh->typ->weapons.count; w++ )
00589          if ( veh->typ->weapons.weapon[w].shootable() )
00590             maxWeapDist = max ( veh->typ->weapons.weapon[w].maxdistance , maxWeapDist );
00591 
00592       int maxMove = minint;
00593       for ( int h = 0; h < 8; h++ )
00594          if ( veh->typ->height & ( 1 << h ))
00595             maxMove = max ( veh->typ->movement[h], maxMove );
00596 
00597       if ( maxWeapDist > 0 ) {
00598          bool enemiesNear = false;
00599          int ydist = (maxMove + maxWeapDist) / maxmalq * 2;
00600          int xdist = ydist / 4;
00601          for ( int x = veh->xpos - xdist; x <= veh->xpos + xdist; x++ )
00602             for ( int y = veh->ypos - ydist; y <= veh->ypos + ydist; y++ ) {
00603                MapField* fld = getMap()->getField(x,y );
00604                if ( fld ) {
00605                   if ( fld->vehicle && getPlayer(veh->getOwner()).diplomacy.isHostile( fld->vehicle->getOwner() )  )
00606                      enemiesNear = true;
00607                   if ( fld->building && getPlayer(veh->getOwner()).diplomacy.isHostile( fld->building->getOwner() )  )
00608                      enemiesNear = true;
00609                }
00610             }
00611 
00612          if ( unitUsable && enemiesNear)
00613             tactVehicles.push_back ( veh->networkid );
00614       }
00615    }
00616 
00617    int hemmingBonus = 5;
00618 
00619    size_t lastTactVehiclesSize; // = tactVehicles.size();
00620 
00621    while ( !tactVehicles.empty() ) {
00622       lastTactVehiclesSize = tactVehicles.size();
00623 
00624       typedef map<int, MoveVariantContainer> Targets;
00625       Targets targets;
00626 
00627       int directAttackNum;
00628       do {
00629          directAttackNum = 0;
00630          for ( TactVehicles::iterator i = tactVehicles.begin(); i != tactVehicles.end(); ) {
00631             Vehicle* veh = getMap()->getUnit( *i );
00632             if ( veh ) {
00633 
00634                unitCounter++;
00635                displaymessage2("tact: processing operation %d", unitCounter );
00636                checkKeys();
00637    
00638                int stat = changeVehicleHeight ( veh, -1 );
00639                
00640                if ( !getMap()->getUnit(*i) )
00641                   continue;
00642                
00643                if ( stat == -1 ) { // couldn't change height due to blocked way or something similar
00644                   veh->aiparam[ getPlayerNum() ]->setTask( AiParameter::tsk_wait );
00645                   result.unitsWaiting++;
00646                   i++;
00647                } else {
00648    
00649                   AStar3D ast ( getMap(), veh, false, veh->getMovement() );
00650                   ast.findAllAccessibleFields ();
00651                   TargetVector tv;
00652                   getAttacks ( ast, veh, tv, hemmingBonus );
00653    
00654                   if ( !getMap()->getUnit(*i) )
00655                      continue;
00656                   
00657                   if ( tv.size() ) {
00658                      MoveVariant* mv = *max_element( tv.begin(), tv.end(), moveVariantComp );
00659    
00660                      // airplane landing constraints
00661    
00662                      bool directAttack = false;
00663                      if ( beeline ( mv->movePos.x, mv->movePos.y, mv->attackx, mv->attacky ) > maxmalq || veh->height >= chtieffliegend || (mv->enemy && mv->enemy->height >= chtieffliegend) )
00664                         directAttack = true;
00665    
00666                      int freeNeighbouringFields = 0;
00667                      for ( int j = 0; j < sidenum; j++ ) {
00668                         MapField* fld = getMap()->getField ( getNeighbouringFieldCoordinate ( MapCoordinate(mv->attackx, mv->attacky), j));
00669                         if ( fld )
00670                            if ( !fld->building && !fld->vehicle )
00671                               freeNeighbouringFields++;
00672                      }
00673    
00674                      if ( freeNeighbouringFields <= 1 )
00675                         directAttack = true;
00676    
00677                      if ( mv->enemyDamage >= 100 )
00678                         directAttack = true;
00679    
00680                      if ( directAttack ) {
00681                         /* to avoid recalculating the vision with every variant we are going to make it
00682                            easy...
00683                            Since all units now only attack the neighbouring fields, which is generally
00684                            visible, the AI does not cheat here.
00685                         */
00686                         VisibilityStates org_vision =  _vision ;
00687                         _vision = visible_all;
00688    
00689                         AiResult res = executeMoveAttack ( veh, tv );
00690                         i = tactVehicles.erase ( i );
00691    
00692                         if ( res.unitsMoved )
00693                            unitsWorkedInTactics.insert(veh);
00694                         
00695                         if ( !res.unitsDestroyed )
00696                            veh->aiparam[ getPlayerNum() ]->setTask( AiParameter::tsk_tactics );
00697    
00698                         result += res;
00699                         
00700                         directAttackNum++;
00701    
00702                         _vision = org_vision;
00703    
00704                      } else {
00705                         targets[mv->enemy->networkid].push_back( *mv );
00706                         i++;
00707                      }
00708                   } else {
00709                      // there is nothing the unit can do in tactics mode
00710                      if ( veh->aiparam[ getPlayerNum() ]->getTask() != AiParameter::tsk_serviceRetreat )
00711                         veh->aiparam[ getPlayerNum() ]->resetTask();
00712                      i = tactVehicles.erase ( i );
00713                   }
00714                }
00715             } else {
00716                // unit not available any more, get next one
00717                i = tactVehicles.erase ( i );
00718 
00719             }
00720          }
00721 
00722          // if there were direct attacks, some fields may now be blocked by units that did a directAttack,
00723          // so all other units plans must be recalculated
00724 
00725          if ( directAttackNum )
00726             targets.clear();
00727 
00728       } while ( directAttackNum );
00729 
00730       if ( !targets.empty() ) {
00731          // we are beginning with the targets that the most own units want to attack
00732          Targets::iterator currentTarget = targets.begin();
00733          for ( Targets::iterator i = targets.begin(); i != targets.end(); i++ )
00734             if ( i->second.size() > currentTarget->second.size())
00735                currentTarget = i;
00736 
00737          if ( currentTarget->second.size()-1 < hemmingBonus )
00738             hemmingBonus = currentTarget->second.size()-1;
00739 
00740          typedef list<MapCoordinate> AffectedFields;
00741          AffectedFields affectedFields;
00742 
00743          /* to avoid recalculating the vision with every variant we are going to make it
00744             easy...
00745             Since all units now only attack the neighbouring fields, which is generally
00746             visible, the AI does not cheat here.
00747          */
00748          VisibilityStates org_vision =  _vision ;
00749          _vision = visible_all;
00750 
00751          /* we don't need to discard all the calculations made above after a single attack.
00752             Only the attacks that are near enough to be affected by the last movement and attack
00753             will be ignored and recalculated in the next pass
00754          */
00755          do {   // while currentTarget != targets.end()
00756 
00757             Vehicle* enemy = getMap()->getUnit( currentTarget->first);
00758             // the enemy may have been shot down by a direct attack earlier
00759             if ( enemy ) {
00760                affectedFields.push_back ( MapCoordinate(enemy->xpos, enemy->ypos) );
00761 
00762                MoveVariantContainer attacker = currentTarget->second;
00763 
00764                // use the most effective units first
00765                sort( attacker.begin(), attacker.end() );
00766 
00767                MoveVariantContainer::iterator mvci = attacker.begin();
00768                Vehicle* positions[sidenum];
00769                Vehicle* finalPositions[sidenum];
00770                for ( int i = 0; i< sidenum; i++ ) {
00771                   positions[i] = NULL;
00772                   finalPositions[i] = NULL;
00773                }
00774                float finalValue = 0;
00775 
00776                tactics_findBestAttackUnits ( attacker, mvci, positions, 0, finalPositions, finalValue, 0, 0, ticker );
00777 
00778                if ( finalValue > 0 ) {
00779                   for ( int i = 0; i < sidenum; i++ )
00780                      if ( finalPositions[i] ) {
00781                         int nwid = finalPositions[i]->networkid;
00782                         _vision = org_vision;
00783                         MapCoordinate affected =  MapCoordinate(finalPositions[i]->xpos, finalPositions[i]->ypos);
00784                         MapCoordinate3D dst = getNeighbouringFieldCoordinate( MapCoordinate3D( enemy->xpos, enemy->ypos, finalPositions[i]->height ), i);
00785                         dst.setnum ( dst.x, dst.y, -2 );
00786                         
00787                         moveUnit ( finalPositions[i], dst );
00788                         _vision = visible_all;
00789 
00790                         affectedFields.push_back ( affected );
00791                         // the unit may have been shot down due to reaction fire
00792 
00793                         if ( !getMap()->getUnit ( nwid ) ) {
00794                            TactVehicles::iterator att = find ( tactVehicles.begin(), tactVehicles.end(), nwid ) ;
00795                            tactVehicles.erase ( att );
00796                            finalPositions[i] = NULL;
00797                         }
00798                      }
00799 
00800 
00801                   int unitcount = 0;
00802                   for ( int i = 0; i < sidenum; i++ )
00803                      if ( finalPositions[i] )
00804                         unitcount++;
00805 
00806                   if ( unitcount ) {
00807                      int attackOrder[sidenum];
00808                      int finalOrder[sidenum];
00809                      for ( int i = 0; i< sidenum; i++ )
00810                         attackOrder[i] = finalOrder[i] = -1;
00811 
00812 
00813                      int finalDamage = -1;
00814                      int finalAttackNum = maxint;
00815                      tactics_findBestAttackOrder ( finalPositions, attackOrder, enemy, 0, enemy->damage, finalDamage, finalOrder, finalAttackNum );
00816 
00817                      vector<Vehicle*> unitsToMoveAwayAfterAttacking;
00818 
00819                      MapField* enemyField = getMap()->getField(enemy->xpos, enemy->ypos);
00820                      for ( int i = 0; i < finalAttackNum && enemyField->vehicle == enemy && finalAttackNum < maxint; i++ ) {
00821                         checkKeys();
00822                         if ( finalOrder[i] < 0 )
00823                            warningMessage("!!!");
00824                         // if ( i+1 < finalAttackNum ) {
00825                         if ( i < finalAttackNum && finalPositions[finalOrder[i]] ) {
00826                            Vehicle* a = finalPositions[finalOrder[i]];
00827                            if ( finalOrder[i] < 0 )
00828                               warningMessage("!!!");
00829 
00830                            unitsWorkedInTactics.insert(a);
00831                            
00832                            
00833                            int nwid = a->networkid;
00834                            auto_ptr<AttackCommand> va ( new AttackCommand ( a ));
00835                            va->searchTargets();
00836                            
00837                            VehicleTypeEfficiencyCalculator vtec (*this, finalPositions[finalOrder[i]], enemy );
00838                            va->setTarget( enemy->getPosition() );
00839                            ActionResult res = va->execute( getContext() );                           
00840                            if ( !res.successful() && strictChecks )
00841                              displaymessage("inconsistency #1 in AI::tactics attack", 1 );
00842                            
00843                            if ( res.successful() )
00844                               va.release();
00845                              
00846 
00847                            vtec.calc();
00848 
00849                            TactVehicles::iterator att = find ( tactVehicles.begin(), tactVehicles.end(), nwid ) ;
00850                            tactVehicles.erase ( att );
00851                            
00852                            if ( getMap()->getUnit( nwid ) && a->typ->hasFunction( ContainerBaseType::MoveAfterAttack ))
00853                               unitsToMoveAwayAfterAttacking.push_back( a );
00854                         }
00855                      }
00856                      
00857                      for ( vector<Vehicle*>::iterator i = unitsToMoveAwayAfterAttacking.begin(); i != unitsToMoveAwayAfterAttacking.end(); ++i )
00858                         moveToSavePlace( *i );
00859                      
00860                   } // unitcount > 0
00861                   
00862                   
00863                } // else { // if finalValue > 0
00864 
00865             } // if enemy
00866 
00867             bool processable = true;
00868             do {
00869                currentTarget++;
00870                if ( currentTarget != targets.end() ) {
00871                   if ( hemmingBonus == currentTarget->second.size()-1 ) {
00872                      for ( AffectedFields::iterator i = affectedFields.begin(); i != affectedFields.end(); i++ )
00873                         for ( MoveVariantContainer::iterator j = currentTarget->second.begin(); j != currentTarget->second.end(); j++ ) {
00874                             Vehicle* veh = j->attacker;
00875 
00876                             // here are only vehicles that attack neighbouring fields; ranged attacks are handled as "directAttacks" earlier
00877                             if ( beeline ( *i, veh->getPosition()) < veh->maxMovement()+20 )
00878                                processable = false;
00879                         }
00880                   } else
00881                      processable = false;
00882                }
00883             } while ( !processable && currentTarget != targets.end() );
00884 
00885          } while ( currentTarget != targets.end() );
00886 
00887          _vision = org_vision;
00888 
00889       } else {
00890          // no attacks are possible
00891          tactVehicles.clear();
00892       }
00893       if ( lastTactVehiclesSize == tactVehicles.size() ) {
00894          // displaymessage ("AI :: tactics ; escaping infinite loop; please report this error !",1 );
00895          return result;
00896       }
00897 
00898    }
00899 
00900    displaymessage2("tactics completed ... ");
00901 
00902    return result;
00903 }
00904 
00905 void AI :: tactics_findBestAttackOrder ( Vehicle** units, int* attackOrder, Vehicle* enemy, int depth, int damage, int& finalDamage, int* finalOrder, int& finalAttackNum )
00906 {
00907    if ( damage > enemy->damage )
00908       if ( (depth < finalAttackNum && damage==finalDamage) || damage > finalDamage ) {
00909          finalDamage = damage;
00910          finalAttackNum = depth;
00911          for ( int i = 0; i < sidenum; i++ )
00912             finalOrder[i] = attackOrder[i];
00913       }
00914 
00915    if ( damage >= 100 || depth >= 6  )
00916       return;
00917 
00918    for ( int i = 0; i< sidenum; i++ ) {
00919        bool found = false;
00920        for ( int j = 0; j < depth; j++ )
00921           if ( attackOrder[j] == i)
00922               found = true;
00923        if ( units[i] && !found ) {
00924          // attackOrder[3] = 1   means that the unit on position 1 should attack as third
00925          attackOrder[depth] = i;
00926 
00927          npush ( units[i]->xpos );
00928          npush ( units[i]->ypos );
00929          MapCoordinate mc = getNeighbouringFieldCoordinate ( MapCoordinate ( enemy->xpos, enemy->ypos), i );
00930          units[i]->xpos = mc.x;
00931          units[i]->ypos = mc.y;
00932 
00933          tunitattacksunit battle ( units[i], enemy );
00934          int newdamage = battle.dv.damage;
00935          battle.calc();
00936          newdamage = battle.dv.damage - newdamage;
00937          npop ( units[i]->ypos );
00938          npop ( units[i]->xpos );
00939 
00940          tactics_findBestAttackOrder ( units, attackOrder, enemy, depth+1, damage+newdamage, finalDamage, finalOrder, finalAttackNum );
00941 
00942          attackOrder[depth] = -1;
00943       }
00944    }
00945 
00946 }
00947 
00948 
00949 int AI :: getValue( Vehicle* v ) 
00950 {
00951    if ( v->aiparam[getPlayerNum()] == NULL ) {
00952       calculateThreat(v);  
00953    }
00954    return v->aiparam[getPlayerNum()]->getValue();
00955 }
00956 
00957 float AI :: getAttackValue ( const tfight& battle, Vehicle* attackingUnit, Vehicle* attackedUnit, float factor )
00958 {
00959    calculateThreat( attackedUnit );
00960    float result =                     (battle.dv.damage - attackedUnit->damage) * getValue(attackedUnit) * factor 
00961           - 1/config.aggressiveness * (battle.av.damage - attackingUnit->damage) * getValue(attackingUnit) ;
00962    if ( battle.dv.damage >= 100 )
00963       result += attackedUnit->aiparam[getPlayerNum()]->getValue() * attack_unitdestroyed_bonus;
00964    return result;
00965 }
00966 
00967 
00968 
00969 class UnitAttacksUnit_FakeHemming : public tunitattacksunit {
00970            bool neighbours[sidenum];
00971            bool checkHemming ( Vehicle* d_eht, int direc )
00972            {
00973               return neighbours[direc];
00974            };
00975         public:
00976           UnitAttacksUnit_FakeHemming ( AI* ai, Vehicle* attacker, Vehicle* defender, Vehicle** _neighbours ) : tunitattacksunit ( attacker , defender )
00977           {
00978              for ( int i = 0; i < sidenum; i++ ) {
00979                 MapField* fld = ai->getMap()->getField ( getNeighbouringFieldCoordinate ( attacker->getPosition(), i ));
00980 
00981                 Vehicle* v = NULL;
00982                 if ( fld )
00983                    v = fld->vehicle;
00984 
00985 
00986                 if ( v && attackpossible2n ( v, defender ) )
00987                    neighbours[i] = true;
00988                 else
00989                    neighbours[i] = false;
00990              }
00991 
00992              for ( int i = 0; i < sidenum; i++ ) {
00993                 if ( _neighbours[i] == attacker )
00994                    break;
00995 
00996                 if ( _neighbours[i] )
00997                    neighbours[i] = true;
00998              }
00999              setup ( attacker, defender, true, -1 );
01000           };
01001         };
01002 
01003 
01004 
01005 void AI :: tactics_findBestAttackUnits ( const MoveVariantContainer& mvc, MoveVariantContainer::iterator& m, Vehicle** positions, float value, Vehicle** finalPosition, float& finalValue, int unitsPositioned, int recursionDepth, int startTime )
01006 {
01007    if ( m == mvc.end() || unitsPositioned >= 6 || recursionDepth >= 8 || (startTime + config.maxTactTime < ticker && !benchMark)) {
01008       float value = 0;
01009       Vehicle* target = mvc.begin()->enemy;
01010       npush ( target->damage );
01011       for ( int i = 0; i < sidenum && target->damage!=100; i++ )
01012          if ( positions[i] ) {
01013             npush ( positions[i]->ypos );
01014             npush ( positions[i]->xpos );
01015 
01016             MapCoordinate mc = getNeighbouringFieldCoordinate ( target->getPosition(), i );
01017             positions[i]->xpos = mc.x;
01018             positions[i]->ypos = mc.y;
01019 
01020             UnitAttacksUnit_FakeHemming uau ( this, positions[i], target, positions );
01021             uau.calc();
01022             value += getAttackValue ( uau, positions[i], target );
01023 
01024             npop ( positions[i]->xpos );
01025             npop ( positions[i]->ypos );
01026          }
01027       npop ( target->damage );
01028       if ( value > finalValue ) {
01029          for ( int i = 0; i < sidenum; i++ )
01030             finalPosition[i] = positions[i] ;
01031 
01032          finalValue = value;
01033       }
01034    } else {
01035       for ( int i = 0; i< sidenum; i++ ) {
01036          if ( m->neighbouringFieldsReachable[i] && !positions[i] ) {
01037             positions[i] = m->attacker;
01038             if ( m->result > 0 )
01039                value += m->result;
01040             else
01041                value += 1;
01042             m++;
01043             tactics_findBestAttackUnits ( mvc, m, positions, value, finalPosition, finalValue, unitsPositioned+1, recursionDepth+1, startTime );
01044             m--;
01045             value -= m->result;
01046             positions[i] = NULL;
01047          } else {
01048             m++;
01049             tactics_findBestAttackUnits ( mvc, m, positions, value, finalPosition, finalValue, unitsPositioned, recursionDepth+1, startTime );
01050             m--;
01051          }
01052       }
01053    }
01054 }
01055 
01056 
01057 
01058 bool AI :: vehicleValueComp ( const Vehicle* v1, const Vehicle* v2 )
01059 {
01060    return v1->aiparam[ v1->color/8 ]->getValue() < v2->aiparam[ v1->color/8 ]->getValue();
01061 }
01062 
01063 bool AI :: buildingValueComp ( const Building* v1, const Building* v2 )
01064 {
01065    return v1->aiparam[ v1->color/8 ]->getValue() < v2->aiparam[ v1->color/8 ]->getValue();
01066 }

Generated on Mon May 21 01:26:38 2012 for Advanced Strategic Command by  doxygen 1.5.1