蚁群算法(C语言实现)

蚁群算法(ant colony optimization, ACO),又称蚂蚁算法,是一种用来在图中寻找优化路径的机率型算法。它由Marco Dorigo于1992年在他的博士论文中提出,其灵感来源于蚂蚁在寻找食物过程中发现路径的行为。蚁群算法是一种模拟进化算法,初步的研究表明该算法具有许多优良的性质.针对PID控制器参数优化设计问题,将蚁群算法设计的结果与遗传算法设计的结果进行了比较,数值仿真结果表明,蚁群算法具有一种新的模拟进化优化方法的有效性和应用价值。

预期的结果:

  各个蚂蚁在没有事先告诉他们食物在什么地方的前提下开始寻找食物。当一只找到食物以后,它会向环境释放一种信息素,吸引其他的蚂蚁过来,这样越来越多的蚂蚁会找到食物!有些蚂蚁并没有象其它蚂蚁一样总重复同样的路,他们会另辟蹊径,如果令开辟的道路比原来的其他道路更短,那么,渐渐,更多的蚂蚁被吸引到这条较短的路上来。最后,经过一段时间运行,可能会出现一条最短的路径被大多数蚂蚁重复着。

原理:

  为什么小小的蚂蚁能够找到食物?他们具有智能么?设想,如果我们要为蚂蚁设计一个人工智能的程序,那么这个程序要多么复杂呢?首先,你要让蚂蚁能够避开障碍物,就必须根据适当的地形给它编进指令让他们能够巧妙的避开障碍物,其次,要让蚂蚁找到食物,就需要让他们遍历空间上的所有点;再次,如果要让蚂蚁找到最短的路径,那么需要计算所有可能的路径并且比较它们的大小,而且更重要的是,你要小心翼翼的编程,因为程序的错误也许会让你前功尽弃。这是多么不可思议的程序!太复杂了,恐怕没人能够完成这样繁琐冗余的程序。

  然而,事实并没有你想得那么复杂,上面这个程序每个蚂蚁的核心程序编码不过100多行!为什么这么简单的程序会让蚂蚁干这样复杂的事情?答案是:简单规则的涌现。事实上,每只蚂蚁并不是像我们想象的需要知道整个世界的信息,他们其实只关心很小范围内的眼前信息,而且根据这些局部信息利用几条简单的规则进行决策,这样,在蚁群这个集体里,复杂性的行为就会凸现出来。这就是人工生命、复杂性科学解释的规律!那么,这些简单规则是什么呢?

下面详细说明:

1、范围:

  蚂蚁观察到的范围是一个方格世界,蚂蚁有一个参数为速度半径(一般是3),那么它能观察到的范围就是3*3个方格世界,并且能移动的距离也在这个范围之内。

2、环境:

  蚂蚁所在的环境是一个虚拟的世界,其中有障碍物,有别的蚂蚁,还有信息素,信息素有两种,一种是找到食物的蚂蚁洒下的食物信息素,一种是找到窝的蚂蚁洒下的窝的信息素。每个蚂蚁都仅仅能感知它范围内的环境信息。环境以一定的速率让信息素消失。

3、觅食规则:

  在每只蚂蚁能感知的范围内寻找是否有食物,如果有就直接过去。否则看是否有信息素,并且比较在能感知的范围内哪一点的信息素最多,这样,它就朝信息素多的地方走,并且每只蚂蚁都会以小概率犯错误,从而并不是往信息素最多的点移动。蚂蚁找窝的规则和上面一样,只不过它对窝的信息素做出反应,而对食物信息素没反应。

4、移动规则:

  每只蚂蚁都朝向信息素最多的方向移,并且,当周围没有信息素指引的时候,蚂蚁会按照自己原来运动的方向惯性的运动下去,并且,在运动的方向有一个随机的小的扰动。为了防止蚂蚁原地转圈,它会记住最近刚走过了哪些点,如果发现要走的下一点已经在最近走过了,它就会尽量避开。

5、避障规则:

  如果蚂蚁要移动的方向有障碍物挡住,它会随机的选择另一个方向,并且有信息素指引的话,它会按照觅食的规则行为。

6、播撒信息素规则:

  每只蚂蚁在刚找到食物或者窝的时候撒发的信息素最多,并随着它走远的距离,播撒的信息素越来越少。

 

  根据这几条规则,蚂蚁之间并没有直接的关系,但是每只蚂蚁都和环境发生交互,而通过信息素这个纽带,实际上把各个蚂蚁之间关联起来了。比如,当一只蚂蚁找到了食物,它并没有直接告诉其它蚂蚁这儿有食物,而是向环境播撒信息素,当其它的蚂蚁经过它附近的时候,就会感觉到信息素的存在,进而根据信息素的指引找到了食

问题:

  说了这么多,蚂蚁究竟是怎么找到食物的呢?? 在没有蚂蚁找到食物的时候,环境没有有用的信息素,那么蚂蚁为什么会相对有效的找到食物呢?这要归功于蚂蚁的移动规则,尤其是在没有信息素时候的移动规则。首先,它要能尽量保持某种惯性,这样使得蚂蚁尽量向前方移动(开始,这个前方是随机固定的一个方向),而不是原地无谓的打转或者震动;其次,蚂蚁要有一定的随机性,虽然有了固定的方向,但它也不能像粒子一样直线运动下去,而是有一个随机的干扰。这样就使得蚂蚁运动起来具有了一定的目的性,尽量保持原来的方向,但又有新的试探,尤其当碰到障碍物的时候它会立即改变方向,这可以看成一种选择的过程,也就是环境的障碍物让蚂蚁的某个方向正确,而其他方向则不对。这就解释了为什么单个蚂蚁在复杂的诸如迷宫的地图中仍然能找到隐蔽得很好的食物。

  当然,在有一只蚂蚁找到了食物的时候,大部分蚂蚁会沿着信息素很快找到食物的。但不排除会出现这样的情况:在最初的时候,一部分蚂蚁通过随机选择了同一条路径,随着这条路径上蚂蚁释放的信息素越来越多,更多的蚂蚁也选择这条路径,但这条路径并不是最优(即最短)的,所以,导致了迭代次数完成后,蚂蚁找到的不是最优解,而是次优解,这种情况下的结果可能对实际应用的意义就不大了。

  蚂蚁如何找到最短路径的?这一是要归功于信息素,另外要归功于环境,具体说是计算机时钟。信息素多的地方显然经过这里的蚂蚁会多,因而会有更多的蚂蚁聚集过来。假设有两条路从窝通向食物,开始的时候,走这两条路的蚂蚁数量同样多(或者较长的路上蚂蚁多,这也无关紧要)。当蚂蚁沿着一条路到达终点以后会马上返回来,这样,短的路蚂蚁来回一次的时间就短,这也意味着重复的频率就快,因而在单位时间里走过的蚂蚁数目就多,洒下的信息素自然也会多,自然会有更多的蚂蚁被吸引过来,从而洒下更多的信息素……;而长的路正相反,因此,越来越多地蚂蚁聚集到较短的路径上来,最短的路径就近似找到了。也许有人会问局部最短路径和全局最短路的问题,实际上蚂蚁逐渐接近全局最短路的,为什么呢?这源于蚂蚁会犯错误,也就是它会按照一定的概率不往信息素高的地方走而另辟蹊径,这可以理解为一种创新,这种创新如果能缩短路途,那么根据刚才叙述的原理,更多的蚂蚁会被吸引过来。

/*ant.c*/

#define SPACE 0x20                /*按键定义*/
#define ESC 0x1b
#define ANT_CHAR_EMPTY '+'
#define ANT_CHAR_FOOD 153            /*携带食物的蚂蚁*/
#define HOME_CHAR 'H'
#define FOOD_CHAR 'F'
#define FOOD_CHAR2 'f'
#define FOOD_HOME_COLOR 12            /*红色*/
#define BLOCK_CHAR 177            /*障碍物*/

#define MAX_ANT 50                /*蚂蚁数量*/
#define INI_SPEED 3                /*速度半径为3*3*/
#define MAXX 80                /*活动空间为80*23格*/
#define MAXY 23
#define MAX_FOOD 10000            /*最大食物量*/
#define TARGET_FOOD 200            /*需要采集回家的食物量*/
#define MAX_SMELL 5000            /*最大信息素*/
#define SMELL_DROP_RATE 0.05        /*信息素释放率*/
#define ANT_ERROR_RATE 0.02            /*蚂蚁犯错率(创新率)*/
#define ANT_EYESHOT 3            /**/
#define SMELL_GONE_SPEED 50            /*信息素消失速度*/
#define SMELL_GONE_RATE 0.05        /*信息素消失比率*/
#define TRACE_REMEMBER 50            /*蚂蚁记忆力*/
#define MAX_BLOCK 100            /*最大障碍物个数*/

#define NULL 0
#define UP 1                    /*方向定义*/
#define DOWN 2
#define LEFT 3
#define RIGHT 4
#define SMELL_TYPE_FOOD 0            /*信息素类型定义*/
#define SMELL_TYPE_HOME 1

#include "stdio.h"
#include "conio.h"                /*getch函数需要此头文件*/
#include "dos.h"
#include "stdlib.h"
#include "dos.h"
#include "process.h"
#include "ctype.h"
#include "math.h"

void WorldInitial(void);                    /*环境初始化函数*/
void BlockInitial(void);                    /*障碍物地图初始化*/
void CreatBlock(void);                    /*产生障碍物地图*/
void SaveBlock(void);                    /*保存障碍物地图*/
void LoadBlock(void);                    /*载入障碍物地图*/
void HomeFoodInitial(void);                    /*食物与家的位置初始化*/
void AntInitial(void);                    /*蚂蚁初始化*/
void WorldChange(void);                    /*更改环境*/
void AntMove(void);                        /*蚂蚁移动*/
void AntOneStep(void);                    /*蚂蚁动作一步*/
void DealKey(char key);                    /*按键扫描,功能键有p,t,1,2,3,s,l*/
void ClearSmellDisp(void);                    /*关闭信息素的显示*/
void DispSmell(int type);                    /*显示当前信息素的情况*/
int AntNextDir(int xxx,int yyy,int ddir);            /*得到蚂蚁下一次移动的方向*/
int GetMaxSmell(int type,int xxx,int yyy,int ddir);    /*获取最大信息素的值*/
int IsTrace(int xxx,int yyy);                /*是否是曾经走过的路径*/
int MaxLocation(int num1,int num2,int num3);        /*获得三个值中最大的一个的序号*/
int CanGo(int xxx,int yyy,int ddir);            /*返回可以走的路径*/
int JudgeCanGo(int xxx,int yyy);                /*判断某方向是否可以通过*/
int TurnLeft(int ddir);                    /*左传,右转,后退*/
int TurnRight(int ddir);
int TurnBack(int ddir);

int MainTimer(void);                        /*返回自上次调用后经历了多少个10ms的时间*/
char WaitForKey(int secnum);                /*没有键则等待键盘输入,有键则返回键值*/
void DispPlayTime(void);                    /*显示运行时间*/
int TimeUse(void);                        /*计算时间花费*/
void HideCur(void);                        /*隐藏鼠标*/
void ResetCur(void);                        /*重置鼠标*/

/* ---------------  */
struct HomeStruct
{
  int xxx,yyy;
  int amount;                            /*已经搬运回家的食物数量*/
  int TargetFood;                        /*需要搬运回家的食物数量*/
}home;

struct FoodStruct
{
  int xxx,yyy;
  int amount;                            /*剩余食物数量*/
}food;

struct AntStruct
{
  int xxx,yyy;                        /*蚂蚁当前位置*/
  int dir;                            /*行走方向*/
  int speed;                            /*蚂蚁速度,即计数器计到此则移动一步,所以越小蚂蚁移动越快*/
  int SpeedTimer;                        /*速度计数器,每10ms记一次*/
  int food;                            /*是否携带食物*/
  int SmellAmount[2];                    /*两种信息素的含量*/
  int tracex[TRACE_REMEMBER];                /*所记忆的x坐标*/
  int tracey[TRACE_REMEMBER];                /*所记忆的y坐标*/
  int TracePtr;                        /*记录路径所用的指针*/
  int IQ;                            /*好象没有用到。。。。。*/
}ant[MAX_ANT];

/*全局变量定义*/
int AntNow;                            /*当前蚂蚁*/
int timer10ms;                        /*记录多少个10ms已经过去*/
struct time starttime,endtime;                /*起始结束时间定义*/
int Smell[2][MAXX+1][MAXY+1];                /*信息素数组*/
int block[MAXX+1][MAXY+1];                    /*障碍物数组*/
int SmellGoneTimer;                        /*信息素消失计数器*/
int SmellDispFlag;                        /*信息素显示标志*/
int CanFindFood;                        /*可以找到获取食物的路径标志*/
int HardtoFindPath;                        /*找到路径比较困难的标志*/

/* ----- Main -------- */
void main(void)
{
  char KeyPress;
  int tu;

  clrscr();
  HideCur();
  WorldInitial();
  do
  {
    timer10ms = MainTimer();
    if(timer10ms) AntMove();
    if(timer10ms) WorldChange();
    tu = TimeUse();
    if(tu>=60&&!CanFindFood)
    {
      gotoxy(1,MAXY+1);
      printf("Can not find food, maybe a block world.");
      WaitForKey(10);
      WorldInitial();
    }
    if(tu>=180&&home.amount<100&&!HardtoFindPath)
    {
      gotoxy(1,MAXY+1);
      printf("God! it is so difficult to find a path.");
      if(WaitForKey(10)==0x0d) WorldInitial();
      else
     {
        HardtoFindPath = 1;
        gotoxy(1,MAXY+1);
        printf("                     ");
     }
    }
    if(home.amount>=home.TargetFood)
    {
      gettime(&endtime);
      KeyPress = WaitForKey(60);
      DispPlayTime();
      WaitForKey(10);
      WorldInitial();
    }
    else if(kbhit())
    {
      KeyPress = getch();
      DealKey(KeyPress);
    }
    else KeyPress = NULL;
  }
  while(KeyPress!=ESC);
  gettime(&endtime);
  DispPlayTime();
  WaitForKey(10);
  clrscr();
  ResetCur();
}

/* ------ general sub process ----------- */
int MainTimer(void)
/* output: how much 10ms have pass from last time call this process */
{
  static int oldhund,oldsec;
  struct  time t;
  int timeuse;

  gettime(&t);
  timeuse = 0;
  if(t.ti_hund!=oldhund)
  {
    if(t.ti_sec!=oldsec)
    {
      timeuse+=100;
      oldsec = t.ti_sec;
    }
    timeuse+=t.ti_hund-oldhund;
    oldhund = t.ti_hund;
  }
  else timeuse = 0;
  return (timeuse);
}

char WaitForKey(int secnum)
/* funtion: if have key in, exit immediately, else wait 'secnum' senconds then exit
   input: secnum -- wait this senconds, must < 3600 (1 hour)
   output: key char, if no key in(exit when timeout), return NULL */
{
  int secin,secnow;
  int minin,minnow;
  int hourin,hournow;
  int secuse;
  struct  time t;

  gettime(&t);
  secin = t.ti_sec;
  minin = t.ti_min;
  hourin = t.ti_hour;

  do
  {
    if(kbhit()) return(getch());
    gettime(&t);
    secnow = t.ti_sec;
    minnow = t.ti_min;
    hournow = t.ti_hour;

    if(hournow!=hourin) minnow+=60;
    if(minnow>minin) secuse = (minnow-1-minin) + (secnow+60-secin);
    else secuse = secnow - secin;

    /* counting error check */
    if(secuse<0)
    {
      gotoxy(1,MAXY+1);
      printf("Time conuting error, any keyto exit...");
      getch();
      exit(3);
    }
  }
  while(secuse<=secnum);
  return (NULL);
}

void DispPlayTime(void)
{
  int ph,pm,ps;

  ph = endtime.ti_hour - starttime.ti_hour;
  pm = endtime.ti_min - starttime.ti_min;
  ps = endtime.ti_sec - starttime.ti_sec;

  if(ph<0) ph+=24;
  if(pm<0) { ph--; pm+=60; }
  if(ps<0) { pm--; ps+=60; }

  gotoxy(1,MAXY+1);
  printf("Time use: %d hour- %d min- %d sec ",ph,pm,ps);
}

int TimeUse(void)
{
  int ph,pm,ps;

  gettime(&endtime);
  ph = endtime.ti_hour - starttime.ti_hour;
  pm = endtime.ti_min - starttime.ti_min;
  ps = endtime.ti_sec - starttime.ti_sec;

  if(ph<0) ph+=24;
  if(pm<0) { ph--; pm+=60; }
  if(ps<0) { pm--; ps+=60; }

  return(ps+(60*(pm+60*ph)));
}

void HideCur(void)
{
  union REGS regs0;
  regs0.h.ah=1;
  regs0.h.ch=0x30;
  regs0.h.cl=0x31;
  int86(0x10,®s0,®s0);
}

void ResetCur(void)
{
  union REGS regs0;
  regs0.h.ah=1;
  regs0.h.ch=0x06;
  regs0.h.cl=0x07;
  int86(0x10,®s0,®s0);
}

/* ------------ main ANT programe ------------- */
void WorldInitial(void)
{
  int k,i,j;
  randomize();
  clrscr();
  HomeFoodInitial();
  for(AntNow=0;AntNow<MAX_ANT;AntNow++)
  {
    AntInitial();
  } /* of for AntNow */;

  BlockInitial();
  for(k=0;k<=1;k++)
  /* SMELL TYPE FOOD and HOME */
    for(i=0;i<=MAXX;i++)
      for(j=0;j<=MAXY;j++)
        Smell[k][i][j] = 0;
  SmellGoneTimer = 0;
  gettime(&starttime);
  SmellDispFlag = 0;
  CanFindFood = 0;
  HardtoFindPath = 0;
}

void BlockInitial(void)
{
  int i,j;
  int bn;

  for(i=0;i<=MAXX;i++)
    for(j=0;j<=MAXY;j++)
      block[i][j] = 0;

  bn = 1+ MAX_BLOCK/2 + random(MAX_BLOCK/2);
  for(i=0;i<=bn;i++) CreatBlock();
}

void CreatBlock(void)
{
  int x1,y1,x2,y2;
  int dx,dy;
  int i,j;

  x1 = random(MAXX)+1;
  y1 = random(MAXY)+1;

  dx = random(MAXX/10)+1;
  dy = random(MAXY/10)+1;

  x2 = x1+dx;
  y2 = y1+dy;

  if(x2>MAXX) x2 = MAXX;
  if(y2>MAXY) y2 = MAXY;

  if(food.xxx>=x1&&food.xxx<=x2&&food.yyy>=y1&&food.yyy<=y2) return;
  if(home.xxx>=x1&&home.xxx<=x2&&home.yyy>=y1&&home.yyy<=y2) return;

  for(i=x1;i<=x2;i++)
    for(j=y1;j<=y2;j++)
    {
      block[i][j] = 1;
      gotoxy(i,j);
      putch(BLOCK_CHAR);
    }
}

void SaveBlock(void)
{
 FILE *fp_block;
 char FileNameBlock[20];
 int i,j;

 gotoxy(1,MAXY+1);
  printf("                     ");
 gotoxy(1,MAXY+1);
 printf("Save to file...",FileNameBlock);
 gets(FileNameBlock);
 if(FileNameBlock[0]==0) strcpy(FileNameBlock,"Ant.ant");
 else strcat(FileNameBlock,".ant");

 if ((fp_block = fopen(FileNameBlock, "wb")) == NULL)
 { gotoxy(1,MAXY+1);
    printf("Creat file %s fail...",FileNameBlock);
  getch();
  exit(2);
 }
 gotoxy(1,MAXY+1);
  printf("                           ");

 fputc(home.xxx,fp_block);
 fputc(home.yyy,fp_block);
 fputc(food.xxx,fp_block);
 fputc(food.yyy,fp_block);

 for(i=0;i<=MAXX;i++)
    for(j=0;j<=MAXY;j++)
      fputc(block[i][j],fp_block);

  fclose(fp_block);
}

void LoadBlock(void)
{
 FILE *fp_block;
 char FileNameBlock[20];
 int i,j,k;

 gotoxy(1,MAXY+1);
  printf("                     ");
 gotoxy(1,MAXY+1);
 printf("Load file...",FileNameBlock);
 gets(FileNameBlock);
 if(FileNameBlock[0]==0) strcpy(FileNameBlock,"Ant.ant");
 else strcat(FileNameBlock,".ant");

 if ((fp_block = fopen(FileNameBlock, "rb")) == NULL)
 { gotoxy(1,MAXY+1);
    printf("Open file %s fail...",FileNameBlock);
  getch();
  exit(2);
 }

 clrscr();
 home.xxx = fgetc(fp_block);
 home.yyy = fgetc(fp_block);
 food.xxx = fgetc(fp_block);
 food.yyy = fgetc(fp_block);
 gotoxy(home.xxx,home.yyy); putch(HOME_CHAR);
  gotoxy(food.xxx,food.yyy); putch(FOOD_CHAR);
  food.amount = random(MAX_FOOD/3)+2*MAX_FOOD/3+1;
  /* food.amount = MAX_FOOD; */
  home.amount = 0;
  home.TargetFood =
    (food.amount<TARGET_FOOD)?food.amount:TARGET_FOOD;

 for(AntNow=0;AntNow<MAX_ANT;AntNow++)
  {
    AntInitial();
  } /* of for AntNow */;

 for(i=0;i<=MAXX;i++)
    for(j=0;j<=MAXY;j++)
    {
      block[i][j] = fgetc(fp_block);
      if(block[i][j])
      {
       gotoxy(i,j);
       putch(BLOCK_CHAR);
     }
    }

  for(k=0;k<=1;k++)
  /* SMELL TYPE FOOD and HOME */
    for(i=0;i<=MAXX;i++)
      for(j=0;j<=MAXY;j++)
        Smell[k][i][j] = 0;
  SmellGoneTimer = 0;
  gettime(&starttime);
  SmellDispFlag = 0;
  CanFindFood = 0;
  HardtoFindPath = 0;

  fclose(fp_block);
}

void HomeFoodInitial(void)
{
  int randnum;
  int homeplace;
  /* 1 -- home at left-up, food at right-down
 -- home at left-down, food at right-up
 -- home at right-up, food at left-down
 -- home at right-down, food at left-up */

  randnum = random(100);
  if(randnum<25) homeplace = 1;
  else if (randnum>=25&&randnum<50) homeplace = 2;
  else if (randnum>=50&&randnum<75) homeplace = 3;
  else homeplace = 4;

  switch(homeplace)
  {
    case 1: home.xxx = random(MAXX/3)+1;
        home.yyy = random(MAXY/3)+1;
        food.xxx = random(MAXX/3)+2*MAXX/3+1;
        food.yyy = random(MAXY/3)+2*MAXY/3+1;
        break;
    case 2: home.xxx = random(MAXX/3)+1;
        home.yyy = random(MAXY/3)+2*MAXY/3+1;
        food.xxx = random(MAXX/3)+2*MAXX/3+1;
        food.yyy = random(MAXY/3)+1;
        break;
    case 3: home.xxx = random(MAXX/3)+2*MAXX/3+1;
        home.yyy = random(MAXY/3)+1;
        food.xxx = random(MAXX/3)+1;
        food.yyy = random(MAXY/3)+2*MAXY/3+1;
        break;
    case 4: home.xxx = random(MAXX/3)+2*MAXX/3+1;
        home.yyy = random(MAXY/3)+2*MAXY/3+1;
        food.xxx = random(MAXX/3)+1;
        food.yyy = random(MAXY/3)+1;
        break;
  }

  food.amount = random(MAX_FOOD/3)+2*MAX_FOOD/3+1;
  /* food.amount = MAX_FOOD; */
  home.amount = 0;
  home.TargetFood = (food.amount<TARGET_FOOD)?food.amount:TARGET_FOOD;

  /* data correctness check */
  if(home.xxx<=0||home.xxx>MAXX||home.yyy<=0||home.yyy>MAXY||
     food.xxx<=0||food.xxx>MAXX||food.yyy<=0||food.yyy>MAXY||
     food.amount<=0)
  {
    gotoxy(1,MAXY+1);
    printf("World initial fail, any key to exit...");
    getch();
    exit(2);
  }

  gotoxy(home.xxx,home.yyy); putch(HOME_CHAR);
  gotoxy(food.xxx,food.yyy); putch(FOOD_CHAR);
}void AntInitial(void)
/* initial ant[AntNow] */
{
  int randnum;
  int i;

  ant[AntNow].xxx = home.xxx;
  ant[AntNow].yyy = home.yyy;

  randnum = random(100);
  if(randnum<25) ant[AntNow].dir = UP;
  else if (randnum>=25&&randnum<50) ant[AntNow].dir = DOWN;
  else if (randnum>=50&&randnum<75) ant[AntNow].dir = LEFT;
  else ant[AntNow].dir = RIGHT;

  ant[AntNow].speed = 2*(random(INI_SPEED/2)+1);
  ant[AntNow].SpeedTimer = 0;
  ant[AntNow].food = 0;
  ant[AntNow].SmellAmount[SMELL_TYPE_FOOD] = 0;
  ant[AntNow].SmellAmount[SMELL_TYPE_HOME] = MAX_SMELL;
  ant[AntNow].IQ = 1;

  for(i=0;i<TRACE_REMEMBER;i++)
  {
    ant[AntNow].tracex[i] = 0;
    ant[AntNow].tracey[i] = 0;
  }
  ant[AntNow].TracePtr = 0;

  /* a sepecail ant */
  if(AntNow==0) ant[AntNow].speed = INI_SPEED;
}

void WorldChange(void)
{
  int k,i,j;
  int smelldisp;

  SmellGoneTimer+=timer10ms;
  if(SmellGoneTimer>=SMELL_GONE_SPEED)
  {
    SmellGoneTimer = 0;
    for(k=0;k<=1;k++)
    /* SMELL TYPE FOOD and HOME */
      for(i=1;i<=MAXX;i++)
        for(j=1;j<=MAXY;j++)
        {
            if(Smell[k][i][j])
          {
              smelldisp = 1+((10*Smell[k][i][j])/(MAX_SMELL*SMELL_DROP_RATE));
              if(smelldisp>=30000||smelldisp<0) smelldisp = 30000;
              if(SmellDispFlag)
           {
                gotoxy(i,j);
                if((i==food.xxx&&j==food.yyy)||(i==home.xxx&&j==home.yyy))
                  /* don't over write Food and Home */;
              else
             {
                  if(smelldisp>9) putch('#');
                  else putch(smelldisp+'0');
             }
           }
              Smell[k][i][j]-= 1+(Smell[k][i][j]*SMELL_GONE_RATE);
              if(Smell[k][i][j]<0) Smell[k][i][j] = 0;
              if(SmellDispFlag)
           {
                if(Smell[k][i][j]<=2)
             {
                  gotoxy(i,j);
                  putch(SPACE);
             }
           }
          }
          } /* of one location */
  } /* of time to change the world */
} /* of world change */

void AntMove(void)
{
  int antx,anty;
  int smelltodrop,smellnow;

  for(AntNow=0;AntNow<MAX_ANT;AntNow++)
  {
    ant[AntNow].SpeedTimer+=timer10ms;
    if(ant[AntNow].SpeedTimer>=ant[AntNow].speed)
    {
      ant[AntNow].SpeedTimer = 0;
      gotoxy(ant[AntNow].xxx,ant[AntNow].yyy);
      putch(SPACE);
      AntOneStep();
      gotoxy(ant[AntNow].xxx,ant[AntNow].yyy);
      /* ant0 is a sepecail ant, use different color */
      if(AntNow==0) textcolor(0xd);
      if(ant[AntNow].food) putch(ANT_CHAR_FOOD);
      else putch(ANT_CHAR_EMPTY);
      if(AntNow==0) textcolor(0x7);

      /* remember trace */
      ant[AntNow].tracex[ant[AntNow].TracePtr] = ant[AntNow].xxx;
      ant[AntNow].tracey[ant[AntNow].TracePtr] = ant[AntNow].yyy;
      if(++(ant[AntNow].TracePtr)>=TRACE_REMEMBER) ant[AntNow].TracePtr = 0;

      /* drop smell */
      antx = ant[AntNow].xxx;
      anty = ant[AntNow].yyy;

      if(ant[AntNow].food)
      /* have food, looking for home */
     {
        if(ant[AntNow].SmellAmount[SMELL_TYPE_FOOD])
       {
          smellnow = Smell[SMELL_TYPE_FOOD][antx][anty];
          smelltodrop = ant[AntNow].SmellAmount[SMELL_TYPE_FOOD]*SMELL_DROP_RATE;
          if(smelltodrop>smellnow) Smell[SMELL_TYPE_FOOD][antx][anty] = smelltodrop;
          /* else Smell[...] = smellnow */
          ant[AntNow].SmellAmount[SMELL_TYPE_FOOD]-= smelltodrop;
          if(ant[AntNow].SmellAmount[SMELL_TYPE_FOOD]<0) ant[AntNow].SmellAmount[SMELL_TYPE_FOOD] = 0;
        } /* of have smell to drop */
      } /* of have food */
      else
      /* no food, looking for food */
     {
        if(ant[AntNow].SmellAmount[SMELL_TYPE_HOME])
       {
          smellnow = Smell[SMELL_TYPE_HOME][antx][anty];
          smelltodrop = ant[AntNow].SmellAmount[SMELL_TYPE_HOME]*SMELL_DROP_RATE;
          if(smelltodrop>smellnow) Smell[SMELL_TYPE_HOME][antx][anty] = smelltodrop;
          /* else Smell[...] = smellnow */
          ant[AntNow].SmellAmount[SMELL_TYPE_HOME]-= smelltodrop;
          if(ant[AntNow].SmellAmount[SMELL_TYPE_HOME]<0) ant[AntNow].SmellAmount[SMELL_TYPE_HOME] = 0;
        } /* of have smell to drop */
     }
    } /* of time to go */
    /* else not go */
  } /* of for AntNow */

  textcolor(FOOD_HOME_COLOR);
  gotoxy(home.xxx,home.yyy); putch(HOME_CHAR);
  gotoxy(food.xxx,food.yyy);
  if(food.amount>0) putch(FOOD_CHAR);
  else putch(FOOD_CHAR2);
  textcolor(7);

  gotoxy(1,MAXY+1);
  printf("Food %d, Home %d   ",food.amount,home.amount);
}

void AntOneStep(void)
{
  int ddir,tttx,ttty;
  int i;

  ddir = ant[AntNow].dir;
  tttx = ant[AntNow].xxx;
  ttty = ant[AntNow].yyy;

  ddir = AntNextDir(tttx,ttty,ddir);

  switch(ddir)
  {
    case UP:  ttty--;
          break;
    case DOWN:  ttty++;
          break;
    case LEFT:  tttx--;
          break;
    case RIGHT: tttx++;
          break;
    default:  break;
  } /* of switch dir */

  ant[AntNow].dir = ddir;
  ant[AntNow].xxx = tttx;
  ant[AntNow].yyy = ttty;

  if(ant[AntNow].food)
  /* this ant carry with food, search for home */
  {
    if(tttx==home.xxx&&ttty==home.yyy)
    {
      home.amount++;
      AntInitial();
    }
    if(tttx==food.xxx&&ttty==food.yyy)
      ant[AntNow].SmellAmount[SMELL_TYPE_FOOD] = MAX_SMELL;
  } /* of search for home */
  else
  /* this ant is empty, search for food */
  {
    if(tttx==food.xxx&&ttty==food.yyy)
    {
      if(food.amount>0)
     {
        ant[AntNow].food = 1;
        food.amount--;
        ant[AntNow].SmellAmount[SMELL_TYPE_FOOD] = MAX_SMELL;
        ant[AntNow].SmellAmount[SMELL_TYPE_HOME] = 0;
        ant[AntNow].dir = TurnBack(ant[AntNow].dir);
        for(i=0;i<TRACE_REMEMBER;i++)
       {
          ant[AntNow].tracex[i] = 0;
          ant[AntNow].tracey[i] = 0;
       }
        ant[AntNow].TracePtr = 0;
        CanFindFood = 1;
      } /* of still have food */
    }
    if(tttx==home.xxx&&ttty==home.yyy)
      ant[AntNow].SmellAmount[SMELL_TYPE_HOME] = MAX_SMELL;
  }  /* of search for food */
}

void DealKey(char key)
{
  int i;
  switch(key)
  {
    case 'p':   gettime(&endtime);
          DispPlayTime();
          getch();
          gotoxy(1,MAXY+1);
          for(i=1;i<=MAXX-1;i++) putch(SPACE);
          break;
    case 't':   if(SmellDispFlag)
        {
            SmellDispFlag=0;
            ClearSmellDisp();
        }
          else SmellDispFlag = 1;
          break;
    case '1':   DispSmell(SMELL_TYPE_FOOD);
          getch();
          ClearSmellDisp();
          break;
    case '2':   DispSmell(SMELL_TYPE_HOME);
          getch();
          ClearSmellDisp();
          break;
    case '3':   DispSmell(2);
          getch();
          ClearSmellDisp();
          break;
    case 's':   SaveBlock();
       break;
    case 'l':   LoadBlock();
       break;
    default:  gotoxy(1,MAXY+1);
          for(i=1;i<=MAXX-1;i++) putch(SPACE);
  } /* of switch */
}

void ClearSmellDisp(void)
{
  int k,i,j;

  for(k=0;k<=1;k++)
  /* SMELL TYPE FOOD and HOME */
    for(i=1;i<=MAXX;i++)
      for(j=1;j<=MAXY;j++)
       {
          if(Smell[k][i][j])
        {
            gotoxy(i,j);
            putch(SPACE);
        }
        } /* of one location */
}

void DispSmell(int type)
/* input: 0 -- Only display food smell
 -- Only display home smell
 -- Display both food and home smell
*/
{
  int k,i,j;
  int fromk,tok;
  int smelldisp;

  switch(type)
  {
    case 0: fromk = 0;
        tok = 0;
        break;
    case 1: fromk = 1;
        tok = 1;
        break;
    case 2: fromk = 0;
        tok = 1;
        break;
    default:fromk = 0;
        tok = 1;
        break;
  }
  SmellGoneTimer = 0;
  for(k=fromk;k<=tok;k++)
  /* SMELL TYPE FOOD and HOME */
    for(i=1;i<=MAXX;i++)
      for(j=1;j<=MAXY;j++)
       {
          if(Smell[k][i][j])
        {
            smelldisp = 1+((10*Smell[k][i][j])/(MAX_SMELL*SMELL_DROP_RATE));
            if(smelldisp>=30000||smelldisp<0) smelldisp = 30000;
            gotoxy(i,j);
            if(i!=food.xxx||j!=food.yyy)
          {
              if((i==food.xxx&&j==food.yyy)||(i==home.xxx&&j==home.yyy))
                /* don't over write Food and Home */;
            else
           {
                if(smelldisp>9) putch('#');
                else putch(smelldisp+'0');
           }
          }
        }
        } /* of one location */
}

int AntNextDir(int xxx,int yyy,int ddir)
{
  int randnum;
  int testdir;
  int CanGoState;
  int cangof,cangol,cangor;
  int msf,msl,msr,maxms;
  int type;

  CanGoState = CanGo(xxx,yyy,ddir);
  if(CanGoState==0||CanGoState==2||CanGoState==3||CanGoState==6) cangof = 1;
  else cangof = 0;
  if(CanGoState==0||CanGoState==1||CanGoState==3||CanGoState==5) cangol = 1;
  else cangol = 0;
  if(CanGoState==0||CanGoState==1||CanGoState==2||CanGoState==4) cangor = 1;
  else cangor = 0;

  if(ant[AntNow].food) type = SMELL_TYPE_HOME;
  else type = SMELL_TYPE_FOOD;

  msf = GetMaxSmell(type,xxx,yyy,ddir);
  msl = GetMaxSmell(type,xxx,yyy,TurnLeft(ddir));
  msr= GetMaxSmell(type,xxx,yyy,TurnRight(ddir));
  maxms = MaxLocation(msf,msl,msr);
  /* maxms - 1 - msf is MAX
 - msl is MAX
 - msr is MAX
 - all 3 number is 0 */

  testdir = NULL;
  switch(maxms)
  {
    case 0: /* all is 0, keep testdir = NULL, random select dir */
        break;
    case 1: if(cangof)
          testdir = ddir;
        else
          if(msl>msr) if(cangol) testdir = TurnLeft(ddir);
          else if(cangor) testdir = TurnRight(ddir);
        break;
    case 2: if(cangol)
          testdir = TurnLeft(ddir);
        else
          if(msf>msr) if(cangof) testdir = ddir;
          else if(cangor) testdir = TurnRight(ddir);
        break;
    case 3: if(cangor)
          testdir = TurnRight(ddir);
        else
          if(msf>msl) if(cangof) testdir =ddir;
          else if(cangol) testdir = TurnLeft(ddir);
        break;
    default:break;
  } /* of maxms */

  randnum = random(1000);
  if(randnum<SMELL_DROP_RATE*1000||testdir==NULL)
  /* 1. if testdir = NULL, means can not find the max smell or the dir to max smell can not go
     then random select dir
. if ant error, don't follow the smell, random select dir
  */
  {
    randnum = random(100);
    switch(CanGoState)
    {
      case 0: if(randnum<90) testdir = ddir;
          else if (randnum>=90&&randnum<95) testdir = TurnLeft(ddir);
          else testdir = TurnRight(ddir);
          break;
      case 1: if(randnum<50) testdir = TurnLeft(ddir);
          else testdir = TurnRight(ddir);
          break;
      case 2: if(randnum<90) testdir = ddir;
          else testdir = TurnRight(ddir);
          break;
      case 3: if(randnum<90) testdir = ddir;
          else testdir = TurnLeft(ddir);
          break;
      case 4: testdir = TurnRight(ddir);
          break;
      case 5: testdir = TurnLeft(ddir);
          break;
      case 6: testdir = ddir;
          break;
      case 7: testdir = TurnBack(ddir);
          break;
      default:testdir = TurnBack(ddir);
    } /* of can go state */
  }
  return(testdir);
}
int GetMaxSmell(int type,int xxx,int yyy,int ddir)
{
  int i,j;
  int ms; /* MAX smell */

  ms = 0;
  switch(ddir)
  {
    case UP:  for(i=xxx-ANT_EYESHOT;i<=xxx+ANT_EYESHOT;i++)
            for(j=yyy-ANT_EYESHOT;j<yyy;j++)
          {
              if(!JudgeCanGo(i,j)) continue;
              if((i==food.xxx&&j==food.yyy&&type==SMELL_TYPE_FOOD)||
                 (i==home.xxx&&j==home.yyy&&type==SMELL_TYPE_HOME))
           {
                ms = MAX_SMELL;
               break;
           }
              if(IsTrace(i,j)) continue;
              if(Smell[type][i][j]>ms) ms = Smell[type][i][j];
          }
          break;
    case DOWN:  for(i=xxx-ANT_EYESHOT;i<=xxx+ANT_EYESHOT;i++)
            for(j=yyy+1;j<=yyy+ANT_EYESHOT;j++)
          {
              if(!JudgeCanGo(i,j)) continue;
              if((i==food.xxx&&j==food.yyy&&type==SMELL_TYPE_FOOD)||
                 (i==home.xxx&&j==home.yyy&&type==SMELL_TYPE_HOME))
           {
                ms = MAX_SMELL;
               break;
           }
              if(IsTrace(i,j)) continue;
              if(Smell[type][i][j]>ms) ms = Smell[type][i][j];
          }
          break;
    case LEFT:  for(i=xxx-ANT_EYESHOT;i<xxx;i++)
            for(j=yyy-ANT_EYESHOT;j<=yyy+ANT_EYESHOT;j++)
          {
              if(!JudgeCanGo(i,j)) continue;
              if((i==food.xxx&&j==food.yyy&&type==SMELL_TYPE_FOOD)||
                 (i==home.xxx&&j==home.yyy&&type==SMELL_TYPE_HOME))
           {
                ms = MAX_SMELL;
               break;
           }
              if(IsTrace(i,j)) continue;
              if(Smell[type][i][j]>ms) ms = Smell[type][i][j];
          }
          break;
    case RIGHT: for(i=xxx+1;i<=xxx+ANT_EYESHOT;i++)
            for(j=yyy-ANT_EYESHOT;j<=yyy+ANT_EYESHOT;j++)
          {
              if(!JudgeCanGo(i,j)) continue;
              if((i==food.xxx&&j==food.yyy&&type==SMELL_TYPE_FOOD)||
                 (i==home.xxx&&j==home.yyy&&type==SMELL_TYPE_HOME))
           {
                ms = MAX_SMELL;
               break;
           }
              if(IsTrace(i,j)) continue;
              if(Smell[type][i][j]>ms) ms = Smell[type][i][j];
          }
          break;
    default:  break;
  }
  return(ms);
}

int IsTrace(int xxx,int yyy)
{
  int i;

  for(i=0;i<TRACE_REMEMBER;i++)
    if(ant[AntNow].tracex[i]==xxx&&ant[AntNow].tracey[i]==yyy) return(1);
  return(0);
}

int MaxLocation(int num1,int num2,int num3)
{
  int maxnum;

  if(num1==0&&num2==0&&num3==0) return(0);

  maxnum = num1;
  if(num2>maxnum) maxnum = num2;
  if(num3>maxnum) maxnum = num3;

  if(maxnum==num1) return(1);
  if(maxnum==num2) return(2);
  if(maxnum==num3) return(3);
}

int CanGo(int xxx,int yyy,int ddir)
/* input: xxx,yyy - location of ant
      ddir - now dir
   output: 0 - forward and left and right can go
 - forward can not go
 - left can not go
 - right can not go
 - forward and left can not go
 - forward and right can not go
 - left and right can not go
 - forward and left and right all can not go
*/
{
  int tx,ty,tdir;
  int okf,okl,okr;

  /* forward can go ? */
  tdir = ddir;
  tx = xxx;
  ty = yyy;
  switch(tdir)
  {
    case UP:  ty--;
          break;
    case DOWN:  ty++;
          break;
    case LEFT:  tx--;
          break;
    case RIGHT: tx++;
          break;
    default:  break;
  } /* of switch dir */
  if(JudgeCanGo(tx,ty)) okf = 1;
  else okf = 0;

  /* turn left can go ? */
  tdir = TurnLeft(ddir);
  tx = xxx;
  ty = yyy;
  switch(tdir)
  {
    case UP:  ty--;
          break;
    case DOWN:  ty++;
          break;
    case LEFT:  tx--;
          break;
    case RIGHT: tx++;
          break;
    default:  break;
  } /* of switch dir */
  if(JudgeCanGo(tx,ty)) okl = 1;
  else okl = 0;

  /* turn right can go ? */
  tdir = TurnRight(ddir);
  tx = xxx;
  ty = yyy;
  switch(tdir)
  {
    case UP:  ty--;
          break;
    case DOWN:  ty++;
          break;
    case LEFT:  tx--;
          break;
    case RIGHT: tx++;
          break;
    default:  break;
  } /* of switch dir */
  if(JudgeCanGo(tx,ty)) okr = 1;
  else okr = 0;

  if(okf&&okl&&okr) return(0);
  if(!okf&&okl&&okr) return(1);
  if(okf&&!okl&&okr) return(2);
  if(okf&&okl&&!okr) return(3);
  if(!okf&&!okl&&okr) return(4);
  if(!okf&&okl&&!okr) return(5);
  if(okf&&!okl&&!okr) return(6);
  if(!okf&&!okl&&!okr) return(7);
  return(7);
}

int JudgeCanGo(int xxx,int yyy)
/* input: location to judeg
   output: 0 -- can not go
 -- can go
*/
{
  int i,j;

  if(xxx<=0||xxx>MAXX) return(0);
  if(yyy<=0||yyy>MAXY) return(0);
  if(block[xxx][yyy]) return(0);
  return(1);
}

int TurnLeft(int ddir)
{
  switch(ddir)
  {
    case UP:  return(LEFT);
    case DOWN:  return(RIGHT);
    case LEFT:  return(DOWN);
    case RIGHT: return(UP);
    default:  break;
  } /* of switch dir */
}

int TurnRight(int ddir)
{
  switch(ddir)
  {
    case UP:  return(RIGHT);
    case DOWN:  return(LEFT);
    case LEFT:  return(UP);
    case RIGHT: return(DOWN);
    default:  break;
  } /* of switch dir */
}

int TurnBack(int ddir)
{
  switch(ddir)
  {
    case UP:  return(DOWN);
    case DOWN:  return(UP);
    case LEFT:  return(RIGHT);
    case RIGHT: return(LEFT);
    default:  break;
  } /* of switch dir */
}

  

posted on 2012-03-21 22:01  Paul_bai  阅读(10138)  评论(0编辑  收藏  举报

导航