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 }