001         package com.croftsoft.apps.evolve;
002         
003         import java.awt.*;
004         import java.awt.event.*;
005         import java.io.*;
006         import java.util.*;
007         import javax.swing.*;
008         import javax.swing.event.*;
009    
010         import com.croftsoft.core.awt.font.FontLib;
011         import com.croftsoft.core.gui.FrameLib;
012         import com.croftsoft.core.gui.plot.PlotLib;
013         import com.croftsoft.core.lang.lifecycle.Lifecycle;
014         import com.croftsoft.core.animation.*;
015         import com.croftsoft.core.animation.component.*;
016         import com.croftsoft.core.util.loop.*;
017    
018         /*********************************************************************
019         * Main Evolve class.
020         *
021         * @version
022         *   $Date: 2008/04/19 21:31:00 $
023         * @since
024         *   1996-09-01
025         * @author
026         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
027         *********************************************************************/
028    
029         public final class  Evolve
030           extends JApplet
031           implements
032             ActionListener, ChangeListener, ComponentListener, MouseListener,
033             Lifecycle, ComponentAnimator
034         //////////////////////////////////////////////////////////////////////
035         //////////////////////////////////////////////////////////////////////
036         {
037    
038         private static final String  VERSION = "$Date: 2008/04/19 21:31:00 $";
039    
040         private static final String  TITLE = "CroftSoft Evolve";
041    
042         private static final String  INFO
043           = "\n" + TITLE + "\n"
044           + "Copyright 2006 CroftSoft Inc\n"
045           + "https://www.croftsoft.com/\n"
046           + "Version " + VERSION + "\n";
047    
048         private static final double  FRAME_RATE = 1.0;
049    
050         private static final Dimension  FRAME_SIZE  = null;
051    
052         private static final String  FRAME_ICON_FILENAME = "/images/david.png";
053    
054         private static final String  SHUTDOWN_CONFIRMATION_PROMPT
055           = "Close " + TITLE + "?";
056    
057         private static final String  FONT_NAME = "TimesRoman";
058    
059         private static final int     FONT_STYLE = Font.BOLD;
060    
061         //
062    
063         private static final int  SPACE_WIDTH       = 100;
064    
065         private static final int  SPACE_HEIGHT      = 100;
066    
067         private static final int  BUGS_MAX          = 10000;
068    
069         private static final int  GENES_MAX         = 8;
070    
071         private static final int  BABY_ENERGY       = 10;
072    
073         private static final int  BIRTH_ENERGY      = 30;
074    
075         private static final int  BIRTH_ENERGY_COST = 20;
076    
077         private static final int  FLORA_ENERGY      = 20;
078    
079         private static final int  MAX_ENERGY        = 60;
080    
081         private static final int  MOVE_COST         = 1;
082    
083         private static final int  EDEN_WIDTH        = 2;
084    
085         private static final int  EDEN_HEIGHT       = 2;
086    
087         private static final int  EDEN_X0           = ( SPACE_WIDTH  - EDEN_WIDTH  ) / 2;
088    
089         private static final int  EDEN_Y0           = ( SPACE_HEIGHT - EDEN_HEIGHT ) / 2;
090    
091         private static final int  EDEN_X1           = EDEN_X0 + EDEN_WIDTH  - 1;
092    
093         private static final int  EDEN_Y1           = EDEN_Y0 + EDEN_HEIGHT - 1;
094    
095         private static final int  MIN_GROWTH_RATE   = 0;
096    
097         private static final int  MAX_GROWTH_RATE   = SPACE_WIDTH * SPACE_HEIGHT;
098    
099         private static final int  SPINNER_STEP_SIZE = 1;
100    
101         private static final int  TEXT_MARGIN       = 10;
102    
103         private static final int  INIT_GROWTH_RATE  = 10;
104    
105         //
106    
107         private static final Color  CRUISER_COLOR = Color.RED;
108    
109         private static final Color  NORMAL_COLOR  = Color.MAGENTA;
110    
111         private static final Color  TWIRLER_COLOR = Color.BLUE;
112    
113         //
114    
115         private Random           random;
116    
117         private Bug [ ]          bugs;
118    
119         private int              flora_growth_rate = INIT_GROWTH_RATE;
120    
121         private int              bugs_alive;
122    
123         private int              time = 0;
124    
125         private boolean [ ] [ ]  flora_present;
126    
127         //
128    
129         private Rectangle           bounds;
130    
131         private AnimatedComponent   animatedComponent;
132    
133         //
134    
135         private JButton             resetButton;
136    
137         private JButton             droughtButton;
138    
139         private JCheckBox           edenCheckBox;
140    
141         private SpinnerNumberModel  growthRateSpinnerNumberModel;
142    
143         //////////////////////////////////////////////////////////////////////
144         // static methods
145         //////////////////////////////////////////////////////////////////////
146    
147         public static void  main ( String [ ]  args )
148         //////////////////////////////////////////////////////////////////////
149         {
150           System.out.println ( INFO );
151    
152           JFrame  jFrame = new JFrame ( TITLE );
153    
154           try
155           {
156             FrameLib.setIconImage ( jFrame, FRAME_ICON_FILENAME );
157           }
158           catch ( IOException  ex )
159           {
160           }
161    
162           Evolve  evolve = new Evolve ( );
163    
164           jFrame.setContentPane ( evolve );
165    
166           FrameLib.launchJFrameAsDesktopApp (
167             jFrame,
168             new Lifecycle [ ] { evolve },
169             FRAME_SIZE,
170             SHUTDOWN_CONFIRMATION_PROMPT );
171         }
172    
173         //////////////////////////////////////////////////////////////////////
174         // overridden Applet methods
175         //////////////////////////////////////////////////////////////////////
176    
177         public String  getAppletInfo ( ) { return INFO; }
178    
179         //////////////////////////////////////////////////////////////////////
180         // interface Lifecycle methods
181         //////////////////////////////////////////////////////////////////////
182    
183         public synchronized void  init ( )
184         //////////////////////////////////////////////////////////////////////
185         {
186           Container  contentPane = getContentPane ( );
187    
188           contentPane.setLayout ( new BorderLayout ( ) );
189    
190           //
191    
192           bounds = new Rectangle ( );
193    
194    // BufferedAnimatedComponent crashes in Java 5       
195           animatedComponent
196    //       = new BufferedAnimatedComponent ( this, FRAME_RATE );
197             = new AnimatedComponent ( this, FRAME_RATE );
198    
199           animatedComponent.setLoopGovernor (
200             new FixedDelayLoopGovernor ( FRAME_RATE ) );
201    
202           animatedComponent.addComponentListener ( this );
203    
204           animatedComponent.addMouseListener ( this );
205    
206           animatedComponent.init ( );
207    
208           contentPane.add ( animatedComponent, BorderLayout.CENTER );
209    
210           //
211    
212           JPanel  southPanel = new JPanel ( );
213    
214           contentPane.add ( southPanel, BorderLayout.SOUTH );
215    
216           //
217    
218           resetButton = new JButton ( "Reset" );
219    
220           resetButton.addActionListener ( this );
221    
222           southPanel.add ( resetButton );
223    
224           //
225    
226           droughtButton = new JButton ( "Blight" );
227    
228           droughtButton.addActionListener ( this );
229    
230           southPanel.add ( droughtButton );
231    
232           //
233    
234           edenCheckBox = new JCheckBox ( "Eden", null, true );
235    
236           southPanel.add ( edenCheckBox );
237    
238           //
239    
240           southPanel.add (
241             new JLabel ( "Food Growth Rate", SwingConstants.RIGHT ) );
242    
243           growthRateSpinnerNumberModel = new SpinnerNumberModel (
244             flora_growth_rate,
245             MIN_GROWTH_RATE,
246             MAX_GROWTH_RATE,
247             SPINNER_STEP_SIZE );
248    
249           growthRateSpinnerNumberModel.addChangeListener ( this );
250    
251           southPanel.add ( new JSpinner ( growthRateSpinnerNumberModel ) );
252    
253           //
254    
255           random = new Random ( );
256    
257           bugs = new Bug [ BUGS_MAX ];
258    
259           flora_present = new boolean [ SPACE_WIDTH ] [ SPACE_HEIGHT ];
260    
261           reset ( );
262         }
263    
264         public void  start ( )
265         //////////////////////////////////////////////////////////////////////
266         {
267           animatedComponent.start ( );
268         }
269    
270         public synchronized void  stop ( )
271         //////////////////////////////////////////////////////////////////////
272         {
273           animatedComponent.stop ( );
274         }
275    
276         public synchronized void  destroy ( )
277         //////////////////////////////////////////////////////////////////////
278         {
279           animatedComponent.destroy ( );
280         }
281    
282         //////////////////////////////////////////////////////////////////////
283         //////////////////////////////////////////////////////////////////////
284    
285         public void  update ( JComponent  component )
286         //////////////////////////////////////////////////////////////////////
287         {
288           moveBugs ( );
289    
290           growFlora ( );
291    
292           component.repaint ( );
293         }
294    
295         public void  paint (
296           JComponent  component,
297           Graphics2D  g )
298         //////////////////////////////////////////////////////////////////////
299         {
300           g.setColor ( Color.BLACK );
301    
302           g.fillRect ( 0, 0, bounds.width, bounds.height );
303    
304           plotFlora ( g );
305    
306           plotBugs ( g );
307    
308           g.setColor ( Color.WHITE );
309    
310           g.drawString (
311             createStatusString ( bugs_alive, time, genesAverageString ( ) ),
312            bounds.x + TEXT_MARGIN,
313            bounds.y + bounds.height - TEXT_MARGIN );
314         }
315    
316         //////////////////////////////////////////////////////////////////////
317         // Listener interface methods
318         //////////////////////////////////////////////////////////////////////
319    
320         public void  actionPerformed ( ActionEvent  actionEvent )
321         //////////////////////////////////////////////////////////////////////
322         {
323           Object  source = actionEvent.getSource ( );
324    
325           if ( source == resetButton )
326           {
327             reset ( );
328           }
329           else if ( source == droughtButton )
330           {
331             setAllFloraPresent ( false );
332           }
333         }
334    
335         public void  componentResized ( ComponentEvent  componentEvent )
336         //////////////////////////////////////////////////////////////////////
337         {
338           Object  source = componentEvent.getSource ( );
339    
340           if ( source == animatedComponent )
341           {
342             animatedComponent.getBounds ( bounds );
343    
344             FontLib.setMaxFont (
345               animatedComponent,
346               createStatusString (
347                 BUGS_MAX, GENES_MAX - 1, genesAverageString ( ) ),
348               FONT_NAME,
349               FONT_STYLE,
350               bounds.width - 2 * TEXT_MARGIN,
351               bounds.height );
352           }
353         }
354    
355         public void  mousePressed ( MouseEvent  mouseEvent )
356         //////////////////////////////////////////////////////////////////////
357         {
358           Point  bugLocation = PlotLib.graphics_to_plot_transform (
359             mouseEvent.getPoint ( ), bounds, animatedComponent.getGraphics ( ),
360             0, SPACE_WIDTH, 0, SPACE_HEIGHT );
361    
362           createNewBug ( bugLocation.x, bugLocation.y );
363         }
364    
365         public void  stateChanged ( ChangeEvent  changeEvent )
366         //////////////////////////////////////////////////////////////////////
367         {
368           Object  source = changeEvent.getSource ( );
369    
370           if ( source == growthRateSpinnerNumberModel )
371           {
372             flora_growth_rate = ( ( Integer )
373               growthRateSpinnerNumberModel.getValue ( ) ).intValue ( );
374           }
375         }
376    
377         public void  componentHidden ( ComponentEvent  componentEvent ) { }
378    
379         public void  componentMoved  ( ComponentEvent  componentEvent ) { }
380    
381         public void  componentShown  ( ComponentEvent  componentEvent ) { }
382    
383         public void  mouseClicked  ( MouseEvent  mouseEvent ) { }
384    
385         public void  mouseEntered  ( MouseEvent  mouseEvent ) { }
386    
387         public void  mouseExited   ( MouseEvent  mouseEvent ) { }
388    
389         public void  mouseReleased ( MouseEvent  mouseEvent ) { }
390    
391         //////////////////////////////////////////////////////////////////////
392         // private methods
393         //////////////////////////////////////////////////////////////////////
394    
395         private void  reset ( )
396         //////////////////////////////////////////////////////////////////////
397         {
398           for ( int  i = 0; i < BUGS_MAX; i++ )
399           {
400             createNewBug ( SPACE_WIDTH / 2, SPACE_HEIGHT / 2, i );
401           }
402          
403           setAllFloraPresent ( true );
404    
405           edenCheckBox.setSelected ( true );
406    
407           growthRateSpinnerNumberModel.setValue (
408             new Integer ( INIT_GROWTH_RATE ) );
409         }
410    
411         private void  createNewBug ( int  x, int  y )
412         //////////////////////////////////////////////////////////////////////
413         {
414           int  i = indexOfFirstDeadBug ( );
415    
416           if ( i > -1 )
417           {
418             createNewBug ( x, y, i );
419           }
420         }
421    
422         private void  createNewBug ( int  x, int  y, int  index )
423         //////////////////////////////////////////////////////////////////////
424         {
425           Bug  bug = bugs [ index ];
426    
427           if ( bug == null )
428           {
429             bug = new Bug ( );
430    
431             bug.genesX = new boolean [ GENES_MAX ];
432    
433             bug.genesY = new boolean [ GENES_MAX ];
434    
435             bugs [ index ] = bug;
436           }
437    
438           bug.x = x;
439    
440           bug.y = y;
441    
442           bug.energy = BABY_ENERGY;
443    
444           for ( int  j = 0; j < GENES_MAX; j++ )
445           {
446             bug.genesX [ j ] = random.nextBoolean ( );
447    
448             bug.genesY [ j ] = random.nextBoolean ( );
449           }
450    
451           setBugColor ( bug );
452         }
453    
454         private void  setBugColor ( Bug  bug )
455         //////////////////////////////////////////////////////////////////////
456         {
457           int  xcount = 0;
458    
459           int  ycount = 0;
460    
461           for ( int  i = 0; i < GENES_MAX; i++ )
462           {
463             if ( bug.genesX [ i ] )
464             {
465               xcount++;
466             }
467    
468             if ( bug.genesY [ i ] )
469             {
470               ycount++;
471             }
472           }
473    
474           Color  color = NORMAL_COLOR;
475    
476           if ( ( xcount == GENES_MAX / 2 )
477             && ( ycount == GENES_MAX / 2 ) )
478           {
479             color = TWIRLER_COLOR;
480           }
481           else if (
482                ( xcount == 0 )
483             || ( xcount == GENES_MAX )
484             || ( ycount == 0 )
485             || ( ycount == GENES_MAX ) )
486           {
487             color = CRUISER_COLOR;
488           }
489    
490           bug.color = color;
491         }
492    
493         private static String  createStatusString (
494           int     bugs_alive,
495           int     time,
496           String  genesAverageString )
497         //////////////////////////////////////////////////////////////////////
498         {
499           return "Alive:  " + bugs_alive
500             + "    Time:  " + time + "    "
501             + "Average Movement Genes  " + genesAverageString;
502         }
503    
504         private void  giveBirth ( Bug  parentBug )
505         //////////////////////////////////////////////////////////////////////
506         {
507           int  index = indexOfFirstDeadBug ( );
508    
509           if ( index < 0 )
510           {
511             return;
512           }
513    
514           parentBug.energy -= BIRTH_ENERGY_COST;
515    
516           Bug  babyBug = bugs [ index ];
517    
518           babyBug.energy = BABY_ENERGY;
519    
520           babyBug.x = parentBug.x;
521    
522           babyBug.y = parentBug.y;
523    
524           for ( int  i = 0; i < GENES_MAX; i++ )
525           {
526             babyBug.genesX [ i ] = parentBug.genesX [ i ];
527    
528             babyBug.genesY [ i ] = parentBug.genesY [ i ];
529           }
530    
531           int  mutantGene = random.nextInt ( GENES_MAX );
532    
533           if ( random.nextBoolean ( ) )
534           {
535             babyBug.genesX [ mutantGene ] = !parentBug.genesX [ mutantGene ];
536           }
537           else
538           {
539             babyBug.genesY [ mutantGene ] = !parentBug.genesY [ mutantGene ];
540           }
541    
542           setBugColor ( babyBug );
543         }
544    
545         private int  indexOfFirstDeadBug ( )
546         //////////////////////////////////////////////////////////////////////
547         {
548           for ( int  i = 0; i < bugs.length; i++ )
549           {
550             if ( bugs [ i ].energy <= 0 )
551             {
552               return i;
553             }
554           }
555    
556           return -1;
557         }
558    
559         private void  moveBugs ( )
560         //////////////////////////////////////////////////////////////////////
561         {
562           time++;
563    
564           if ( time >= GENES_MAX )
565           {
566             time = 0;
567           }
568    
569           for ( int  i = 0; i < bugs.length; i++ )
570           {
571             Bug  bug = bugs [ i ];
572    
573             int  x = bug.x;
574    
575             int  y = bug.y;
576    
577             if ( bug.energy > 0 )
578             {
579               if ( flora_present [ x ] [ y ] )
580               {
581                 flora_present [ x ] [ y ] = false;
582    
583                 bug.energy += FLORA_ENERGY;
584    
585                 if ( bug.energy > MAX_ENERGY )
586                 {
587                   bug.energy = MAX_ENERGY;
588                 }
589               }
590    
591               if ( bug.energy >= BIRTH_ENERGY )
592               {
593                 giveBirth ( bug );
594               }
595    
596               if ( random.nextBoolean ( ) )
597               {
598                 if ( bug.genesX [ time ] ) 
599                 {
600                   x++;
601                 }
602                 else
603                 {
604                   x--;
605                 }
606    
607                 if ( x < 0 )
608                 {
609                   x = SPACE_WIDTH - 1;
610                 }
611                 else if ( x >= SPACE_WIDTH )
612                 {
613                   x = 0;
614                 }
615    
616                 bug.x = x;
617               }
618    
619               if ( random.nextBoolean ( ) )
620               {
621                 if ( bug.genesY [ time ] ) 
622                 {
623                   y++;
624                 }
625                 else
626                 {
627                   y--;
628                 }
629    
630                 if ( y < 0 )
631                 {
632                   y = SPACE_HEIGHT - 1;
633                 }
634                 else if ( y >= SPACE_HEIGHT )
635                 {
636                   y = 0;
637                 }
638    
639                 bug.y = y;
640               }
641    
642               bug.energy -= MOVE_COST;
643             }
644           }
645         }
646    
647         private void  growFlora ( )
648         //////////////////////////////////////////////////////////////////////
649         {
650           for ( int  i = 0; i < flora_growth_rate; i++ )
651           {
652             // randomly position food flora
653    
654             int  x = random.nextInt ( SPACE_WIDTH  );
655    
656             int  y = random.nextInt ( SPACE_HEIGHT );
657    
658             flora_present [ x ] [ y ] = true;
659           }
660    
661           // Replenishing the Garden of Eden.
662    
663           if ( edenCheckBox.isSelected ( ) )
664           {
665             for ( int  x = EDEN_X0; x <= EDEN_X1; x++ )
666             {
667               for ( int  y = EDEN_Y0; y <= EDEN_Y1; y++ )
668               {
669                 flora_present [ x ] [ y ] = true;
670               }
671             }
672           }
673         }
674    
675         private void  setAllFloraPresent ( boolean  floraPresent )
676         //////////////////////////////////////////////////////////////////////
677         {
678           for ( int  x = 0; x < SPACE_WIDTH; x++ )
679           {
680             for ( int  y = 0; y < SPACE_HEIGHT; y++ )
681             {
682               flora_present [ x ] [ y ] = floraPresent;
683             }
684           }
685         }
686    
687         private String  genesAverageString ( )
688         //////////////////////////////////////////////////////////////////////
689         {
690           long    x_sum, y_sum;
691    
692           String  gene_x_String = "X:  ";
693    
694           String  gene_y_String = "Y:  ";
695    
696           bugs_alive = 0;
697    
698           for ( int i = 0; i < bugs.length; i++ )
699           {
700             if ( bugs [ i ].energy > 0 )
701             {
702               bugs_alive++;
703             }
704           }
705    
706           for ( int  i = 0; i < GENES_MAX; i++ )
707           {
708             x_sum = 0;
709             
710             y_sum = 0;
711    
712             for ( int  j = 0; j < bugs.length; j++ )
713             {
714               Bug  bug = bugs [ j ];
715    
716               if ( bug.energy > 0 )
717               {
718                 if ( bug.genesX [ i ] )
719                 {
720                   x_sum++;
721                 }
722    
723                 if ( bug.genesY [ i ] )
724                 {
725                   y_sum++;
726                 }
727               }
728             }
729    
730             if ( ( double ) x_sum / ( double ) bugs_alive >= 0.5 )
731             {
732               gene_x_String += "1";
733             }
734             else
735             {
736               gene_x_String += "0";
737             }
738    
739             if ( ( double ) y_sum / ( double ) bugs_alive >= 0.5 )
740             {
741               gene_y_String += "1";
742             }
743             else
744             {
745               gene_y_String += "0";
746             }
747           }
748    
749           return gene_x_String + "    " + gene_y_String;
750         }
751    
752         private void  plotBugs ( Graphics  g )
753         //////////////////////////////////////////////////////////////////////
754         {
755           for ( int  i = 0; i < bugs.length; i++ )
756           {
757             Bug  bug = bugs [ i ];
758    
759             if ( bug.energy > 0 )
760             {
761               PlotLib.xy (
762                 bug.color,
763                 bug.x + 0.5,
764                 bug.y + 0.5,
765                 bounds, g,
766                 0, SPACE_WIDTH, 0, SPACE_HEIGHT,
767                 1, true );
768             }
769           }
770         }
771    
772         private void  plotFlora ( Graphics  g )
773         //////////////////////////////////////////////////////////////////////
774         {
775           for ( int  x = 0; x < SPACE_WIDTH; x++ )
776           {
777             for ( int  y = 0; y < SPACE_HEIGHT; y++ )
778             {
779               if ( flora_present [ x ] [ y ] )
780               {
781                 PlotLib.xy (
782                   Color.GREEN,
783                   x + 0.5,
784                   y + 0.5,
785                   bounds, g,
786                   0, SPACE_WIDTH, 0, SPACE_HEIGHT,
787                   1, true );
788               }
789             }
790           }
791         }
792    
793         //////////////////////////////////////////////////////////////////////
794         //////////////////////////////////////////////////////////////////////
795         }