程序设计II上机实验10A:杀蚂蚁
2017-07-13
15043 字
50 分钟

杀蚂蚁

Time limit: 3000 ms

Memory limit: 256 MB

Standard I/O

Content

最近,佳佳迷上了一款好玩的小游戏:antbuster。

游戏规则非常简单:在一张地图上,左上角是蚂蚁窝,右下角是蛋糕,蚂蚁会源源不断地从窝里爬出来,试图把蛋糕搬回蚂蚁窝。而你的任务,就是用原始资金以及杀蚂蚁获得的奖金造防御塔,杀掉这些试图跟你抢蛋糕的蚂蚁~

下附一张游戏截图:

Input Description

输入的第一行是2个用空格隔开的整数,n、m,分别表示了地图的长和宽。

第二行是3个用空格隔开的整数,s、d、r,依次表示炮塔的个数、单次攻击伤害以及攻击范围。

接下来s行,每行是2个用空格隔开的整数x、y,描述了一个炮塔的位置。当然,蚂蚁窝的洞口以及蛋糕所在的位置上一定没有炮塔。

最后一行是一个正整数t,表示我们模拟游戏的前t秒钟。

Output Description

如果在第t秒或之前蚂蚁抢到了蛋糕,输出一行“Game over after x seconds”,其中x为游戏结束的时间,否则输出“The game is going on”。

如果游戏在t秒或之前结束,输出游戏结束时所有蚂蚁的信息,否则输出t秒后所有蚂蚁的信息。格式如下:

第一行是1个整数s,表示此时活着的蚂蚁的总数。

接下来s行,每行5个整数,依次表示一只蚂蚁的年龄(单位为秒)、等级、当前血量,以及在地图上的位置(a,b)。输出按蚂蚁的年龄递减排序。

Sample

INPUT

3 5
1 1 2
2 2
5

OUTPUT

The game is going on
5
5 1 3 1 4
4 1 3 0 4
3 1 3 0 3
2 1 3 0 2
1 1 4 0 1

Hint

样例说明:

3*5的地图,有1个单次伤害为1、攻击范围为2的激光炮塔,它的位置为(2,2),模拟游戏的前5秒。5秒内有5只蚂蚁出生,都是向东爬行,其中第14只在路过(0,2)点时被激光塔伤了1格血。在第5秒的时候,最早出生的蚂蚁按移动规则13本来该向东移动,但由于规则4的作用,它在发现向北和向西移动都会到达不可达点后,最终选择了向南移动。

数据说明:

100%的数据满足1 ≤ n,m ≤ 8,s ≤ 20,t ≤ 200,000

Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define CANNON 1
#define ANT 2
//#define DEBUG 0
struct antdata{
    int age;
    int id;//蚂蚁的id,从1开始不重复,往上递增,为0表示不存在 
    int rank;//等级 
    int health;//血量 
    int x,y;
    int lastx,lasty;
    int cake;//0或1,是否有蛋糕 
}ant[6]={}; 
struct cannondata{
    int x,y;
};
int time=0;
int cake=-1;//记录蛋糕在哪只蚂蚁身上0-5 
int antid=0;//记录蚂蚁出生的顺序 
int antnum=0;//记录当前场上的蚂蚁数量 
int **map,**info;//记录地图上的蚂蚁/炮台,信息素 
int n,m;
int cnum,churt,cdistance;//炮台cannon数,炮台伤害,炮台攻击范围
struct cannondata *cannon;
int direction[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//右下左上(东南西北,顺时针) 
int directionavailable[4]={};//记录可前进的方向 
void initialize(){
    int i,j;
    for(i=0;i<=n;i++) for(j=0;j<=m;j++) {info[i][j]=0; map[i][j]=0;}
}
void MakeAnt(){//不满6只则生成蚂蚁 
    int i;
    if(antnum<6 && map[0][0]==0){
        //for(i=0;i<6;i++) 
            i=antnum;
            //if(ant[i].id==0){
                antid++;
                ant[i].id=antid;
                ant[i].x=0;
                ant[i].y=0;
                ant[i].lastx=0;
                ant[i].lasty=0;
                ant[i].age=0;
                ant[i].rank=(antid-1)/6+1;
                ant[i].health=floor(4*pow(1.1,ant[i].rank));
                ant[i].cake=0;
            //}
            antnum++;
            map[0][0]=ANT+ant[i].id;
    }
}
void DeleteAnt(int i){//i为0-5
    int j;
    for(j=i+1;j<antnum;j++){//后面的前移 
        ant[j-1]=ant[j];
    }
    antnum--; 
    for(j=antnum;j<6;j++){//置零表示不存在 
        ant[j].id=0;
        
    }    
}
void GiveInfo(){//增加信息素 
    int i;
    for(i=0;i<6;i++) 
        if(ant[i].id!=0)
            if(ant[i].cake)
                info[ant[i].x][ant[i].y]+=5;
            else 
                info[ant[i].x][ant[i].y]+=2;
}
void LostInfo(){//失去信息素 
    int i,j;
    for(i=0;i<=n;i++) for(j=0;j<=m;j++) if(info[i][j]) info[i][j]--;
}
void PutAnt(int i,int x,int y){//放置蚂蚁并在地图上标记 
    ant[i].lastx=ant[i].x;
    ant[i].lasty=ant[i].y;
    map[ant[i].x][ant[i].y]=0;
#ifdef DEBUG 
    printf("AntID:%d from (%d,%d) to (%d,%d), it's time/Rank/Health is %d/%d/%d\n",ant[i].id,ant[i].x,ant[i].y,x,y,ant[i].age+1,ant[i].rank,ant[i].health);
#endif
    ant[i].x=x;
    ant[i].y=y;
    map[x][y]=ANT+ant[i].id;    
}
int SelectDirection(int i){//返回一个方向的序号,不能移动返回-1 
    int j,tx,ty,anum=0,a,amaxnum=0;
    memset(directionavailable,0,sizeof(directionavailable));
    for(j=0;j<4;j++){
        tx=ant[i].x+direction[j][0];
        ty=ant[i].y+direction[j][1];
        if(tx==ant[i].lastx && ty==ant[i].lasty) continue;
        if(tx<0 || ty<0 || tx>n || ty>m) continue;
        if(map[tx][ty]) continue;
        directionavailable[j]=1;//置为可达 
        a=j;
        anum++;//可达的数量加1 
    }
    if(anum==0) return -1; 
    if(anum==1) return a; 
    //此时有多个可达点 
    int max=-1;
    for(j=0;j<4;j++){
        if(directionavailable[j]){
            tx=ant[i].x+direction[j][0];
            ty=ant[i].y+direction[j][1];
            directionavailable[j]=info[tx][ty]+1;//将可达标志改为该方向前进一格的信息素+1,+1为了将信息素为0的与不可达点区分 
            if(directionavailable[j]>max) max=directionavailable[j];//更新最大值 
        }
    }
    for(j=0;j<4;j++){//该循环将信息素不是最大值的置零 (后来发现题意不是如此,不是最大值的仍是可达点,不该置零,这里做特殊标记-1) 
        if(directionavailable[j]){//是可达点
            if(directionavailable[j]<max){//但不是信息素最大的 
                directionavailable[j]=-1;
                //anum--;
            }
            else{//是最大值 
                a=j;// 记下最大值的下标,如果有多个,a将是最后一个最大值 
                amaxnum++;//最大值方向的数量 
            }
        }
    }
    if(amaxnum!=1){//有多个最大值     
        a=0;//东
        while(directionavailable[a]<=0){//该方向不是最大值的方向(-1),或不可达(0) 
             a++;//顺时针转90度 
        }
    }
    //在多个可达点时才考虑年龄为5的倍数的情况,因为只有1个方向可达时,逆时针多次后最终仍是该方向 
    if((ant[i].age+1)%5==0){
        do{
             a--;//逆时针转90度 
             if(a<0) a+=4;
        }while(directionavailable[a]==0);//该方向不可达 
    }
    return a;
} 
void Move(){
    int i,tx,ty,d;
    for(i=0;i<antnum;i++){
        //if(ant[i].id==0) break;//后面都是空的 
        d=SelectDirection(i); 
        if(d==-1) PutAnt(i,ant[i].x,ant[i].y);
        else {
            tx=ant[i].x+direction[d][0];
            ty=ant[i].y+direction[d][1];
            PutAnt(i,tx,ty);
        }
        if(ant[i].x==n && ant[i].y==m && cake==-1){//到达蛋糕位置 
            ant[i].cake=1; 
            cake=i;
            ant[i].health+=floor(floor(4*pow(1.1,ant[i].rank))/2);
            if(ant[i].health>(int)floor(4*pow(1.1,ant[i].rank)))
                ant[i].health=(int)floor(4*pow(1.1,ant[i].rank));
        }
    } 
}
int GetDistance2(int x1,int y1,int x2,int y2){//为了避免浮点数运算,这里计算距离的平方 
    return (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1);
}
int VectorProduct2(int x1,int y1,int x2,int y2){//两向量矢积的平方 
    return (x1*y2-x2*y1)*(x1*y2-x2*y1);
}
int Mult(int x1,int y1,int x2,int y2){//两向量数量积 
    return x1*x2+y1*y2;
}
int IsInAttackArea(int x1,int y1,int x2,int y2,int x3,int y3){
    //判断以3点为圆心,半径为0.5的圆和1,2点连成的线段有无公共点(后来才注意蚂蚁的直径是1,不是半径...) 
    int tmp,tmp2;
    tmp2=(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1);
    tmp=Mult(x3-x1,y3-y1,x2-x1,y2-y1);
    if(tmp<0 || tmp>tmp2){//printf("----------s1---------\n"); 
        return 0;
    }
    tmp=VectorProduct2(x3-x1,y3-y1,x2-x1,y2-y1);
    if(4*tmp>tmp2) {//printf("----------s2---------\n"); 
        return 0;
    }
    return 1;
}
void Attack(){//所有炮塔是一起攻击的,攻击结束后才判断蚂蚁生死 
    int i,j;
    for(j=0;j<cnum;j++){
        int imin=-1,target=-1;
        int d2min=9999999,d2;
        if(cake!=-1 && GetDistance2(ant[cake].x,ant[cake].y,cannon[j].x,cannon[j].y)<=cdistance*cdistance){
            target=cake;
        }
        else{
            for(i=0;i<antnum;i++){
                //if(ant[i].id==0) break;//后面都是空的 
                d2=GetDistance2(ant[i].x,ant[i].y,cannon[j].x,cannon[j].y);
                if(d2<d2min) {d2min=d2;imin=i;}
            }
            target=imin;
            if(GetDistance2(ant[target].x,ant[target].y,cannon[j].x,cannon[j].y)>cdistance*cdistance) continue;//最近的都打不到 
            
        }
        ant[target].health-=churt;
#ifdef DEBUG
        printf("Targer:%d,TargetAntId:%d\n",target,ant[target].id);
#endif
        for(i=0;i<antnum;i++){
            if(i==target) continue;
            if(IsInAttackArea(ant[target].x,ant[target].y,cannon[j].x,cannon[j].y,ant[i].x,ant[i].y)){
                ant[i].health-=churt;
#ifdef DEBUG
                printf("!!!!!!!!!!!!!!!!!ExtraTargetAntId:%d!!!!!!!!!!!!!!!!!!!\n",ant[i].id);
                //system("pause");
#endif
            } 
        } 
    }
}
void Judge1(){
    int i;
    for(i=0;i<antnum;i++){
        if(ant[i].health<0){
#ifdef DEBUG
            printf("AntID:%d died.\n",ant[i].id);
#endif
            map[ant[i].x][ant[i].y]=0;
            if(ant[i].cake) cake=-1;//蛋糕归位
            else if(cake!=-1) if(cake>i) cake--; 
            DeleteAnt(i);
            i--;
        }
    }
}
int Judge2(){
    if(cake!=-1) if(ant[cake].x==0 && ant[cake].y==0) return 1;
    return 0;
}
void AgeAdd(){
    int i;
    for(i=0;i<antnum;i++) ant[i].age++;
}
//-------------调试-----------

int GetHealthById(int id){
    int i;
    for(i=0;i<antnum;i++) if(ant[i].id==id) return ant[i].health;
    return -1;
} 
void PFSTATE(){
    int i,j;

    for(i=0;i<=n;i++){
        for(j=0;j<=m;j++){
            printf("[%3d]",info[i][j]);
            if(map[i][j]==CANNON) printf(" CANNON ");
            else if(map[i][j]>ANT) printf("%4d(%2d)",map[i][j]-ANT,GetHealthById(map[i][j]-ANT));
            else printf("|------|");
        }
        printf("\n");
    }
    printf("Cake:%d,CakeAntId:%d,ant[cake].cake:%d\n",cake,ant[cake].id,ant[cake].cake);
    printf("=====================time: %d=====================\n",time);
} 

//---------------------------
int Clock(){//返回1表示结束 
    time++; 
    MakeAnt(); 
    GiveInfo(); 
    Move(); 
    Attack();
    Judge1(); 
    if(Judge2()) return 1;
    AgeAdd();
    LostInfo();
#ifdef DEBUG
    PFSTATE();
#endif
    return 0;
}
int main(){
    int i,tx,ty,runtime,isend=0;
    scanf("%d%d",&n,&m);
    map=(int **)malloc(sizeof(int *)*(n+1));
    info=(int **)malloc(sizeof(int *)*(n+1));
    for(i=0;i<=n;i++){
        map[i]=(int *)malloc(sizeof(int)*(m+1));
        info[i]=(int *)malloc(sizeof(int *)*(m+1));
    }
    initialize();
    scanf("%d%d%d",&cnum,&churt,&cdistance);
    cannon=(struct cannondata *)malloc(cnum*sizeof(struct cannondata));
    for(i=0;i<cnum;i++){
        scanf("%d%d",&tx,&ty);
        map[tx][ty]=CANNON;
        cannon[i].x=tx;
        cannon[i].y=ty;
    }
    scanf("%d",&runtime);
    for(i=0;i<runtime;i++){
        if(Clock()){
            printf("Game over after %d seconds\n",time);
            isend=1;
            break;
        }
    }
    if(!isend) printf("The game is going on\n");
    printf("%d\n",antnum);
    for(i=0;i<antnum;i++){
        printf("%d %d %d %d %d\n",ant[i].age,ant[i].rank,ant[i].health,ant[i].x,ant[i].y);
    }
    return 0;     
}