home *** CD-ROM | disk | FTP | other *** search
- /* Bacteria simulation program. Scientific American, may 1989 */
- /* Written by Jurjen N.E. Bos */
- /* Compile this file with THINK C 3.0, MacHeaders on */
- /* Link it with TransSkel.c, MacTraps and add the resources of Bugs Project.rsrc */
- /* Of course, portions ⌐THINK technologies, inc. */
-
- #define nil (void *)0
- #define FrontMost -1L
-
- #define FieldWidth 507 /* width of window */
- #define FieldHeight 299 /* height of window */
- #define MaxBugs 150 /* maximum number of bugs allowed */
- #define HealthPerBact 40 /* number of moves to live from an eaten bacteria */
- #define MaxHealth 1500 /* maximum health */
- #define StrongHealth 1000 /* health to be strong enough for children */
- #define SplitAge 800/2 /* age to be old enough for children */
- #define MaxAge 1200/2 /* maximum displayable age. Value makes nice picture */
- #define InitBugs 10 /* initial number of bugs */
- #define InitHealth 500 /* initial health of first bugs */
- #define DieTime 50 /* die delay */
- #define InitBact 1000 /* initial number of bacteria */
- #define InitMinDist 50*50 /* square of minimal distance for clicking */
-
- typedef struct {
- Point loc;
- int gene[6],sum;
- int dir;
- int health,age;
- } Bug;
-
- void MenuClobber(MenuHandle theMenu);
- void About(void);
- void EditMenu(int item);
- void FileMenu(void);
- void BactMenu(int item);
- void WindowClobber(void);
- void WindowUpdate(void);
- void WindowActivate(Boolean active);
- void MakePort(GrafPtr port);
- void Init(void);
- void DoGeneration(void);
- void WindowMouse(Point thePoint);
- void PutBacteria(void);
- void MoveBug(Bug *theBug);
- void KillBug(Bug *theBug);
- void SplitBug(Bug *theBug);
-
- unsigned int RandomRange(/* unsigned int top */);
- long int SQDist(/* Point a,b */);
- int Scale(int a,int b,int c);
- int CountPixels(int h,int v);
-
- WindowPtr myWindow;
- enum {
- F=0,R,HR,RV,HL,L
- };
- enum {
- growUniform=4,growNone,growPattern,growGarden,growCentered
- };
- int hmove[]={2,1,-1,-2,-1,1};
- int vmove[]={0,2,2,0,-2,-2};
- Bug bug[MaxBugs];
- int numBugs=InitBugs; /* number of bugs */
- GrafPort bactPort;
- GrafPtr windowMap;
- int growMode=growUniform; /* bacteria grow mode */
- Point bactLoc={10,10}; /* location for pattern grow mode */
-
-
- main()
- {
- Rect R;
- SkelInit(1,nil);
- SkelApple("\pAbout Bugs╔",About);
- SkelMenu(GetMenu(129),FileMenu,MenuClobber,false);
- SkelMenu(GetMenu(130),EditMenu,MenuClobber,false);
- SkelMenu(GetMenu(131),BactMenu,MenuClobber,true);
- /* make our window */
- R=screenBits.bounds;
- /* Watch this: centering the right way! */
- R.bottom=( R.top=(R.top+R.bottom+38-FieldHeight)/2 )+FieldHeight;
- R.right=( R.left=(R.left+R.right-FieldWidth)/2 )+FieldWidth;
- myWindow=NewWindow(nil,&R,"\pJurjen╒s Bugs",true,4,FrontMost,false,0);
- SkelWindow(myWindow,
- WindowMouse,nil,WindowUpdate,WindowActivate,nil,WindowClobber,DoGeneration,false);
- Init();
- SkelMain();
- SkelClobber();
- }
-
- void MenuClobber(theMenu)
- MenuHandle theMenu;
- {
- ReleaseResource(theMenu);
- }
-
- void About()
- {
- Alert(1002,nil);
- }
-
- void EditMenu(item)
- int item;
- {
- SystemEdit(item-1);
- }
-
- void FileMenu()
- {
- SkelWhoa();
- }
-
- void BactMenu(item)
- int item;
- {
- register DialogPtr myDialog;
- int type;
- Handle itemHandle;
- Rect box;
- if(item==1){
- /* change bacteria population */
- register int population=5, add=0;
- register int value;
- myDialog=GetNewDialog(1000,nil,FrontMost);
- SetPort(myDialog);
- GetDItem(myDialog,1,&type,&itemHandle,&box);
- PenSize(3,3);
- InsetRect(&box,-4,-4);
- FrameRoundRect(&box,16,16);
- item=5;value=1;
- do {
- GetDItem(myDialog,item,&type,&itemHandle,&box);
- SetCtlValue(itemHandle,value);
- ModalDialog(nil,&item);
- /* 1: OK, 2: cancel, 4: Add to current population,
- 5: Random, 6: None, 7: Pattern */
- if(item==4)
- value=add=1-add;
- else if(item>4) {
- GetDItem(myDialog,population,&type,&itemHandle,&box);
- SetCtlValue(itemHandle,0);
- value=population=item;
- }
- } while (item>2);
- DisposDialog(myDialog);
- if(item==1) {
- SetPort(&bactPort);
- if(!add) EraseRect(&bactPort.portRect);
- if(population==7) {
- PenMode(patOr);
- PenPat(ltGray);
- PaintRect(&bactPort.portRect);
- PenMode(patCopy);
- PenPat(black);
- }
- SetPort(windowMap);
- WindowUpdate();
- if(population==5) for(value=0;value<InitBact;value++) PutBacteria();
- }
- }else{
- /* change bacteria growth */
- register int growth=growUniform;
- myDialog=GetNewDialog(1001,nil,FrontMost);
- SetPort(myDialog);
- GetDItem(myDialog,1,&type,&itemHandle,&box);
- PenSize(3,3);
- InsetRect(&box,-4,-4);
- FrameRoundRect(&box,16,16);
- do {
- GetDItem(myDialog,growth,&type,&itemHandle,&box);
- SetCtlValue(itemHandle,1);
- ModalDialog(nil,&item);
- /* 1: OK, 2: cancel, 4: q,
- 5: q, 6: q, 7: q, 8:q */
- if(item>3) {
- GetDItem(myDialog,growth,&type,&itemHandle,&box);
- SetCtlValue(itemHandle,0);
- growth=item;
- }
- } while (item>2);
- DisposDialog(myDialog);
- if(item==1) growMode=growth;
- } }
-
- void WindowClobber()
- {
- DisposeWindow(myWindow);
- }
-
- void WindowUpdate(){
- register int i;
- CopyBits(&bactPort.portBits,&thePort->portBits,
- &bactPort.portRect,&thePort->portRect,
- srcCopy,nil);
- PenSize(3,3);
- for(i=0;i<numBugs;i++){
- MoveTo(bug[i].loc.h,bug[i].loc.v);
- Line(0,0);
- }
- PenSize(1,1);
- }
-
- void WindowActivate(active)
- Boolean active;
- {
- if(active){
- DisableItem(Get1Resource('MENU',130),0);
- EnableItem(Get1Resource('MENU',131),0);
- DrawMenuBar();
- }else{
- EnableItem(Get1Resource('MENU',130),0);
- DisableItem(Get1Resource('MENU',131),0);
- DrawMenuBar();
- }
- }
-
- void MakePort(port)
- register GrafPtr port; /* port must be a GrafPtr to a valid port */
- {
- Rect r;
- register int rowBytes;
- r=thePort->portRect;
- rowBytes=(r.right-r.left+15)/16*2;
- OpenPort(port);
- port->portBits.baseAddr=NewPtr((long)rowBytes*(r.bottom-r.top));
- port->portBits.rowBytes=rowBytes;
- port->portBits.bounds=port->portRect=r;
- RectRgn(port->visRgn,&r);
- }
-
- /* interesting part */
-
- void Init()
- {
- register int i;
- GetDateTime(&randSeed);
- PenSize(3,3);
- for (i=0;i<numBugs;++i) {
- register int gene;
- int sum;
- bug[i].loc.h=RandomRange(FieldWidth-3);
- bug[i].loc.v=RandomRange(FieldHeight-3);
- bug[i].dir=RandomRange(6);
- sum=0;
- for(gene=0;gene<6;gene++) sum+= bug[i].gene[gene]=1<<RandomRange(10);
- bug[i].sum=sum;
- bug[i].health=InitHealth;
- }
- PenSize(1,1);
- GetPort(&windowMap);
- MakePort(&bactPort);
- EraseRect(&thePort->portRect);
- SetPort(windowMap);
- for(i=0;i<InitBact;i++) PutBacteria();
- }
-
- void DoGeneration()
- {
- register Bug *curBug;
- ObscureCursor();
- PutBacteria();
- /* Move everybody and adapt other values */
- PenSize(3,3);
- curBug=&bug[numBugs];
- do {
- MoveBug(--curBug);
- if(curBug->health < -DieTime)KillBug(curBug);
- else if(curBug->age++>SplitAge && curBug->health>StrongHealth)SplitBug(curBug);
- } while (curBug!=&bug[0]);
- PenPat(black);
- PenSize(1,1);
- }
-
- void WindowMouse(thePoint)
- Point thePoint;
- {
- register int i;
- register Bug *theBug;
- long minDist=InitMinDist;
- for(i=numBugs;i--;){
- register long d;
- if(minDist> (d=SQDist(bug[i].loc,thePoint))){
- minDist=d;
- theBug=&bug[i];
- } }
- if(minDist<InitMinDist){
- WindowRecord showWindow;
- Rect R;
- register int x;
- R.left=50;R.top=70;R.right=225;R.bottom=160;
- if(theBug->loc.h<FieldWidth/2)OffsetRect(&R,FieldWidth/2,0);
- NewWindow(&showWindow,&R,"\p",true,2,FrontMost,false,0);
- SetPort(&showWindow);
- TextFont(systemFont);
- MoveTo(10,20);
- DrawString("\pBug ");
- x=theBug-&bug[0]+1;
- #define DrawDigit(d) DrawChar('0'+(d))
- if(x>=100)DrawDigit(x/100);
- if(x>=10)DrawDigit(x/10%10);
- DrawDigit(x%10);
- DrawString("\p of ");
- x=numBugs;
- if(x>=100)DrawDigit(x/100);
- if(x>=10)DrawDigit(x/10%10);
- DrawDigit(x%10);
- #undef DrawDigit
- TextFont(geneva);
- MoveTo(10,36);
- DrawString("\pAge");
- R.left=60;R.top=28;R.right=160;R.bottom=36;
- FrameRect(&R);
- InsetRect(&R,1,1);
- R.right=R.left+Scale(80,MaxAge,theBug->age);
- if(R.right>=160){
- R.right=160;
- MoveTo(162,32);
- Line(5,0);
- Line(-2,-2);
- Move(2,2);
- Line(-2,2);
- }
- FillRect(&R,gray);
- MoveTo(60,28);
- Line(0,10);
- MoveTo(60+Scale(100,MaxAge,SplitAge),28);
- Line(0,10);
- MoveTo(10,52);
- DrawString("\pHealth");
- R.left=60;R.top=44;R.right=160;R.bottom=52;
- FrameRect(&R);
- InsetRect(&R,1,1);
- R.right=R.left+Scale(98,MaxHealth,theBug->health);
- FillRect(&R,gray);
- MoveTo(60,44);
- Line(0,10);
- MoveTo(60+Scale(100,MaxHealth,StrongHealth),44);
- Line(0,10);
- MoveTo(159,44);
- Line(0,10);
- MoveTo(10,69);
- DrawString("\pGenes");
- PenSize(2,1);
- for(x=0;x<6;x++){
- MoveTo(72+15*x,68);
- switch(theBug->gene[(x+HL)%6]){
- case 1<<0:Line(0,-1); break;
- case 1<<1:Line(0,-2); break;
- case 1<<2:Line(0,-3); break;
- case 1<<3:Line(0,-4); break;
- case 1<<4:Line(0,-5); break;
- case 1<<5:Line(0,-6); break;
- case 1<<6:Line(0,-7); break;
- case 1<<7:Line(0,-8); break;
- case 1<<8:Line(0,-9); break;
- case 1<<9:Line(0,-10); break;
- case 1<<10:Line(0,-11); break;
- }
- }
- PenSize(1,1);
- TextSize(9);
- MoveTo(68,77);DrawString("\pHL");
- MoveTo(86,77);DrawString("\pL");
- MoveTo(101,77);DrawString("\pF");
- MoveTo(116,77);DrawString("\pR");
- MoveTo(128,77);DrawString("\pHR");
- MoveTo(143,77);DrawString("\pRV");
- SetPort(windowMap);
- PenSize(3,3);
- MoveTo(theBug->loc.h,theBug->loc.v);
- while(StillDown()){
- long *junk;
- PenPat(white);
- Line(0,0);
- Delay(10,&junk);
- PenPat(black);
- Line(0,0);
- Delay(10,&junk);
- }
- CloseWindow(&showWindow);
- PenSize(1,1);
- } else SysBeep(5); /* no bug is close enough */
- }
-
- void PutBacteria(){
- register int h,v;
- /* Make a bacteria for food */
- switch(growMode){
- case growGarden:
- if(RandomRange(2)){
- h=RandomRange(50)+(FieldWidth-50)/2;
- v=RandomRange(50)+(FieldHeight-50)/2;
- break;
- }
- case growUniform:
- h=RandomRange(FieldWidth-10)+5;
- v=RandomRange(FieldHeight-10)+5;
- break;
- case growNone:
- return;
- case growPattern:
- h=bactLoc.h;
- if( (h+=307) >FieldWidth-5) h-=FieldWidth-10;
- bactLoc.h=h;
- v=bactLoc.v;
- if( (v+=113) >FieldHeight-5) v-=FieldHeight-10;
- bactLoc.v=v;
- break;
- case growCentered:
- h=RandomRange((FieldWidth-10)/2)+RandomRange((FieldWidth-10)/2)+10;
- v=RandomRange((FieldHeight-10)/2)+RandomRange((FieldHeight-10)/2)+10;
- break;
- }
- asm{ /* set the bit in our bitmap, the quick and dirty way */
- move.w v,D0
- ; sub.w bactPort.portBits.bounds.top,D0 ;this number is zero here
- mulu bactPort.portBits.rowBytes,D0
- movea.l bactPort.portBits.baseAddr,A0
- adda.l D0,A0
- move.w h,D1
- ; sub.w bactPort.portBits.bounds.left,D1 ;this number is zero here
- move.w D1,D0
- not.w D0
- lsr.w #3,D1
- bset D0,0(A0,D1.w)
- }
- /* draw the bit on screen */
- MoveTo(h,v);
- Line(0,0);
- }
-
- void MoveBug(theBug) /* assume window active, PenSize = 3,3 */
- register Bug *theBug;
- {
- register int r,dir;
- register int h,v;
- /* remove bug from old location */
- PenPat(white);
- MoveTo( h=theBug->loc.h , v=theBug->loc.v );
- Line(0,0);
- /* compute new location */
- if(theBug->health>=0) {
- { /* compute direction */
- register int *gene;
- dir=theBug->dir;
- r=RandomRange(theBug->sum);
- for(gene=&theBug->gene[6];--gene!=&theBug->gene[0];)
- if( (r-=*gene) <0)dir++;
- if(dir>=6)dir-=6;
- }
- theBug->dir=dir;
- /* move in new direction */
- if( (h+=hmove[dir]) <0)h+=FieldWidth-2;
- else if(h>FieldWidth-3)h-=FieldWidth-2;
- if( (v+=vmove[dir]) <0)v+=FieldHeight-2;
- else if(v>FieldHeight-3)v-=FieldHeight-2;
- }
- SetPort(&bactPort);
- /* eat bacteria, adapt health */
- /* if(CountPixels(h,v)!=GetPixel(h,v)+GetPixel(h+1,v)+GetPixel(h+2,v)+
- GetPixel(h,v+1)+GetPixel(h+1,v+1)+GetPixel(h+2,v+1)+
- GetPixel(h,v+2)+GetPixel(h+1,v+2)+GetPixel(h+2,v+2)){
- SysBeep(1);SysBeep(1);SysBeep(1);Debugger();}*/
- r=CountPixels(h,v)*HealthPerBact+theBug->health-1;
- if(r>MaxHealth)r=MaxHealth;
- SetPort(windowMap);
- if(r>0 || (r>=-DieTime && r%2==0) ){
- /* draw bug at new location */
- PenPat(black);
- MoveTo( theBug->loc.h=h , theBug->loc.v=v );
- Line(0,0);
- }
- theBug->health=r;
- }
-
- void KillBug(theBug)
- Bug *theBug;
- {
- *theBug=bug[--numBugs];
- }
-
- void SplitBug(theBug)
- Bug *theBug;
- {
- register int i,r,g,sum;
- if(numBugs<MaxBugs){
- bug[numBugs++]=*theBug;
- r=RandomRange(6);
- g=theBug->gene[r];
- g*=2;
- theBug->gene[r]=g;
- if(g>1024)
- for(i=F;i<=L;i++)
- theBug->gene[i]/=2;
- theBug->health/=2;
- theBug->age=0;
- sum=0;
- for(i=F;i<=L;i++)
- sum+=theBug->gene[i];
- theBug->sum=sum;
- r=RandomRange(6);
- g=(theBug=&bug[numBugs-1])->gene[r];
- g/=2;
- theBug->gene[r]=g;
- theBug->health/=2;
- theBug->age=0;
- sum=0;
- for(i=F;i<=L;i++)sum+=theBug->gene[i];
- theBug->sum=sum;
- } else SysBeep(20);
- }
-
- unsigned int RandomRange(/* unsigned int top */)
- /* Produces random number 0..top-1 */
- { asm{
- clr.w -(SP)
- _Random
- move.w (SP)+,D0
- mulu 4(SP),D0
- swap D0
- } }
-
- long int SQDist(/* Point a,b */)
- /* square of distance. Is awful to write in C (takes 67 instead of 8 instructions!) */
- { asm{
- move.w 4(SP),D0 ;b.v
- sub.w 8(SP),D0 ;a.v
- muls D0,D0
- move.w 6(SP),D1 ;b.h
- sub.w 10(SP),D1 ;a.h
- muls D1,D1
- add.l D1,D0
- } }
-
- int Scale(a, b, c)
- int a, b, c;
- { /* this routine computes the rounded value of (c*(a/b)).
- This is done as (c*a+b/2)/b, where intermediate results are long. */
- asm{
- move.w a,D0 ;get a
- muls c,D0 ;a*c
- move.w b,D1 ;get b
- ext.l D1
- divs #2,D1 ;b/2
- ext.l D1
- add.l D1,D0 ;c*a+b/2
- divs b,D0 ;result
- } }
-
- int CountPixels(h,v)
- register int h;
- int v;
- { register bit;
- /* count the number of pixels that would fall under a 3*3 pen at location h,v */
- /* also clear the pixels */
- asm{
- movea.l thePort,A0
- move.w v,D0
- ; compensation for origin is not neccesary here
- ; sub.w OFFSET(GrafPort,portBits.bounds.top) (A0),D0
- ; sub.w OFFSET(GrafPort,portBits.bounds.left),bit
- mulu OFFSET(GrafPort,portBits.rowBytes) (A0),D0
- movea.l OFFSET(GrafPort,portBits.baseAddr) (A0),A1
- adda.l D0,A1 ;address of the top row
- clr.w D0 ;for summing up the result
- move.w #3-1,D2 ;three rows, you know?
-
- @0 move.w h,bit
- not.w bit ;lower three bits are bit #
- move.w h,D1
- lsr.w #3,D1 ;offset for byte in row
- btst bit,0(A1,D1.w) ;test h,v
- beq.s @1
- add.w #1,D0
- bclr bit,0(A1,D1.w) ;reset the bit
- @1 sub.w #1,bit ;h+1
- move.w h,D1
- add.w #1,D1
- lsr.w #3,D1 ;offset for byte in row
- btst bit,0(A1,D1.w) ;test h+1,v
- beq.s @2
- add.w #1,D0
- bclr bit,0(A1,D1.w) ;reset the bit
- @2 sub.w #1,bit ;h+2
- move.w h,D1
- add.w #2,D1
- lsr.w #3,D1 ;offset for byte in row
- btst bit,0(A1,D1.w) ;test h+2,v
- beq.s @3
- add.w #1,D0
- bclr bit,0(A1,D1.w) ;reset the bit
- @3 move.w OFFSET(GrafPort,portBits.rowBytes) (A0),bit
- ext.l bit
- adda.l bit,A1 ;bit=thePort->portBits.rowBytes here
-
- dbra d2,@0
- ;ready!
- } }