From 9eca62e7851a3ed79728ad8eeffb1562f0dd3f90 Mon Sep 17 00:00:00 2001 From: droideparanoico Date: Sun, 4 Apr 2021 01:21:49 +0200 Subject: [PATCH] Initial commit --- src/main/java/GenerationAlgorithm.java | 82 ++++++++++++++++++++ src/main/java/Main.java | 9 +++ src/main/java/controller/Controller.java | 70 +++++++++++++++++ src/main/java/model/Universe.java | 48 ++++++++++++ src/main/java/view/GameOfLife.java | 99 ++++++++++++++++++++++++ src/main/java/view/Grid.java | 43 ++++++++++ 6 files changed, 351 insertions(+) create mode 100644 src/main/java/GenerationAlgorithm.java create mode 100644 src/main/java/Main.java create mode 100644 src/main/java/controller/Controller.java create mode 100644 src/main/java/model/Universe.java create mode 100644 src/main/java/view/GameOfLife.java create mode 100644 src/main/java/view/Grid.java diff --git a/src/main/java/GenerationAlgorithm.java b/src/main/java/GenerationAlgorithm.java new file mode 100644 index 0000000..b8e7f22 --- /dev/null +++ b/src/main/java/GenerationAlgorithm.java @@ -0,0 +1,82 @@ +package main.java; + +import main.java.model.Universe; + +public class GenerationAlgorithm { + + private final Universe universe; + private final int uniSize; + private static final int[][] NEIGHBOURS = { + {-1, +1}, { 0,+1}, {+1, +1}, + {-1, 0}, {+1, 0}, + {-1, -1}, { 0,-1}, {+1, -1}}; + + public GenerationAlgorithm(Universe universe) { + this.universe = universe; + this.uniSize = universe.getUniverseSize(); + } + + public void evolveUniverse () { + boolean[][] nextMatrix = new boolean[uniSize][uniSize]; + + int nextLivingCells = 0; + + for (int col = 0; col < uniSize; col++) { + for (int row = 0; row < uniSize; row++) { + int neighbors = countAliveNeighbors(col, row); + + // Game of life rules implementation + // Survival: Each live cell with either 2 or 3 alive neighbors will remain alive. + // Births: Each dead cell adjacent to exactly three living neighbors will become alive. + // Death: Each live cell with less than 2 or more than 3 alive neighbors will die. + if ((isAlive(col, row) && (neighbors > 1 && neighbors < 4)) + || (!isAlive(col, row) && neighbors == 3)) { + nextMatrix[col][row] = true; + nextLivingCells++; + } else { + nextMatrix[col][row] = false; + } + } + } + + universe.setLivingCells(nextLivingCells); + universe.setMatrix(nextMatrix); + } + + public int countAliveNeighbors(int x, int y) { + int alive = 0; + for (int[] offset : NEIGHBOURS) { + if (isAlive(x + offset[0], y + offset[1])) { + alive++; + } + } + return alive; + } + + public int getAliveCells(){ + return this.universe.getLivingCells(); + } + + public boolean isAlive(int x, int y) { + return universe.getMatrix()[checkBoundaries(x)][checkBoundaries(y)]; + } + + public int checkBoundaries(int coordinate) { + // Universe is periodic + if (coordinate < 0) { + coordinate = uniSize - 1; + } + if (coordinate > uniSize - 1) { + coordinate = 0; + } + return coordinate; + } + + public boolean[][] getMatrix() { + return this.universe.getMatrix(); + } + + public void resetUniverse() { + this.universe.initializeUniverse(); + } +} diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000..e63a2c1 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,9 @@ +package main.java; + +import main.java.view.GameOfLife; + +public class Main { + public static void main(String[] args) { + new GameOfLife(); + } +} \ No newline at end of file diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java new file mode 100644 index 0000000..80dd1b6 --- /dev/null +++ b/src/main/java/controller/Controller.java @@ -0,0 +1,70 @@ +package main.java.controller; + +import main.java.GenerationAlgorithm; +import main.java.model.Universe; +import main.java.view.Grid; + +import javax.swing.*; + +public class Controller extends Thread { + + private GenerationAlgorithm generationAlgorithm; + private final JLabel genLabel; + private final JLabel aliveLabel; + private final Grid grid; + private boolean playing; + private int generation = 1; + private int speed = 500; + + public Controller(GenerationAlgorithm generationAlgorithm, JLabel genLabel, JLabel aliveLabel, Grid grid) { + this.generationAlgorithm = generationAlgorithm; + this.genLabel = genLabel; + this.aliveLabel = aliveLabel; + this.grid = grid; + updateUI(); + } + + @Override + public void run() { + while (!Thread.interrupted()) { + try { + Thread.sleep(speed); + if (playing) { + generationAlgorithm.evolveUniverse(); + updateUI(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public void playPause() { + playing = !playing; + } + + public void reset() { + playing = false; + generationAlgorithm.resetUniverse(); + generation = 1; + updateUI(); + } + + public void changeUniSize(Universe universe) { + this.grid.setSize(universe.getUniverseSize()); + this.generationAlgorithm = new GenerationAlgorithm(universe); + reset(); + } + + public void changeSpeed(int speed) { + this.speed = speed; + } + + private void updateUI() { + genLabel.setText("Generation: " + generation++); + aliveLabel.setText("Living cells: " + generationAlgorithm.getAliveCells()); + grid.setMatrix(generationAlgorithm.getMatrix()); + grid.repaint(); + } +} + diff --git a/src/main/java/model/Universe.java b/src/main/java/model/Universe.java new file mode 100644 index 0000000..abdc14f --- /dev/null +++ b/src/main/java/model/Universe.java @@ -0,0 +1,48 @@ +package main.java.model; + +import java.util.Random; + +public class Universe { + + private final int universeSize; + private int livingCells; + private boolean[][] matrix; + + public Universe(int universeSize) { + this.universeSize = universeSize; + matrix = new boolean[universeSize][universeSize]; + initializeUniverse(); + } + + public void initializeUniverse() { + livingCells = 0; + for(int i = 0; i < universeSize; i++) { + for(int j = 0; j < universeSize; j++) { + matrix[i][j] = new Random().nextBoolean(); + if ((matrix[i][j])) { + livingCells++; + } + } + } + } + + public int getUniverseSize() { + return universeSize; + } + + public int getLivingCells() { + return livingCells; + } + + public void setLivingCells(int livingCells) { + this.livingCells = livingCells; + } + + public boolean[][] getMatrix() { + return matrix; + } + + public void setMatrix(boolean[][] matrix) { + this.matrix = matrix; + } +} diff --git a/src/main/java/view/GameOfLife.java b/src/main/java/view/GameOfLife.java new file mode 100644 index 0000000..7d0ee80 --- /dev/null +++ b/src/main/java/view/GameOfLife.java @@ -0,0 +1,99 @@ +package main.java.view; + +import main.java.GenerationAlgorithm; +import main.java.controller.Controller; +import main.java.model.Universe; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.util.Enumeration; + +public class GameOfLife extends JFrame { + private static final int WINDOW_WIDTH= 600; + private static final int WINDOW_HEIGHT= 730; + private static final int UNIVERSE_SIZE = 20; + private transient Controller controller; + + public GameOfLife() { + super("Game of life"); + + Universe universe = new Universe(UNIVERSE_SIZE); + GenerationAlgorithm generationAlgorithm = new GenerationAlgorithm(universe); + + setDefaultCloseOperation(EXIT_ON_CLOSE); + setSize(WINDOW_WIDTH, WINDOW_HEIGHT); + setLocationRelativeTo(null); + setLayout(new BorderLayout()); + setUIFont(new javax.swing.plaf.FontUIResource(new Font("SansSerif", Font.BOLD, 16))); + FlowLayout flowlayout = new FlowLayout(FlowLayout.LEFT, 10, 10); + Dimension buttonDimension = new Dimension(100,30); + + JPanel inputPanel = new JPanel(); + inputPanel.setLayout(flowlayout); + + JToggleButton playButton = new JToggleButton("Play"); + playButton.setPreferredSize(buttonDimension); + playButton.addActionListener(e -> controller.playPause()); + playButton.addItemListener(event -> { + if (event.getStateChange() == ItemEvent.SELECTED) { + playButton.setText("Pause"); + } else { + playButton.setText("Play"); + } + }); + inputPanel.add(playButton); + + JButton resetButton = new JButton("Reset"); + resetButton.setPreferredSize(buttonDimension); + resetButton.addActionListener(e -> controller.reset()); + resetButton.addActionListener(e -> playButton.setSelected(false)); + inputPanel.add(resetButton); + + JPanel sizeSpinner = new JPanel(); + sizeSpinner.setLayout(new BoxLayout(sizeSpinner, BoxLayout.X_AXIS)); + sizeSpinner.add(new JLabel("Size ")); + SpinnerNumberModel sizeInput = new SpinnerNumberModel(20, 10, 30, 1); + sizeInput.addChangeListener(e -> controller.changeUniSize(new Universe((Integer)sizeInput.getValue()))); + sizeInput.addChangeListener(e -> playButton.setSelected(false)); + sizeSpinner.add(new JSpinner(sizeInput)); + inputPanel.add(sizeSpinner); + + inputPanel.add(new JLabel("Speed")); + + JSlider speedSlider = new JSlider(50, 1500, 1000); + speedSlider.setInverted(true); + speedSlider.setPreferredSize(new Dimension(190,30)); + speedSlider.addChangeListener(e -> controller.changeSpeed(speedSlider.getValue())); + inputPanel.add(speedSlider); + + add(inputPanel, BorderLayout.NORTH); + + Grid grid = new Grid(UNIVERSE_SIZE); + add(grid, BorderLayout.CENTER); + + JPanel labelPanel = new JPanel(); + JLabel genLabel = new JLabel(); + JLabel aliveLabel = new JLabel(); + labelPanel.add(genLabel); + labelPanel.add(aliveLabel); + labelPanel.setLayout(flowlayout); + add(labelPanel, BorderLayout.SOUTH); + + controller = new Controller(generationAlgorithm, genLabel, aliveLabel, grid); + controller.start(); + + setResizable(false); + setVisible(true); + } + + public static void setUIFont (javax.swing.plaf.FontUIResource f){ + Enumeration keys = UIManager.getDefaults().keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + Object value = UIManager.get (key); + if (value instanceof javax.swing.plaf.FontUIResource) + UIManager.put (key, f); + } + } +} \ No newline at end of file diff --git a/src/main/java/view/Grid.java b/src/main/java/view/Grid.java new file mode 100644 index 0000000..9ead64f --- /dev/null +++ b/src/main/java/view/Grid.java @@ -0,0 +1,43 @@ +package main.java.view; + +import javax.swing.*; +import java.awt.*; + +public class Grid extends JPanel { + private int size; + private boolean[][] matrix; + + public Grid(int size) { + this.size = size; + } + + public void setSize(int size) { + this.size = size; + } + + public void setMatrix(boolean[][] matrix) { + this.matrix = matrix; + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + int cellWidth = getWidth()/size; + int cellHeight = getHeight()/size; + + for (int col = 0; col < size; col++) { + // Paint grid + g.drawLine(0, cellHeight * col, cellWidth * size, cellHeight * col); + g.drawLine(cellWidth * col, 0, cellWidth * col, cellHeight * size); + g.drawLine(0, cellHeight * size, cellWidth * size, cellHeight * size); + g.drawLine(cellWidth * size, 0, cellWidth * size, cellHeight * size); + for (int row = 0; row < size; row++) { + // Paint living cells + if (matrix[col][row]) { + g.fillRect(cellWidth * row, cellHeight * col, cellWidth, cellHeight); + } + } + } + } +}