Created
April 21, 2015 16:59
-
-
Save subnomo/e103c5d724ddcfa94a34 to your computer and use it in GitHub Desktop.
Game of Life
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javax.swing.*; | |
import java.awt.*; | |
import java.awt.event.ActionEvent; | |
import java.awt.event.ActionListener; | |
import java.awt.event.MouseAdapter; | |
import java.awt.event.MouseEvent; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class GameOfLife extends JPanel implements ActionListener, Runnable { | |
static JFrame frame = new JFrame("Conway's Game of Life"); | |
static boolean[][] darwin = new boolean[100][100]; | |
static boolean[][] backupDarwin = new boolean[100][100]; | |
Container container = new Container(); | |
JButton stepButt = new JButton("Step"); | |
JButton runButt = new JButton("Run"); | |
JButton stopButt = new JButton("Stop"); | |
JButton saveButt = new JButton("Save"); | |
JButton loadButt = new JButton("Load"); | |
public void paintComponent(Graphics g) { | |
for (int r = 0; r < 100; r++) { | |
for (int c = 0; c < 100; c++) { | |
if (darwin[r][c]) { | |
g.clearRect(c * 10, r * 10, 10, 10); | |
g.setColor(Color.black); | |
g.fillRect(c * 10, r * 10, 10, 10); | |
} else { | |
g.clearRect(c * 10, r * 10, 10, 10); | |
g.setColor(Color.lightGray); | |
g.drawRect(c * 10, r * 10, 10, 10); | |
} | |
} | |
} | |
} | |
public static void main(String[] args) { | |
new GameOfLife(); | |
} | |
public GameOfLife() { | |
addMouseListener(new MouseAdapter() { | |
@Override | |
public void mouseClicked(MouseEvent e) { | |
click(e.getX(), e.getY()); | |
} | |
}); | |
container.add(stepButt); | |
container.add(runButt); | |
container.add(stopButt); | |
container.add(saveButt); | |
container.add(loadButt); | |
// Sets the size of the actual viewport (without borders) | |
frame.getContentPane().setPreferredSize(new Dimension(1001, 1027)); | |
container.setLayout(new GridLayout(1, 3)); | |
frame.add(container, BorderLayout.SOUTH); | |
stepButt.addActionListener(this); | |
runButt.addActionListener(this); | |
stopButt.addActionListener(this); | |
saveButt.addActionListener(this); | |
loadButt.addActionListener(this); | |
frame.pack(); | |
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); | |
frame.setVisible(true); | |
frame.getContentPane().add(this); | |
} | |
public static void click(int x, int y) { | |
// Checks if the user is clicking outside of the designated area | |
if (x > 1000 || y > 1000) { | |
System.out.println("User clicked outside of the arena."); | |
return; | |
} | |
int[] position = getPosition(x, y); | |
int row = position[0], column = position[1]; | |
// Flips the living/dead state of cell | |
darwin[row][column] = !darwin[row][column]; | |
frame.getContentPane().repaint(); | |
} | |
public static int[] getPosition(int x, int y) { | |
int xLength = String.valueOf(x).length(); | |
int yLength = String.valueOf(y).length(); | |
// Gets the column that was clicked | |
int column = 0; | |
if (xLength == 4) { | |
column = 99; | |
} else if (xLength != 0) { | |
column = x / 10; | |
} | |
// Gets the row that was clicked | |
int row = 0; | |
if (yLength == 4) { | |
row = 99; | |
} else if (yLength != 0) { | |
row = y / 10; | |
} | |
return new int[]{row, column}; | |
} | |
public static int[][] getNeighbors(int x, int y) { | |
int[] pos1 = new int[2]; | |
int[] pos2 = new int[2]; | |
int[] pos3 = new int[2]; | |
int[] pos4 = new int[2]; | |
int[] pos5 = new int[2]; | |
int[] pos6 = new int[2]; | |
int[] pos7 = new int[2]; | |
int[] pos8 = new int[2]; | |
// Starts with upper left corner and goes around clockwise | |
pos1[0] = x - 1; | |
pos1[1] = y - 1; | |
pos2[0] = x; | |
pos2[1] = y - 1; | |
pos3[0] = x + 1; | |
pos3[1] = y - 1; | |
pos4[0] = x + 1; | |
pos4[1] = y; | |
pos5[0] = x + 1; | |
pos5[1] = y + 1; | |
pos6[0] = x; | |
pos6[1] = y + 1; | |
pos7[0] = x - 1; | |
pos7[1] = y + 1; | |
pos8[0] = x - 1; | |
pos8[1] = y; | |
return new int[][]{pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8}; | |
} | |
public static List getLivingNeighbors(int[][] neighbors) { | |
List<int[]> livingNeighbors = new ArrayList<int[]>(); | |
// For each neighbor in the list of neighbors | |
for (int neighbor = 0; neighbor < 8; neighbor++) { | |
int[] coordinates = new int[2]; | |
System.arraycopy(neighbors[neighbor], 0, coordinates, 0, 2); | |
if (coordinates[0] < 0) { | |
coordinates[0] += 100; | |
} | |
if (coordinates[1] < 0) { | |
coordinates[1] += 100; | |
} | |
if (coordinates[0] > 99) { | |
coordinates[0] -= 100; | |
} | |
if (coordinates[1] > 99) { | |
coordinates[1] -= 100; | |
} | |
if (darwin[coordinates[0]][coordinates[1]]) { | |
livingNeighbors.add(coordinates); | |
} | |
} | |
return livingNeighbors; | |
} | |
public static void step() { | |
// Stores all the cells that will be birthed/killed | |
List<int[]> pleaseLive = new ArrayList<int[]>(); | |
List<int[]> pleaseDie = new ArrayList<int[]>(); | |
// Loops through all cells | |
for (int r = 0; r < 100; r++) { | |
for (int c = 0; c < 100; c++) { | |
// If alive cell | |
if (darwin[r][c]) { | |
// If it has less than 2 living neighbors or more than 3, schedule its death | |
if (getLivingNeighbors(getNeighbors(r, c)).size() < 2) { | |
int[] coordinates = {r, c}; | |
pleaseDie.add(coordinates); | |
} else if (getLivingNeighbors(getNeighbors(r, c)).size() > 3) { | |
int[] coordinates = {r, c}; | |
pleaseDie.add(coordinates); | |
} | |
} else { | |
// If dead cell with 3 living neighbors, schedule its birth | |
if (getLivingNeighbors(getNeighbors(r, c)).size() == 3) { | |
int[] coordinates = {r, c}; | |
pleaseLive.add(coordinates); | |
} | |
} | |
} | |
} | |
// Loop through schedules and birth/kill accordingly | |
for (int[] birth : pleaseLive) { | |
darwin[birth[0]][birth[1]] = true; | |
frame.getContentPane().repaint(); | |
} | |
for (int[] die : pleaseDie) { | |
darwin[die[0]][die[1]] = false; | |
frame.getContentPane().repaint(); | |
} | |
} | |
private volatile boolean stopRequested = false; | |
private int speed = 100; | |
@Override | |
public void run() { | |
try { | |
while (!stopRequested) { | |
step(); | |
Thread.sleep(speed); | |
} | |
} catch (InterruptedException ex) { | |
ex.printStackTrace(); | |
} | |
} | |
private boolean firstRun = true; | |
// Gets button clicks | |
@Override | |
public void actionPerformed(ActionEvent e) { | |
if (e.getSource() == stepButt) { | |
step(); | |
} else if (e.getSource() == runButt) { | |
// If this is first run or stop is requested, spawn new thread | |
// Else, increase speed of simulation on click | |
if (firstRun || stopRequested) { | |
stopRequested = false; | |
firstRun = false; | |
new Thread(this).start(); | |
} else { | |
if (speed > 0) speed -= 20; | |
} | |
} else if (e.getSource() == stopButt) { | |
stopRequested = true; | |
speed = 100; | |
} else if (e.getSource() == saveButt) { | |
for (int i = 0; i < 100; i++) { | |
System.arraycopy(darwin[i], 0, backupDarwin[i], 0, 100); | |
} | |
} else if (e.getSource() == loadButt) { | |
for (int i = 0; i < 100; i++) { | |
System.arraycopy(backupDarwin[i], 0, darwin[i], 0, 100); | |
} | |
frame.getContentPane().repaint(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment