package rst.biowar; /** * @author rst * * simple program that simulates a stable (biophysical) balance * * there are two types of movable entities: * * entity A consumes positive energy and leaves * antienergy (negative energy or mass) * * entity B consumes antienenergy (or mass) * and leaves energy * * * * on a certain native energy level the entities reproduce. * when their energy sinks lower than a minimum level they die and disappear * */ import java.applet.Applet; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.MediaTracker; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.IndexColorModel; import java.awt.image.MemoryImageSource; import java.awt.image.PixelGrabber; public class BioAppletA extends Applet { //APPLET stuff int width= 600; int height= 200; IndexColorModel icm; byte[][] paletteTable; byte[] pixels; MemoryImageSource source; private Image img; // offscreen stuff... //Application stuff final boolean draw= true; // flag to disable drawing (speed!) boolean info= false; int skip= 0; // how many frames are to be skipped. int reproenergy= 350; // energy when the fuzzles reproduce int deathenergy= -1000; // energy when the fuzzles die int numfuzzle= 100; // initial size of population int numpop= 0; // holds size of population int numtotal= 0; // total number of all fuzzles private int[] walls; // rst-walls, currently capital "RST" int dx[]= { 0, 1, 0, -1 }; // clockwise direction constants: int dy[]= { -1, 0, 1, 0 }; // 0=up, 1=right,2=down,3=left int field[][]= new int[width][height]; //energy field Anim anim; class Anim extends Thread // animation class { class Fuzzle // nasty nasty nested classes :-) { public boolean active; // dead or alive public boolean moved; // public int x= 0; // x- pos public int y= 0; // y- pos public int dir; // move direction (see above) public int energy= 0; public Fuzzle() // constructor { init(); } public void init() // set to unused { active= false; x= width / 2; y= height - 5; // starting direction dir= getRandom(4); energy= 0; } void move(int walls[]) { moved= true; // changes dir on possibility of 0.5 if (getRandom(3) > 1) { if (getRandom(2) > 0) dir= (dir + 3) & 3; else dir= (dir + 1) & 3; } int x1= x + dx[dir]; int y1= y + dy[dir]; if (!checkRange(x1, y1)) { moved= false; } else { if (!checkWall(walls, x1, y1)) { setPixel(x1, y1, 12); moved= false; } else { x= x1; y= y1; } } } /** range check, true if ok */ public boolean checkRange(int x, int y) { return (x >= 0) && (x < width) && (y >= 0) && (y < height); } /** wall check (rst outlined), draws a pixel when on a wall */ public boolean checkWall(int walls[], int x, int y) { return ((walls[x + y * width] & 0xffffff) == 0); } /** eat function for fuzzles for type A */ public void eatA(int field[][]) { if (!moved) return; energy -= 4; energy += field[x][y]; // fieldenergy if (field[x][y] > 0) field[x][y]--; } /** eat function for fuzzles for type B */ public void eatB(int field[][]) { if (!moved) return; energy -= 4; energy += 8 - field[x][y]; // fieldenergy if (field[x][y] < 8) field[x][y]++; } }; /** simple fast object pool. * does not use slow NEW and implicit DELETE on the java heap, * marks them unused instead */ class Pool { int MAX= 10000; int minnum= 0; // points to the lowest unused fuzzle int maxnum= 0; int num= 0; public Fuzzle fuzzle[]; public Pool(int num) { fuzzle= new Fuzzle[MAX]; // we need 2 loops here for (int n= 0; n < MAX; n++) { fuzzle[n]= new Fuzzle(); } for (int n= 0; n < num; n++) { alloc(); } } // alloc a new fuzzle public int alloc() { if (num == MAX) return -1; for (int n= minnum; n < MAX; n++) { if (!fuzzle[n].active) { fuzzle[n].init(); fuzzle[n].active= true; minnum= n; num++; if (maxnum < n) maxnum= n; return n; } } return -1; } void free(int nr) { if (!fuzzle[nr].active) return; if (num <= 0) return; num--; fuzzle[nr].active= false; if (minnum > nr) minnum= nr; if (nr == maxnum) for (int n= nr;(!fuzzle[n].active); n--) maxnum--; } }; // constructor public Anim() { } /** thread function */ public void run() { pixels= new byte[width * height]; /** initialize some rgb colors */ paletteTable= new byte[3][256]; //green land pixels paletteTable[1][0]= 0; paletteTable[1][1]= 0x10; paletteTable[1][2]= 0x20; paletteTable[1][3]= 0x30; paletteTable[1][4]= 0x40; paletteTable[1][5]= 0x50; paletteTable[1][6]= 0x60; paletteTable[1][7]= 0x70; paletteTable[1][8]= (byte) 0x80; paletteTable[1][9]= (byte) 0xd0; paletteTable[0][9]= (byte) 0x70; paletteTable[2][9]= (byte) 0x70; // fuzzle type A paletteTable[0][10]= (byte) 0xff; paletteTable[1][10]= (byte) 0xff; paletteTable[2][10]= (byte) 0xff; // fuzzle type B paletteTable[0][11]= (byte) 0xff; paletteTable[1][11]= (byte) 0x8f; paletteTable[2][11]= (byte) 0xff; // outline mask rst paletteTable[0][12]= (byte) 0xff; paletteTable[1][12]= (byte) 0xff; paletteTable[2][12]= (byte) 0xff; // pixelbuffer icm= new IndexColorModel( 8, 256, paletteTable[0], paletteTable[1], paletteTable[2]); source= new MemoryImageSource(width, height, icm, pixels, 0, width); source.setAnimated(true); img= createImage(source); Pool pA= new Pool(numfuzzle); Pool pB= new Pool(numfuzzle); for (int x= 0; x < width; x++) { for (int y= 0; y < height; y++) { field[x][y]= getRandom(8); field[x][y]= 4 + (int) (Math .sin(x / 70 * Math.sin(Math.cos(y / 60))) * 2.0); field[x][y]= 0; setPixel(x, y, field[x][y]); } } // counts frames, skips drawing of n frames on turbo mode int count= 0; while (true) { // all A-fuzzles for (int n= 0; n < pA.maxnum; n++) { if (pA.fuzzle[n].active) { if (draw) { setPixel( pA.fuzzle[n].x, pA.fuzzle[n].y, field[pA.fuzzle[n].x][pA.fuzzle[n].y]); } // move pA.fuzzle[n].move(walls); // energy pA.fuzzle[n].eatA(field); // draw if (draw) setPixel(pA.fuzzle[n].x, pA.fuzzle[n].y, 10); if (pA.fuzzle[n].energy > reproenergy) { pA.fuzzle[n].energy= 0; int n2= pA.alloc(); if (n2 > 0) { pA.fuzzle[n2].x= pA.fuzzle[n].x; pA.fuzzle[n2].y= pA.fuzzle[n].y; } } if (pA.fuzzle[n].energy < deathenergy) { setPixel( pA.fuzzle[n].x, pA.fuzzle[n].y, field[pA.fuzzle[n].x][pA.fuzzle[n].y]); pA.free(n); } } } // all B-fuzlles for (int n= 0; n < pB.maxnum; n++) { if (pB.fuzzle[n].active) { if (draw) setPixel( pB.fuzzle[n].x, pB.fuzzle[n].y, field[pB.fuzzle[n].x][pB.fuzzle[n].y]); // move pB.fuzzle[n].move(walls); // energy pB.fuzzle[n].eatB(field); // draw if (draw) setPixel(pB.fuzzle[n].x, pB.fuzzle[n].y, 11); if (pB.fuzzle[n].energy > reproenergy) { pB.fuzzle[n].energy= 0; int n2= pB.alloc(); if (n2 > 0) { pB.fuzzle[n2].x= pB.fuzzle[n].x; pB.fuzzle[n2].y= pB.fuzzle[n].y; } } if (pB.fuzzle[n].energy < deathenergy) { setPixel( pB.fuzzle[n].x, pB.fuzzle[n].y, field[pB.fuzzle[n].x][pB.fuzzle[n].y]); pB.free(n); } } } // avoid dying of all type A-fuzzles! if (pA.num < 10) pA.alloc(); // avoid dying of all type B-fuzzles! if (pB.num < 10) pB.alloc(); if (count++ > skip) { count= 0; numtotal= pA.num + pB.num; paletteTable[0][9]= (byte) getRandom(255); source.newPixels(); repaint(); } // dont really need Thread.yield(); } } }; public void setPixel(int x, int y, int colorindex) { pixels[x + width * y]= (byte) colorindex; } public void stop() { anim.stop(); } public void init() { this.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { int k= e.getKeyCode(); System.out.println("Key" + e.getKeyCode()); if (k == 84) //turbo { if (skip == 0) skip= 50; else skip= 0; } if (k == 73) //info if (info) info= false; else info= true; } }); anim= new Anim(); String name= getParameter("imagename"); Image picture= getImage(getDocumentBase(), name); MediaTracker mediaTracker= new MediaTracker(this); try { mediaTracker.addImage(picture, 0); mediaTracker.waitForID(0); } catch (Exception e) { } int picWidth= picture.getWidth(this); int picHeight= picture.getHeight(this); // allocate enough storage for the pixel data walls= new int[width * height]; System.out.println("pixwith=" + picWidth); System.out.println("pixheight=" + picHeight); try { PixelGrabber grabber= new PixelGrabber(picture, 0, 0, width, height, walls, 0, width); // copy the data to the pixel array grabber.grabPixels(); } catch (Exception e) { } // start the thread anim.start(); } /** draws the pixel-buffer */ public void update(Graphics g) { if (img != null) g.drawImage(img, 0, 0, this); if (info) { g.setColor(Color.white); g.fillRect(0, 0, 50, 20); g.setColor(Color.black); g.drawString(" " + numtotal, 10, 10); } } /** random number in a range */ public int getRandom(int range) { return ((int) (Math.random() * range)); } }