||| | |||||| | ||| |||| on Tue, 2 Mar 2004 20:07:55 +0100 (CET)


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[nettime-lat] no-content.net vintage collection ||| GameOfLife.java


\|/
 |
 |
 | @ | - - -> [ -------- ] <- - - - - -  o
     | http://no-content.net/vintage/collection |

          0    0      0      0     <0>    0
        <|>   \|/    \|\    /|\     |    <|>
        / \   / \    / \    / \    / \   / \
o   - - - - - - - - - - - - - - - - - - - - - - -   o


no-content.net/vintage/collection

presents:
pack# 0000 0001 // GameOfLife.java


......


 \|/ `..`..`
  |
  |
__+__netart.org.uy__________
----- Original Message -----


/**
 * Game of Life v1.4
 * Copyright 1996-2001 Edwin Martin <edwin-at-bitstorm.nl>
 * version 1.0 online since July 3 1996
 * Changes:
 * 1.1: Double buffering to screen; faster paint
 * 1.2: Arrowkeys changed; better use of `synchronized'
 * 1.3: Choose speed from drop down menu and draw with mouse
 * 1.4: Use Java 1.1 events, remove 13 deprecated methods, some refactoring.
2003-11-08
 * @author Edwin Martin
 *
 */

package org.bitstorm.gameoflife;

import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.util.*;

/**
 *
 * The Game Of Life Applet.
 * @author Edwin Martin
 */
public class GameOfLife extends Applet implements Runnable {
 protected GameOfLifeCanvas gameOfLifeCanvas;
 protected GameOfLifeGrid gameOfLifeGrid;
 protected int cellSize;
 protected int cellCols;
 protected int cellRows;
 protected int genTime;
 protected GameOfLifeControls controls;
 private static Thread gameThread = null;
 private final String VIRGIN = "First draw a shape or select a shape
from\nthe pull-down menu.";

 /**
  * Initialize UI.
  * @see java.applet.Applet#init()
  */
 public void init() {
  getParams();

  // set background colour
  setBackground(new Color(0x999999));

  // create gameOfLifeGrid
  gameOfLifeGrid = new GameOfLifeGrid(cellCols, cellRows);

  // create GameOfLifeCanvas
  gameOfLifeCanvas = new GameOfLifeCanvas(gameOfLifeGrid, cellSize);

  // create GameOfLifeControls
  controls = new GameOfLifeControls( this );

  // put it all together
  setLayout(new BorderLayout());
  add(BorderLayout.SOUTH, controls);
  add(BorderLayout.NORTH, gameOfLifeCanvas);
  setVisible(true);
  validate();
 }

 protected void getParams() {
  // get applet parameters: cellSize, cellCols, cellRows, genTime
  cellSize = getParamInteger( "cellsize", 11 );
  cellCols = getParamInteger( "cellcols", 50 );
  cellRows = getParamInteger( "cellrows", 30 );
  genTime  = getParamInteger( "gentime", 500 );
 }

 /**
  * Read applet parameter (int) or, when unavailable, get default value.
  * @param name name of parameter
  * @param defaultParam default when parameter is unavailable
  * @return value of parameter
  */
 protected int getParamInteger( String name, int defaultParam ) {
  String param;
  int paramInt;

  param = getParameter( name );
  if ( param == null )
   paramInt = defaultParam;
  else
   paramInt = Integer.valueOf(param).intValue();
  return paramInt;
 }

 /**
  * Starts creating new generations.
  * No start() to prevent starting immediately.
  */
 public synchronized void start2() {
  if ( gameOfLifeGrid.isEmpty() ) {
   alert( VIRGIN );
  } else {
   controls.start();
   if (gameThread == null) {
    gameThread = new Thread(this);
    gameThread.start();
   }
  }
 }

 /**
  * @see java.applet.Applet#stop()
  */
 public void stop() {
  controls.stop();
  gameThread = null;
 }

 /**
  * @see java.lang.Runnable#run()
  */
 public synchronized void run() {
  while (gameThread != null) {
   nextGeneration();
   try {
    Thread.sleep(genTime);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
 }

 /**
  * Is the applet running?
  * @return true: applet is running
  */
 public boolean isRunning() {
  return gameThread != null;
 }

 /**
  * Go to the next generation.
  */
 public void nextGeneration() {
  if ( gameOfLifeGrid.isEmpty() ) {
   alert( VIRGIN );
  } else {
   gameOfLifeGrid.next();
   gameOfLifeCanvas.repaint();
   showGenerations();
  }
 }

 /**
  * Set the new shape
  * @param shapeName name of shape
  */
 public void setShape( String shapeName ) {
  try {
   gameOfLifeGrid.setShape( shapeName );
   reset();
  } catch (ShapeException e) {
   alert( e.getMessage() );
  }
 }

 /**
  * Resets applet (after loading new shape)
  */
 public void reset() {
  stop(); // might otherwise confuse user
  gameOfLifeCanvas.repaint();
  showGenerations();
  showStatus( "" );
 }

 /**
  * @see java.applet.Applet#getAppletInfo()
  */
 public String getAppletInfo() {
  return "Game Of Life v. 1.4\nCopyright 1996-2003 Edwin Martin";
 }

 /**
  * Show number of generations.
  */
 private void showGenerations() {
  controls.setGeneration( gameOfLifeGrid.getGenerations() );
 }

 /**
  * Set speed of new generations.
  * @param fps generations per second
  */
 public void setSpeed( int fps ) {
  genTime = fps;
 }

 /**
  * Shows an alert
  * @param s text to show
  */
 public void alert( String s ) {
  showStatus( s );
 }
}

/**
 * Controls of the Game of Life (Shape and speed selector, next and
start/stop-button).
 * @author Edwin Martin
 *
 */
class GameOfLifeControls extends Panel {
 private Label genLabel;
 private final String genLabelText = "Generations: ";
 private final String slow = "Slow";
 private final String fast = "Fast";
 private final String hyper = "Hyper";
 private final String nextLabelText = "Next";
 private final String startLabelText = "Start";
 private final String stopLabelText = "Stop";
 private Button startstopButton;
 private Button nextButton;
 private GameOfLife gameOfLife;

 /**
  * Contructor
  * @param gof parent Game Of Life object
  */
 public GameOfLifeControls( GameOfLife gameOfLife ) {
  this.gameOfLife = gameOfLife;

  // pulldown menu with shapes
  Choice shapesChoice = new Choice();

  // Put names of shapes in menu
  Enumeration shapes = GameOfLifeGrid.getShapes();
  while (shapes.hasMoreElements()) {
   shapesChoice.addItem((String) shapes.nextElement());
  }

  // when shape is selected
  shapesChoice.addItemListener(
   new ItemListener() {
    public void itemStateChanged(ItemEvent e) {
     String shapeName = (String) e.getItem();
     getGameOfLife().setShape( shapeName );
    }
   }
  );

  // pulldown menu with speeds
  Choice speedChoice = new Choice();

  // add speeds
  speedChoice.addItem(slow);
  speedChoice.addItem(fast);
  speedChoice.addItem(hyper);

  // when item is selected
  speedChoice.addItemListener(new ItemListener() {
   public void itemStateChanged(ItemEvent e) {
    String arg = (String) e.getItem();
    if (slow.equals(arg)) // slow
     getGameOfLife().setSpeed(1000);
    else if (fast.equals(arg)) // fast
     getGameOfLife().setSpeed(100);
    else if (hyper.equals(arg)) // hyperspeed
     getGameOfLife().setSpeed(10);
   }
  });

  // number of generations
  genLabel = new Label(genLabelText+"0             ");

  // start and stop buttom
  startstopButton = new Button(startLabelText);

  // when start/stop button is clicked
  startstopButton.addActionListener(
   new ActionListener() {
    public void actionPerformed(ActionEvent e) {
     startStopButtonClicked();
    }
   }
  );

  // next generation button
  nextButton = new Button(nextLabelText);

  // when next button is clicked
  nextButton.addActionListener(
   new ActionListener() {
    public void actionPerformed(ActionEvent e) {
     getGameOfLife().nextGeneration();
    }
   }
  );

  // create panel with controls
  this.add(shapesChoice);
  this.add(nextButton);
  this.add(startstopButton);
  this.add(speedChoice);
  this.add(genLabel);
  this.validate();
 }

 /**
  * Set the number of generations in the control bar.
  * @param generations number of generations
  */
 public void setGeneration( int generations ) {
  genLabel.setText(genLabelText + generations);
 }

 /**
  * Change the label of the start/stop-button to "Stop".
  */
 public void start() {
  startstopButton.setLabel(stopLabelText);
 }

 /**
  * Change the label of the start/stop-button to "Start".
  */
 public void stop() {
  startstopButton.setLabel(startLabelText);
 }

 /**
  * Called when the start/stop-button is clicked.
  */
 public void startStopButtonClicked() {
  if ( gameOfLife.isRunning() ) {
   gameOfLife.stop();
  } else {
   gameOfLife.start2();
  }
 }

 /**
  * Return GameOfLife applet object.
  * @return GameOfLife applet object
  */
 public GameOfLife getGameOfLife() {
  return gameOfLife;
 }
}

/**
 *
 * Subclass of Canvas which deals with the GameOfLife.
 * @author Edwin Martin
 */
class GameOfLifeCanvas extends Canvas {
 private boolean cellUnderMouse;
 private Image offScreenImage = null;
 private Graphics offScreenGraphics;
 private int cellSize;
 private GameOfLifeGrid gameOfLifeGrid;

 /**
  * Constructor.
  * @param gameOfLifeGrid
  * @param cellSize size of cell in pixels
  */
 public GameOfLifeCanvas(GameOfLifeGrid gameOfLifeGrid, int cellSize) {
  this.gameOfLifeGrid = gameOfLifeGrid;
  this.cellSize = cellSize;
  gameOfLifeGrid.clear();

  addMouseListener(
   new MouseAdapter() {
    public void mouseReleased(MouseEvent e) {
     draw(e.getX(), e.getY());
    }
    public void mousePressed(MouseEvent e) {
     saveCellUnderMouse(e.getX(), e.getY());
    }
   });

  addMouseMotionListener(new MouseMotionAdapter() {
   public void mouseDragged(MouseEvent e) {
    draw(e.getX(), e.getY());
   }
  });
 }

 /**
  * Remember state of cell for drawing.
  *
  * @param x x-coordinate
  * @param y y-coordinate
  */
 public void saveCellUnderMouse(int x, int y) {
  try {
   cellUnderMouse = gameOfLifeGrid.getCell(x / cellSize, y / cellSize);
  } catch (java.lang.ArrayIndexOutOfBoundsException e) {
   // ignore
  }
 }

 /**
  * Draw in this cell.
  *
  * @param x x-coordinate
  * @param y y-coordinate
  */
 public void draw(int x, int y) {
  try {
   gameOfLifeGrid.setCell(x / cellSize, y / cellSize, !cellUnderMouse );
   repaint();
   gameOfLifeGrid.notEmpty();
  } catch (java.lang.ArrayIndexOutOfBoundsException e) {
   // ignore
  }
 }

 /**
  * Use double buffering.
  * @see java.awt.Component#update(java.awt.Graphics)
  */
 public void update(Graphics theG) {
  Dimension d = getSize();
  if ((offScreenImage == null)) {
   offScreenImage = createImage(d.width, d.height);
   offScreenGraphics = offScreenImage.getGraphics();
  }
  paint(offScreenGraphics);
  theG.drawImage(offScreenImage, 0, 0, null);
 }

 /**
  * Draw this generation.
  * @see java.awt.Component#paint(java.awt.Graphics)
  */
 public void paint(Graphics g) {
  // draw background (MSIE doesn't do that)
  Dimension dim = gameOfLifeGrid.getDimension();
  g.setColor(Color.gray);
  g.fillRect(0, 0, cellSize * dim.width - 1, cellSize * dim.height - 1);
  // draw grid
  g.setColor(getBackground());
  for (int x = 1; x < dim.width; x++) {
   g.drawLine(x * cellSize - 1, 0, x * cellSize - 1, cellSize * dim.height -
1);
  }
  for (int y = 1; y < dim.height; y++) {
   g.drawLine( 0, y * cellSize - 1, cellSize * dim.width - 1, y * cellSize -
1);
  }
  // draw populated cells
  g.setColor(Color.yellow);
  for (int y = 0; y < dim.height; y++) {
   for (int x = 0; x < dim.width; x++) {
    if (gameOfLifeGrid.getCell(x, y)) {
     g.fillRect(x * cellSize, y * cellSize, cellSize - 1, cellSize - 1);
    }
   }
  }
 }

 /**
  * This is the preferred size.
  * @see java.awt.Component#getPreferredSize()
  */
 public Dimension getPreferredSize() {
  Dimension dim = gameOfLifeGrid.getDimension();
  return new Dimension( cellSize * dim.width, cellSize * dim.height );
 }
}

/**
 * Contains all the Game Of Life algorithms and shapes.
 *
 * @author Edwin Martin
 */
class GameOfLifeGrid {
 protected boolean cells[][];
 protected int cellRows;
 protected int cellCols;
 protected int cellsBuffer[][];
 private int generations;
 private static Shape[] shapes;
 private boolean empty;

 static {
  // define all available shapes
  shapes = new Shape[8];
  shapes[0] = new Shape("Clear", new int[][] {} );
  shapes[1] = new Shape("Glider", new int[][] {{0,1}, {1,2}, {2,2}, {2,1},
{2,0}});
  shapes[2] = new Shape("Small Exploder", new int[][] {{0,1}, {0,2}, {1,0},
{1,1}, {1,3}, {2,1}, {2,2}});
  shapes[3] = new Shape("Exploder", new int[][] {{0,0}, {0,1}, {0,2}, {0,3},
{0,4}, {2,0}, {2,4}, {4,0}, {4,1}, {4,2}, {4,3}, {4,4}});
  shapes[4] = new Shape("10 Cell Row", new int[][] {{0,0}, {1,0}, {2,0},
{3,0}, {4,0}, {5,0}, {6,0}, {7,0}, {8,0}, {9,0}});
  shapes[5] = new Shape("Fish", new int[][] {{0,1}, {0,3}, {1,0}, {2,0},
{3,0}, {3,3}, {4,0}, {4,1}, {4,2}});
  shapes[6] = new Shape("Pump", new int[][] {{0,3}, {0,4}, {0,5}, {1,0},
{1,1}, {1,5}, {2,0}, {2,1}, {2,2}, {2,3}, {2,4}, {4,0}, {4,1}, {4,2}, {4,3},
{4,4}, {5,0}, {5,1}, {5,5}, {6,3}, {6,4}, {6,5}});
  shapes[7] = new Shape("Shooter", new int[][] {{0,2}, {0,3}, {1,2}, {1,3},
{8,3}, {8,4}, {9,2}, {9,4}, {10,2}, {10,3}, {16,4}, {16,5}, {16,6}, {17,4},
{18,5}, {22,1}, {22,2}, {23,0}, {23,2}, {24,0}, {24,1}, {24,12}, {24,13},
{25,12}, {25,14}, {26,12}, {34,0}, {34,1}, {35,0}, {35,1}, {35,7}, {35,8},
{35,9}, {36,7}, {37,8}});
 }

 /**
  * Contructor.
  *
  * @param cellCols number of columns
  * @param cellRows number of rows
  */
 public GameOfLifeGrid(int cellCols, int cellRows) {
  this.cellCols = cellCols;
  this.cellRows = cellRows;
  cellsBuffer = new int[cellCols][cellRows];
  cells = new boolean[cellCols][cellRows];
  empty = true;
 }

 /**
  * @return enumeration of shapes
  */
 public static Enumeration getShapes() {
  return new ShapeEnumeration( shapes );
 }

 /**
  * Clears grid.
  */
 public void clear() {
  generations = 0;
  //virgin = true;
  for (int x = 0; x < cellCols; x++) {
   for (int y = 0; y < cellRows; y++) {
    cells[x][y] = false;
   }
  }
 }

 /**
  * Create next generation of shape.
  */
 public synchronized void next() {
  int x;
  int y;

  generations++;
  // clear the buffer
  for (x = 0; x < cellCols; x++) {
   for (y = 0; y < cellRows; y++) {
    cellsBuffer[x][y] = 0;
   }
  }

  // count neighbors of off-edge cells
  for (x = 1; x < cellCols - 1; x++) {
   for (y = 1; y < cellRows - 1; y++) {
    if (cells[x][y]) {
     cellsBuffer[x - 1][y - 1]++;
     cellsBuffer[x][y - 1]++;
     cellsBuffer[x + 1][y - 1]++;
     cellsBuffer[x - 1][y]++;
     cellsBuffer[x + 1][y]++;
     cellsBuffer[x - 1][y + 1]++;
     cellsBuffer[x][y + 1]++;
     cellsBuffer[x + 1][y + 1]++;
    }
   }
  }

  // count neighbors of edge cells
  x = 1; // start at (1,0)
  y = 0;
  int dx = 1;
  int dy = 0;
  while (true) {
   if (cells[x][y]) {
    if (x > 0) {
     if (y > 0)
      cellsBuffer[x - 1][y - 1]++;
     if (y < cellRows - 1)
      cellsBuffer[x - 1][y + 1]++;
     cellsBuffer[x - 1][y]++;
    }
    if (x < cellCols - 1) {
     if (y < cellRows - 1)
      cellsBuffer[x + 1][y + 1]++;
     if (y > 0)
      cellsBuffer[x + 1][y - 1]++;
     cellsBuffer[x + 1][y]++;
    }
    if (y > 0)
     cellsBuffer[x][y - 1]++;
    if (y < cellRows - 1)
     cellsBuffer[x][y + 1]++;
   }

   // turn clockwise at collision with edge
   if (x == cellCols - 1 ) {
    if (y == 0) {
     dx = 0;
     dy = 1;
    } else if ( y == cellRows - 1) {
     dx = -1;
     dy = 0;
    }
   } else if (x == 0) {
    if ( y == cellRows - 1) {
     dx = 0;
     dy = -1;
    } else if ( y == 0) {
     // all edge cells done
     break;
    }
   }
   x += dx;
   y += dy;
  }

  // here is the life algorithm
  // simple, isn't it?
  for (x = 0; x < cellCols; x++) {
   for (y = 0; y < cellRows; y++) {
    switch (cellsBuffer[x][y]) {
     case 2 :
      // no change
      break;
     case 3 :
      cells[x][y] = true;
      break;
     default :
      cells[x][y] = false;
      break;
    }
   }
  }

 }

 /**
  * Draws shape in grid.
  *
  * @param shapeName name of shape
  * @return true when shape fits, false otherwise
  */
 public synchronized void setShape(String shapeName) throws ShapeException {
  int xOffset;
  int yOffset;
  int[][] shape = null;
  Dimension dim = null;
  int i;

  notEmpty();
  for ( i = 0; i < shapes.length; i++ ) {
   if ( shapes[i].getName().equals( shapeName ) )
    break;
  }

  // not found
  if ( i == shapes.length )
   throw new ShapeException( "Unknown shape" ); // shape doesn't fit on
canvas

  // get shape properties
  shape = shapes[i].getShape();
  dim =  shapes[i].getDimension();

  if (dim.width > cellCols || dim.height > cellRows)
   throw new ShapeException( "Shape doesn't fit on canvas" ); // shape
doesn't fit on canvas

  // center the shape
  xOffset = (cellCols - dim.width) / 2;
  yOffset = (cellRows - dim.height) / 2;
  clear();

  // draw shape
  for ( i = 0; i < shape.length; i++ )
   cells[xOffset + shape[i][0]][yOffset + shape[i][1]] = true;
 }

 /**
  * Get value of cell.
  * @param x x-coordinate of cell
  * @param y y-coordinate of cell
  * @return value of cell
  */
 public boolean getCell( int x, int y ) {
  try {
   return cells[x][y];
  } catch (ArrayIndexOutOfBoundsException e) {
   return false;
  }
 }

 /**
  * Set value of cell.
  * @param x x-coordinate of cell
  * @param y y-coordinate of cell
  * @param c value of cell
  */
 public void setCell( int x, int y, boolean c ) {
  try {
   cells[x][y] = c;
  } catch (ArrayIndexOutOfBoundsException e) {
   // ignore
  }
 }

 /**
  * @return number of generations
  */
 public int getGenerations() {
  return generations;
 }

 /**
  * @return dimension of grid
  */
 public Dimension getDimension() {
  return new Dimension( cellCols, cellRows );
 }

 /**
  * Grid is not empty anymore
  */
 public void notEmpty() {
  empty = false;
 }

 /**
  * Is grid empty?
  * @return true if empty, false otherwise
  */
 public boolean isEmpty() {
  return empty;
 }
}

/**
 * Shape contains data of one shape.
 *
 * @author Edwin Martin
 */
class Shape {
 private String name;
 private int[][] shape;

 /**
  * Constructor.
  * @param name name of shape
  * @param shape shape data
  */
 public Shape( String name, int[][] shape ) {
  this.name = name;
  this.shape = shape;
 }

 /**
  * @return dimension of the shape in cells
  */
 public Dimension getDimension() {
  int shapeWidth = 0;
  int shapeHeight = 0;
  for (int cell = 0; cell < shape.length; cell++) {
   if (shape[cell][0] > shapeWidth)
    shapeWidth = shape[cell][0];
   if (shape[cell][1] > shapeHeight)
    shapeHeight = shape[cell][1];
  }
  shapeWidth++;
  shapeHeight++;
  return new Dimension( shapeWidth, shapeHeight );
 }

 /**
  * @return name of shape
  */
 public String getName() {
  return name;
 }

 /**
  * @return shape data
  */
 public int[][] getShape() {
  return shape;
 }
}

/**
 *
 * Enumerate through array of shapes.
 *
 * @author Edwin Martin
 */
class ShapeEnumeration implements Enumeration {
 private int index;
 private Shape[] shapes;

 /**
  * Contructor.
  * @param shapes
  */
 public ShapeEnumeration( Shape[] shapes ) {
  index = 0;
  this.shapes = shapes;
 }

 /**
  * @see java.util.Enumeration#hasMoreElements()
  */
 public boolean hasMoreElements() {
  return index < shapes.length;
 }

 /**
  * @see java.util.Enumeration#nextElement()
  */
 public Object nextElement() {
  index++;
  return shapes[index-1].getName();
 }
}

/**
 * Exception for shapes (too big, not found...).
 *
 * @author Edwin Martin
 */
class ShapeException extends Exception {
 /**
  * Constructor.
  */
 public ShapeException() {
  super();
 }
 /**
  * Constructor with description.
  */
 public ShapeException( String s ) {
  super( s );
 }
}


  ___ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ____
./|n |||e |||t |||a |||r |||t |||- |||l |||a |||t |||i |||n |||o | \|/
.||__|||__|||__|||__|||__|||__|||__|||__|||__|||__|||__|||__|||__|  |
.||____|||__|||__|||__|||__|||__|||__|||__|||__| |                  |
.|/_____\|/__\|/__\|/__\|/__\|/__\|/__\|/__\|/__\|__netart.org.uy __|
----- Original Message -----


_______________________________________________
Nettime-lat mailing list
Nettime-lat@nettime.org
http://amsterdam.nettime.org/cgi-bin/mailman/listinfo/nettime-lat