/*
* Artificial Intelligence for Humans
* Volume 2: Nature Inspired Algorithms
* Java Version
* http://www.aifh.org
* http://www.jeffheaton.com
*
* Code repository:
* https://github.com/jeffheaton/aifh
*
* Copyright 2014 by Jeff Heaton
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For more information on Heaton Research copyrights, licenses
* and trademarks visit:
* http://www.heatonresearch.com/copyright
*/
package com.heatonresearch.aifh.examples.ca;
import com.heatonresearch.aifh.examples.util.WorldPanel;
import com.heatonresearch.aifh.randomize.GenerateRandom;
import com.heatonresearch.aifh.randomize.MersenneTwisterGenerateRandom;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
/**
* Conway's game of life. A classic cellular automation.
* <p/>
* Rules:
* 1. Any live cell with fewer than two live neighbors dies, as if caused by under-population.
* 2. Any live cell with two or three live neighbors lives on to the next generation. (not needed)
* 3. Any live cell with more than three live neighbors dies, as if by overcrowding.
* 4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
* <p/>
* References:
* http://en.wikipedia.org/wiki/Conway's_Game_of_Life
*/
public class ConwayCA extends JFrame implements ActionListener, WindowListener, Runnable {
/**
* Perform an iteration.
*/
private JButton iterationButton;
/**
* Start running.
*/
private JButton startButton;
/**
* Stop running.
*/
private JButton stopButton;
/**
* Reset the simulation.
*/
private JButton resetButton;
/**
* Has a stop been requested.
*/
private boolean requestStop;
/**
* The world.
*/
private WorldPanel worldArea;
/**
* Used to locate the x coordinate of neighbors.
*/
private static final int[] neighborsX = {0, 0, 1, -1, -1, 1, -1, 1};
/**
* Used to locate the y coordinate of neighbors.
*/
private static final int[] neighborsY = {1, -1, 0, 0, -1, -1, 1, 1};
/**
* The number of rows.
*/
public static final int ROWS = 75;
/**
* The number of columns.
*/
public static final int COLS = 75;
/**
* The constructor.
*/
public ConwayCA() {
setSize(500, 500);
setTitle("Conway's Game of Life");
Container c = getContentPane();
c.setLayout(new BorderLayout());
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
c.add(buttonPanel, BorderLayout.NORTH);
buttonPanel.add(iterationButton = new JButton("Iteration"));
buttonPanel.add(startButton = new JButton("Start"));
buttonPanel.add(stopButton = new JButton("Stop"));
buttonPanel.add(resetButton = new JButton("Reset"));
this.worldArea = new WorldPanel(ROWS, COLS, true);
/*
Scroll bar.
*/
final JScrollPane scroll = new JScrollPane(this.worldArea);
c.add(scroll, BorderLayout.CENTER);
iterationButton.addActionListener(this);
startButton.addActionListener(this);
stopButton.addActionListener(this);
resetButton.addActionListener(this);
this.addWindowListener(this);
this.stopButton.setEnabled(false);
performReset();
}
/**
* Perform one iteration.
*/
public void performIteration() {
boolean[][] grid = this.worldArea.getPrimaryGrid();
for (int row = 0; row < grid.length; row++) {
for (int col = 0; col < grid[row].length; col++) {
int total = 0;
for (int i = 0; i < neighborsX.length; i++) {
int nCol = col + neighborsX[i];
int nRow = row + neighborsY[i];
if (nCol >= 0 && nCol < this.worldArea.getCols()) {
if (nRow >= 0 && nRow < this.worldArea.getRows()) {
if (grid[nRow][nCol]) {
total++;
}
}
}
}
boolean alive = grid[row][col];
if (alive) {
// 1. Any live cell with fewer than two live neighbors dies, as if caused by under-population.
if (total < 2) {
alive = false;
}
// 2. Any live cell with two or three live neighbors lives on to the next generation. (not needed)
// 3. Any live cell with more than three live neighbors dies, as if by overcrowding.
if (alive && total > 3) {
alive = false;
}
} else {
// 4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
if (total == 3) {
alive = true;
}
}
this.worldArea.getBackupGrid()[row][col] = alive;
}
}
this.worldArea.advanceBackupGrid();
}
/**
* Start the simulation.
*/
public void performStart() {
this.iterationButton.setEnabled(false);
this.stopButton.setEnabled(true);
this.startButton.setEnabled(false);
/*
The background thread.
*/
final Thread thread = new Thread(this);
thread.start();
}
/**
* Request stop.
*/
public void performStop() {
this.requestStop = true;
}
/**
* Perform a reset.
*/
public void performReset() {
boolean[][] grid = this.worldArea.getBackupGrid();
GenerateRandom rnd = new MersenneTwisterGenerateRandom();
for (int row = 0; row < this.worldArea.getRows(); row++) {
for (int col = 0; col < this.worldArea.getCols(); col++) {
grid[row][col] = rnd.nextBoolean();
}
}
this.worldArea.advanceBackupGrid();
}
/**
* {@inheritDoc}
*/
@Override
public void actionPerformed(ActionEvent ev) {
if (ev.getSource() == iterationButton) {
performIteration();
repaint();
} else if (ev.getSource() == startButton) {
performStart();
} else if (ev.getSource() == stopButton) {
performStop();
} else if (ev.getSource() == resetButton) {
performReset();
}
}
/**
* {@inheritDoc}
*/
@Override
public void windowActivated(WindowEvent arg0) {
}
/**
* {@inheritDoc}
*/
@Override
public void windowClosed(WindowEvent arg0) {
}
/**
* {@inheritDoc}
*/
@Override
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
/**
* {@inheritDoc}
*/
@Override
public void windowDeactivated(WindowEvent arg0) {
}
/**
* {@inheritDoc}
*/
@Override
public void windowDeiconified(WindowEvent arg0) {
}
/**
* {@inheritDoc}
*/
@Override
public void windowIconified(WindowEvent arg0) {
}
/**
* {@inheritDoc}
*/
@Override
public void windowOpened(WindowEvent arg0) {
performReset();
}
/**
* Main entry point.
*
* @param args Not used.
*/
public static void main(String[] args) {
try {
JFrame f = new ConwayCA();
f.setVisible(true);
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
this.requestStop = false;
while (!this.requestStop) {
performIteration();
this.repaint();
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
// Update GUI
this.iterationButton.setEnabled(true);
this.stopButton.setEnabled(false);
this.startButton.setEnabled(true);
}
}