import java.awt.*; import java.applet.Applet; import java.util.Vector; import java.util.Enumeration; /* * Malthusian population model 8 Feb 1998 * Grazer characteristics mostly defined by height. * Predators - dead grazers can be eaten. * Fast Grass flow left. * Grazer 0 no longer has priority : no pecking order. * Vector version. * Grazer parameters. * Contention counts. * defaults to grass max 100, grazer bmr 3 etc. * * All the usual health warnings. * (c) Chris Davis 1998 */ public class Malthus extends Applet implements Runnable { Runtime runtime; long freeMem; int frameNumber = -1; int delay; Thread animatorThread; boolean frozen = true; Dimension offDimension; Image offImage, fieldImage; public Graphics offGraphics, fieldGraphics; public int dr = 1; Field field; FieldCanvas canvas1; GraphCanvas canvas2; Button b1; Label counter; Label grasslevel; Label day, avAge, variance; FactSheet factsheet; GraphSheet graphsheet; public int flowPARAM, decayPARAM; public int repstepPARAM; public int initrandPARAM; public int pARAM[] = new int[15]; // initialization reads parameters, sets up screen layouts. public void init() { String str; int gridsize; int repro, prepro, ng, np, vpt, grow, size; Dimension d = size(); int s[] = new int[5]; int season[] = new int[3]; showFreeMem(); System.out.println( d.width + "," + d.height ); gridsize = d.width / 20; System.out.println( "gridsize " + gridsize ); // get grass flow 0=static 1=moving try { flowPARAM = Integer.parseInt( getParameter("FLOWGRASS") ); } catch( Exception e ) { flowPARAM = 0; } // get grass initial size try { size = Integer.parseInt( getParameter("GRASSSIZE") ); } catch( Exception e ) { size = 500; } // get N grazers try { ng = Integer.parseInt( getParameter("NGRAZERS") ); } catch( Exception e ) { ng = 5; } // get grazer repro power try { repro = Integer.parseInt( getParameter("GREPRO") ); } catch( Exception e ) { repro = 8; } // get Nunmber of predators try { np = Integer.parseInt( getParameter("NPREDATORS") ); } catch( Exception e ) { np = 0; } // get predator repro power try { prepro = Integer.parseInt( getParameter("PREPRO") ); } catch( Exception e ) { prepro = 4; } // get repro step change try { repstepPARAM = Integer.parseInt( getParameter("REPSTEP") ); } catch( Exception e ) { repstepPARAM = 1; } // get init randomization try { initrandPARAM = Integer.parseInt( getParameter("INITRAND") ); } catch( Exception e ) { initrandPARAM = 10; } // get grazer params for ( int pn = 0; pn <15; pn++ ) { try { pARAM[pn] = Integer.parseInt( getParameter("P" + Integer.toString(pn) ) ); } catch( Exception e ) { if ( pn == 0 ) pARAM[pn] = 100; // grass maxstore if ( pn == 1 ) pARAM[pn] = 1; // grass repro if ( pn == 2 ) pARAM[pn] = 0; // grass defence if ( pn == 3 ) pARAM[pn] = 0; if ( pn == 4 ) pARAM[pn] = 100; // grazer maxstore if ( pn == 5 ) pARAM[pn] = 900; // grazer repstore if ( pn == 6 ) pARAM[pn] = 3; // grazer bmr if ( pn == 7 ) pARAM[pn] = 5; // grazer emove if ( pn == 8 ) pARAM[pn] = 0; // grazer defence if ( pn == 9 ) pARAM[pn] = 50; // grazer hungry if ( pn == 10) pARAM[pn] = 0; // grazer starving if ( pn == 11) pARAM[pn] = 0; // grazer seekmode if ( pn == 12) pARAM[pn] = 0; if ( pn == 13) pARAM[pn] = 0; if ( pn == 14) pARAM[pn] = 0; } } // get old starvation parameter, if present try { pARAM[10] = Integer.parseInt( getParameter("STARVATION") ); } catch( Exception e ) { } // get variability try { vpt = Integer.parseInt( getParameter("VPER1000") ); } catch( Exception e ) { vpt = 0; } // get dead grazer % decay rate try { decayPARAM = Integer.parseInt( getParameter("DEADDECAY") ); } catch( Exception e ) { decayPARAM = 10; } // get graphic data stream ( item 19 on stream 3 = 19 ) for ( int pn = 0; pn <=5; pn++ ) { try { s[pn] = Integer.parseInt( getParameter("STREAM" + Integer.toString(pn) ) ); } catch( Exception e ) { if ( pn == 0 ) s[pn] = 2; // grazer pop if ( pn == 1 ) s[pn] = 3; // predator pop if ( pn == 2 ) s[pn] = 4; // grass percent if ( pn == 3 ) s[pn] = 0; if ( pn == 4 ) s[pn] = 0; } } // get season parameters for ( int pn = 0; pn <=2; pn++ ) { try { season[pn] = Integer.parseInt( getParameter("SEASON" + Integer.toString(pn) ) ); } catch( Exception e ) { if ( pn == 0 ) season[pn] = 500; // season length (days) if ( pn == 1 ) season[pn] = 0; // percent change max to min if ( pn == 2 ) season[pn] = 1; // mean radiation intensity } } // grass + grazer field constructor field = new Field( this ); field.init( gridsize, size, ng, repro, np, prepro, vpt, s, season ); // set up the GUI GridBagLayout gridBag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridBag); b1 = new Button(" Start/Stop "); c.gridwidth = GridBagConstraints.EAST; c.gridx = 0; c.gridy = 0; c.gridwidth = 1; c.gridheight= 1; c.weightx = 1.0; c.weighty = 0.0; gridBag.setConstraints(b1, c); add(b1); counter = new Label("Pop "); c.fill = GridBagConstraints.BOTH; c.gridx = 1; c.gridy = 0; c.gridwidth = 1; c.gridheight= 1; c.weightx = 1.0; c.weighty = 0.0; gridBag.setConstraints(counter, c); add(counter); grasslevel = new Label("grass %"); c.fill = GridBagConstraints.BOTH; c.gridx = 3; c.gridy = 0; c.gridwidth = 1; c.gridheight= 1; c.weightx = 1.0; c.weighty = 0.0; gridBag.setConstraints(grasslevel, c); add(grasslevel); day = new Label("Day 0 "); c.fill = GridBagConstraints.BOTH; c.gridx = 4; c.gridy = 0; c.gridwidth = 1; c.gridheight= 1; c.weightx = 1.0; c.weighty = 0.0; gridBag.setConstraints(day, c); add(day); canvas1 = new FieldCanvas(field); canvas1.resize( d.width, d.width ); c.fill = GridBagConstraints.BOTH; c.gridx = 0; c.gridy = 1; c.gridwidth = 5; c.gridheight= 1; c.weightx = 1.0; c.weighty = 1.0; gridBag.setConstraints(canvas1, c); add(canvas1); canvas2 = new GraphCanvas(field); canvas2.resize( d.width, 100 ); c.fill = GridBagConstraints.BOTH; c.gridx = 0; c.gridy = 2; c.gridwidth = 5; c.gridheight= 1; c.weightx = 1.0; c.weighty = 1.0; gridBag.setConstraints(canvas2, c); add(canvas2); validate(); } void showFreeMem() { runtime = Runtime.getRuntime(); freeMem = runtime.freeMemory() / 1024; // System.out.println(" Free mem: " + freeMem + "K" ); System.gc(); } public void start() { if (animatorThread == null) { animatorThread = new Thread(this); animatorThread.start(); } } public void stop() { if ( animatorThread != null ) { animatorThread.stop(); animatorThread = null; } } public void destroy() { } public boolean mouseDown(Event e, int x, int y) { return true; } public boolean handleEvent( Event e ) { // start/stop button if ( e.target == b1 ) { frozen = !frozen; } return true; } public void run() { Thread.currentThread().setPriority(Thread.MIN_PRIORITY); while (Thread.currentThread() == animatorThread) { try { if ( !frozen ) { //Advance the animation frame. frameNumber++; repaint(); // Grow the field field.engine( offGraphics ); // Display field. canvas1.repaint(); // Display graphs canvas2.repaint(); animatorThread.sleep(20); if ( field.flowgrass > 0 ) animatorThread.sleep(50); } else { animatorThread.sleep(500); } } catch ( InterruptedException e ) { stop(); } } } /* update label */ public void showGrazerCount( int n ) { counter.setText( " Pop " + n ); } public void showGrassLevel( int n ) { grasslevel.setText( "grass " + n +"%" ); } public void showTime( int n ) { day.setText( "Day " + n ); } public void showAvAge( int n ) { avAge.setText( "Av. age: " + n ); } public void showVariance( int n1, int n2, int n3 ) { variance.setText( "-" +n1 + "+" + n3 ); } } class Helper{ // return a random integer 1, 0, or -1 public static int rnd101() { int n = (int)(Math.random() * 2.9999999) - 1; return ( n ); } // make sure a value is kept within the range 0 to maxd-1 public static int boundCheck( int d, int maxd ) { int r = d; if ( d >= maxd ) r = maxd - 1; if ( d < 0 ) r = 0; return(r); } } class Lifeform { } /****** plant growth and draw methods**************************************/ class Grass extends Lifeform { public int x; // x start locatiom public int y; // y start location public int xs; // flow x-shifted location public double size; // grass plant size ( 0.0 - 1.0 ) public double lastsize; public int age; // grass age public int store; // energy store public int maxstore; // max energy store size public int repro; // growth energy added per unit time public int growth; // energy growth /* grass constructor */ public Grass( int x, int y, double size, int p[] ) { this.x = x; this.y = y; this.xs = x; this.size = size; this.lastsize = this.size; this.age = (int)( size * 100); this.maxstore = p[0]; this.store = (int)(this.maxstore * this.size); this.repro = p[1]; } /* copy grass from one grid location to another */ public void copyGrass( Grass that ) { this.size = that.size; this.lastsize = that.lastsize; this.age = that.age; this.maxstore = that.maxstore; this.store = that.store; this.repro = that.repro; } // Flowing grass x-shifted location. // As grass flows left 1 grid location, the location of the grass // under a grazer is 1 greater than the grazer x-location public int xShiftGrass( int xFlowShift, int grid ) { this.xs = this.x + xFlowShift; if ( this.xs >= grid ) this.xs -= grid; return( this.xs ); } // Grow some grass, flagging growth = true only at preset milestones. // The grass grows by a multiple, gprob, of its repro rate. // The integer portion of gprob gives a multiple, // and the fractional portion of gprob an additional probability. // This done because this.repro is usually a low-value integer ( e,g, 1 ) public boolean growGrass( double gprob ) { boolean growth = false; double g2 = 0; double mark[] = { 0.01, 0.25, 0.5, 0.75, 1.0 }; int n, g1; if ( this.size < 1.0 ) { this.growth = 0; g1 = (int)gprob; // integer growth if ( g1 > 0 ) this.growth += g1 * this.repro; if ( g1 < 2 ) { // low growth values g2 = gprob % 1.0; // probable growth if ( Math.random() < g2 ) this.growth += this.repro; } /* mark growth=true as plant passes preset stages of growth */ if ( this.growth > 0 ) { this.store += this.growth; if (this.store > this.maxstore) this.store = this.maxstore; this.size = (float)this.store / (float)this.maxstore; for ( n = 0; n < mark.length; n++ ) { if ( ( this.size >= mark[n] ) & ( this.lastsize < mark[n] ) ) { growth = true; } } } this.lastsize = this.size; } return(growth); } // trim off some amount of grass public void trimGrass( int a ) { this.store -= a; if ( this.store < 0 ) this.store = 0; this.size = (double)this.store / (double)this.maxstore; } // paint clump of grass public void paintGrass( Graphics offGraphics, int gridsize ) { int n, x, y, xoff, yoff; int gx[] = new int[8]; int gy[] = new int[8]; int addx, addy, step; x = this.x; y = this.y; xoff = x * gridsize; yoff = y * gridsize; gx[0] = xoff + gridsize/2; gy[0] = yoff + gridsize -1; gx[1] = xoff + 1; gy[1] = yoff + 2*gridsize/3; gx[2] = xoff + 1; gy[2] = yoff + gridsize/3; gx[3] = xoff + gridsize/5; gy[3] = yoff + 1; gx[4] = xoff + gridsize/2; gy[4] = yoff + 1; gx[5] = xoff + 4*gridsize/5; gy[5] = yoff + 1; gx[6] = xoff + gridsize; gy[6] = yoff + gridsize/3; gx[7] = xoff + gridsize; gy[7] = yoff + 2*gridsize/3; // draw the background for the grid position offGraphics.setPaintMode(); offGraphics.setColor(Color.darkGray); offGraphics.fillRect( xoff, yoff, gridsize, gridsize ); // draw the grass offGraphics.setColor(Color.green); if (this.size > 0 ) { if ( size < 0.4 ) { step = 2; } else { step = 1; } for ( n = 1; n < 8; n = n + step ) { addx = (int)((double)(gx[n] - gx[0]) * this.size); addy = (int)((double)(gy[n] - gy[0]) * this.size); offGraphics.drawLine( gx[0], gy[0], gx[0]+addx, gy[0]+addy ); } } } } /****** grazer plant search, reproduction, and draw methods*****************/ class Grazer extends Lifeform { public int name; // unique grazer name public int kind; // kind of grazer 0=grass 1=omnivore 2=predator public int seekmode; // search method public int x; // x start locatiom public int y; // y start location public int lastx; public int lasty; public float size; // grazerp size ( 0.0 - 1.0 ) public int height; // grazer height public int age; // grazer age public int store; // energy store public int maxstore; // maximum energy store public int repstore; // energy required to reproduce. public int hungry; // store level at which action is triggered public int starving; // store level at which reproduction shuts down public int repro; // energy / day for reproduction public int bmr; // basal metabolic rate public int emove; // average energy / move in any direction public int xesc; // x escape direction public int yesc; // y escape direction public double varprob; // probability of mutation public float newGrazerSize; // growing offspring size (0 to 1) public float idleness; // idleness public double preference; // predator prey preference; /* grazer constructor */ public Grazer( int name, int x, int y, double varib, int kind, int ngs, int r, int repstep, int p[] ) { int d; this.name = name; // unique name this.kind = kind; // kind of animal this.x = x; // location; this.y = y; this.lastx = x; this.lasty = y; this.xesc = 1; this.yesc = 1; this.size = (float)1.0; // full size this.age = 0; // zero age this.varprob = varib; this.height = 10; this.maxstore = p[4]; // max store varies with volume; this.repstore = p[5]; // reproduction cost varies with area this.bmr = p[6]; // basal metabolic rate varies with area this.emove = p[7]; // movement energy varies with volume. this.hungry = p[9]; this.starving = p[10]; this.store = this.hungry * 2; this.seekmode = p[11]; /* reproduction rate may vary */ this.repro = varyValue( r, repstep, this.varprob); if ( this.repro < 0 ) this.repro = 0; /* At initialization may want to avoid step changes in pop. */ if ( ngs == 0 ) { this.newGrazerSize = 0; } else { this.newGrazerSize = (float)Math.random(); } } // vary value v +/- depending on probability of change public int varyValue( int v, int change, double varprob ) { double t = Math.random(); if ( t > (1.0 - varprob/2.0) ) { v += change; } else if ( t < (varprob/2.0) ) { v -= change; } return( v ); } /* grow grazer offspring, if not starving */ public boolean reproduceGrazer() { boolean birth = false; float size_increase; if ( (this.store > this.repro) && (this.store > this.starving) ) { size_increase = (float)this.repro / (float)(this.repstore + this.hungry*2); this.newGrazerSize += size_increase; this.store -= this.repro; if ( this.newGrazerSize >= 1.0 ){ this.newGrazerSize = 0; birth = true; } } return(birth); } /* 1. move the grazer */ public void randomMove( int grid ) { int xm = (int)(Math.random() * 2.99999) - 1; this.x += xm; this.x = Helper.boundCheck( this.x, grid ); int ym = (int)(Math.random() * 2.99999) - 1; this.y += ym; this.y = Helper.boundCheck( this.y, grid ); /* use energy for movement */ if ( (ym != 0 ) || (xm != 0) ) { this.store -= this.emove; } } /* 2. move the grazer */ public void localSearchMove( Grass gr[][], int grid ) { int a[] = new int[2]; int amin, x, y, gx; int greenest = 0; int xnew = this.x; int ynew = this.y; if ( this.store < this.hungry ) { /* search locally for greenest patch, randomizing start corner */ int ix = sgn1( Math.random() - 0.5 ); int iy = sgn1( Math.random() - 0.5 ); for ( int xm = -1 * ix; Math.abs(xm) <= 1; xm = xm + ix ) { x = Helper.boundCheck( this.x + xm, grid ); for ( int ym = -1 * iy; Math.abs(ym) <= 1; ym = ym + iy ) { y = Helper.boundCheck( this.y + ym, grid ); gx = gr[x][y].xs; // grass x-shift location if ( gr[gx][y].store > greenest ) { xnew = x; ynew = y; greenest = gr[gx][y].store; } } } if (this.store > this.emove) { if ( greenest > 0 ) { // move to greenest patch if ((this.x != xnew) || (this.y != ynew)) { this.store -= emove; } this.x = xnew; this.y = ynew; } else { randomMove( grid ); } } /* eat as much as possible */ gx = gr[this.x][this.y].xs; // grass x-shift location a[0] = this.maxstore - this.store; a[1] = gr[gx][this.y].store; if ( a[0] < a[1] ) { amin = a[0]; } else { amin = a[1]; } gr[gx][this.y].trimGrass( amin ); // trim grass this.store += amin; } } // hunter predator homes in on nearest public void hunterPredator( Vector v, int ngrazers, int grid ) { int nearest = -1; double d, w, dx, dy; double dmin = 100000.0; double wmin = 100000.0; int b[] = new int[2]; int amin, x, y; Grazer a = this; // if just born, get away from mother. if (this.age < 6) { if ( this.age < 2) { this.xesc = (int)(Math.random() * 2.99999) - 1; // random direction this.yesc = (int)(Math.random() * 2.99999) - 1; } this.x += this.xesc; this.x = Helper.boundCheck( this.x, grid ); this.y += this.yesc; this.y = Helper.boundCheck( this.y, grid ); this.store -= this.emove; System.out.println( this.name + " escapes " + this.xesc + "," + this.yesc ); } else if ( this.store < this.hungry ) { for ( int m = 0; m < v.size(); m++ ) { a = (Grazer)v.elementAt(m); if ( a.repstore > this.store ) { dx = Math.abs((double)(a.x - this.x)); dy = Math.abs((double)(a.y - this.y)); if ( dx < dmin && dy < dmin ) { d = Math.sqrt( dx * dx + dy * dy ); w = d; if ( a.kind == 2 ) w = w + 3; // bias against predators w = (5000.0 * w)/a.repstore; // grazer weighting if ( a.store <= 0 ) w = 0.66 * w; // bias to dead grazers if ( (a.name != this.name) ) { if ( (w < wmin) ) { nearest = m; dmin = d; wmin = w; } } } } } if ( nearest >= 0 ) a = (Grazer)v.elementAt(nearest); // if nearest prey on adjacent square, make the kill if ( dmin < 1.5 ) { // basic attack cost if ( a.store > 0 ) this.store = this.store - 2 * this.emove; if ( (a.kind == 2) && (a.store > 0) ) { this.store = 0; // it was a fight!! } if ( dmin > 0 ) { this.store -= this.emove; System.out.println( this.name + " attacks " + a.name ); } this.x = a.x; // go to prey position this.y = a.y; b[0] = this.maxstore - this.store; // consume body if ( a.store > 0 ) b[1] = a.store; else b[1] = a.repstore; if ( b[0] < b[1] ) { amin = b[0]; } else { amin = b[1]; } this.store += amin; a.repstore -= amin; System.out.println( this.name + " eats " + amin + " off " + a.name ); a.store = 0; } // otherwise move towards prey else { if (nearest >= 0) { if ( a.x > this.x ) { this.x++; } else if ( a.x < this.x ) { this.x--; } this.x = Helper.boundCheck( this.x, grid ); if ( a.y > this.y ) { this.y++; } else if ( a.y < this.y ) { this.y--; } this.y = Helper.boundCheck( this.y, grid ); this.store -= this.emove; System.out.println( this.name + " tracks " + a.name ); } } } } // feed Grazer, returning true if it move public boolean feedGrazer( Grass gr[][], Vector v, int ngrazers, int grid ) { boolean move = false; this.lastx = this.x; this.lasty = this.y; if (this.kind == 0) localSearchMove( gr, grid ); // look for grass else if (this.kind == 2) hunterPredator( v, ngrazers, grid ); // look for grazers if ( (this.x != this.lastx) || (this.y != this.lasty)) move = true; return(move); } // basal metabolism and ageing public void ageGrazer() { this.store -= this.bmr; /* basic metabolism */ this.age++; /* age increase */ } // paint Grazer public void paintGrazer( Graphics offGraphics, int gridsize, int kind, int xoffset, int repstep ) { /* this grazer size assumes 20 x 20 gridsize */ int gx[] = { 0, 4, 6, 7, 8, 10, 12, 14, 14, 4, 0, 0 }; int gy[] = {-2, -5, 0, 0, -5, -5, 0, 0,-12,-12, -6, -2 }; /* resize the grazer */ for ( int n = 0; n < gx.length; n++ ) { gx[n] = (int)((double)gridsize/20.0 * (double)gx[n]); gy[n] = (int)((double)gridsize/20.0 * (double)gy[n]); } /* move grazer to (x,y) location */ for ( int n = 0; n < gx.length; n++ ) { gx[n] = gx[n] + this.x * gridsize + 3 + xoffset; gy[n] = gy[n] + this.y * gridsize + 3*gridsize/4; } /* paint it */ if (this.repro > (repstep *12)) { offGraphics.setColor(Color.white); } if (this.repro <= (repstep *12)) { offGraphics.setColor(Color.yellow); } if (this.repro <= (repstep * 9)) { offGraphics.setColor(Color.red); } if (this.repro <= (repstep * 6)) { offGraphics.setColor(Color.blue); } if (this.repro <= (repstep * 3)) { offGraphics.setColor(Color.black); } offGraphics.fillPolygon( gx, gy, gx.length ); if (kind == 0 ) { offGraphics.setColor(Color.white); } if (kind == 2 ) { offGraphics.setColor(Color.red); } offGraphics.drawPolygon( gx, gy, gx.length ); } // paint dead Grazer public void paintDeadGrazer( Graphics offGraphics, int gridsize, int kind, int xoffset ) { /* this grazer size assumes 20 x 20 gridsize */ int gx[] = { 0, 14, 5, 5, 5, 5, 10, 10, 10 }; int gy[] = {-6, -6, -6, -2,-10, -6, -6, -2,-10 }; /* resize the grazer */ for ( int n = 0; n < gx.length; n++ ) { gx[n] = (int)((double)gridsize/20.0 * (double)gx[n]); gy[n] = (int)((double)gridsize/20.0 * (double)gy[n]); } /* move grazer to (x,y) location */ for ( int n = 0; n < gx.length; n++ ) { gx[n] = gx[n] + this.x * gridsize + 3 + xoffset; gy[n] = gy[n] + this.y * gridsize + 3*gridsize/4; } /* paint it */ offGraphics.setColor(Color.white); offGraphics.drawPolygon( gx, gy, gx.length ); } /* returns sign, with zero given as 1 */ static int sgn1( double v ) { if ( v < 0.0 ) return -1; else return 1; } } /******* Field class initializes field and runs main simulation engine ******/ class Field { Malthus thisApplet; public Vector v = new Vector(); public int deadlist[] = new int[400]; public int errorCount = 0; public int grid = 20; // 20 x 20 squares public int gridsize = 20; // square side length public double displayStream[] = new double[5]; public int stream[] = new int[5]; static final String streamName[] = {"null", "grazer idleness", "grazer population", "predator population", "percent grass cover", "grazer age", "predator age", "grazer repro power", "predator repro power", "predator idleness", "2-day grazer idleness", "contentions", " ", " ", " ", " ", " ", " " }; public boolean change[][] = new boolean[grid][grid]; public Grass gr[][] = new Grass[grid][grid]; public int flowgrass; public double wavelength, wavemean, waveamp, wa; public int xFlowShift; public int contention[][] = new int[grid][grid]; public int contentions; int ngrazers; int num_grazers; int num_predators; double grazer_age, grazer_repro, stv; double predator_age, predator_repro; double grazer_idleness, grazer_idleness2; double last_grazer_idleness; double predator_idleness; float size; int uniqueNumber, total_age, time, grasspercent; int oldest; int death_register; int repstep; // repro display colour interval int variant[] = new int[3]; /* Constructor allows access to Applet */ public Field( Malthus a ) { super(); thisApplet = a; } /* Field initialization with grass and grazers */ public void init( int grids, int gsize, int ng, int repro, int np, int prepro, int vpt, int s[], int season[] ) { int x, y, n, kind, rep, irand; Grazer g; gridsize = grids; for ( n=0; n < 5; n++ ) { stream[n] = s[n]; } flowgrass = thisApplet.flowPARAM; // grass flows if > 0 xFlowShift = 0; // grass flow offset wavelength = (double)season[0]; // season length wavemean = (double)season[2]; // season mean amplitude System.out.println( wavemean ); waveamp = (double)season[1] / 100.0; // season swing amplitude System.out.println( waveamp ); wa = 0; // season wave angle System.out.println("Dead decay at " + thisApplet.decayPARAM + "/day " ); time = 0; total_age = 0; oldest = 0; death_register = 0; for ( n = 0; n < 3; n++ ) { variant[n] = 0; } double size = (double)gsize / 1000.0; /* grass size */ /* initialize the grass */ for ( x = 0; x < grid; x++ ) { for ( y = 0; y < grid; y++) { gr[x][y] = new Grass( x, y, size, thisApplet.pARAM ); change[x][y] = true; // force initial complete paint } } System.out.println("grass maxstore: " + thisApplet.pARAM[0] + " repro: " + thisApplet.pARAM[1]); System.out.println("grass maxstore: " + gr[0][0].maxstore + " repro: " + gr[0][0].repro); System.out.println("Variability " + ((double)vpt)/1000.0 ); repstep = thisApplet.repstepPARAM; irand = thisApplet.initrandPARAM; System.out.println("repstep " + repstep ); /* initialize some grazers and predators */ ngrazers = 0; uniqueNumber = 0; double varib = ((double)vpt)/1000.0; for ( n = (ng+np-1); n >= 0; n-- ) { x = (int)((double)grid * Math.random()); y = (int)((double)grid * Math.random()); size = (float)1.0; if (n < (np) ) { kind = 2; rep = prepro; } else { kind = 0; rep=repro;} g = new Grazer( uniqueNumber, x, y, varib, kind, irand, rep, repstep, thisApplet.pARAM ); // g.age = 20; v.addElement(g); ngrazers++; uniqueNumber++; } } /* the simulation engine room */ public void engine(Graphics offGraphics) { int store = 0; int rightx = 0; int i, x, y, n, r, x2, xshift, gx; Grazer a, newborn; // set the fraction/probability of grass growth for the season wa = 2.0 * Math.PI * (double)time / wavelength; double gprob = wavemean + waveamp * wavemean * Math.sin( wa ); // System.out.println("gprob " + gprob ); /* flow grass left */ contentions = 0; if ( flowgrass > 0 ) { store =(int)( gprob * (double)grid ); // amount to add to each righthandside grid if ( (time%1) == 0 ) { xFlowShift += 1; if ( xFlowShift >= grid ) xFlowShift = 0; /* work out all the x-shifts for each tuft of grass */ for ( x = 0; x < grid; x++ ) { xshift = gr[x][0].xShiftGrass( xFlowShift, grid ); for ( y = 0; y < grid; y++ ) { gr[x][y].xs = xshift; // put in x-shifted locations contention[x][y] = 0; } } /* add grass at right border */ rightx = gr[grid-1][0].xShiftGrass( xFlowShift, grid ); for ( y = 0; y < grid; y++ ) { if ( ((y+flowgrass/2) % flowgrass) == 0 ) { gr[rightx][y].store = store * flowgrass; gr[rightx][y].size = (float)gr[rightx][y].store/(float)gr[rightx][y].maxstore; } else { gr[rightx][y].store = 0; gr[rightx][y].size = (float)0; } change[rightx][y] = true; } } } else { /* grow some grass */ for ( x = 0; x < grid; x++ ) { for ( y = 0; y < grid; y++) { contention[x][y] = 0; if ( gr[x][y].growGrass( gprob ) ) { change[x][y] = true; } } } } // move the grazers, alternating pecking order for ( i = 0; i < v.size(); i++ ) { if ( (time%2) == 0 ) { n = i; } else { n = v.size()-1 - i; } // alternate a = (Grazer)v.elementAt(n); a.ageGrazer(); // bmr a.idleness = 1; if ( a.store > 0 ) { if ( a.feedGrazer( gr, v, ngrazers, grid ) ) { gx = gr[ a.x ][ a.y ].xs; change[gx][ a.y ] = true; if ( gr[gx][a.y].size > (float)1 ) change[gx][ a.y -1 ] = true; gx = gr[ a.lastx ][ a.lasty ].xs; change[gx][ a.lasty ] = true; a.idleness = 0; // System.out.println( xFlowShift ); } gx = gr[ a.x ][ a.y ].xs; contention[gx][a.y] += 1; if ( contention[gx][a.y] > 1 ) contentions += 1; } else { if ( a.repstore > 0 ) { gx = gr[ a.x ][ a.y ].xs; change[ gx ][ a.y ] = true; a.repstore -= thisApplet.decayPARAM; if ( a.repstore < 10 ) a.repstore = 0; } } } // grazer reproduction for ( n = 0; n < v.size(); n++ ) { a = (Grazer)v.elementAt(n); total_age += a.age; if ( a.store > 0 ) { if ( a.reproduceGrazer() ) { int newx = Helper.boundCheck( (a.x + Helper.rnd101()), grid); int newy = Helper.boundCheck( (a.y + Helper.rnd101()), grid); newborn = new Grazer( uniqueNumber, newx, newy, a.varprob, a.kind, 0, a.repro, repstep, thisApplet.pARAM ); v.addElement(newborn); gx = gr[ newx ][ newy ].xs; change[ gx ][ newy ] = true; uniqueNumber++; ngrazers++; } } } // garbage-collection of dead grazers for ( n = v.size()-1; n >= 0; n-- ) { a = (Grazer)v.elementAt(n); if ( (a.store <= 0) && (a.repstore <= 0) ) { change[ a.x ][ a.y ] = true; death_register++; ngrazers--; v.removeElementAt( n ); } } // work out a few average values last_grazer_idleness = grazer_idleness; grazer_idleness = 0; grazer_age = 0; predator_idleness = 0; predator_age = 0; num_grazers = 0; num_predators = 0; grazer_repro = 0; predator_repro = 0; for ( n = 0; n < v.size(); n++ ) { a = (Grazer)v.elementAt(n); if ( a.kind == 0 && a.store > 0 ) { grazer_idleness += (double)a.idleness; num_grazers++; grazer_age += (double)a.age; grazer_repro += (double)a.repro; } if ( a.kind == 2 && a.store > 0 ) { predator_idleness += (double)a.idleness; num_predators++; predator_age += (double)a.age; predator_repro += (double)a.repro; } } grazer_idleness = (100.0 * grazer_idleness) / (double)num_grazers; grazer_idleness2 = (last_grazer_idleness + grazer_idleness ) /2.0; predator_idleness = (100.0 * predator_idleness) / (double)num_predators; if ( num_grazers > 0 ) { grazer_age = grazer_age/ (double)num_grazers; grazer_repro = (5.0 * grazer_repro) / (double)num_grazers; } if ( num_predators > 0 ) { predator_age = predator_age / (double)num_predators; predator_repro = (5 * predator_repro) / (double)num_predators; } /* calculate % grass in field */ int grassStore = 0; int maxGrassStore = 0; for ( x = 0; x < grid; x++ ) { for ( y = 0; y < grid; y++) { grassStore += gr[x][y].store; maxGrassStore += gr[x][y].maxstore; } } grasspercent = (100 * grassStore) / maxGrassStore; /* display a few results */ thisApplet.showGrassLevel( grasspercent ); thisApplet.showGrazerCount( num_grazers + num_predators ); thisApplet.showTime( time++ ); if ( (time % 100) == 0 ) { thisApplet.showFreeMem(); } setStreamData( stream ); } void setStreamData( int s[] ) { for ( int n = 0; n < 5; n++ ) { switch( s[n] ) { case 0: displayStream[n] = 0.0; break; case 1: displayStream[n] = grazer_idleness; break; case 2: displayStream[n] = (double)num_grazers; break; case 3: displayStream[n] = (double)num_predators; break; case 4: displayStream[n] = (double)grasspercent; break; case 5: displayStream[n] = grazer_age; break; case 6: displayStream[n] = predator_age; break; case 7: displayStream[n] = grazer_repro; break; case 8: displayStream[n] = predator_repro; break; case 9: displayStream[n] = predator_idleness; break; case 10: displayStream[n] = grazer_idleness2; break; case 11: displayStream[n] = (double)(2* contentions); break; default: displayStream[n] = 0.0; break; } } if ( (ngrazers == 0) || (errorCount > 25) ) thisApplet.frozen = true; } } /************ Draw the grass and grazers ******************************/ class FieldCanvas extends Canvas { Dimension offDimension; Image offImage; Graphics offGraphics; Field field; FactSheet factsheet; boolean factsheetStatus; // Constructor allows access to Field objects by this class public FieldCanvas( Field f ) { super(); field = f; this.setBackground( Color.black ); factsheetStatus = false; } public void stop() { offGraphics = null; offImage = null; } public boolean handleEvent( Event evt ) { if (evt.id == Event.MOUSE_UP ) { if ( factsheetStatus == false ) { factsheet = new FactSheet( field, evt.x, evt.y ); factsheetStatus = true; } else { factsheet.killFactSheet(); factsheetStatus = false; } } return true; } public void redraw() { repaint(); } // update calls paint(), otherwise clears the screen. public void update(Graphics g) { paint(g); } public void paint(Graphics g) { Dimension d = size(); Grazer a; // The offscreen graphics area is 2x the width of the screen "field". // Grass growth is drawn into the left (0,0) end, and copied to the // right side. The grazers and predators are then drawn over the // top of the right side, which is then displayed. This allows the // stream mode to move the grass leftwards under the grazers, //Create the offscreen graphics context, if no good one exists. if ( (offGraphics == null) || (d.width != offDimension.width) || (d.height != offDimension.height) ) { offDimension = d; System.out.println( offDimension.height + "," + offDimension.width ); offImage = createImage(d.width*2, d.height); offGraphics = offImage.getGraphics(); System.out.println( "FieldCanvas setup" ); } // draw changed grass on lefthand side. int changes = 0; int grid = field.grid; for ( int y = 0; y < grid; y++ ) { for ( int x = 0; x < grid; x++ ) { if ( field.change[x][y] ) { field.gr[x][y].paintGrass( offGraphics, field.gridsize); changes++; } field.change[x][y] = false; } } // copy grass field tricksily from lefthand side to righthand side int width = field.grid * field.gridsize; int width1 = (field.grid - field.xFlowShift) * field.gridsize; int x0 = field.xFlowShift * field.gridsize; int dx = width1; offGraphics.copyArea( x0 ,0, width1, offDimension.height, dx, 0 ); if ( field.xFlowShift > 0 ) { int width2 = field.xFlowShift * field.gridsize; x0 = 0; dx = width + width1; offGraphics.copyArea( x0 ,0, width2, offDimension.height, dx, 0 ); } for ( int n = 0; n < field.v.size(); n++ ) { a = (Grazer)field.v.elementAt(n); if ( a.store > 0 ) { a.paintGrazer( offGraphics, field.gridsize, a.kind, offDimension.height, field.repstep ); } else { a.paintDeadGrazer( offGraphics, field.gridsize, a.kind, offDimension.height ); } } if ( field.errorCount > 0 ) { offGraphics.setColor(Color.red); offGraphics.drawString("TOO MANY GRAZERS", width + 30, 30 ); } // Paint the righthand side onto the screen. g.drawImage(offImage, -offDimension.height, 0, this); // g.drawImage(offImage, 0, 0, this); } } /*********** moving graph canvas *****************************************/ class GraphCanvas extends Canvas { Dimension fielddimension; Image fieldImage; Graphics fieldGraphics; GraphSheet graphsheet; Field field; int nstreams = 5; int lasty[] = new int[nstreams]; boolean started; boolean graphsheetStatus; // Constructor allows access to Field objects by this class public GraphCanvas( Field f ) { super(); field = f; for ( int n = 0; n < nstreams; n++ ) { lasty[n] = -65001; } started = false; graphsheetStatus = false; } public void stop() { fieldGraphics = null; fieldImage = null; } public boolean handleEvent( Event evt ) { if (evt.id == Event.MOUSE_UP ) { if ( graphsheetStatus == false ) { graphsheet = new GraphSheet( field ); graphsheetStatus = true; } else { graphsheet.killGraphSheet(); graphsheetStatus = false; } } return true; } public void redraw() { repaint(); } // update calls paint(), otherwise clears the screen. public void update(Graphics g) { paint(g); } public void paint(Graphics g) { int n; Dimension d = size(); int graph_height = 100; //Create the offscreen graphics context, if no good one exists. if ( (fieldGraphics == null) ) { fieldImage = createImage(d.width, 100); fieldGraphics = fieldImage.getGraphics(); System.out.println( "GraphCanvas setup" ); fieldGraphics.setColor(Color.black); fieldGraphics.fillRect(0, 0, d.width, graph_height); } if ( started ) { /* move image 1 pixel leftward */ fieldGraphics.setPaintMode(); fieldGraphics.copyArea(1,0, d.width-1, graph_height-1, -1, 0 ); /* rub out unmoved vertical line */ fieldGraphics.setColor(Color.black); fieldGraphics.drawLine( d.width-1, 0, d.width-1, 100 ); /* draw gridlines */ for ( n = graph_height/4; n < graph_height; n = n + graph_height/4 ) { fieldGraphics.setColor(Color.darkGray); fieldGraphics.drawLine( d.width-2, n, d.width-1, n ); } if ( (field.time % 100) == 0 ) { fieldGraphics.setColor(Color.darkGray); fieldGraphics.drawLine( d.width-1, 0, d.width-1, graph_height ); } for ( n = nstreams-1; n >= 0; n-- ) { fieldGraphics.setColor(Color.orange); if ( n==0 ) fieldGraphics.setColor(Color.white); if ( n==1 ) fieldGraphics.setColor(Color.red); if ( n==2 ) fieldGraphics.setColor(Color.green); if ( n==3 ) fieldGraphics.setColor(Color.blue); if ( n==4 ) fieldGraphics.setColor(Color.yellow); fieldGraphics.drawLine( d.width-2, 100-lasty[n], d.width-1, 100-(int)(field.displayStream[n]+0.5) ); } //Paint the image onto the screen. g.drawImage(fieldImage, 0, 0, this); } else { started = true; fieldGraphics.setColor(Color.black); fieldGraphics.fillRect(0, 0, d.width, graph_height); } for ( n = 0; n < nstreams; n++ ) lasty[n] = (int)(field.displayStream[n]+0.5); } } /********* show factsheet frame about nearest grazer to cursor ************/ class FactSheet extends Frame { // Factsheet frame constructor public FactSheet( Field f, int x, int y) { super("Fact Sheet"); int n = 23; Grazer a; n = findNearestGrazer( f, x, y ); a = (Grazer)f.v.elementAt(n); setLayout(new GridLayout(12,1)); add(new Label(x + " , " + y ) ); add(new Label("Grazer name: " + a.name )); add(new Label("Grazer age: " + a.age )); add(new Label("Grazer maxstore: " + a.maxstore )); add(new Label("Grazer store: " + a.store )); add(new Label("Grazer repstore: " + a.repstore )); add(new Label("Grazer bmr: " + a.bmr )); add(new Label("Grazer emove: " + a.emove )); add(new Label("Grazer hungry: " + a.hungry )); add(new Label("Grazer repro: " + a.repro )); add(new Label("Grazer newgrazer:" + a.newGrazerSize )); int gx = f.gr[a.x][a.y].xs; add(new Label("Grass store: " + f.gr[gx][a.y].store )); pack(); resize( 200, 250); show(); } public void killFactSheet() { dispose(); } public int findNearestGrazer( Field f, int x, int y) { double x0, y0, d, dx, dy, dmin; int n, nearest; Grazer a; x0 = (double)x; y0 = (double)y; nearest = 0; dmin = 10E+30; // some large number for (n = 0; n < f.v.size(); n++ ) { a = (Grazer)f.v.elementAt(n); dx = x0 - (double)a.x * f.gridsize; dy = y0 - (double)a.y * f.gridsize; d = Math.sqrt( dx*dx + dy*dy ); if ( d < dmin ){ nearest = n; dmin = d; } } return( nearest ); } } /********* show factsheet frame about graph display ***********************/ class GraphSheet extends Frame { // GraphSheet frame constructor public GraphSheet(Field f) { super("Graphic Streams"); setLayout(new GridLayout(6,1)); add(new Label("White: " + f.streamName[f.stream[0]] + " " + f.displayStream[0])); add(new Label("Red: " + f.streamName[f.stream[1]] + " " + f.displayStream[1])); add(new Label("Green: " + f.streamName[f.stream[2]] + " " + f.displayStream[2])); add(new Label("Blue: " + f.streamName[f.stream[3]] + " " + f.displayStream[3])); add(new Label("Yellow: " + f.streamName[f.stream[4]] + " " + f.displayStream[4])); pack(); resize( 250, 125); show(); } public void killGraphSheet() { dispose(); } }