||| | |||||| | ||| |||| 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 [email protected] http://amsterdam.nettime.org/cgi-bin/mailman/listinfo/nettime-lat