/* GameOfLife - demonstrator for later Mips+LCD game of life * * 21.02.06 - new class */ import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.swing.*; public class GameOfLife { JFrame frame; JPanel buttonPanel; JButton initButton, startButton, stopButton, stepButton, exitButton; LCDCanvas lcd; Callback callback; Worker worker; int nx = 128; int ny = 64; int matrix[][], future[][]; boolean running = false; int sleepMillis = 20; // 20 msec, or 50 repaints per sec (theoretically) int generation = 0; int population = 0; long lastMillis = System.currentTimeMillis(); public GameOfLife() { matrix = new int[nx][ny]; future = new int[nx][ny]; } public void createGUI() { frame = new JFrame( "Game of Life" ); lcd = new LCDCanvas(); callback = new Callback(); initButton = makeButton( "Init", callback ); stepButton = makeButton( "Step", callback ); startButton = makeButton( "Start", callback ); stopButton = makeButton( "Stop", callback ); exitButton = makeButton( "Exit", callback ); buttonPanel = new JPanel( new FlowLayout( FlowLayout.CENTER )); buttonPanel.add( initButton ); buttonPanel.add( stepButton ); buttonPanel.add( new JLabel( " " ) ); buttonPanel.add( startButton ); buttonPanel.add( stopButton ); buttonPanel.add( new JLabel( " " ) ); buttonPanel.add( exitButton ); frame.getContentPane().add( "Center", lcd ); frame.getContentPane().add( "South", buttonPanel ); frame.pack(); frame.show(); } public JButton makeButton( String label, ActionListener al ) { JButton tmp = new JButton( label ); tmp.addActionListener( al ); return tmp; } public void setSleepMillis( int millis ) { sleepMillis = millis; } public class Callback implements ActionListener { public void actionPerformed( ActionEvent evt ) { String cmd = evt.getActionCommand(); if ("Exit".equals(cmd)) { doExit(); } else if ("Init".equals(cmd)) { doInitializeBoard(); } else if ("Step".equals(cmd)) { doSingleStep(); } else if ("Stop".equals(cmd)) { doStopAnimation(); } else if ("Start".equals(cmd)) { doStartAnimation(); } else { System.out.println( "unknown command: " + evt ); } } } public class Worker extends Thread { public void run() { while( running ) { iteration(); display(); if (sleepMillis > 0) { try { Thread.sleep( sleepMillis ); } catch( InterruptedException ie ) { running = false; } } } } } public class LCDCanvas extends JPanel { private BufferedImage buffer; private int sx, sy; private Color activePixel = new Color( 30, 30, 40 ); private Color passivePixel = new Color( 200, 180, 240 ); private Color gridPixel = new Color( 255, 240, 240 ); public LCDCanvas() { super(); sx = 4*nx; sy = 4*ny; // buffer = new BufferedImage( sx, sy, BufferedImage.TYPE_INT_RGB ); } /** not used at the moment: image painting is mmuucchh slower than "raw" * AWT painting on all my JDKs and classpath+jamvm... */ public void updateBuffer() { Graphics g = buffer.getGraphics(); g.setColor( gridPixel ); g.fillRect( 0, 0, sx, sy ); Color pixelColor = null; for( int ix=0; ix < nx; ix++ ) { for( int iy=0; iy < ny; iy++ ) { if (matrix[ix][iy] != 0) pixelColor = activePixel; else pixelColor = passivePixel; g.setColor( pixelColor ); g.fillRect( 4*ix, 4*iy, 3, 3 ); } } } public void paintComponent( Graphics g ) { // for buffered drawing - not used atm // g.drawImage( buffer, 0, 0, null ); g.setColor( gridPixel ); g.fillRect( 0, 0, sx, sy ); Color pixelColor = null; for( int ix=0; ix < nx; ix++ ) { for( int iy=0; iy < ny; iy++ ) { if (matrix[ix][iy] != 0) pixelColor = activePixel; else pixelColor = passivePixel; g.setColor( pixelColor ); g.fillRect( 4*ix, 4*iy, 3, 3 ); } } } public Dimension getPreferredSize() { return new Dimension(sx,sy); } public Dimension getMinimumSize() { return new Dimension(sx,sy); } } public void doExit() { System.exit( 0 ); } public void doInitializeBoard() { initialize(); display(); } public void doSingleStep() { iteration(); display(); } public void doStartAnimation() { running = true; worker = new Worker(); worker.start(); } public void doStopAnimation() { running = false; if (worker != null) { worker.interrupt(); worker = null; } } public void display() { // lcd.updateBuffer(); lcd.repaint(); } /** * the actual game of life algorithm: cell is born when three neighbors, * survives when exactly two live neighbors, dies otherwise. */ public void iteration() { // future = new int[nx][ny]; int lifes = 0; for( int i=0; i < nx; i++ ) { for( int j=0; j < ny; j++ ) { // count neighbors int n = countNeighbors( i, j ); if ((n != 0) && (Math.random() > 0.99)) { // some slight random offset if (Math.random() > 0.5) future[i][j] = 1; else future[i][j] = 0; } else if (n == 3) future[i][j] = 1; else if ((n == 2) && (matrix[i][j] == 1)) future[i][j] = 1; else future[i][j] = 0; if (future[i][j] == 1) lifes++; } } generation ++; if ((generation % 100) == 0) { long t = System.currentTimeMillis(); System.out.println( " population= " + population + " generation= " + generation + " iterations/sec= " + 100.0*1000/(t-lastMillis) ); lastMillis = t; } // avoid extinction :-) if (lifes < 20) initialize(); else { // swap buffers int [][] tmp = matrix; matrix = future; future = tmp; } } private int countNeighbors( int i, int j ) { int im = i-1; int ip = i+1; int jm = j-1; int jp = j+1; // wrap-around if (ip >= nx) ip = 0; if (jp >= ny) jp = 0; if (im < 0) im = nx-1; if (jm < 0) jm = ny-1; // check eight neighbors int n = 0; n += matrix[im][jm]; // nw n += matrix[im][j]; // west n += matrix[im][jp]; // sw n += matrix[i ][jm]; // north // n += matrix[i ][j ]; // center n += matrix[i ][jp]; // south n += matrix[ip][jm]; // ne n += matrix[ip][j ]; // east n += matrix[ip][jp]; // se return n; } public void initialize() { for( int x=0; x < nx; x++ ) { for( int y=0; y < ny; y++ ) { double d = Math.random(); if (d > 0.85) matrix[x][y] = 1; else matrix[x][y] = 0; } } generation = 0; population ++; } public static void main( String args[] ) { GameOfLife gol = new GameOfLife(); if (args.length > 0) { if ("-sleep".equals(args[0])) gol.setSleepMillis( Integer.parseInt(args[1]) ); } gol.createGUI(); gol.doStartAnimation(); // gol.initialize(); // gol.display(); } }