经过几天的调试和修改,我的坦克大战AI代码终于算是初步完工了,这里我写下我的思路(就当做是实验报告吧。。),由于第一次接触这个东西,所以没有想的太复杂,寻路用的A*算法,遇到敌军就开火,关于战术我是仿照的Dota的战术,我把整个地图分为中路,右路,左路三个区域,中路为地图中央一个长宽都为 7 的小正方形(地图为21*21),右路为地图右边7排,左路为地图左边7排~~
自己的5辆坦克分别为 Pioneer(肉盾)单中,一辆Strike(干客)和 一辆Sniper(火枪)组成一个战斗小组,算上单中的 Pioneer 一共三个战斗小组,分别走中,左,右三路(即寻找相应区域的非己方资源)。
这样就要求自己的地图不能太非常规,最好是这样的。。。。
这种战术在某些地图上可以险胜中等电脑,我把我的AI.cpp贴一下,所有的函数都是我自己写然后调试的,希望各位前辈看后不要喷。。。(有一个情况我没有考虑:即场上所有的矿点都是己方的,我基本没有遇到这种情况,打弱智电脑的时候有过,那时候就会访问越界。。。。)
复制内容到剪贴板
代码:
//************************************************************************
//Project:坦克大战AI设计
//Time:2011.7.28
//Programmer:YellowBaby
//************************************************************************
#include "Tank.h"
#include <string.h>
#include <stdio.h>
#include <vector>
#include <math.h>
#include <cstdlib>
#include <iostream>
#define FLASE 0
#define TRUE 1
using namespace std;
//********************************以下是Astar寻路算法所需要的函数以及声明******************************
typedef struct{ //A*算法需要的结点表
int ID; //该结点的编号(从1开始)
int father; //该结点的父节点的编号
int gcost; //从初始节点到当前结点的花费
int hcost; //从当前结点到目标结点的估价
}Node;
static vector <Node> OPEN; //A*算法中的OPEN表
static vector <Node> ClOSE; //A*算法中的CLOSE表
static vector <int> path[MAX_TANK_NUM]; // 路径存储位置
Point IDtoPoint(int ID)
{ //将结点在矩阵中的位置返回
Point Temp;
Temp.row = ID / (MAP_WIDTH + 2); //算出该点的列数
Temp.col = ID % (MAP_WIDTH + 2) - 1; //算出该点的行数
return Temp;
}//MakeNumber
int PointtoID(Point P)
{ //已知点的位置,求出该点的ID
int id = P.row * (MAP_WIDTH + 2) + P.col + 1;
return id;
}//PointtoID
int Find(int ID,vector <Node> vec)
{ //判断在(关闭/开启)列表中是否有该结点,若有则返回索引号,否则返回FLASE
for(unsigned int i = 0;i < vec.size();i++)
if(ID == vec[i].ID)
return i;
return FLASE;
}//Find
void CanAdd(Node FatherNode,Point P,DataForAI data,Point goal)
{ //判断 P 点是否能加入开启列表,若能,则加入
int ID = PointtoID(P); //求出该点的ID
if(data.map[P.row][P.col].type != STONE && FLASE == Find(ID,ClOSE)) //地形不为石头,且不在关闭列表里
{
if(FLASE == Find(ID,OPEN)) //若OPEN没有该点
{ //则建立新结点,并赋值加入到OPEN表中
Node Temp;
Temp.ID = ID;
Temp.father = FatherNode.ID;
Temp.gcost = FatherNode.gcost + 1/*AddCost(P,data)*/;
Temp.hcost = abs(goal.row - P.row) + abs(goal.col - P.col);
OPEN.push_back(Temp);
}
else //若OPEN中有该结点,则判断消耗是否更少
{ //若消耗更小,就把父节点改了
int index = Find(ID,OPEN);
if(FatherNode.gcost + 1 < OPEN[index].gcost)
{
OPEN[index].gcost = FatherNode.gcost + 1 ;
OPEN[index].father = FatherNode.ID;
}
}
}
}//CanAdd
void FindNode(Node N,DataForAI data,Point goal)
{ //寻找周围可通过的点,并把它们加入开启列表中
Point NewP[4]; //分别为P四个方向的点
Point P = IDtoPoint(N.ID); //算出当前点的位置
NewP[0].col = P.col + 1; //P0为P右边的点
NewP[0].row = P.row;
NewP[1].col = P.col - 1; //P1为P左边的点
NewP[1].row = P.row;
NewP[2].col = P.col; //P2为P上边的点
NewP[2].row = P.row - 1;
NewP[3].col = P.col;
NewP[3].row = P.row + 1; //P3为P上边的点
for(int i = 0;i < 4;i++) //判断是否能够加入OPEN列表
CanAdd(N,NewP[i],data,goal);
}//FindNode
Node ChooseNode()
{ //从OPEN列表中选出消耗值最小的结点,把该结点作为新的父节点,并加入关闭列表,且返回该结点
unsigned int min = 0;
Node Temp;
for(unsigned int i = 1;i < OPEN.size();i++) //找出消耗值最小的位置
if((OPEN[i].hcost + OPEN[i].gcost) < (OPEN[i-1].hcost + OPEN[i-1].gcost))
min = i;
Temp = OPEN[min];
ClOSE.push_back(OPEN[min]); //将该点压入CLOSE中
OPEN.erase(OPEN.begin() + min); //把min位置的结点删除
return Temp;
}//ChooseNode
void SaveWays(int Myflag,int goalID)
{ //存储编号为 Myflag 坦克到目的编号为 goalID的路径到 path[Myflag]
path[Myflag].clear(); //将原有路径清空
path[Myflag].push_back(goalID); //将目标结点压入
int index = Find(goalID,ClOSE); //在CLOSE中找到最后一个结点
Node Temp = ClOSE[index];
while(Temp.father != -1) //沿着目标结点通过 father 找到起点
{
path[Myflag].insert(path[Myflag].begin(),Temp.father); //将当前节点的 father 结点压入路径容器
index = Find(Temp.father,ClOSE); //找到 father 结点的索引
Temp = ClOSE[index]; //将 father 结点赋值给 Temp
}
OPEN.clear(); //将OPEN表清空
ClOSE.clear(); //将CLOSE表清空
}//SaveWays
void AstarWay(int Myflag,Point StartPoint,Point GoalPoint,DataForAI data)
{ //运用A*寻路要坦克从点 StartPoint 到 GoalPoint
int ID = PointtoID(StartPoint); //确定起始点的 ID
int EndID = PointtoID(GoalPoint); //确定目标的的 ID
Node Start,Temp; //对起始节点的初始化
Start.ID = ID;
Start.father = -1;
Start.hcost = 0;
OPEN.push_back(Start); //将起始点压入OPEN表中
Temp = Start;
ClOSE.push_back(Start);
while(FLASE == Find(EndID,ClOSE)) //当目标结点被加入CLOSE表中是说明路径已经找到了
{
FindNode(Temp,data,GoalPoint); //以 Temp 为父结点向外扩展
Temp = ChooseNode(); //选出消耗最小的作为新的父节点
}
SaveWays(Myflag,EndID); //将路径存入vector容器
}//AstarWay
//********************************以上是Astar寻路算法所需要的函数以及声明******************************
//********************************以下是坦克寻矿作业所需要的函数*****************************************
int isOurSource(int myID,Point Source,DataForAI data)
{ //判断是否为己方矿点
int mySource = RedSource; //判断自己矿点的颜色
if(data.tank[myID].flag == BLUE)
mySource = BlueSource;
if(data.map[Source.row][Source.col].isHereSource == mySource)
return TRUE;
else
return FLASE;
}//isOurSource
int isBetter(Point mySite,Point SourceA,Point SourceB)
{ //若 A 点离自己近,返回TRUE,否则返回 FLASE
int costA = abs(SourceA.row - mySite.row) + abs(SourceA.col - mySite.col);
int costB = abs(SourceB.row - mySite.row) + abs(SourceB.col - mySite.col);
if(costA <= costB)
return TRUE;
else
return FLASE;
}//isBetter
Point bestSource(int myID,int sourceID[],int index,DataForAI data)
{ //已知所以的非己方矿点的编号,返回离自己最近的矿点位置
Point mySite; //坦克自己的位置
mySite.row = data.tank[myID].row;
mySite.col = data.tank[myID].col;
int bestID = sourceID[0];
for(int i = 1;i < index;i++)
{
if(TRUE == isBetter(mySite,data.source[sourceID[i]],data.source[sourceID[i-1]]))
bestID = sourceID[i];
}
return data.source[bestID];
}//bestSource
void AllocateSource(int myID,Point &myGoal,DataForAI data)
{ //给每一辆坦克分配目标
int index,indexLeft,indexRight,indexMiddle;
int sourceID[MAX_SOURCE_NUM]; //存放所有的矿点
int middleSource[MAX_SOURCE_NUM]; //存放中路的矿点
int rightSource[MAX_SOURCE_NUM]; //存放右路的矿点
int leftSource[MAX_SOURCE_NUM]; //存放左路的矿点
index = indexLeft = indexRight = indexMiddle = 0;
for(int i = 0;i < MAX_SOURCE_NUM;i++)
{ //找出所以的非己方矿点的编号,以及中路矿点,左右路矿点的编号
if(isOurSource(myID,data.source[i],data) != TRUE)
{
sourceID[index] = i;
index++;
//中路的范围为整个比赛场地中间的一个长和宽都是 7 的正方形
if(data.source[i].row > 7 && data.source[i].row < 14 && data.source[i].col > 7 && data.source[i].col < 14)
{
middleSource[indexMiddle] = i;
indexMiddle++;
}
//左路的范围为比赛场地 col 小于 7的部分
if(data.source[i].col < 7)
{
leftSource[indexLeft] = i;
indexLeft++;
}
//右路的范围为比赛场地 col 大于 14的部分
if(data.source[i].col > 14)
{
rightSource[indexRight] = i;
indexRight++;
}
}
}//找出所以的非己方矿点的编号,以及中路矿点,左右路矿点的编号
//战略:Poineer 单中,Strike 和 sniper 组成两个个战斗小组,分别走左右路
//从三路的矿点中找出离自己最近的一个,
//若范围内都是己方矿点,就从所有的矿点里面找
if(myID == 0|| myID == 5) //Poinner 走中路
{
if(indexMiddle != 0) //若中路有非己方矿点
myGoal = bestSource(myID,middleSource,indexMiddle,data);
else //若没有则从全部矿点找
myGoal = bestSource(myID,sourceID,index,data);
}
else if(myID == 1 || myID == 6 || myID == 3 || myID == 8) //一号战斗小组走右路
{ //一号战斗小组由 Strike 1 号 与 Sniper 3 号组成
if(indexRight != 0) //若右路有非己方矿点
myGoal = bestSource(myID,rightSource,indexRight,data);
else //若没有则从全部矿点找
myGoal = bestSource(myID,sourceID,index,data);
}
else if(myID == 2 || myID == 7 || myID == 4 || myID == 9) //二号战斗小组走左路
{ //二号战斗小组由 Strike 2 号 与 Sniper 4 号组成
if(indexLeft != 0) //若左路有非己方矿点
myGoal = bestSource(myID,leftSource,indexLeft,data);
else //若没有则从全部矿点找
myGoal = bestSource(myID,sourceID,index,data);
}
}//AllocateSource
//********************************以上是坦克寻矿作业所需要的函数*****************************************
//********************************以下是坦克开火作业所需要的函数*****************************************
int isEnemy(short tankA,short tankB)
{ //已知两辆坦克的编号,若坦克为同一方,返回FLASE,否则返回TRUE
if((tankA <= 4 && tankA >= 0 && tankB > 4) || (tankA > 4 && tankB >= 0 && tankB <= 4))
return TRUE;
else
return FLASE;
}//Enemy
int FindEnemy(Point &enemySite,int myID,int range,DataForAI data)
{ //寻找自己攻击范围内是否有敌军,若有返回 TRUE,否则返回 FLASE
Point mySite;
mySite.row = data.tank[myID].row;
mySite.col = data.tank[myID].col;
int findRange; //由攻击范围来确定查找范围
switch(range)
{
case 1:
findRange = 1;
break;
case 3:
findRange = 2;
break;
case 5:
findRange = 3;
break;
}
for(int row = mySite.row - findRange;row < mySite.row + findRange;row++)
for(int col = mySite.col - findRange;col < mySite.col + findRange;col++)
if(row >=0 && row < MAP_WIDTH +1 && col >= 0 && col < MAP_HEIGHT +1 && TRUE == isEnemy(myID,data.map[row][col].whoIsHere ))
{ //若发现敌军,就把坐标返回
enemySite.row = row;
enemySite.col = col;
return TRUE;
}
return FLASE;
}//FindEnemy
//********************************以上是坦克开火作业所需要的函数*****************************************
Order getOrder(int myID,int range,Point mySite,Point nextStep,DataForAI data)
{ //根据下一步目标方向返回运动指令
Order order;
Point enemySite; //敌军的位置
int ID = PointtoID(mySite); //自己的位置编号
int nextID = PointtoID(nextStep); //下一步的位置编号
MapCell Temp = data.map[mySite.row][mySite.col]; //坦克所在地的地图信息
MapCell Next = data.map[nextStep.row][nextStep.col]; //下一目标地点的地图信息
if(TRUE == FindEnemy(enemySite,myID,range,data))
{ //若在自己的攻击范围内找到敌军,就开火
order.type = FIRE;
order.row = enemySite.row;
order.col = enemySite.col;
}
else if(Next.type == PERVIOUS) //若目标地点可通过
{
if(Next.whoIsHere == -1) //若目标地点没有坦克
{
switch(nextID-ID) //通过ID之间的差来判断坦克下一步的指令是什么
{
case 1:
order.type = GORIGHT;
break;
case -1:
order.type = GOLEFT;
break;
case 23:
order.type = GODOWN;
break;
case -23:
order.type = GOUP;
break;
}
}//目标地点没有坦克
else //目标地点有友军坦克
{
order.type = STOP;
}
}//目标地点没有砖块
else if(Next.type == BRICK || Next.type == BREAKBRICK)
{
order.type = FIRE;
order.row = nextStep.row;
order.col = nextStep.col;
}
return order;
}//getOrder
//平台0回合时调用此函数获取AI名称及坦克类型信息,请勿修改此函数声明。
extern "C" InitiateInfo chooseType()
{
InitiateInfo Info;
Info.tank[0]=Pioneer;
Info.tank[1]=Striker;
Info.tank[2]=Striker;
Info.tank[3]=Sniper;
Info.tank[4]=Sniper;
strcpy(Info.aiName,"MyAIName"); //AI名请勿使用中文。
return Info;
}
//平台从第1回合开始调用此函数获得每回合指令,请勿修改此函数声明。
extern "C" Order makeOrder(DataForAI data)
{
//定义Order类型变量用于返回
Order order;
int myID = data.myID; //找到当前坦克的位置
Point mySite;
mySite.row = data.tank[myID].row;
mySite.col = data.tank[myID].col;
Point myGoal[MAX_TANK_NUM]; //存放每辆坦克的目标
AllocateSource(myID,myGoal[myID],data); //给每一辆坦克选择目标资源
AstarWay(myID,mySite,myGoal[myID],data); //用A*寻路给每一辆坦克将路径找出来
int attackRange = data.tank[myID].range; //攻击范围
Point nextStep = IDtoPoint(path[myID][1]); //下一步应该到的位置
order = getOrder(myID,attackRange,mySite,nextStep,data);
return order;
}
最后,感谢一下版主和枫哥给我的帮助~:(mars_25):
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include "Tank.h"
#include <string.h>
#include <stdio.h>
//请勿修改以上头文件
/*
您可以在这里添加您所需头文件
*/
#include <cmath>
#include <iostream>
/*
您可以在这里添加您的自定义函数
*/
struct Point_mix{
short row,col;
bool fire ; // fire = true; 开火 ,fire = false;冲撞;
OrderType order;
};
int total;
int goal[10];
int source_size[15];
int smaxset;
Point_mix get_other_point(DataForAI data,int TankID,bool fflag)
{
Point_mix p ;
p.row = -2;
p.col = -2;
p.fire =false;
p.order = STOP;
int reduced = -10000;
int map_set[25][25];
int x = data.tank[TankID].row;
int y = data.tank[TankID].col;
int father[500];
int step1[25][25];
for (int i = 0 ; i <= 22; ++i)
for (int j = 0 ; j <= 22; ++j)
{
map_set[i][j] = -1;
step1[i][j] = 0;
if (data.map[i][j].type == BRICK )
map_set[i][j] = 3;
if (data.map[i][j].type == BREAKBRICK )
map_set[i][j] = 2;
if (data.map[i][j].type == STONE)
map_set[i][j] = -2;
if (data.map[i][j].type == PERVIOUS )
map_set[i][j] = 1;
}
bool flag = true;
step1[x][y] = 1;
int temp[25][25];
for (int i = 1; i <= 21; ++i)
for (int j = 1; j <= 21; ++j)
{
father[(i-1)*21+j] = -1;
temp[i][j] = 0 ;
}
int len_[25][25];
int len = 0;
while (flag)
{
len++;
flag = false;
for (int i = 1; i <= 21; ++i)
for (int j = 1; j <= 21; ++j)
{
if (map_set[i][j] == -2 || step1[i][j] == map_set[i][j])
continue;
if (step1[i-1][j] == map_set[i-1][j])
{
temp[i][j] = 1;
if (father[(i-1)*21+j] == -1)
father[(i-1)*21+j] = (i-2)*21+j;
flag = true;
continue;
}
if (step1[i+1][j] == map_set[i+1][j])
{
temp[i][j] = 1;
if (father[(i-1)*21+j] == -1)
father[(i-1)*21+j] = (i)*21+j;
flag = true;
continue;
}
if (step1[i][j+1] == map_set[i][j+1])
{
temp[i][j] = 1;
if (father[(i-1)*21+j] == -1)
father[(i-1)*21+j] = (i-1)*21+j+1;
flag = true;
continue;
}
if (step1[i][j-1] == map_set[i][j-1])
{
temp[i][j] = 1;
if (father[(i-1)*21+j] == -1)
father[(i-1)*21+j] = (i-1)*21+j-1;
flag = true;
continue;
}
}
for (int i = 1 ; i <= 21; i++ )
for (int j = 1 ; j <= 21; j++)
{
if (temp[i][j] ==1 )
{
step1[i][j]++;
if (step1[i][j] == map_set[i][j])
len_[i][j] = len;
}
temp[i][j] = 0;
}
}
int max = 10000;
int sign = -1;
bool tflag = false;
for (int i = 0; i<total; i++)
{
if (fflag && source_size[i]>= smaxset) continue;
if (data.map[data.source[i].row][data.source[i].col].isHereSource == RedSource && data.tank[TankID].flag== RED)
continue;
if (data.map[data.source[i].row][data.source[i].col].isHereSource == BlueSource && data.tank[TankID].flag == BLUE)
continue;
if (len_[data.source[i].row][data.source[i].col]< max)
{
max = len_[data.source[i].row][data.source[i].col];
sign = i;
}
if (len_[data.source[i].row][data.source[i].col]== max && data.map[data.source[i].row][data.source[i].col].isHereSource != NeutralSource)
sign = i;
}
if (sign == -1 ) return p;
int x1 = data.source[sign].row;
int y1 = data.source[sign].col;
int now = (x1-1)*21+y1;
while (father[now] != (x-1)*21+y)
{
now = father[now];
}
if (fflag)
{
source_size[sign]++;
}
x1 = (now-1) / 21 +1;
y1 = now % 21;
if (y1 == 0 )
y1 = 21;
switch (x1 - x)
{
case -1 : if (data.map[x1][y1].type ==PERVIOUS) p.order = GOUP; else {p.row = x1; p.col = y1; p.fire =true; return p;}; break;
case 1 : if (data.map[x1][y1].type ==PERVIOUS) p.order = GODOWN; else {p.row = x1; p.col = y1; p.fire =true; return p;}; break;
}
switch (y1 - y)
{
case -1 : if (data.map[x1][y1].type ==PERVIOUS) p.order = GOLEFT; else {p.row = x1; p.col = y1; p.fire =true; return p;}; break;
case 1 : if (data.map[x1][y1].type ==PERVIOUS) p.order = GORIGHT; else {p.row = x1; p.col = y1; p.fire =true; return p;}; break;
}
return p;
}
Point_mix get_fire_point(DataForAI data)
{
Point_mix p;
p.row = -2;
p.col = -2;
int reduced = -10000;
for (int i = 1; i <= 21; ++i)
for (int j = 1; j <= 21; ++j)
{
if (data.map[i][j].whoIsHere==-1)
continue;
if (data.tank[data.map[i][j].whoIsHere].flag == data.tank[data.myID].flag)
continue;
TankData tank_goal =data.tank[data.map[i][j].whoIsHere];
TankData tank_source=data.tank[data.myID];
int tt = abs(tank_goal.row-tank_source.row)+abs(tank_source.col-tank_goal.col);
if (tt > tank_source.range)
continue;
short a1 = tank_source.attack,a2 = tank_goal.attack,l1 = tank_source.life,l2 = tank_goal.life;
while (l2 > 0 && tank_goal.range >= tt )
{
l1 -= a2;
l2 -= a1;
}
if (l1 > reduced)
{
p.row = tank_goal.row;
p.col = tank_goal.col;
reduced = l1;
p.fire = true;
}
short ll1 = tank_source.life,ll2 = tank_goal.life;
if (tt > 1) continue;
if (tank_source.noharm <= 0)
ll1 = ll1-ll2-a2;
if (ll1 > reduced)
{
reduced = ll1;
p.row = 0 ;
p.col = 0;
p.fire = false;
switch (tank_goal.row - tank_source.row)
{
case -1 : p.order = GOUP; break;
case 1 : p.order = GODOWN; break;
}
switch (tank_goal.col - tank_source.col)
{
case -1 : p.order = GOLEFT; break;
case 1 : p.order = GORIGHT; break;
}
}
}
if (p.col != -2 || p.row !=-2)
return p;
//帮着其他打砖块。
for (int i = 0; i < 10 ; ++i)
{
if (i == data.myID || data.myFlag!=data.tank[i].flag)
continue;
if(data.tank[i].life <=0) continue;
Point_mix p1 = get_other_point(data,i,false);
if (p1.row != -2 && p1.col != -2 && p1.fire == true)
{
if ( abs(p1.row-data.tank[data.myID].row)+abs(p1.col-data.tank[data.myID].col)>data.tank[data.myID].range)
{
continue;
}
return p1;
}
//调用其他坦克的行动指令,帮忙打;
}
return p;
}
Point_mix getOrder(DataForAI data)
{
}
//平台0回合时调用此函数获取AI名称及坦克类型信息,请勿修改此函数声明。
extern "C" InitiateInfo chooseType()
{
InitiateInfo Info;
for (int i =0; i < 10; ++i)
{
goal[i] = -1;
}
Info.tank[0]=Striker; // 高攻
Info.tank[1]=Striker; //Striker 高攻
Info.tank[2]=Sniper; // 高防
Info.tank[3]=Pioneer; // 高防
Info.tank[4]=Sniper; // 高射程
strcpy(Info.aiName,"hustpawpaw"); //AI名请勿使用中文。
return Info;
}
//砖墙有两点生命值
//平台从第1回合开始调用此函数获得每回合指令,请勿修改此函数声明。
extern "C" Order makeOrder(DataForAI data)
{
//定义Order类型变量用于返回
total = 0;
for (int i = 1; i <= 21; ++i)
for (int j = 1; j <= 21; ++j)
{
if(data.map[i][j].isHereSource != NotSource)
++total;
}
//total = data.totalSource;
int temp ;
int left = 0 ;
if (data.myFlag == RED)
temp = 0;
else temp = 5;
bool flag1 = true;
for (int i = temp ; i <= data.myID -1; ++i)
{
if (data.tank[i].life > 0 )
flag1 = false;
}
for (int i = temp; i < temp+5; ++i)
{
if (data.tank[i].life>0)
++left;
}
int le ;
if (data.myFlag == RED )
le = data.totalSource-data.blueSource;
else le = data.totalSource - data.redSource;
if (flag1)
{
//smaxset = 2;
if (left/le+1> smaxset)
smaxset = left/le+1;
// std::cout << data.myID << " " << smaxset <<std::endl;
for (int i = 0 ; i< total ; ++i)
{
source_size[i] = 0;
}
}
Order order;
/* for (int i = 0 ; i < total ; ++i)
{
if (data.myFlag == RED && data.map[data.source[i].row][data.source[i].col].isHereSource == RedSource)
continue;
if (data.myFlag == BLUE && data.map[data.source[i].row][data.source[i].col].isHereSource == BlueSource)
continue;
if (source_size[i] >= smaxset)
continue;
if (abs(data.source[i].row-data.tank[data.myID].row)+abs(data.source[i].col-data.tank[data.myID].col) == 1)
{
int x2 = data.source[i].row;
int y2 = data.source[i].col;
int x3 = data.tank[data.myID].row;
int y3 = data.tank[data.myID].col;
source_size[i]++;
// if(data.map[data.source[i].row][data.source[i].col].isHereSource == NeutralSource)
std::cout <<x2<<" "<<y2<<" "<<x3<<" " << y3<<" "<<source_size[i]<<std::endl;
switch (x2 - x3)
{
case -1 : order.type = GOUP; return order ; break;
case 1 : order.type = GODOWN; return order ; break;
}
switch (y2 - y3)
{
case -1 : order.type = GOLEFT; return order ; break;
case 1 : order.type = GORIGHT; return order ; break;
}
}
}*/
int myID = data.myID;
int flag = data.myFlag;
Point_mix p = get_fire_point(data);
if (p.row != -2 || p.col != -2)
{
if (p.fire == true)
{
order.type = FIRE;
order.row = p.row;
order.col = p.col;
return order;
}
if (p.fire == false && p.row == 0 && p.col ==0 )
{
order.type = p.order;
return order;
}
}
Point_mix p_temp = get_other_point(data,data.myID,true);
order.type = p_temp.order;
if (p_temp.fire ==true)
order.type = FIRE;
order.row = p_temp.row;
order.col = p_temp.col;
/*
//当自己是4号坦克或9号坦克时,返回STOP指令
if (data.myID == 4 || data.myID == 9)
{
order.type = STOP;
return order;
}
//根据自己红蓝方及初始化变量
int center_row,center_col,my_row,my_col;
if (data.myFlag == RED)
{
center_row = data.tank[4].row;
center_col = data.tank[4].col;
}
else
{
center_row = data.tank[9].row;
center_col = data.tank[9].col;
}
my_row = data.tank[data.myID].row;
my_col = data.tank[data.myID].col;
//Let‘s Dance!
if (my_row > center_row && my_col >= center_col)
order.type = GOLEFT;
if (my_row >= center_row && my_col < center_col)
order.type = GOUP;
if (my_row < center_row && my_col <= center_col)
order.type = GORIGHT;
if (my_row <= center_row && my_col > center_col)
order.type = GODOWN;
*/
//返回指令
if (order.type == STOP)
{
for (int i = 1 ; i <= 21; i++)
for (int j = 1; j <= 21; j++)
{
if (abs(data.tank[data.myID].row-i)+abs(data.tank[data.myID].col-j) > data.tank[data.myID].range)
continue;
if (data.map[i][j].type == STONE)
continue;
if (data.map[i][j].whoIsHere != -1 && data.tank[data.map[i][j].whoIsHere].flag == data.myFlag)
continue;
if (data.map[i][j].whoIsHere == -1 && data.map[i][j].type == PERVIOUS)
continue;
order.type = FIRE;
order.row = i;
order.col = j;
goto ret;
}
}
ret:
return order;
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#include "Tank.h"
#include <string.h>
#include "main.cpp"
//请勿修改以上头文件
/* 您可以在这里添加您所需头文件 */
#include "stdio.h"
#include <vector>
#include <set>
using namespace std;
/* 您可以在这里添加您的自定义函数 */
/* A*寻路算法 */
// 所需结构定义
typedef struct node // aStar 算法需要的动态表节点
{
int id;
int father; // 父节点编号
int cost; // 从初始位置到达此次的花费g(x)
}node;
static vector<node> openT; // open表
static vector<node> closedT; // closed表
static vector<int> path[MAX_TANK_NUM]; // 路径存储位置
static set<int> targeted; // 标记目标矿点是否被己方锁定,存在则锁定
int point2id(int row, int col) // 节点编号
{
int id = row * (MAP_WIDTH + 2) + col;
return id;
}
Point id2point(int id) // 节点反编号
{
Point po;
po.row = id / (MAP_WIDTH + 2);
po.col = id % (MAP_WIDTH + 2);
return po;
}
// 查找某vector表中是否有一节点
int find(int id, vector<node> vet)
{
if (vet.empty())
{
return -1;
}
for (int i = 0; i < vet.size(); i ++)
{
if (id == vet[i].id)
{
return i;
}
}
return -1;
}
// 查找当然位置在path[myID]中的index
int findPathIdx(int id, vector<int> pathT)
{
if (pathT.empty())
{
return -1;
}
for (int i = 0; i < pathT.size(); i ++)
{
if (id == pathT[i])
{
return i;
}
}
return -1;
}
// 返回从一节点到邻近节点的花费(坦克要走的步数)
// 可通过PERVIOUS为1,BRICK为2,BREAKBRICK为3
int addcost(CellType type)
{
switch(type) {
case PERVIOUS:
return 1;
break;
case BRICK:
return 2;
break;
default:
return 3;
}
}
// 对给定扩展点进行检测添加
void addExtNode(node tmpNode, int extid, CellType type)
{
Point extPo;
node extNode;
extPo = id2point(extid);
if ((-1 == find(extid, closedT)) && (STONE != type)) // 要扩展点不在closed表中,且不为石头则扩展
{
int idx = find(extid, openT); // 在open表中的索引
if (-1 == idx) // 如果不在open表中则添加
{
extNode.id = extid;
extNode.father = tmpNode.id;
extNode.cost = tmpNode.cost + addcost(type);
openT.push_back(extNode);
}
else // 如果在open表中,看新路径是否更近,近则修改
{
if (tmpNode.cost + addcost(type) < openT[idx].cost)
{
openT[idx].father = tmpNode.id;
openT[idx].cost = tmpNode.cost + addcost(type);
}
}
}
}
// 得到本坦克的路径
void getPath(int myID, int goalIdx)
{
// id为myID的坦克路径存于path[myID]中
path[myID].clear();
node tmpNode = openT[goalIdx];
path[myID].insert(path[myID].begin(), tmpNode.id);
/*
Point debug_po = id2point(tmpNode.id);
printf("Path Star!\nGoal (%d, %d)\n", debug_po.row, debug_po.col);
*/
while (-1 != tmpNode.father)
{
int tmpIdx = find(tmpNode.father, closedT);
//printf("%d ", tmpIdx);
if (-1 != tmpIdx)
{
tmpNode = closedT[tmpIdx];
/*
Point debug_po = id2point(tmpNode.id);
printf("(%d, %d)\n", debug_po.row, debug_po.col);
*/
path[myID].insert(path[myID].begin(), tmpNode.id);
}
else
{
path[myID].clear();
return;
}
}
}
// 根据下一步目标返回:方向运动指令
OrderType getOrder(FlagType myFlag, Point mySite, Point nextStep)
{
int rowD = nextStep.row - mySite.row;
printf("%d ",rowD);
int colD = nextStep.col - mySite.col;
printf("%d ",colD);
switch(rowD)
{
case -1:
return GOUP;
break;
case 1:
return GODOWN;
break;
default:
;
}
switch(colD)
{
case -1:
return GOLEFT;
break;
case 1:
return GORIGHT;
break;
default:
;
}
return STOP;
}
// A*寻路算法,路径存储在path[myID]中
void aStar(int myID, Point start, Point goal, MapCell map[][MAP_WIDTH +2])
{
openT.clear();
closedT.clear();
path[myID].clear();
// 1. 把初始节点放入openT
node tmpNode;
tmpNode.id = point2id(start.row, start.col);
tmpNode.father = -1; // 初始节点无父节点
tmpNode.cost = 0;
openT.push_back(tmpNode);
// 2. 如果openT为空,搜索失败退出
bool findGoal = false;
while (! openT.empty() && ! findGoal)
{
tmpNode = openT[0];
int no = 0; // 记录最小f(x)的索引
//3. 移除openT中f(x)值最小的
Point p1 = id2point(tmpNode.id); // 当前考察点位置
int hx1 = abs(goal.row - p1.row) + abs(goal.col - p1.col); // 当前路径cost
int fx1 = hx1 + tmpNode.cost;
// 寻找f(x)最小的节点
for (int i = 0; i < openT.size(); i ++)
{
node tmpNode2 = openT[i];
Point p2 = id2point(tmpNode2.id);
int hx2 = abs(goal.row - p2.row) + abs(goal.col - p2.col);
int fx2 = hx2 + tmpNode2.cost;
if (fx1 > fx2)
{
tmpNode = openT[i];
no = i;
}
}
// 如果节点为目标节点,则路径找到,返回路径
if (point2id(goal.row, goal.col) == tmpNode.id)
{
// 存储路径
getPath(myID, no); // 当目标在open中,得到的最小f(x)的点是目标节点索引为no
return;
}
// 从openT移节点如closedT中
openT.erase(openT.begin() + no);
closedT.push_back(tmpNode);
//4. 从四个方向扩展节点
Point tmpPo;
tmpPo = id2point(tmpNode.id);
int extid;
CellType type;
// 上
extid = point2id(tmpPo.row - 1, tmpPo.col);
type = (map[tmpPo.row - 1][tmpPo.col]).type;
addExtNode(tmpNode, extid, type);
// 下
extid = point2id(tmpPo.row + 1, tmpPo.col);
type = (map[tmpPo.row + 1][tmpPo.col]).type;
addExtNode(tmpNode, extid, type);
// 左
extid = point2id(tmpPo.row, tmpPo.col - 1);
type = (map[tmpPo.row][tmpPo.col - 1]).type;
addExtNode(tmpNode, extid, type);
// 右
extid = point2id(tmpPo.row, tmpPo.col + 1);
type = (map[tmpPo.row][tmpPo.col + 1]).type;
addExtNode(tmpNode, extid, type);
}
}
//平台0回合时调用此函数获取AI名称及坦克类型信息,请勿修改此函数声明。
extern "C" InitiateInfo chooseType()
{
InitiateInfo Info;
Info.tank[0]=Pioneer;
Info.tank[1]=Sniper;
Info.tank[2]=Pioneer;
Info.tank[3]=Sniper;
Info.tank[4]=Pioneer;
strcpy(Info.aiName,"AISeminar_cn"); //AI名请勿使用中文。
targeted.clear();
return Info;
}
//平台从第1回合开始调用此函数获得每回合指令,请勿修改此函数声明。
extern "C" Order makeOrder(DataForAI data)
{
Order order;
Point mySite;
// 获得当前坦克位置
int myID = data.myID;
int tankType = data.tank[myID].type;
// 获得自己的坐标点
mySite.row = data.tank[myID].row;
mySite.col = data.tank[myID].col;
// 获得自己的资源类型
int mySrcType;
switch(data.myFlag)
{
case RED:
mySrcType = RedSource;
break;
case BLUE:
mySrcType = BlueSource;
break;
}
// 获得自己的射程
int actDist;
switch(tankType)
{
case Sniper:
actDist = 5;
break;
case Striker:
actDist = 3;
break;
default:
actDist = 1;
}
// 获取敌方坦克位置信息(行列的第0、MAP_WIDTH + 1处为墙)
// 如果,在攻击范围内,则攻击
for (int mCol = 1; mCol < MAP_WIDTH + 1; mCol ++)
{
for (int mRow = 1; mRow < MAP_HEIGHT + 1; mRow ++)
{
int tankIdx = data.map[mRow][mCol].whoIsHere;
if (tankIdx != -1 && data.tank[tankIdx].flag != data.myFlag)
{
int tankDist = abs(data.tank[tankIdx].row - mySite.row) + abs(data.tank[tankIdx].col - mySite.col);
if (tankDist <= actDist)
{
order.type = FIRE;
order.row = mRow;
order.col = mCol;
return order;
}
}
}
}
// 不进行攻击时,获取矿点信息
// 命令构造:如果攻击范围内有坦克,先攻击,在寻路
int idx = findPathIdx(point2id(mySite.row, mySite.col), path[myID]);
bool needPath = false;
if (path[myID].empty()) // 不在存储路径中
{
needPath = true;
}
else
{
int endIdx = path[myID].size() - 1;
Point endPo = id2point(path[myID][endIdx]);
// 查找目标是否已经是己方占领
if ((-1 == idx) || (endIdx == idx) || ((data.map[endPo.row][endPo.col]).isHereSource == mySrcType))
{
targeted.erase(point2id(endPo.row, endPo.col));
needPath = true;
}
}
// 当前位置不存在路径中,或已经达到目标、或目标点已经是己方则重新计算路径
while (needPath || (-1 == idx))
{
// 获得离自己最近的非己方矿点位置
Point goalSite; // 目标坐标点
goalSite.row = data.source[0].row; // 防止目标全部被锁定,无目标时传递给astar的数据出错
goalSite.col = data.source[0].col;
int my2srcDist = MAP_WIDTH * MAP_WIDTH + MAP_HEIGHT * MAP_HEIGHT; // 记录与最近非己方资源的距离(直线距离平方,“行距 + 列距”效果不好,初始最大)
// 寻找“距离本坦克最近”的非己方矿点(**且“自己在己方中也是最近”)
for (int i = 0; i < data.totalSource; i ++)
{
int tempSR = data.source[i].row;
int tempSC = data.source[i].col;
int srcID = point2id(tempSR, tempSC);
// 在矿点非己方时,且矿点未被己方坦克锁定时(锁定表中不存在),规划路径
if (((data.map[tempSR][tempSC]).isHereSource != mySrcType) && (targeted.find(srcID) == targeted.end()))
{
//int tempDist = abs(tempSR - mySite.row) + abs(tempSC - mySite.col);
int tempDist = (tempSR - mySite.row) * (tempSR - mySite.row) + (tempSC - mySite.col) * (tempSC - mySite.col);
if (tempDist < my2srcDist)
{
my2srcDist = tempDist;
goalSite.row = tempSR;
goalSite.col = tempSC;
}
}
}
// 如果目标未被锁定则加入锁定表,如果已锁定,说明无满足条件者
if(targeted.find(point2id(goalSite.row, goalSite.col)) == targeted.end())
{
targeted.insert(point2id(goalSite.row, goalSite.col));
}
printf("Tand %d - Goal: (%d, %d)\n", myID, goalSite.row, goalSite.col); // debug!
// A*寻路
aStar(myID, mySite, goalSite, &(data.map[0]));
needPath = false;
//*显示路径 debug
for (int pi = 0; pi < path[0].size(); pi ++)
{
Point debug_po = id2point(path[0][pi]);
printf("(%d, %d)\n", debug_po.row, debug_po.col);
}
//*/
// 寻找当前位置在path中的索引
idx = findPathIdx(point2id(mySite.row, mySite.col), path[myID]);
}
// 路径判断后进行命令处理
Point nextStep = id2point(path[myID][idx + 1]);
if (PERVIOUS != (data.map[nextStep.row][nextStep.col]).type) // 如果路径不是可通过的,则轰炸
{
order.type = FIRE;
order.row = nextStep.row;
order.col = nextStep.col;
}
else
{
order.type = getOrder(data.myFlag, mySite, nextStep);
}
return order;
}