守望者--AIR技术交流
标题: 蚁群算法ACO(ant colony optimization)的原理以及实现源代码 [打印本页]
作者: 破晓 时间: 2015-2-6 10:33
标题: 蚁群算法ACO(ant colony optimization)的原理以及实现源代码
本帖最后由 破晓 于 2015-2-6 10:58 编辑
[attach]790[/attach]之前说的算法基本上都比较枯燥的(废话,算法都很枯燥……),这次要介绍的蚁群算法(Ant Colony Algorithm)却是一种源于自然现象的算法,也是一种 meta heuristic,即与具体问题关系不大的优化算法,也就是它是一种用来在图中寻找优化路径的机率型技术。Marco Dorigo于1992年在他的博士论文中引入,其灵感来源于蚂蚁在寻找食物过程中发现路径的行为。
小小的蚂蚁总是能够找到食物,他们具有什么样的智能呢?设想,如果我们要为蚂蚁设计一个人工智能的程序,那么这个程序要多么复杂呢?首先,你要让蚂蚁能够避开障碍物,就必须根据适当的地形给它编进指令让他们能够巧妙的避开障碍物,其次,要让蚂蚁找到食物,就需要让他们遍历空间上的所有点;再次,如果要让蚂蚁找到最短的路径,那么需要计算所有可能的路径并且比较它们的大小,而且更重要的是,你要小心翼翼的编程,因为程序的错误也许会让你前功尽弃。这是多么不可思议的程序!太复杂了,恐怕没人能够完成这样繁琐冗余的程序。
[attach]791[/attach]
为什么这么简单的程序会让蚂蚁干这样复杂的事情?答案是:简单规则的涌现。事实上,每只蚂蚁并不是像我们想象的需要知道整个世界的信息,他们其实只关心很小范围内的眼前信息,而且根据这些局部信息利用几条简单的规则进行决策,这样,在蚁群这个集体里,复杂性的行为就会凸现出来。这就是人工生命、复杂性科学解释的规律!
下面就是实现如此复杂性的七条简单规则:
1、范围:
蚂蚁观察到的范围是一个方格世界,蚂蚁有一个参数为速度半径(一般是3),那么它能观察到的范围就是3*3个方格世界,并且能移动的距离也在这个范围之内。
2、环境:
蚂蚁所在的环境是一个虚拟的世界,其中有障碍物,有别的蚂蚁,还有信息素,信息素有两种,一种是找到食物的蚂蚁洒下的食物信息素,一种是找到窝的蚂蚁洒下的窝的信息素。每个蚂蚁都仅仅能感知它范围内的环境信息。环境以一定的速率让信息素消失。
3、觅食规则:
在每只蚂蚁能感知的范围内寻找是否有食物,如果有就直接过去。否则看是否有信息素,并且比较在能感知的范围内哪一点的信息素最多,这样,它就朝信息素多的地方走,并且每只蚂蚁多会以小概率犯错误,从而并不是往信息素最多的点移动。蚂蚁找窝的规则和上面一样,只不过它对窝的信息素做出反应,而对食物信息素没反应。
4、移动规则:
每只蚂蚁都朝向信息素最多的方向移,并且,当周围没有信息素指引的时候,蚂蚁会按照自己原来运动的方向惯性的运动下去,并且,在运动的方向有一个随机的小的扰动。为了防止蚂蚁原地转圈,它会记住最近刚走过了哪些点,如果发现要走的下一点已经在最近走过了,它就会尽量避开。
5、避障规则:
如果蚂蚁要移动的方向有障碍物挡住,它会随机的选择另一个方向,并且有信息素指引的话,它会按照觅食的规则行为。
7、播撒信息素规则:
每只蚂蚁在刚找到食物或者窝的时候撒发的信息素最多,并随着它走远的距离,播撒的信息素越来越少。
下面的程序开始运行之后,蚂蚁们开始从窝里出动了,寻找食物;他们会顺着屏幕爬满整个画面,直到找到食物再返回窝。
其中,‘F’点表示食物,‘H’表示窝,白色块表示障碍物,‘+’就是蚂蚁了。
参数说明:
最大信息素:蚂蚁在一开始拥有的信息素总量,越大表示程序在较长一段时间能够存在信息素。信息素消减的速度:随着时间的流逝,已经存在于世界上的信息素会消减,这个数值越大,那么消减的越快。
错误概率表示这个蚂蚁不往信息素最大的区域走的概率,越大则表示这个蚂蚁越有创新性。
速度半径表示蚂蚁一次能走的最大长度,也表示这个蚂蚁的感知范围。
记忆能力表示蚂蚁能记住多少个刚刚走过点的坐标,这个值避免了蚂蚁在本地打转,停滞不前。而这个值越大那么整个系统运行速度就慢,越小则蚂蚁越容易原地转圈。
源代码如下(不同编译器可能需做一定修改):
- /*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
- #define MAXX 80
- #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"
- #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);
- 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);
- 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;
- int food;
- int SmellAmount[2];
- int tracex[TRACE_REMEMBER];
- int tracey[TRACE_REMEMBER];
- int TracePtr;
- int IQ;
- }ant[MAX_ANT];
- int AntNow;
- int timer10ms;
- 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
- 2 -- home at left-down, food at right-up
- 3 -- home at right-up, food at left-down
- 4 -- 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
- 1 -- Only display home smell
- 2 -- 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
- 2 - msl is MAX
- 3 - msr is MAX
- 0 - 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
- 2. 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
- 1 - forward can not go
- 2 - left can not go
- 3 - right can not go
- 4 - forward and left can not go
- 5 - forward and right can not go
- 6 - left and right can not go
- 7 - 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
- 1 -- 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 */
- }
复制代码
作者: 破晓 时间: 2015-2-6 10:40
本帖最后由 破晓 于 2015-2-6 10:42 编辑
AS3 实现
http://www.airmyth.com/res/AntsTest.swf
- /**
- * Copyright tencho ( http://wonderfl.net/user/tencho )
- * MIT License ( http://www.opensource.org/licenses/mit-license.php )
- * Downloaded from: http://wonderfl.net/c/2Yzr
- */
- /**
- * アリのエサ運びをシミュレーションしてみました
- * 土をクリックするとお菓子が置けて、左下のボタンでアリが増やせます(1000匹まで)
- * ※増やしすぎ注意(負荷的にも見た目的にも・・・)
- *
- * アリは普段ランダムに動いていますが、
- * エサを見つけると地面にフェロモンを残しながら巣まで戻り、
- * そのフェロモンを発見した他のアリがそれを辿ってエサの在り処を見つけるという流れです。
- * フェロモン濃度だけで判断するとなかなかうまく辿れなかったので
- * 濃度と一緒に進む方向も記録するようにしています。
- * (濃度と向きはBitmapDataのピクセルに色情報としてまとめて記録してます)
- * フェロモンが蒸発して消えないかぎり複雑な道のりも辿れるんですが、
- * 複雑すぎると巣に帰るプログラムかけなくなりそうだったのでやめました。。。
- */
- package {
- import com.bit101.components.Label;
- import com.bit101.components.PushButton;
- import flash.display.Bitmap;
- import flash.display.BitmapData;
- import flash.display.BlendMode;
- import flash.display.Sprite;
- import flash.display.StageQuality;
- import flash.events.Event;
- import flash.events.MouseEvent;
- import flash.filters.DropShadowFilter;
- import flash.geom.ColorTransform;
- import flash.geom.Rectangle;
- public class AntsTest extends Sprite {
- ///画面サイズ
- public const DISPLAY:Rectangle = new Rectangle(0, 0, 465, 465);
- ///最初からいるアリの数
- public const ANTSNUM:int = 50;
- ///一度に追加するアリの数
- public const ADDNUM:int = 50;
- ///アリの最大数
- public const ANTSMAX:int = 1000;
- public const MATERIAL_URL:String = "http://assets.wonderfl.net/images/related_images/4/40/40c1/40c16871d560d547ebb1fc1725db47922ef9f261";
- public var world:World;
- public var bg:Sprite;
- public var containerAnts:Sprite;
- public var containerFoods:Sprite;
- public var canvasBmp:Bitmap;
- public var pheromoneBmp:Bitmap;
- public var canvas:BitmapData;
- public var loader:ImageLoader;
- public var stats:Label;
- public var whiteColor:ColorTransform = new ColorTransform();
- ///コンストラクタ
- public function AntsTest() {
- stage.frameRate = 30;
- stage.quality = StageQuality.MEDIUM;
- //Wonderfl.capture_delay(5);
- whiteColor.color = 0xFFFFFF;
-
- world = new World();
- world.home.setPosition(110, 350);
- world.obstacles.push(new Obstacle(280, 225, 90));
- world.obstacles.push(new Obstacle(0, -150, 280));
- world.init(DISPLAY.width, DISPLAY.height);
-
- bg = new Sprite();
- bg.graphics.beginFill(0x444444, 1);
- bg.graphics.drawRect(0, 0, DISPLAY.width, DISPLAY.height);
- bg.graphics.endFill();
-
- addChild(bg);
-
- //外部画像読み込み開始
- loader = new ImageLoader();
- loader.load(MATERIAL_URL, onLoadImage, onErrorImage);
- }
- ///画像読み込み失敗
- private function onErrorImage(str:String):void {
- var msg:Label = new Label(this, 5, 5, str);
- msg.textField.wordWrap = true;
- msg.textField.width = 440;
- msg.transform.colorTransform = whiteColor;
- }
- ///画像読み込み完了
- private function onLoadImage():void {
- loader.ground.width = DISPLAY.width;
- loader.ground.height = DISPLAY.height;
-
- pheromoneBmp = new Bitmap(world.pheromone.map);
- pheromoneBmp.visible = false;
- pheromoneBmp.blendMode = BlendMode.LIGHTEN;
-
- //障害物領域をマウスクリックできなくさせる
- for each(var obs:Obstacle in world.obstacles) {
- var sp:Sprite = addChild(new Sprite()) as Sprite;
- sp.graphics.beginFill(0x444444, 0);
- sp.graphics.drawCircle(obs.center.x, obs.center.y, obs.radius);
- sp.graphics.endFill();
- }
- canvas = new BitmapData(DISPLAY.width, DISPLAY.height, true, 0x00FFFFFF);
- canvasBmp = new Bitmap(canvas);
- canvasBmp.filters = [new DropShadowFilter(3, 45, 0x222222, 0.8, 3, 3, 1, 1)];
- containerAnts = new Sprite();
- containerFoods = new Sprite();
- containerFoods.mouseChildren = false;
- containerFoods.mouseEnabled = false;
-
- //画面下のメニュー
- var menu:Sprite = new Sprite();
- var blackBox:Sprite = menu.addChild(new Sprite()) as Sprite;
- blackBox.graphics.beginFill(0x000000, 0.5);
- blackBox.graphics.drawRect(0, 0, DISPLAY.width, 25);
- blackBox.graphics.endFill();
- new SwitchButton(menu, DISPLAY.width - 160, 5, ["PHEROMONE: OFF", "PHEROMONE: ON"], onSwitchPheromon);
- new PushButton(menu, DISPLAY.width - 55, 5, "RESET", onClickClear).setSize(50, 16);
- new PushButton(menu, 5, 5, "+" + ADDNUM + " ANTS", onClickAdd).setSize(70, 16);
- stats = new Label(menu, 85, 3, "");
- stats.transform.colorTransform = whiteColor;
- menu.y = DISPLAY.height - menu.height;
-
- //画面に色々配置
- addChild(loader.ground);
- addChild(pheromoneBmp);
- addChild(world.home);
- addChild(canvasBmp);
- addChild(containerFoods);
- addChild(menu);
- new Label(this, 5, 3, "CLICK TO FEED").transform.colorTransform = whiteColor;
-
- //メイン処理開始
- init(ANTSNUM);
- bg.addEventListener(MouseEvent.MOUSE_DOWN, onClickStage);
- addEventListener(Event.ENTER_FRAME, onEnter);
- }
- ///蟻の数を指定して初期化
- private function init(num:int):void {
- world.clear();
- var wait:Number = 10;
- for (var i:int = 0; i < num; i++) {
- wait += Math.max(0.05, 15 / (i * i / 100 + 1));
- containerAnts.addChild(world.addAnt(wait).body);
- }
- updateStats();
- }
- ///情報更新
- private function updateStats():void {
- stats.text = "ANTS: " + world.ants.length;
- }
- ///全てリセット
- private function onClickClear(e:MouseEvent):void{
- init(ANTSNUM);
- }
- ///アリ追加
- private function onClickAdd(e:MouseEvent):void {
- var num:int = Math.min(ADDNUM, ANTSMAX - world.ants.length);
- for (var i:int = 0; i < num; i++)
- containerAnts.addChild(world.addAnt(i/2).body);
- updateStats();
- }
- ///フェロモン切り替え
- private function onSwitchPheromon(mode:int):void{
- pheromoneBmp.visible = !!mode;
- }
- ///土をクリック
- private function onClickStage(e:MouseEvent):void {
- var img:ImageData = loader.feeds[Math.random() * loader.feeds.length | 0];
- containerFoods.addChild(world.addFood(stage.mouseX, stage.mouseY, img).sprite);
- }
- //毎フレーム処理
- private function onEnter(e:Event):void {
- world.pheromone.map.lock();
- for each(var a:Ant in world.ants) a.action(world);
- world.pheromone.vaporize();
- world.pheromone.map.unlock();
- canvas.fillRect(DISPLAY, 0x00000000);
- canvas.draw(containerAnts);
- }
- }
- }
- import com.bit101.components.PushButton;
- import flash.display.Bitmap;
- import flash.display.BitmapData;
- import flash.display.BlendMode;
- import flash.display.DisplayObjectContainer;
- import flash.display.Loader;
- import flash.display.Sprite;
- import flash.events.ErrorEvent;
- import flash.events.Event;
- import flash.events.IOErrorEvent;
- import flash.events.MouseEvent;
- import flash.events.SecurityErrorEvent;
- import flash.filters.DropShadowFilter;
- import flash.geom.ColorTransform;
- import flash.geom.Point;
- import flash.geom.Rectangle;
- import flash.net.URLRequest;
- import flash.system.LoaderContext;
- import flash.system.System;
- //角度変換用
- class Angle {
- static public const ALL_RADIAN:Number = Math.PI * 2;
- static public const TO_RADIAN:Number = Math.PI / 180;
- static public const TO_ROTATION:Number = 180 / Math.PI;
- //角度を合成する
- static public function between(a1:Number, a2:Number, per:Number):Number {
- var minus:Number = a1 - a2;
- var r180:Number = (minus % Angle.ALL_RADIAN + Angle.ALL_RADIAN) % Angle.ALL_RADIAN;
- if (r180 > Math.PI) r180 -= Angle.ALL_RADIAN;
- var a0:Number = r180 + a2;
- return a0 * (1 - per) + a2 * (per);
- }
- }
- /**
- * 全てのデータ
- */
- class World {
- ///ワールドサイズ
- public var area:Rectangle;
- ///全てのアリ
- public var ants:Vector.<Ant> = new Vector.<Ant>();
- ///全てのエサ
- public var foods:Vector.<Food> = new Vector.<Food>();
- ///全ての障害物
- public var obstacles:Vector.<Obstacle> = new Vector.<Obstacle>();
- ///アリ塚
- public var home:AntsHill = new AntsHill();
- ///フェロモン
- public var pheromone:Pheromone;
- public function World() {
- }
- ///サイズを指定して初期化
- public function init(width:Number, height:Number):void {
- area = new Rectangle(0, 0, width, height);
- pheromone = new Pheromone(width, height);
- }
- ///エサを削って無くなったら削除
- public function cutFood(f:Food):void {
- if (f.cut()) {
- f.remove();
- foods.splice(foods.indexOf(f), 1);
- }
- }
- ///エサを追加
- public function addFood(x:int, y:int, img:ImageData):Food {
- var f:Food = new Food(x, y, 50, 200, img);
- foods.push(f);
- return f;
- }
- ///アリを追加
- public function addAnt(wait:int):Ant {
- var a:Ant = new Ant(home.position.x, home.position.y);
- a.thinkTime = wait;
- ants.push(a);
- return a;
- }
- ///色々リセット
- public function clear():void {
- for each(var f:Food in foods) f.remove();
- for each(var a:Ant in ants) a.remove();
- ants.length = 0;
- foods.length = 0;
- pheromone.clear();
- System.gc();
- }
- }
- /**
- * エサの画像
- */
- class ImageData {
- private var _colors:Array;
- public var bmd:BitmapData;
- public function ImageData(bmd:BitmapData) {
- var px:int, py:int, rgba:uint;
- this.bmd = bmd;
- //画像のピクセルカラーを調べる(透明領域は無視)
- _colors = new Array();
- for (px = 0; px < bmd.width; px += 8) {
- for (py = 0; py < bmd.width; py += 8) {
- rgba = bmd.getPixel32(px, py);
- if (rgba >>> 24 == 255) _colors.push(rgba & 0xFFFFFF);
- }
- }
- }
- ///画像の色をランダムに取得
- public function getRandomColor():uint {
- return _colors[Math.random() * _colors.length | 0];
- }
- }
- /**
- * 画像をロードして分割
- */
- class ImageLoader {
- ///背景画像
- public var ground:Bitmap;
- ///エサ画像リスト
- public var feeds:Vector.<ImageData>;
- ///エサ画像の数
- private const FEED_NUM:int = 5;
- private var _loader:Loader;
- private var _completeFunc:Function;
- private var _errorFunc:Function;
- public function ImageLoader() {
- _loader = new Loader();
- }
- public function load(src:String, complete:Function, error:Function):void{
- _completeFunc = complete;
- _errorFunc = error;
- _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onErrorImage);
- _loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorImage);
- _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadImage);
- _loader.load(new URLRequest(src), new LoaderContext(true));
- }
- private function removeEvent():void {
- _loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onErrorImage);
- _loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onErrorImage);
- _loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoadImage);
- }
- private function onErrorImage(e:ErrorEvent):void {
- removeEvent();
- _errorFunc(e.text);
- }
- private function onLoadImage(e:Event):void {
- removeEvent();
- var bmp:BitmapData = Bitmap(_loader.content).bitmapData;
- feeds = new Vector.<ImageData>();
- for (var i:int = 0; i < FEED_NUM; i++) {
- var bmp2:BitmapData = new BitmapData(64, 64, true, 0x00FFFFFF);
- bmp2.copyPixels(bmp, new Rectangle(64 * i, 0, 64, 64), new Point(0, 0));
- feeds.push(new ImageData(bmp2));
- }
- ground = new Bitmap(new BitmapData(bmp.width, bmp.height-64, false));
- ground.bitmapData.copyPixels(bmp, new Rectangle(0, 64, ground.width, ground.height), new Point());
- ground.smoothing = true;
- _completeFunc();
- }
- }
- /**
- * 切り替えボタン
- */
- class SwitchButton extends PushButton {
- private var _mode:int = 0;
- private var _labels:Array;
- private var _clickFunc:Function;
- public function SwitchButton(parent:DisplayObjectContainer, xpos:Number, ypos:Number, labels:Array = null, func:Function = null) {
- if(labels == null) labels = [""];
- _labels = labels;
- _clickFunc = func;
- super(parent, xpos, ypos, _labels[0], onClick);
- height = 16;
- }
- private function onClick(e:MouseEvent):void {
- _mode = ++_mode % _labels.length;
- label = _labels[_mode];
- if (_clickFunc != null) _clickFunc(_mode);
- }
- }
- /**
- * アリ塚
- */
- class AntsHill extends Sprite {
- public var position:Point;
- public function AntsHill() {
- graphics.beginFill(0x000000, 0.3);
- graphics.drawCircle(0, 0, 9);
- graphics.beginFill(0x000000, 1);
- graphics.drawCircle(0, 0, 7);
- graphics.endFill();
- position = new Point();
- }
- ///位置変更
- public function setPosition(x:Number, y:Number):void {
- this.x = position.x = x;
- this.y = position.y = y;
- }
- }
- //フェロモン
- class Pheromone {
- public var map:BitmapData;
- private var _ct:ColorTransform;
- private var _timeCount:int = 0;
- public function Pheromone(width:Number, height:Number) {
- _ct = new ColorTransform(1, 0.99, 1, 1, 0, 0, 0, 0);
- map = new BitmapData(width, height, true, 0x00000000);
- }
- ///指定座標のフェロモンから進む角度を調べる
- public function getGuidepost(x:int, y:int):Number {
- var isNone:Boolean = true, tx:Number = 0, ty:Number = 0, px:int, py:int;
- //周囲のフェロモン濃度を調べて濃い方向を調べる
- for (px = -2; px <= 2; px++) {
- for (py = -2; py <= 2; py++) {
- if (px != 0 || py != 0) {
- var per:Number = (map.getPixel32(x + px * 4, y + py * 4) >> 8 & 0xFF) / 255;
- tx += per * px;
- ty += per * py;
- if (per) isNone = false;
- }
- }
- }
- var angle:Number;
- if ((!tx && !ty) || isNone) {
- angle = NaN;
- } else {
- //足元のフェロモンから進む角度を調べる
- var angleRate:Number = (map.getPixel32(x, y) & 0xFF) / 255;
- var dx:Number, dy:Number;
- if (angleRate == 0) {
- //フェロモンが無ければ濃い方へ
- dx = tx;
- dy = ty;
- } else {
- //フェロモンがあれば先に進む(周囲濃度で若干角度補正)
- var radian:Number = angleRate * Angle.ALL_RADIAN + Math.PI;
- dx = Math.cos(radian) * 40 + tx;
- dy = Math.sin(radian) * 40 + ty;
- }
- angle = Math.atan2(dy, dx);
- }
- return angle;
- }
- //フェロモンをつける
- public function putPheromone(x:int, y:int, radianPer:Number):void {
- var rgb:uint = 0xF0 << 24 | 0xFF << 16 | 0xFF << 8 | uint(0xFF * radianPer);
- map.fillRect(new Rectangle(x-5, y-5, 11, 11), rgb);
- }
- //フェロモン拡散
- public function vaporize():void {
- if (!(++_timeCount % 2)) map.colorTransform(map.rect, _ct);
- }
- //フェロモンリセット
- public function clear():void {
- map.fillRect(map.rect, 0x00000000);
- }
- }
- /**
- * アリのエサ
- */
- class Food {
- public var sprite:Sprite;
- ///位置
- public var position:Point;
- ///半径
- public var size:Number;
- ///残りの量
- public var quantity:int;
- ///画像データ
- public var image:ImageData;
- private var _max:int;
- private var _mask:BitmapData;
- private var _noise:BitmapData;
- public function Food(x:Number = 0, y:Number = 0, size:Number = 10, num:int = 10, img:ImageData = null) {
- position = new Point(x, y);
- this.size = size;
- quantity = num;
- _max = num;
- sprite = new Sprite();
- sprite.x = x;
- sprite.y = y;
- sprite.scaleX = sprite.scaleY = 0.7;
- sprite.filters = [new DropShadowFilter(5, 45, 0x111111, 0.7, 8, 8, 1, 1)];
- image = img;
- var foodBmp:Bitmap = sprite.addChild(new Bitmap(image.bmd)) as Bitmap;
- foodBmp.smoothing = true;
- //徐々に削られるエフェクト用
- _mask = new BitmapData(image.bmd.width, image.bmd.height, true);
- _mask.fillRect(_mask.rect, 0x00888888);
- _noise = new BitmapData(image.bmd.width, image.bmd.height, false);
- _noise.perlinNoise(20, 20, 3, int(Math.random() * 100), false, true, 1, true);
- var maskBmp:Bitmap = sprite.addChild(new Bitmap(_mask)) as Bitmap;
- maskBmp.blendMode = BlendMode.ERASE;
- foodBmp.x = maskBmp.x = -foodBmp.width / 2;
- foodBmp.y = maskBmp.y = -foodBmp.height / 2;
- }
- ///削る
- public function cut():Boolean {
- quantity--;
- var per:Number = quantity / _max * 0.4 + 0.4;
- _mask.fillRect(_mask.rect, 0xFF888888);
- _mask.threshold(_noise, _noise.rect, new Point(), "<", per * 255, 0x00000000, 255, false);
- return !quantity;
- }
- ///削除
- public function remove():void {
- _mask.dispose();
- _noise.dispose();
- if (sprite.parent) sprite.parent.removeChild(sprite);
- }
- }
- /**
- * 円形障害物
- */
- class Obstacle {
- ///中心点
- public var center:Point;
- ///半径
- public var radius:Number;
- public function Obstacle(x:Number = 0, y:Number = 0, radius:Number = 50) {
- center = new Point(x, y);
- this.radius = radius;
- }
- }
- /**
- * アリ
- */
- class Ant {
- ///グラフィック
- public var body:Sprite;
- ///位置
- public var position:Point;
- ///停止時間
- public var thinkTime:int = 0;
- private var _locus:Vector.<Point>; //数フレーム前までの位置リスト
- private var _radian:Number = 0; //角度
- private var _speed:Number = 2; //速度
- private var _status:int = 0; //状況 0:エサ探し 1:巣に帰る
- private var _freeTime:int = 0; //フェロモン無効時間
- private var _view:Number = 20; //視界範囲
- private var _wanderCnt:int = 1;
- private var _food:Sprite;
- private var _randomRad:Number;
- private var _startReturn:Boolean = false;
- private var _searchCnt:int = -1;
- private var _targetFood:Food = null;
- public function Ant(x:Number = 0, y:Number = 0) {
- body = new Sprite();
- body.graphics.beginFill(0x000000, 1);
- body.graphics.drawRect(-4, -1, 4, 2);
- body.graphics.drawRect(1, -1, 1, 2);
- body.graphics.drawRect(3, -1, 2, 2);
- body.graphics.endFill();
- body.visible = false;
- _food = body.addChild(new Sprite()) as Sprite;
- _food.graphics.beginFill(0xFFFFFF, 1);
- _food.graphics.drawRect(-2, -2, 4, 4);
- _food.graphics.endFill();
- _food.x = 6;
- _food.visible = false;
- position = new Point(x, y);
- _locus = new Vector.<Point>();
- _radian = Math.random() * Angle.ALL_RADIAN;
- _randomRad = (Math.random() * 10 - 5) * Angle.TO_RADIAN;
- }
- ///行動
- public function action(w:World):void {
- if (thinkTime) {
- if (--thinkTime == 0) {
- if (_status == 0) startSearch();
- if (_startReturn) {
- _startReturn = false;
- toFace(w.home.position);
- }
- }
- return;
- } else {
- randomThink(0.015, Math.random() * 10 + 15);
- }
- //エサ探しモード
- if (_status == 0) {
- _searchCnt = ++_searchCnt % 3;
- if (!_searchCnt) {
- var near:Number = Number.MAX_VALUE;
- _targetFood = null;
- for each(var f:Food in w.foods) {
- var distance:Number = position.subtract(f.position).length;
- //エサに接触したら持ち帰り始める
- if (distance <= f.size/2 + 1) {
- thinkTime = Math.random() * 50 + 30;
- getFood(f.image.getRandomColor());
- w.cutFood(f);
- return;
- }
- //エサを見つけた
- var d:Number = distance - f.size/2;
- if (d <= _view && d < near) {
- near = d;
- _targetFood = f;
- }
- }
- }
- //エサを見つけているか
- if (_targetFood) {
- toFace(_targetFood.position);
- } else {
- if (_freeTime > 0) _freeTime--;
- //フェロモンが近くにあるかチェック
- var rad:Number = (_freeTime > 0)? NaN : w.pheromone.getGuidepost(position.x, position.y);
- if (isNaN(rad)) {
- //なければうろつく
- wander();
- } else {
- //あればフォロモンの流れに向く
- _radian = Angle.between(rad, _radian, 0.5) + Angle.TO_RADIAN + _randomRad;
- checkStay();
- }
- }
- }
-
- //エサ持ち帰りモード
- if (_status == 1) {
- var per:Number = _radian / Angle.ALL_RADIAN;
- w.pheromone.putPheromone(position.x, position.y, per);
- goto(w.home.position);
- if (w.home.position.subtract(position).length < 5) backHome();
- }
-
- //進行方向に進む
- position.x += Math.cos(_radian) * _speed;
- position.y += Math.sin(_radian) * _speed;
-
- //障害物を避ける
- adjustPosition(w);
-
- //表示更新
- body.x = position.x;
- body.y = position.y;
- body.rotation = _radian * Angle.TO_ROTATION;
- }
- ///ランダム回転
- private function wander():void {
- _wanderCnt = ++_wanderCnt % 4;
- if (!_wanderCnt) _radian += (Math.random() * 60 - 30) * Angle.TO_RADIAN;
- }
- ///指定の座標を向く
- private function toFace(target:Point):void {
- _radian = Math.atan2(target.y - position.y, target.x - position.x);
- }
- ///指定座標に向かいながらランダム回転
- private function goto(target:Point):void {
- _wanderCnt = ++_wanderCnt % 4;
- if (!_wanderCnt) {
- var minus:Point = target.subtract(position);
- var rad:Number = Math.atan2(minus.y, minus.x);
- var per:Number = minus.length / 100;
- if (per > 1) per = 1;
- _radian = Angle.between(rad, _radian, 0.75 * per) + (Math.random() * 30 - 15) * Angle.TO_RADIAN;
- }
- }
- ///一定時間その場でうろついていたらフェロモン無効に
- private function checkStay():void {
- _locus.unshift(position.clone());
- _locus.length = 4;
- if (_locus[3] && _locus[0].subtract(_locus[3]).length <= _speed * 2) _freeTime = 60;
- }
- ///障害物を避けるように位置と角度を調整
- private function adjustPosition(w:World):void {
- //円形障害物を避ける角度を調べる
- var adjustRad1:Number = NaN;
- var plus:Point = new Point(Math.cos(_radian) * _speed, Math.sin(_radian) * _speed);
- var nextPos:Point = position.add(plus);
- for each(var obs:Obstacle in w.obstacles) {
- var distance:Number = Point.distance(nextPos, obs.center);
- if (distance < obs.radius) {
- var radius:Point = nextPos.subtract(obs.center);
- if (radius.length < obs.radius) {
- radius.normalize(obs.radius);
- var fixPos:Point = obs.center.add(radius);
- adjustRad1 = Math.atan2(fixPos.y - position.y, fixPos.x - position.x);
- }
- break;
- }
- }
- if(!isNaN(adjustRad1)) _radian = Angle.between(adjustRad1, _radian, 0.85);
-
- //ワールドエリア内に収まる位置と角度を調べる
- var rect:Rectangle = w.area;
- var adjustRad2:Number = NaN;
- var padding:int = 5;
- if (position.x < rect.left + padding) {
- position.x = rect.left + padding;
- adjustRad2 = 0;
- }
- if (position.x > rect.right - padding) {
- position.x = rect.right - padding;
- adjustRad2 = Math.PI;
- }
- if (position.y > rect.bottom - padding) {
- position.y = rect.bottom - padding;
- adjustRad2 = Math.PI * 1.5;
- }
- if (position.y < rect.top + padding) {
- position.y = rect.top + padding;
- adjustRad2 = Math.PI * 0.5;
- }
- if (!isNaN(adjustRad2)) _radian = Angle.between(adjustRad2, _radian, 0.9);
- }
- ///一定確率で立ち止まる
- private function randomThink(per:Number, time:int):void {
- if (Math.random() <= per) thinkTime = time;
- }
- ///エサを探し始める
- private function startSearch():void {
- body.visible = true;
- _status = 0;
- }
- ///巣に入れる
- private function backHome():void {
- _status = 0;
- _food.visible = false;
- body.visible = false;
- thinkTime = Math.random() * 100 + 100;
- }
- ///エサを取得
- private function getFood(color:uint = 0xFFFFFF):void {
- _status = 1;
- _startReturn = true;
- _food.visible = true;
- var ct:ColorTransform = new ColorTransform();
- ct.color = color;
- _food.transform.colorTransform = ct;
- }
- ///親から削除
- public function remove():void {
- _targetFood = null;
- if (body.parent != null) body.parent.removeChild(body);
- }
- }
复制代码
下载:
[attach]789[/attach]
作者: lxz 时间: 2017-10-26 01:04
感谢分享!~
| 欢迎光临 守望者--AIR技术交流 (http://www.airmyth.com/) |
|