/* * jlife.java - A Java implementation of Conway's Life game. * * Author : Jeff Mercer * Written: 7/24/99 * Updated: 8/10/99 * JDK Ver: 1.1.8 * * Purpose: Does a life simulation using the LifeGrid class. Displays the * generation and population counts, as well as average cell age. * User can start/stop simulation, single-step, and reset simulation. * * Copyright 1999 by Jeff C. Mercer. All rights reserved. * This program may be freely distributed and used for non-profit purposes. * For commercial or business usage, please contact the author. */ /************************************* * Import needed classes and packages *************************************/ import java.applet.Applet; // Of course, we need the applet class. import java.awt.*; // Need windowing functions. import java.awt.event.*; // Need event class for user interaction. import java.awt.image.IndexColorModel; // For creating images. import java.awt.image.MemoryImageSource; // For creating images, also. import LifeGrid; // Need the Life classes. public class jlife extends Applet implements Runnable, ActionListener { /****************************** * Data members of this applet ******************************/ final int LIFESIZE=128; // Size of life grid (both dimensions). final int DISP_X=12; // X-coord of display area. final int DISP_Y=96; // Y-coord of display area. final int SCALE=2; // Scale to draw grid image at. final int NAPTIME=75; // Length of loop delay in milliseconds. LifeGrid theUniverse; // The Conway arena. Thread jlifeThread; // For this applet's thread. boolean stepRun, fullRun; // Loop flags, for run control. boolean flameOn; // Flame effect on|off flag. long startNap, endNap; // For computing sleep time. byte[] pixels; // Array of bytes for screen buffer. Image gridImg; // Image object for grid display. IndexColorModel icm; // ICM instance for creating images. // Button controls, for user input. Button startBtn, stopBtn, stepBtn, resetBtn, flameBtn; // Label controls for displaying stats, etc. Label genLabel, genValueLabel, popLabel, popValueLabel, avgAgeLabel, avgAgeValueLabel, titleLabel; /************************** * Methods of this applet. ****************************************************************************/ // Initialization method for the jlife applet, called when applet is first // loaded and primary execution has not started yet. public void init() { // Make a whole new life universe. theUniverse=new LifeGrid(LIFESIZE); // Reset the universe and re-populate it. theUniverse.remakeAll(); // Initialize the user buttons. startBtn=new Button("Start"); startBtn.addActionListener(this); resetBtn=new Button("Reset"); resetBtn.addActionListener(this); stopBtn=new Button("Stop"); stopBtn.addActionListener(this); stepBtn=new Button("Step"); stepBtn.addActionListener(this); flameBtn=new Button("Flame!"); flameBtn.addActionListener(this); // Create and initialize the status display labels. popLabel=new Label("Population: ", Label.RIGHT); popValueLabel=new Label("00000", Label.LEFT); genLabel=new Label("Generation: ", Label.RIGHT); genValueLabel=new Label("00000", Label.LEFT); avgAgeLabel=new Label("Average age: ", Label.RIGHT); avgAgeValueLabel=new Label("00000.00000000", Label.LEFT); // Create and initialize title label. titleLabel=new Label("jlife - Conway's Life in Java by Jeff Mercer", Label.CENTER); // Add the title to the applet display first. add (titleLabel); // Now add the user buttons to the applet display. add (startBtn); add (stopBtn); add (stepBtn); add (resetBtn); add (flameBtn); // Finally, add the labels to the applet display. add (popLabel); add (popValueLabel); add (genLabel); add (genValueLabel); add (avgAgeLabel); add (avgAgeValueLabel); // Make the screen display object. pixels=new byte[LIFESIZE*LIFESIZE]; // Create the index color model for image production. This requires // setting up some RGB data, but that's only needed this once. byte[] red=new byte[256]; // Reds array. byte[] green=new byte[256]; // Blues array. byte[] blue=new byte[256]; // Greens array. // Step through 64 different palette values... for (int i=0; i<64; i++) { // Set red values. red[i]=(byte) (i*4); red[i+192]=(byte) (i*4); // Set green values. green[i+64]=(byte) (i*4); green[i+192]=(byte) (i*4); // Set blue values. blue [i+128]=(byte) (i*4); blue[i+192]=(byte) (i*4); // Remaining empty spaces are black by default... } // Create the ICM object from the RGB set that was just initialized. icm=new IndexColorModel(8, 256, red, green, blue); // Set run mode flags off, so simulation needs to be triggered by user. stepRun=false; fullRun=false; // By default the flame effect should be off. flameOn=false; } /****************************************************************************/ // Custom update method, which just jumps straight to the paint method. // This is done to prevent screen flickering. public void update(Graphics g) { // Just hop straight to the paint method. paint(g); } /****************************************************************************/ // Custom paint method for drawing/updating the display. Called when applet // first starts, a repaint is requested, or applet gets focus. This draws // the entire applet display as well as life simulation grid. public void paint(Graphics g) { // Display the current population. popValueLabel.setText(Long.toString(theUniverse.givePopCount())); // Display the current generation. genValueLabel.setText(Long.toString(theUniverse.giveGenCount())); // Display the average age of this generation. avgAgeValueLabel.setText(Float.toString(theUniverse.giveAvgAge())); // If the flame effect is not turned on, clear screen buffer... if (! flameOn) clearScreen(); else // Otherwise, apply the flame effect. flame(); // Have the life universe update the screen buffer for display. theUniverse.drawGridIMG(pixels); // Create a new image object from the screen buffer. gridImg=createImage(new MemoryImageSource(LIFESIZE, LIFESIZE, icm, pixels, 0, LIFESIZE)); // Draw the image in the applet display. g.drawImage(gridImg, DISP_X, DISP_Y+24, LIFESIZE*SCALE, LIFESIZE*SCALE, null); } /****************************************************************************/ // Start the applet thread running. public void start() { // If this applet's thread is dead... if (jlifeThread==null) { jlifeThread=new Thread(this, "jlife"); // Re-create the applet thread. jlifeThread.start(); // And start it up. } } /****************************************************************************/ // Applet's primary run loop, where all the action takes place. This // executes as a thread for better performance. public void run() { // Get ahold of the thread that this run() method belongs to. Thread thisThread=Thread.currentThread(); // As long as this thread is the same as the applet thread (and // thus still alive)... while (thisThread==jlifeThread) { // Get the current time, to measure how long this iteration takes. startNap=System.currentTimeMillis(); // Do a generation sweep, if run flags are true. if (fullRun || stepRun) { theUniverse.doGen(); // Everybody dance now. repaint(); // Update the display. if (stepRun) // If this was a single-step run... stepRun=false; // ...reset the flag. } // If that wiped everything out... if (theUniverse.givePopCount()==0) fullRun=false; // turn off the full run flag. // So how long was that, anywho? endNap=System.currentTimeMillis(); // Well, let's nap the differential. if (endNap-startNap0) pixavg--; // Set the current pixel to it's new value. pixels[idx]=(byte) pixavg; } } // End of applet. }