#include #include "ai.h" #include "mathroutines.h" #include "options.h" int Ai::calcFrameIncrement[Options::EnumAiDifficulty::COUNT] = {15,8,6,2}; int Ai::calcPositionNumber[Options::EnumAiDifficulty::COUNT] = {10,15,20,60}; int Ai::calcShotDirections[Options::EnumAiDifficulty::COUNT] = {4,7,10,12}; int Ai::calcCollisions[Options::EnumAiDifficulty::COUNT] = {30,15,10,10}; int Ai::calcNextShot[Options::EnumAiDifficulty::COUNT] = {300,200,90,60}; Ai::Ai(int pn,ShipSprite* s[2],TQPtrList* b[2], TQPtrList* m[2],SConfig *c) { int i; playerNumber=pn; opponentNumber=(pn+1)%2; cfg=c; for(i=0;i<2;i++) { ship[i]=s[i]; bullets[i]=b[i]; mines[i]=m[i]; shipsNextPositions[i]=new TQMemArray ((int)(calcPositionNumber[Options::aiDifficulty(playerNumber)]/cfg->gamespeed)); aiMines[i]=new TQMemArray(cfg->maxMines); mineNumber[i]=0; } myShots.setAutoDelete(true); objectsHitByShip.setAutoDelete(true); minesHitByShot.setAutoDelete(true); } void Ai::newRound() { accelerateFramesNumber=0; rotateFramesNumber=0; shoot=false; score=1e10; rotation=RNONE; acc=false; bullet=false; mine=false; borderTime=-1; sunTime=-1; calculateCollisions=(int)(calcCollisions[Options::aiDifficulty(playerNumber)] /cfg->gamespeed); waitShot=(int) rint( random.getDouble() * calcNextShot[Options::aiDifficulty(playerNumber)] /cfg->gamespeed); myShots.clear(); objectsHitByShip.clear(); minesHitByShot.clear(); } void Ai::think() { setSpriteFieldSize(); myShots.clear(); borderTime=-1; sunTime=-1; score--; if(waitShot>0) waitShot--; calculateNextPositions(); if(Options::aiDifficulty(playerNumber)!=Options::EnumAiDifficulty::Trainee) testForHits(); if(waitShot<=0) { tryShots(); shotScores(); } chooseAction(); if(rotateFramesNumber<=0) { rotation=RNONE; if(accelerateFramesNumber<=0) { acc=false; if(shoot) { bullet=true; shoot=false; } else bullet=false; score=1e10; } else { acc=true; accelerateFramesNumber--; } } else rotateFramesNumber--; } AiSprite Ai::nextPosition(AiSprite sp,double mult) { double abs_2,nx,ny,sq,eg; if(!sp.sun) { abs_2=sp.x*sp.x+sp.y*sp.y; if(abs_2<1) abs_2=1; sq=sqrt(abs_2); nx=sp.x/sq; ny=sp.y/sq; eg=cfg->gravity*mult; sp.dx-=eg*nx/abs_2; sp.dy-=eg*ny/abs_2; sp.x+=sp.dx*mult; sp.y+=sp.dy*mult; if(sp.x*sp.x+sp.y*sp.y<1600) sp.sun=true; else { //simple bounds actions if(sp.x>sfwidth_2) { sp.x-=sfwidth; sp.border=true; } else if(sp.x<-sfwidth_2) { sp.x+=sfwidth; sp.border=true; } if(sp.y>sfheight_2) { sp.y-=sfheight; sp.border=true; } else if(sp.y<-sfheight_2) { sp.y+=sfheight; sp.border=true; } } } return sp; } void Ai::nextPositions(AiSprite sp,TQMemArray *a,int frames) { int i,num; double fmult=cfg->gamespeed*frames; (*a)[0]=nextPosition(sp,cfg->gamespeed); num=a->size(); for(i=1;igamespeed); if(shipsNextPositions[0]->size() != j) for(i=0;i<2;i++) shipsNextPositions[i]->resize(j); for(i=0;i<2;i++) nextPositions(ship[i]->toAiSprite(),shipsNextPositions[i], calcFrameIncrement[Options::aiDifficulty(playerNumber)]); if(cfg->maxMines > aiMines[0]->size()) for(i=0;i<2;i++) aiMines[i]->resize(cfg->maxMines); for(i=0;i<2;i++) { j=0; ms=mines[i]->first(); while(ms) { (*(aiMines[i]))[j]=ms->toAiSprite(); ms=mines[i]->next(); j++; } mineNumber[i]=j; } } void Ai::tryShots() { AiSprite shot,me; double rot,nr,nx,ny; int i,f,frameIncrement,frameNum; Hit hit; Shot *goodShot; me=ship[playerNumber]->toAiSprite(); rot=ship[playerNumber]->getRotation(); //Each 'frameIncrement' frames a shot is tried frameIncrement=(int)((2*M_PI/calcShotDirections[Options::aiDifficulty(playerNumber)]) /cfg->rotationSpeed); if(frameIncrement==0) frameIncrement=1; //Number of frames needed to rotate 180 degrees frameNum=(int)(M_PI/(frameIncrement*cfg->rotationSpeed)); //if too much bullets are on the playfield, no shot is tried if(bullets[playerNumber]->count() < (cfg->maxBullets+ship[playerNumber]->getBulletPowerups())) { for(f=0;f<=frameNum;f++) { if(f!=0) for(i=0;igamespeed); else me=nextPosition(me,cfg->gamespeed); if(!ship[playerNumber]->reloadsBullet(f*frameIncrement*cfg->gamespeed)) { for(i=0;i<2;i++) { if((f==0)&&(i==1)) continue; if(i==0) nr=rot+frameIncrement*f*cfg->rotationSpeed; else nr=rot-frameIncrement*f*cfg->rotationSpeed; nx=cos(nr); ny=sin(nr); shot.x=me.x+nx*SHOTDIST; shot.y=me.y+ny*SHOTDIST; shot.dx=me.dx+nx*cfg->shotSpeed; shot.dy=me.dy+ny*cfg->shotSpeed; shot.sun=false; shot.border=false; hit=firstObject(shot,f*frameIncrement, calcFrameIncrement[Options::aiDifficulty(playerNumber)]); if((hit.object!=HNOTHING) && !((hit.object==HSHIP)&&(hit.playerNumber==playerNumber))) { goodShot=new Shot; goodShot->hit=hit; goodShot->rotation=(i==0?RLEFT:RRIGHT); goodShot->rotationFrames=f*frameIncrement; goodShot->score=1e10; myShots.append(goodShot); } } } } } } Hit Ai::firstObject(AiSprite shot,int time,int frames) { int optime,i,num,rtime,basetime,t,m; double dist,distx,disty,shiplastdist=0; bool shipdistgreater=true,hitfound=false; Hit hit={HNOTHING,0,0,0,1e10}; basetime=time/frames; if((time%frames)>0) basetime++; rtime=basetime*frames-time; optime=shipsNextPositions[0]->size(); num=optime-basetime; if(num>0) { for(t=0;(tgamespeed*rtime); else shot=nextPosition(shot,cfg->gamespeed*frames); //distance to other objects for(i=0;i<2;i++) { distx=(*(shipsNextPositions[i]))[basetime].x-shot.x; disty=(*(shipsNextPositions[i]))[basetime].y-shot.y; dist=distx*distx+disty*disty; //own ship if(i==playerNumber) { if(dist0) { calculateCollisions--; h=objectsHitByShip.first(); while(h) { if(h->hitTime>0) { h->hitTime--; h=objectsHitByShip.next(); } else { objectsHitByShip.remove(); h=objectsHitByShip.current(); } } h=minesHitByShot.first(); while(h) { if(h->hitTime>0) { h->hitTime--; h=minesHitByShot.next(); } else { minesHitByShot.remove(); h=minesHitByShot.current(); } } } else { objectsHitByShip.clear(); minesHitByShot.clear(); for(i=0;i<2;i++) { for(bullet=bullets[i]->first();bullet;bullet=bullets[i]->next()) { shot=bullet->toAiSprite(); hit=firstObject(shot,0,calcFrameIncrement[Options::aiDifficulty(playerNumber)]); if(hit.object==HMINE) { h=new Hit(hit); minesHitByShot.append(h); } if((hit.object==HSHIP)&&(hit.playerNumber==playerNumber)) { h=new Hit(hit); h->object=HSHOT; objectsHitByShip.append(h); } } } hit.object=HNOTHING; hit.distance=400; for(i=0;(isize()) && !(*shipsNextPositions[playerNumber])[i].sun;i++) { if((borderTime<0) && (*shipsNextPositions[playerNumber])[i].border) borderTime=i*calcFrameIncrement[Options::aiDifficulty(playerNumber)]; dx=(*shipsNextPositions[playerNumber])[i].x; dy=(*shipsNextPositions[playerNumber])[i].y; distance=dx*dx+dy*dy; if((distance<3025)&&(sunTime<0)) sunTime=i*calcFrameIncrement[Options::aiDifficulty(playerNumber)]; if(!hitfound) for(p=0;p<2;p++) for(m=0;mdistance) { hit.object=HMINE; hit.playerNumber=p; hit.objectNumber=m; hit.hitTime=i*calcFrameIncrement[Options::aiDifficulty(playerNumber)]; hit.distance=distance; if(distance<100) hitfound=true; } } } if(hit.object!=HNOTHING) { h=new Hit(hit); objectsHitByShip.append(h); } calculateCollisions=(int)(calcCollisions[Options::aiDifficulty(playerNumber)]/cfg->gamespeed); } } void Ai::shotScores() { Shot *s; Hit *h,*mh; bool found,foundmh; double dist,dx,dy,fuel; dx=(*shipsNextPositions[playerNumber])[0].x-(*shipsNextPositions[opponentNumber])[0].x; dy=(*shipsNextPositions[playerNumber])[0].y-(*shipsNextPositions[opponentNumber])[0].y; dist=dx*dx+dy*dy; for(s=myShots.first();s;s=myShots.next()) { fuel=(100-(ship[playerNumber]->getEnergy()-cfg->shotEnergyNeed)); s->score=fuel*fuel/10 + s->hit.distance+s->hit.hitTime; if(dist > (75*75)) s->score+=waitShot*8; else s->score+=waitShot*4; if(s->hit.object==HMINE) { found=false; for(h=objectsHitByShip.first();h && !found;h=objectsHitByShip.next()) { if((h->object==HMINE)&&(h->playerNumber==s->hit.playerNumber) &&(h->objectNumber==s->hit.objectNumber)) //ship will hit a mine that will be hitten by the shot { found=true; //ship hits earlier then shot if(h->hitTimehit.hitTime) s->score+=1000; else { foundmh=false; for(mh=minesHitByShot.first();mh && !foundmh;mh=minesHitByShot.next()) { if((mh->playerNumber==s->hit.playerNumber) &&(mh->objectNumber==s->hit.objectNumber)) //another shot will hit the mine { if(mh->hitTimehit.hitTime) s->score+=500; else s->score-=300; } } } } } if(!found) s->score+=1000; } } } void Ai::chooseAction() { double bestScore=1e10; Shot *bestShot=NULL,*s; AiSprite actualpos; double posangle,movephi,phiright,phileft,torotate=0,velangle; int framesleft,framesright; bool rotateAndAccelerate=false; Hit *nextHit=0; int shotHitTime; shotHitTime=1000000; nextHit=0; /* for(h=objectsHitByShip.first();h;h=objectsHitByShip.next()) if(h->object==HSHOT) if(h->hitTimehitTime; }*/ if((borderTime>0) || (sunTime>0) || (nextHit)) { actualpos=ship[playerNumber]->toAiSprite(); posangle=rectToAngle(actualpos.x,actualpos.y); movephi=rectToAngle((*shipsNextPositions[playerNumber])[0].x, (*shipsNextPositions[playerNumber])[0].y) - posangle; phileft=movephi+cfg->rotationSpeed; phiright=movephi-cfg->rotationSpeed; if((borderTime>0)&& !((sunTime>0)&&(sunTimegamespeed; if(score>bestScore) { velangle=rectToAngle(actualpos.dx,actualpos.dy); if(fabs(difference(posangle+3*M_PI/4,velangle)) < fabs(difference(posangle-3*M_PI/4,velangle))) torotate=posangle-3*M_PI/4-ship[playerNumber]->getRotation(); else torotate=posangle+3*M_PI/4-ship[playerNumber]->getRotation(); rotateAndAccelerate=true; score=bestScore; accelerateFramesNumber=(int)(8/cfg->gamespeed); } } else if(sunTime>0) { bestScore=sunTime/(cfg->gamespeed*10) +(actualpos.x*actualpos.x+actualpos.y*actualpos.y)/5000; if(score>bestScore) { velangle=rectToAngle(actualpos.dx,actualpos.dy); if(fabs(difference(posangle+2*M_PI/5,velangle)) < fabs(difference(posangle-2*M_PI/5,velangle))) torotate=posangle+2*M_PI/5-ship[playerNumber]->getRotation(); else torotate=posangle-2*M_PI/5-ship[playerNumber]->getRotation(); rotateAndAccelerate=true; score=bestScore; accelerateFramesNumber=(int)(8/cfg->gamespeed); } } else { /* bestScore=abs(nextHit->hitTime-90)*4/cfg->gamespeed + nextHit->distance*2 + (100-(ship[playerNumber]->getEnergy()-cfg->shotEnergyNeed))*4; if((score>bestScore)&&(bestScore<400)) { velangle=rectToAngle(actualpos.dx,actualpos.dy); if(fabs(difference(posangle+2*M_PI/5,velangle)) < fabs(difference(posangle-2*M_PI/5,velangle))) torotate=posangle+2*M_PI/5-ship[playerNumber]->getRotation(); else torotate=posangle-2*M_PI/5-ship[playerNumber]->getRotation(); rotateAndAccelerate=true; score=bestScore; accelerateFramesNumber=(int)(4/cfg->gamespeed); }*/ } if(rotateAndAccelerate) { if(phileft<0) framesleft=1000; else { while(torotate<0) torotate+=2*M_PI; while(torotate>=2*M_PI) torotate-=2*M_PI; framesleft=(int)(torotate/phileft+0.5); } if(phiright>0) framesright=1000; else { while(torotate>0) torotate-=2*M_PI; while(torotate<=-2*M_PI) torotate+=2*M_PI; framesright=(int)(torotate/phiright+0.5); } if(framesrightscorescore; bestShot=s; } if(bestShot) { if((bestScorerotation; rotateFramesNumber=bestShot->rotationFrames; accelerateFramesNumber=0; shoot=true; score=bestScore; calculateCollisions = 0; waitShot=(int) rint( random.getDouble() * calcNextShot[Options::aiDifficulty(playerNumber)] /cfg->gamespeed); } } } } void Ai::setSpriteFieldSize() { sfwidth=(double)(ship[playerNumber]->spriteFieldWidth()); sfheight=(double)(ship[playerNumber]->spriteFieldHeight()); sfwidth_2=sfwidth/2.0; sfheight_2=sfheight/2.0; }