Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

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

Generated on Tue Jun 24 01:27:53 2008 for Advanced Strategic Command by  doxygen 1.4.2