// C6Applet.java // // gg gg ggg ggg gggg gg 66 g g ggg gggg ggg gg gg // g g g g g g g g 6 g g g g g g g g // g g g ggg ggg gggg 666 g g ggg ggg gg g g // g g g g g g 6 6 g g g g g g // ggg g g gggg g g 66 gg g g gggg ggg // // // MR June 2000 // // Menno Rubingh (c) 2000 // http://www.rubinghscience.org/ // // ---------------------------------------------------------------------- // Copyright (C) 2000 Menno Rubingh // // This source code / program is free software; you are allowed to // redistribute it and/or to modify it under the terms of the GNU Public // Licence (GNU GPL) as published by the Free Software Foundation. See the // file 'COPYING' which should have accompanied this source file. // // This program comes with ABSOLUTELY NO WARRANTY. // ---------------------------------------------------------------------- import ../evol/java.applet._.css; import ../evol/java.awt._.css; import ../evol/java.lang._.css; import ../evol/java.util._.css; // -------------------------------------------------------------------------- // // Class C6Rand : Random-number generator that returns integers // within a specified range [0,m) where m > 0. // // Menno Rubingh (c) 1999, 2000 // // // Note that ''scaling down'' the return values of Random.nextInt() with the // ''%'' operator (i.e., // // return ( Math.abs(nextInt()) % m ); // // ) does NOT work well at all, since the random generator in Java's Random // class, like 99.9% of all random generators, is made in such a way that the // returned integer **VALUES** are distributed uniformly over the interval // between the minimum and maximum return values. The values obtained from // the ''%'' application quoted above, for an arbitrary choice of 'm', will // therefore very probably *NOT* be distibited uniformly over the interval // [0,m). // // See William H. Press et al., _Numerical Recepes in Fortran_, 2nd Edition, // Cambridge University Press, 1992 (ISBN 0-521-43064-X), Chapter 7 ''Random // Numbers'', section 7.1, p. 268. // class C6Rand extends Random { //Max. positive value of Java 4-byte 'int'. static private final int RMAX = (256*256*256*(256/2)) - 1; private int uintRand() //Return random integer in [0,RMAX) { return ( Math.abs( nextInt() ) ); } public int n_rand( int m ) //Return random integer in [0,m) { int K = ( RMAX / m ) + 1; return ( uintRand() / K ); } } // -------------------------------------------------------------------------- // // Class C6Gene : General-purpose class for n-bit gene. // // Implements general operations like mutation, crossover, etc. // // NOTE: For 'C6Gene(g2)', 'copy(g2)' and 'mingleMutual(g2,r), the two // C6Gene objects ''this'' and 'g2' must contain the same length // ''bits'' array, otherwise the results are ''strange''. class C6Gene { protected boolean[] bits; public C6Gene( int n ) // Initialize to length of n bits, and initialize with // all bits to value zero. { bits = new boolean[n]; for ( int i = 0; i // -------------------------------------------------------------------------- // // Class C6World : Container for the crea6ures, and simultaneously // a ''canvas'' to draw them (and the ''chessboard they // walk around on) on the screen. // // // All the public 'C6World' member functions (methods) that affect the array // of crea6ures maintained in the 'C6World' object and that affect the data // associated with any single crea6ure, plus also the public 'C6World' member // functions that do drawing, are all 'synchronized': i.e., only one thread // at a time can enter and run code in only one single one of these // 'synchronized' member functions of the same 'C6World' object. This is // implemented in this way not only to keep the ''data integrity'' array of // crea6ures maintained by the 'C6World' object, but also to keep what is // shown (drawn) on the screen of the crea6ures always completely updated // and correct. // class C6World extends Canvas { // Constant maximum size (width and height) of chessboard. The // chessboard is ALWAYS square. public static final int MAXWSIZE = 15; // Member variables protected C6Rand r; // Reference to the central random generator // used by everything in the ''C6World''. protected int ncr; // Current # of creatures in C6World. protected int ixcurcr; // Index in crarr of next creature to be moved. protected C6Crea6ure[] crarr; protected int wsize; // Current size of the ''chessboard''. // -------- public C6World( C6Rand r0, int sz0 ) // Initialize to chessboard size 'sz0', and with 'r0' // as the random generator used. // But as yet with NO creatures in the C6World (= empty // array 'crarr[]'). { r = r0; wsize = sz0; // Initialize the crarr[] array object to suffice for the // max number of crea6ures in the max-size chessboard, // i.e. half the squares on that max-size chessboard // occupied. crarr = new C6Crea6ure[ (MAXWSIZE * MAXWSIZE)/2 ]; ncr = 0; ixcurcr = 0; //setBackground( Color. .... C6Crea6ure.BCKCOLOR ); } // -------- public synchronized void reinitAllGenes() { for ( int i = 0; i = crarr.length ) { return false; } /*array full*/ if ( ncr >= (wsize*wsize)/2 ) { return false; } C6Crea6ure cnew = new C6Crea6ure(); cnew.initGene( r ); do //find a still-empty square { cnew.initPosAndDir( wsize, wsize, r ); } while( squareContents( cnew.getX(), cnew.getY() ) != null ); crarr[ncr] = cnew; ncr++; return true; /*success*/ } public synchronized boolean deleteLastCrea6ure() { if ( ncr ncr-1 ) { ixcurcr = 0; } return true; /*success*/ } // -------- public synchronized void reinitAllPositions() { // (Note that the coding of this function is an ugly, dirty, // implementation-specific trick.) // Step 1: Move everything ''outside of the board'' :-):-) for ( int i = 0; i (wsize*wsize)/2 ) { deleteLastCrea6ure(); } reinitAllPositions(); } // -------- public synchronized boolean doOneMove( boolean doPaint ) // Move one (the ''current'') creature, // perform the necessary ''incremental'' repainting on // the screen, and update ixcurcr. // If 'doPaint' == null, then don't do any drawing. // Return true if the move executed is the // last move in a ''cycle'' through all the // creatures on the chessboard. { if ( ncr turn. // curcr.randturn( r ); } else { C6Crea6ure othercr = squareContents( xdest, ydest ); if ( othercr == null ) { // // EMPTY SQUARE AHEAD ==> advance, or // with probability 1/5 turn. // // (The small turn probability serves to // increase the ''randomness'' with which // creatures run into each other and // exchange information.) // if ( r.n_rand(5) == 0 ) { curcr.randturn( r ); } else { curcr.moveTo( xdest, ydest ); } } else { // // OTHER CREATURE AHEAD ==> perform gene-coded // action. // if ( curcr.attack( othercr, r ) ) { // Attack executed ? ==> // set 'PA', and // redraw othercr, too. curcr.setPA( true ); if ( doPaint ) { drawCrea6ure( g, othercr, true ); } } else { //no attack ==> curcr turns instead. curcr.randturn( r ); } } } // Redraw curcr in its new state/position if ( doPaint ) { drawCrea6ure( g, curcr, true ); } // Update ixcurcr ixcurcr = ( ixcurcr + 1 ) % ncr; return ( ixcurcr == 0 ); } // -------- // // Graphics stuff // private static final Color BCKCOLOR = Color.gray; private static final Color colorarr[] = { Color.red, Color.yellow, Color.green, Color.blue }; private static final int BITPERIOD = 3; private static final int BITWIDTH = 2; private static final int GHALFWIDE = BITPERIOD * (C6Crea6ure.GENELEN/2); private static final int GHALFHIGH = 4; private static final int BODYRADIUS = GHALFWIDE + 2; private static final int NOSERADIUS = BODYRADIUS / 3; public static final int SQSZ = 2*BODYRADIUS + 4; //Diameter of the square in the ''C6World'' chessboard // which a creature occupies. public static final int WMARGIN = 3; public static final int CHBMAXSIZE = SQSZ*MAXWSIZE + 2*WMARGIN; // Chessboard total max size in pixels public Dimension preferredSize() // This function is necessary to get the FlowLayout in the // constructor of our C6Applet work correctly with // our ''C6World'' Canvas. { return ( new Dimension( CHBMAXSIZE, CHBMAXSIZE ) ); } public Dimension minimumSize() { return ( new Dimension( CHBMAXSIZE, CHBMAXSIZE ) ); } protected void drawCrea6ure( Graphics g, C6Crea6ure c, boolean drawNotErase ) // Draw crea6ure in its correct (x,y) position on the // chessboard. // If 'drawNotErase' == false, then erase the crea6ure (i.e., // overpaint with background color) instead of drawing it. { int or = ( CHBMAXSIZE - (SQSZ-1)*wsize ) / 2; // 'or' = graphics pixel coordinate within Canvas of the // CENTER of the top-left square of the chessboard // in its current size of 'wsize' squares. The // chessboard is drawn centered within the Canvas. int x0 = or + ( SQSZ * c.x ); int y0 = or + ( SQSZ * c.y ); // (x0,y0) = graphics pixel coordinate within Canvas // of the center of the chessboard square occupied by // the crea6ure 'c'. g.setColor( BCKCOLOR ); //initialize for erase // Draw colored body if ( drawNotErase ) { g.setColor( colorarr[ c.getColorValue() ] ); } g.fillOval( x0-BODYRADIUS-1, y0-BODYRADIUS-1, 2*BODYRADIUS, 2*BODYRADIUS ); // Draw direction marker (the ''nose'' of the creature) if ( drawNotErase ) { g.setColor( Color.black ); } g.fillOval( x0 + BODYRADIUS*C6Crea6ure.DX[c.dirn] - NOSERADIUS, y0 + BODYRADIUS*C6Crea6ure.DY[c.dirn] - NOSERADIUS, 2*NOSERADIUS, 2*NOSERADIUS ); if ( ! drawNotErase ) { return; } // Draw grey background box for the gene g.setColor( Color.gray ); g.fillRect( x0 - GHALFWIDE - 1, y0 - GHALFHIGH - 1, 2*GHALFWIDE+ 1, 2*GHALFHIGH + 2 ); // Draw the gene for ( int i = 0; i repaint each individual // creature move. //false ==> repaint whole world after // a complete cycle through // all creatures. C6AnimationManager( C6Applet app_init ) //Constructor { ourapp = app_init; cycleTime = 600; repaintMode = false; } public void toggleRepaintMode() { repaintMode = ! repaintMode; } public void run() { while( true ) { int nCr = ourapp.world.getNCr(); int n = ( nCr == 0 ? 1 : nCr ); try { sleep( cycleTime / n ); } catch ( InterruptedException e ) { //empty } boolean last; last = ourapp.world.doOneMove( repaintMode ); if ( last && ( repaintMode == false ) ) { ourapp.world.repaint(); } if ( last ) { ourapp.iLabel ++ ; if ( ourapp.iLabel > 100 ) { ourapp.iLabel = 0; } ourapp.ourLabel.setText( "[" + ourapp.iLabel + "]"); } } } } // -------------------------------------------------------------------------- public class C6Applet extends Applet { // Constants //Fixed size of total ''Frame'' of our Applet: public static final int APPXSIZE = C6World.CHBMAXSIZE + 10; public static final int APPYSIZE = C6World.CHBMAXSIZE + 75; //Initial chessboard size: private static final int WSZINI = 10; // Instance variables public C6Rand r; // Use *ONE* central random generator. private C6AnimationManager am; public C6World world; public int iLabel = 0; Label ourLabel; public C6Applet() // Constructor { am = null; //am = new C6AnimationManager( this ); r = new C6Rand(); // Initialize our central random generator. // Add buttons and add our ''C6World'' Canvas: setLayout( new FlowLayout() ); add( new Button( "reinit" ) ); add( new Button( "world++" ) ); add( new Button( "world--" ) ); add( new Button( "ncrea++" ) ); add( new Button( "ncrea--" ) ); add( new Button( "mutate1" ) ); add( new Button( "ff" ) ); //''Fast Forward'' add( new Button( "vmode" ) ); //add( new Label( APPXSIZE + "x" + APPYSIZE ) ); add( ourLabel = new Label( "[" + iLabel + "]" ) ); world = new C6World( r, //Random generator used by 'world' WSZINI ); //initial chessboard size add( world ); } // ------ public void init() // Web browser tells applet to initialize { resize( APPXSIZE, APPYSIZE ); // Put some Creat6ures into the C6World : for ( int i = 0; i