page@swan.ulowell.edu (Bob Page) (12/07/88)
Submitted-by: ain@k.cc.purdue.edu (Pat-bob White) Posting-number: Volume 2, Issue 94 Archive-name: fun/evo-life.1 Here is an evolving life program I wrote several years ago in AmigaBasic. I have other (extended) versions is anybody is interested. # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. #----cut here-----cut here-----cut here-----cut here----# #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # readme # evolving_life_140 # tracefile # This archive created: Tue Nov 22 11:08:37 1988 # By: Pat-bob White (PUCC Land, USA) cat << \SHAR_EOF > readme These two programs go together and were written in Amiga Basic.. first a bit about what they are and what they do: Evolving Life 140 is a program I wrote about 2 years ago when I wanted to explore the concept of genetic learning. I abstracted real life into an environment (sort of an electronic petri dish) and amoebe-like cells (sort of e-amoeba). The environment consists of a 24x38 matrix of locations that cells can occupy. Each location has a food level which is incremented by one each step, and is decrememnted when the cells eat. The levels within the locations are represented by a spectrum from red to blue (red = lots of food). One step is executed from each cell, then the environment is updated.. repeats till all cells die or the machine crashes :-) The cells are represented by black dots in the locations. They run "genetic programs" that have 6 different kind of steps: move, reproduce, eat, goto, if hungry, and nop -- each step uses up internalized food. Move: a cell tries to move to a (randomly selected) adjacent empty location, Reproduce: reproduction is sexual; A cell on the make first finds a mate (a cell in an adjacent location), then an empty adjacent location for the child cell, and finally, creates the child cell's "genetic program" by copying half from it's own program, and half from the mate's program. Then, 1 random step in the new program is changed to a random value (on the theory that mutations are good for the soul :-) eat: internalize food from the environment (if there is any), goto: goto a new step in the program (goes back to my own theory that loops are essential to any programming language), if hungry: if internalized food level is below some amount, then go to some program step (same theory).. since I used bytes to represent each step, this put a limt of 32 steps on the size of a program (I found out that a cell only uses about 8 steps, but needed about 16 locations to save them -- the other 8 steps being spent as wasted space... when I tried to shorten the program to only 8 steps, I couldn't get colonies to start). nop: every language needs one.. and what else was I to do with the other 3 3-bit patterns I wasn't using? There is a special cell that I used for keeping a tracefile -- cell 1. (I represented the cells with an array of strings, so this was the cell in the first position of the array). This cells is marked with a red dot in it's center, and each time a cell that is born reuses that array location, I write a snapshot of the current matrix, and where the cells are into the tracefile. The tracefile program is the other one here, and all it does is display a tracefile on the screen. It's comments document it fairly well, so I won't bother to say anthing else about it. implementation: Both programs are implemented in Amiga Basic, and I use the graphics heavily.. so a bit of description about Amiga graphics is in order for those who don't (aren't fortunate enough? :-) have an Amiga... (those who have an Amiga may find my terminology confusing since much of it is from the meager graphics background I have) The Amiga has screens and windows. A screen is where the colors (bitplanes) are defined, and the actual storage is attached to. A window is a graphics viewport onto a screen that has it's own internal coordinate system.. when something is drawn into a window, the OS performs any necessary clipping and coordinate translation, and then actually renders it into the screen's memory (therefore, windows appear to have their own memory but really don't). Therefore, I open a new 32-color screen to get access to a whole set of 32 colors, open a window on it to render into, and then start rendering. One confusing call (basic's fault, not mine) is the line drawing command, which I use to draw filled rectangles by specifying the upper left and lower right corners of the rectangle, and adding the "bf" option to draw a box and fill it. PALLETTE sets a color in the pallette. POINT returns the color of the referenced pixel. LOCATE moves the cursor to the desired character position. CVI converts a two byte string to the integer it represents in binary (it's much the same as "asc() * 256 + asc()"). MKI$ is the opposite of CVI -- converts an integer into a string containing the 2-byte binary representation. These are the comments from the evolving life program and it's corresponding tracefile program: Evolving Life 140 ----------------- max number of cells is 140 cells are kept as basic strings there are 8 types of program steps a cell program knows: move, reproduce, eat, goto, if hungry, and 3 nop's. move moves a cell in any 1 of te 8 possible directions, eat takes food from the environment, goto unconditionally branches to another step i the program, if hungry branches if the cell is hungry, and nop's do nothing. All actions cause food consumption of the internalized food (via eat). death is caused by falling off the end of the program, running out of food, or executing more than the max number of steps allowed (the lifespan). A trace snapshot is written to a file each time a cell is born into cell 1's storage (the cell storage is reused since I have only a small number of them -- so, everytime a cell is born that reuses storage position 1, a snapshot is taken). The snapshots files are generally large (>200K) and are viewed through the "Tracefile" program. Food levels are represented by a spectrum of colors -- red is max food, dark blue is min (somehow, purple slipped in as a high-but-not- max color for food.. don't really know why, but I suspect it is because I didn't want it to feel lonely :-) The program runs till all the cells die.. alt least it is supposed to do that. The stats at the bottome are: nc = number of currently alive cells, tc = total number of cells that have ever lived or are currently living, ns = total number of cell-program steps have been done. Have fun reading the code -- it is commented but mostly consists of mid$ accesses to get characters out of the cell storage -- the cell's program steps are stored as characters in a basic string. Tracefile --------- Tracefile -- written to display the trace files saved by Evolving Live 140. Written sometime arround 6/86 Patrick White currently (11/88) reachable at ain@k.cc.purdue.edu displays the tracefile -- one frame at a time. the stats at the bottom of the picture are: which screen is being displayed, (nc) the number of cells on the screen, (tc) the total number of cells that have ever lived, (ns) the total number of cell-program-steps that have been executed. The colors on the screen indicate: red is most food, dark blue is least food. THe outer square is the fool level scaled on a screen-wise basis with red being greatest amount of food. The inner square is the food level scaled from 0 to max possible - again red is max. I did it this way because I couldn't decided how to do it -- so, naturally, I did it both ways :-) When finished with the data file, the program waits for a mouse-click in the display window before closing the window/screen. If you stop the program in any other way, the screen and window will be left open and take memory -- best to let it finish on it's own. Oh, BTW, trace snapshots are taken whenever a cell is born into cell 1 (since I have to reuse the cell locations, this makes soms sense). I also have a version in C for the Amiga somewhere -- I didn't submit it because I'm not sure if it even compiles anymore -- if you want it, let me know and I'll send it to you. Well, that's about it... (I think it's documented enough now :-) If you have any questions, please ask... -- Pat White ARPA/UUCP: k.cc.purdue.edu!ain BITNET: PATWHITE@PURCCVM PHONE: (317) 743-8421 U.S. Mail: 320 Brown St. apt. 406, West Lafayette, IN 47906 SHAR_EOF cat << \SHAR_EOF > evolving_life_140 REM Evolving Life 140 (140 cells max) REM Patrick White written -- I have no idea but somewhere around 6/86 REM currently (11/88) reachable at ain@k.cc.purdue.edu REM max number of cells is 140 REM cells are kept as basic strings REM there are 8 types of program steps a cell program knows: move, REM reproduce, eat, goto, if hungry, and 3 nop's. move moves a cell in REM any 1 of te 8 possible directions, eat takes food from the environment, REM goto unconditionally branches to another step i the program, if hungry REM branches if the cell is hungry, and nop's do nothing. All actions REM cause food consumption of the internalized food (via eat). REM death is caused by falling off the end of the program, running out REM of food, or executing more than the max number of steps allowed (the REM lifespan). REM A trace snapshot is written to a file each time a cell is born into REM cell 1's storage (the cell storage is reused since I have only a small REM number of them -- so, everytime a cell is born that reuses storage REM position 1, a snapshot is taken). The snapshots files are generally REM large (>200K) and are viewed through the "Tracefile" program. REM Food levels are represented by a spectrum of colors -- red is max REM food, dark blue is min (somehow, purple slipped in as a high-but-not- REM max color for food.. don't really know why, but I suspect it is because REM I didn't want it to feel lonely :-) REM The program runs till all teh cells die.. alt least it is supposed to REM do that. REM The stats at teh bottome are: nc = number of currently alive cells, REM tc = total number of cells that have ever lived or are currently living, REM ns = total number of cell-program steps have been done. REM Have fun reading the code -- it is commented but mostly consists of REM mis$ accesses to get characters out of the cell storage -- the cell's REM program steps are stored as characters in a basic string. REM life CLEAR,20000,5000 RANDOMIZE TIMER INPUT "filename of trace file ";filename$ : OPEN "O",#1,filename$ ncels = 139 : nstps = 50 : clrs = 16 : alimit = 94 : ns& = 0 nc% = 0 : tc& = 0 DIM food%(37,23), cell$(ncels) REM initialize screen SCREEN 1,300,200,4,1 WINDOW 2,"Evolving Life Display",(0,0)-(290,180),0,1 PALETTE 0, 0,0,0 : PALETTE 1, 0,0,1 : PALETTE 2, 0,.29,1 PALETTE 3, 0,.57,1 : PALETTE 4, 0,.86,1 : PALETTE 5, 0,1,.86 PALETTE 6, 0,1,.57 : PALETTE 7, 0,1,.29 : PALETTE 8, 0,1,0 PALETTE 9, .29,1,0 : PALETTE 10,.57,1,0 : PALETTE 11,.86,1,0 PALETTE 12,1,.86,0 : PALETTE 13,1,.57,0 : PALETTE 14,1,.29,0 PALETTE 15,1,0,0 : CLS : mf = 8510 : stp = mf / (clrs - 2) FOR i = 0 TO 37 FOR j = 0 TO 23 food%(i,j) = i*j*10 LINE (i*7,j*7)-(i*7+6,j*7+6),(INT(food%(i,j) / stp) + 1),bf NEXT j NEXT i FOR i = 9 TO 15 : LINE (266,i*7+2)-(272,i*7+4),15,bf : NEXT i REM initialize cells FOR c = 0 TO 69 x = INT (RND * 38) : y = INT (RND * 24) IF POINT(x*7+3,y*7+3) > 0 THEN a% = INT (RND * mf) : b% = INT (RND * mf / 2) cell$(c) = CHR$(x) + CHR$(y) + MKI$(a%) + MKI$(b%) + MKI$(INT(RND*alimit)) + MKI$(0) LINE (x*7+2,y*7+2)-(x*7+4,y*7+4),0,bf FOR i = 1 TO nstps : x = INT (RND * 256) : cell$(c) = cell$(c) + CHR$(x) : NEXT i nc% = nc% + 1 END IF NEXT c tc& = nc% LOCATE 22,1 : PRINT "fre= ";FRE(0);FRE(-1);FRE(-2); GOSUB pic REM quit stuff ON ERROR GOTO e ON MOUSE GOSUB quit MOUSE ON runagain: REM run cell programs REM increment food in environment PALETTE 0,.2,.2,.2 : ns& = ns& + 1 FOR i = 0 TO 37 FOR j = 0 TO 23 a% = food%(i,j) : food%(i,j) = a% + 1 : IF food%(i,j) > mf THEN food%(i,j) = mf IF INT(a% / stp) <> INT(food%(i,j) / stp) THEN x = POINT(i*7+3,j*7+3) : LINE (i*7,j*7)-(i*7+6,j*7+6),INT(food%(i,j) / stp) + 1,bf IF x = 0 THEN LINE (i*7+2,j*7+2)-(i*7+4,j*7+4),0,bf END IF NEXT j NEXT i PALETTE 0,0,0,0 FOR c = 0 TO ncels IF cell$(c) <> "" THEN x = ASC( MID$( cell$(c),1,1)) : y = ASC( MID$( cell$(c),2,1)) IF (CVI( MID$(cell$(c), 7,2)) > alimit) OR (CVI( MID$( cell$(c), 3, 2)) < 0) THEN LINE (x*7,y*7)-(x*7+6,y*7+6),INT(food%(x,y)/ stp)+1, bf cell$(c) = "" : nc% = nc% - 1 ELSE MID$(cell$(c), 7,2) = MKI$( CVI( MID$(cell$(c),7,2)) + 1) IF (y > 8) AND (y < 16) THEN MID$( cell$(c), 11 + INT(RND*(LEN(cell$(c))-10)), 1) = CHR$( INT(RND*256) ) a% = CVI( MID$( cell$(c), 9, 2) ) : y = ASC( MID$( cell$(c), 11 + a%, 1) ) a% = a% + 1 IF a% > (LEN( cell$(c)) - 11) THEN : MID$( cell$(c), 7, 2) = MKI$( alimit + 1) : a% = 0 MID$( cell$(c), 9, 2) = MKI$( a% ) ON (y AND 7)+1 GOSUB mov, repro, eat, gto, ifhung, nop, nop, nop END IF END IF NEXT c LOCATE 22,1 : PRINT "nc="; nc%;" tc=";tc&;" ns=";ns&; GOTO runagain e: WINDOW CLOSE 2 : SCREEN CLOSE 1 : CLOSE #1 : prinr ERR END pic: REM store a picture of the screen WRITE #1,mf;stp;ns&;tc&;nc%;cell$(0) FOR i = 0 TO 37 WRITE #1,food%(i,0);food%(i,1);food%(i,2);food%(i,3);food%(i,4);food%(i,5);food%(i,6);food%(i,7);food%(i,8);food%(i,9);food%(i,10);food%(i,11) WRITE #1,food%(i,12);food%(i,13);food%(i,14);food%(i,15);food%(i,16);food%(i,17);food%(i,18);food%(i,19);food%(i,20);food%(i,21);food%(i,22);food%(i,23) NEXT i FOR i = 0 TO ncels : IF cell$(i) <> "" THEN WRITE #1,MID$(cell$(i),1,2) NEXT i RETURN quit: REM end WINDOW CLOSE 2 : SCREEN CLOSE 1 : CLOSE #1 PALETTE 0, 0,.5,1 : PALETTE 1, 1,1,1 : PALETTE 2, 0,0,0 PALETTE 3, 1,.73,0 END nop: REM no op MID$(cell$(c), 3, 2) = MKI$( CVI( MID$( cell$(c), 3, 2)) - 10) : RETURN mov: REM move in specified direction MID$( cell$(c), 3, 2) = MKI$( CVI( MID$( cell$(c), 3,2) ) - 20) REM erase old cell position a% = (y AND 224) / 32 x = ASC( MID$( cell$(c), 1, 1) ) : y = ASC( MID$( cell$(c), 2, 1) ) : x1 = x : y1 = y LINE (x*7,y*7)-(x*7+6,y*7+6),INT( food%(x,y) / stp) + 1,bf REM pick direction and move cell if possible IF a% = 0 THEN x = x - 1: y = y - 1 IF a% = 1 THEN y = y - 1 IF a% = 2 THEN y = y - 1: x = x + 1 IF a% = 3 THEN x = x + 1 IF a% = 4 THEN x = x + 1: y = y + 1 IF a% = 5 THEN y = y + 1 IF a% = 6 THEN y = y + 1: x = x - 1 IF a% = 7 THEN x = x - 1 x = (x + 38) MOD 38 y = (y + 24) MOD 24 IF POINT( x*7 + 3, y*7 + 3) <> 0 THEN MID$(cell$(c), 1, 1) = CHR$( x ) : MID$(cell$(c), 2, 1) = CHR$( y ) LINE (x*7+2, y*7+2)-(x*7+4,y*7+4),0,bf ELSE LINE (x1*7+2, y1*7+2)-(x1*7+4, y1*7+4),0,bf END IF RETURN repro: REM reproduce if a cell nearby MID$( cell$(c), 3, 2) = MKI$( CVI( MID$( cell$(c), 3, 2)) - 60) IF nc% = ncels THEN RETURN REM determine which cell to mate with a% = 0 : b% = 0 : x1 = ASC( MID$( cell$(c),1,1)) : y1 = ASC( MID$( cell$(c),2,1)) l1: IF (cell$(a%) <> "") AND (a% <> c) THEN x = ASC( MID$( cell$(a%), 1, 1) ) : y = ASC( MID$( cell$(a%), 2, 1) ) IF ((x<x1-1) OR (x>x1+1) OR (y<y1-1) OR (y>y1+1)) AND (a% < ncels) THEN a%=a%+1 : b% = 1 ELSE IF a% < ncels THEN a% = a% + 1 : b% = 1 END IF IF b% = 1 THEN b% = 0 : GOTO l1 IF (c = a%) OR (cell$(a%) = "") OR (x<x1-1) OR (x>x1+1) OR (y<y1-1) OR (y>y1+1) THEN RETURN REM determine free cell b% = 0 l2: IF (cell$(b%) <> "") AND (b% < ncels) THEN b% = b% + 1 : GOTO l2 IF cell$(b%) <> "" THEN RETURN REM find empty spot for child cell c% = x1 : d% = y1 FOR i = 1 TO 15 x = INT(RND * 3) - 1 + x1 : y = INT(RND * 3) - 1 + y1 IF ((POINT(x*7+3,y*7+3) > 0) AND (x > 0) AND (x < 38) AND (y > 0) AND (y < 24)) THEN c% = x : d% = y NEXT i IF ((c% = x1) AND (d% = y1)) THEN RETURN REM create child nc% = nc% + 1 : tc& = tc& + 1 x = LEN( cell$(c) ) : y = LEN( cell$(a%) ) cell$(b%) = MID$(cell$(c), 1, INT(x / 2) ) + MID$(cell$(a%), INT(y / 2), y - INT(y / 2) ) MID$(cell$(b%), 1, 1) = CHR$(c%) : MID$(cell$(b%), 2, 1) = CHR$(d%) LINE (c%*7+2,d%*7+2)-(c%*7+4,d%*7+4),0,bf a% = INT( CVI( MID$( cell$(c), 3, 2) ) / 2) MID$( cell$(c), 3, 2) = MKI$( a% ) : MID$( cell$(b%), 3, 2)= MKI$( a% ) MID$(cell$(b%), 7, 2) = MKI$( 0 ) : MID$(cell$(b%), 9, 2) = MKI$( 0 ) REM mutate one step in child REM x = LEN( cell$(b%) ) REM a = RND : b = RND : MID$( cell$(b%), 11 + INT( a * (x - 10)), 1) = CHR$( INT( b * 256 )) IF b% = 0 THEN GOSUB pic RETURN eat: REM eat food MID$( cell$(c), 3, 2) = MKI$( CVI( MID$( cell$(c), 3, 2)) - 20) x = ASC( MID$( cell$(c), 1, 1)) : y = ASC( MID$( cell$(c), 2, 1)) IF food%(x,y) > 100 THEN food%(x,y) = food%(x,y) - 100 MID$( cell$(c), 3,2) = MKI$( CVI( MID$(cell$(c),3,2)) + 100) ELSE MID$( cell$(c), 3,2) = MKI$( CVI( MID$(cell$(c),3,2)) + food%(x,y)) food%(x,y) = 0 END IF LINE (x*7,y*7)-(x*7+6,y*7+6),INT(food%(x,y)/ stp) + 1,bf LINE (x*7+2,y*7+2)-(x*7+4,y*7+4),0,bf RETURN gto: REM jump in program MID$( cell$(c), 3, 2) = MKI$( CVI( MID$( cell$(c), 3, 2)) - 20) a% = CVI( MID$( cell$(c), 9, 2)) : a% = ASC( MID$( cell$(c), a% + 11, 1)) MOD (LEN(cell$(c)) - 10) MID$( cell$(c), 9,2) = MKI$( a% ) RETURN ifhung: REM if hungry then jump in program MID$( cell$( c), 3,2) = MKI$( CVI( MID$( cell$(c), 3,2)) -40) a% = CVI( MID$( cell$(c), 9, 2)) : x = ASC( MID$( cell$(c), a% + 11, 1)) MOD (LEN(cell$(c)) - 10) IF CVI( MID$( cell$(c), 3,2)) < CVI( MID$( cell$(c), 5,2)) THEN a% = x ELSE a% = a% + 1 IF a% > (LEN(cell$(c)) - 11) THEN a% = 0 MID$( cell$(c), 9, 2) = MKI$( a% ) RETURN SHAR_EOF cat << \SHAR_EOF > tracefile REM Tracefile -- written to display the trace files saved by REM Evolving Live 140. Written sometime arround 6/86 REM Patrick White REM currently (11/88) reachable at ain@k.cc.purdue.edu REM displays the tractefile -- one frame at a time. the stats at the bottom REM of the picture are: which screen is being displayed, (nc) the number of REM cells on the screen, (tc) the total number of cells that have ever REM lived, (ns) the total number of cell-program-steps that have been REM executed. REM The colors on the screen indicate: red is most food, dark blue is least REM food. THe outer square is the fool level scaled on a screen-wise basis REM with red being greatest amount of food. The inner square is the food REM level scaled from 0 to max possible - again red is max. I did it this REM way because I couldn't decided how to do it -- so, naturally, I did it REM both ways :-) REM When finished with the data file, the program waits for a mouse-click REM in the display window before closing the window/screen. If you stop REM the program in any other way, the screen and window will be left open REM and take memory -- best to let it finish on it's own. REM Oh, BTW, trace snapshots are taken whenever a cell is born into cell 1 REM (since I have to reuse the cell locations, this makes soms sense). REM trace viewer INPUT "trace filename";filename$ OPEN "I",#1,filename$ DIM f%(37,23) sc = 0 SCREEN 1,300,200,4,1 WINDOW 2,filename$,(0,0)-(290,180),0,1 PALETTE 0, 0,0,0 : PALETTE 1, 0,0,1 : PALETTE 2, 0,.29,1 PALETTE 3, 0,.57,1 : PALETTE 4, 0,.86,1 : PALETTE 5, 0,1,.86 PALETTE 6, 0,1,.57 : PALETTE 7, 0,1,.29 : PALETTE 8, 0,1,0 PALETTE 9, .29,1,0 : PALETTE 10, .57,1,0 : PALETTE 11, .86,1,0 PALETTE 12, 1,.86,0 : PALETTE 13, 1,.57,1 : PALETTE 14, 1,.29,0 PALETTE 15, 1,0,0 nxt: sc = sc + 1 INPUT #1,mf,stp,ns&,tc&,nc% c$ = INPUT$ (64, #1) : c$ = MID$( c$, 3, 60) REM PRINT nc%;"***";c$;"***" FOR i = 0 TO 37 INPUT #1,f%(i,0),f%(i,1),f%(i,2),f%(i,3),f%(i,4),f%(i,5),f%(i,6),f%(i,7),f%(i,8),f%(i,9),f%(i,10),f%(i,11) INPUT #1,f%(i,12),f%(i,13),f%(i,14),f%(i,15),f%(i,16),f%(i,17),f%(i,18),f%(i,19),f%(i,20),f%(i,21),f%(i,22),f%(i,23) NEXT i max = f%(0,0) FOR i = 0 TO 37 : FOR j = 0 TO 23 IF f%(i,j) > max THEN max = f%(i,j) NEXT j : NEXT i mstp = max / 14 FOR i = 0 TO 37 FOR j = 0 TO 23 LINE (i*7,j*7)-(i*7+6,j*7+6),INT(f%(i,j) / mstp) + 1,b LINE (i*7+1,j*7+1)-(i*7+5,j*7+5),INT(f%(i,j) / stp) + 1,bf NEXT j NEXT i FOR i = 1 TO nc% a$ = INPUT$ (5,#1) : x = ASC( MID$( a$, 2,1)) : y = ASC( MID$( a$, 3,1)) REM PRINT i;"***";a$;"***",x, y LINE (x*7+2,y*7+2)-(x*7+4,y*7+4),0,bf IF i = 1 THEN LINE (x*7+3,y*7+3)-(x*7+3,y*7+3),15,bf NEXT i LOCATE 22,1 : PRINT sc;"nc= ";nc%;"tc= ";tc&;"ns= ";ns&; IF EOF(1) THEN GOTO cnt ELSE GOTO nxt cnt: BEEP ON MOUSE GOSUB quit MOUSE ON slp: SLEEP GOTO slp quit: CLOSE #1 : WINDOW CLOSE 2 : SCREEN CLOSE 1 PALETTE 0, .4,.6,1 : PALETTE 1, 1,1,1 : PALETTE 2, 0,0,0 PALETTE 3, 1,.73,0 END SHAR_EOF # End of shell archive exit 0 -- Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page Have five nice days.