├── README.md └── src ├── main └── Main.java └── game ├── Game.java └── Field.java /README.md: -------------------------------------------------------------------------------- 1 | # GameOfLife 2 | My implementation of Conway's Game Of Life 3 | -------------------------------------------------------------------------------- /src/main/Main.java: -------------------------------------------------------------------------------- 1 | package main; 2 | 3 | import java.awt.Canvas; 4 | import java.awt.Color; 5 | import java.awt.Dimension; 6 | import java.awt.event.ComponentAdapter; 7 | import java.awt.event.ComponentEvent; 8 | 9 | import javax.swing.JFrame; 10 | import javax.swing.SwingUtilities; 11 | 12 | import game.Game; 13 | 14 | public class Main { 15 | 16 | private static JFrame f; 17 | private static Canvas c; 18 | 19 | public static void main(String[] args) { 20 | initFrame(); 21 | new Game(f, c, 600, 600).start(40); 22 | } 23 | 24 | private static void initFrame() { 25 | try { 26 | SwingUtilities.invokeAndWait(() -> { 27 | f = new JFrame(); 28 | f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 29 | // f.setResizable(false); 30 | f.setTitle("Conway's Game of life by 0x666c"); 31 | 32 | c = new Canvas(); 33 | c.setBackground(Color.BLACK); 34 | c.setPreferredSize(new Dimension(600, 600)); 35 | f.add(c); 36 | f.pack(); 37 | 38 | f.addComponentListener(new ComponentAdapter() { 39 | public void componentResized(ComponentEvent e) { 40 | c.setPreferredSize(f.getSize()); 41 | } 42 | }); 43 | 44 | f.setLocationRelativeTo(null); 45 | 46 | f.setVisible(true); 47 | 48 | c.createBufferStrategy(2); 49 | }); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/game/Game.java: -------------------------------------------------------------------------------- 1 | package game; 2 | 3 | import java.awt.Canvas; 4 | import java.awt.Color; 5 | import java.awt.Graphics; 6 | import java.awt.Point; 7 | import java.awt.event.KeyAdapter; 8 | import java.awt.event.KeyEvent; 9 | import java.awt.event.MouseAdapter; 10 | import java.awt.event.MouseEvent; 11 | import java.awt.image.BufferStrategy; 12 | 13 | import javax.swing.JFrame; 14 | import javax.swing.SwingUtilities; 15 | 16 | public class Game { 17 | public static final int SW = 600, SH = 600; 18 | 19 | private final BufferStrategy bs; 20 | private final JFrame frame; 21 | private final Canvas canvas; 22 | 23 | private boolean lmb = false, rmb = false; 24 | private boolean paused = false; 25 | 26 | private Field field; 27 | 28 | private Thread gameThread; 29 | 30 | public Game(JFrame f, Canvas c, int w, int h) { 31 | this.frame = f; 32 | this.canvas = c; 33 | 34 | c.requestFocus(); 35 | c.requestFocusInWindow(); 36 | 37 | this.bs = c.getBufferStrategy(); 38 | 39 | c.addMouseListener(new MouseAdapter() { 40 | public void mousePressed(MouseEvent e) { 41 | if (SwingUtilities.isLeftMouseButton(e)) { 42 | lmb = true; 43 | } 44 | if (SwingUtilities.isRightMouseButton(e)) { 45 | rmb = true; 46 | } 47 | } 48 | 49 | public void mouseReleased(MouseEvent e) { 50 | if (SwingUtilities.isLeftMouseButton(e)) { 51 | lmb = false; 52 | } 53 | if (SwingUtilities.isRightMouseButton(e)) { 54 | rmb = false; 55 | } 56 | } 57 | }); 58 | c.addKeyListener(new KeyAdapter() { 59 | public void keyPressed(KeyEvent e) { 60 | if(e.getKeyCode() == KeyEvent.VK_SPACE) { 61 | paused ^= true; 62 | } 63 | if(e.getKeyCode() == KeyEvent.VK_N) { 64 | field.clear(); 65 | } 66 | if(e.getKeyCode() == KeyEvent.VK_G) { 67 | field.toggleGrid(); 68 | } 69 | if(e.getKeyCode() == KeyEvent.VK_ESCAPE) { 70 | System.exit(0); 71 | } 72 | if(e.getKeyCode() == KeyEvent.VK_S) { 73 | start(25); 74 | } 75 | if(e.getKeyCode() == KeyEvent.VK_F) { 76 | start(40); 77 | } 78 | } 79 | }); 80 | } 81 | 82 | @SuppressWarnings("deprecation") 83 | public void start(int dt) { 84 | if(gameThread != null) gameThread.stop(); 85 | gameThread = new Thread(() -> run(dt)); 86 | gameThread.start(); 87 | } 88 | 89 | private void run(int dt) { 90 | long now; 91 | long last = System.nanoTime(); 92 | double delta = 0; 93 | double deltaTime = 1e9 / dt; 94 | 95 | field = new Field(this); 96 | 97 | while (true) { 98 | Graphics g = bs.getDrawGraphics(); 99 | 100 | field.input(); 101 | 102 | now = System.nanoTime(); 103 | delta += (now - last) / deltaTime; 104 | last = now; 105 | if (delta >= 1) { 106 | g.setColor(Color.BLACK); 107 | g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight()); 108 | 109 | field.render(g); 110 | field.newGeneration(); 111 | 112 | delta--; 113 | } 114 | 115 | g.dispose(); 116 | bs.show(); 117 | } 118 | } 119 | 120 | public boolean isLPressed() { 121 | return lmb; 122 | } 123 | 124 | public boolean isRPressed() { 125 | return rmb; 126 | } 127 | 128 | public boolean isPressed() { 129 | return lmb || rmb; 130 | } 131 | 132 | public boolean isPaused() { 133 | return paused; 134 | } 135 | 136 | public Point getMouseLocation() { 137 | return canvas.getMousePosition(); 138 | } 139 | 140 | public JFrame getFrame() { 141 | return frame; 142 | } 143 | 144 | public Canvas getCanvas() { 145 | return canvas; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/game/Field.java: -------------------------------------------------------------------------------- 1 | package game; 2 | 3 | import java.awt.BasicStroke; 4 | import java.awt.Color; 5 | import java.awt.Font; 6 | import java.awt.Graphics; 7 | import java.awt.Graphics2D; 8 | import java.awt.Point; 9 | 10 | public class Field { 11 | 12 | private int CELLSX = 200, CELLSY = 200; 13 | private final int CELLSIZE = 10; 14 | private int OFFSETX = 0,//(Game.SW / 2) - ((CELLSX * CELLSIZE) / 2), 15 | OFFSETY = 0;//(Game.SH / 2) - ((CELLSY * CELLSIZE) / 2); 16 | 17 | private int WIDTH = -1, 18 | HEIGHT = -1; 19 | 20 | private boolean[][] present, future; 21 | 22 | private final Game game; 23 | 24 | private boolean drawGrid = false; 25 | 26 | public Field(Game game) { 27 | present = new boolean[CELLSX][CELLSY]; 28 | future = new boolean[CELLSX][CELLSY]; 29 | 30 | this.game = game; 31 | 32 | WIDTH = game.getCanvas().getWidth(); 33 | HEIGHT = game.getCanvas().getHeight(); 34 | } 35 | 36 | public void newGeneration() { 37 | 38 | /* 39 | * 40 | * 1. Any live cell with fewer than two live neighbors dies 41 | * 42 | * 2. Any live cell with two or three live neighbors lives 43 | * 44 | * 3. Any live cell with more than three live neighbors dies 45 | * 46 | * 4. Any dead cell with exactly three live neighbors becomes a live cell 47 | * 48 | */ 49 | 50 | if (!game.isPaused()) { 51 | for (int x = 0; x < CELLSX; x++) { 52 | for (int y = 0; y < CELLSY; y++) { 53 | int n = checkNeighbours(present, x, y); 54 | 55 | if (n == 3 && !present[x][y]) { 56 | future[x][y] = true; 57 | } else if (n < 2 && present[x][y]) { 58 | future[x][y] = false; 59 | } else if (n > 3 && present[x][y]) { 60 | future[x][y] = false; 61 | } else { 62 | future[x][y] = present[x][y]; 63 | } 64 | } 65 | } 66 | for (int i = 0; i < CELLSX; i++) { 67 | for (int j = 0; j < CELLSY; j++) { 68 | present[i][j] = future[i][j]; 69 | future[i][j] = false; 70 | } 71 | } 72 | } 73 | 74 | if(CELLSX != game.getCanvas().getWidth() / CELLSIZE || CELLSY != game.getCanvas().getHeight() / CELLSIZE) { 75 | WIDTH = game.getCanvas().getWidth(); 76 | HEIGHT = game.getCanvas().getHeight(); 77 | 78 | CELLSX = WIDTH / CELLSIZE; 79 | CELLSY = HEIGHT / CELLSIZE; 80 | 81 | OFFSETX = (WIDTH / 2) - ((CELLSX * CELLSIZE) / 2); 82 | OFFSETY = (HEIGHT / 2) - ((CELLSY * CELLSIZE) / 2); 83 | 84 | clear(); 85 | } 86 | } 87 | 88 | public void input() { 89 | if (game.isPressed()) { 90 | Point click = game.getMouseLocation(); 91 | if (click != null) { 92 | int indX = (click.x - OFFSETX) / CELLSIZE; 93 | int indY = (click.y - OFFSETY) / CELLSIZE; 94 | 95 | if (!(indX < 0 || indY < 0 || indX > CELLSX - 1 || indY > CELLSY - 1)) { 96 | if(game.isLPressed()) 97 | present[indX][indY] = true; 98 | else if(game.isRPressed()) 99 | present[indX][indY] = false; 100 | } 101 | } 102 | } 103 | } 104 | 105 | public void render(Graphics g) { 106 | for (int i = 0; i < CELLSX; i++) { 107 | for (int j = 0; j < CELLSY; j++) { 108 | if(present[i][j]) { 109 | g.setColor(Color.YELLOW); 110 | g.fillRect(i * CELLSIZE + OFFSETX, j * CELLSIZE + OFFSETY, CELLSIZE, CELLSIZE); 111 | if(!(CELLSIZE < 3) && !drawGrid) { 112 | g.setColor(Color.BLACK); 113 | g.drawRect(i * CELLSIZE + OFFSETX, j * CELLSIZE + OFFSETY, CELLSIZE, CELLSIZE); 114 | } 115 | } 116 | } 117 | } 118 | 119 | if(drawGrid) { 120 | g.setColor(game.isPaused() ? Color.DARK_GRAY : Color.DARK_GRAY.darker()); 121 | for (int i = 0; i < CELLSX; i++) { 122 | for (int j = 0; j < CELLSY; j++) { 123 | g.drawLine(i * CELLSIZE, 0, i * CELLSIZE, HEIGHT); 124 | g.drawLine(0, j * CELLSIZE, WIDTH, j * CELLSIZE); 125 | } 126 | } 127 | } 128 | 129 | drawBorder(g); 130 | 131 | Point click = game.getMouseLocation(); 132 | if(click != null && click.x > OFFSETX && click.x < CELLSIZE*CELLSX+OFFSETX && 133 | click.y > OFFSETY && click.y < CELLSIZE*CELLSY+OFFSETY) 134 | g.drawRect((click.x / CELLSIZE) * CELLSIZE, (click.y / CELLSIZE) * CELLSIZE, CELLSIZE, CELLSIZE); 135 | 136 | drawTip(g); 137 | } 138 | 139 | private int checkNeighbours(boolean[][] arr, int x, int y) { 140 | int neighbours = 0; 141 | 142 | for (int colNum = y - 1; colNum <= (y + 1); colNum += 1) { 143 | for (int rowNum = x - 1; rowNum <= (x + 1); rowNum += 1) { 144 | if (!((colNum == y) && (rowNum == x))) { 145 | if (withinGrid(colNum, rowNum)) { 146 | if(arr[rowNum][colNum]) { 147 | neighbours++; 148 | } 149 | } 150 | } 151 | } 152 | } 153 | 154 | return neighbours; 155 | } 156 | 157 | private boolean withinGrid(int colNum, int rowNum) { 158 | if((colNum < 0) || (rowNum < 0) ) { 159 | return false; 160 | } 161 | if((colNum >= CELLSY) || (rowNum >= CELLSX)) { 162 | return false; 163 | } 164 | return true; 165 | } 166 | 167 | public void drawBorder(Graphics g) { 168 | g.setColor(game.isPaused() ? Color.LIGHT_GRAY : Color.WHITE); 169 | Graphics2D g2 = (Graphics2D) g.create(); 170 | g2.setStroke(new BasicStroke(3f)); 171 | g2.drawRect(OFFSETX+1, OFFSETY+1, CELLSIZE * CELLSX-2, CELLSIZE * CELLSY-2); 172 | } 173 | 174 | public void clear() { 175 | present = new boolean[CELLSX][CELLSY]; 176 | future = new boolean[CELLSX][CELLSY]; 177 | } 178 | 179 | public void toggleGrid() { 180 | drawGrid ^= true; 181 | } 182 | 183 | private float tipFreeTicks = 0; 184 | private float tipTicks = 0; 185 | private void drawTip(Graphics g) { 186 | if(tipFreeTicks > 120) { 187 | if(tipTicks > 0.999f) return; 188 | tipTicks += 0.01; 189 | } 190 | tipFreeTicks++; 191 | 192 | g.setColor(Color.getHSBColor(0, 0, 1f - tipTicks)); 193 | g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 14)); 194 | final String s1 = "Welcome to Conway's Game of life!"; 195 | final String s2 = "Controls:"; 196 | final String s3 = "G - Show grid"; 197 | final String s4 = "N - Clear field"; 198 | final String s5 = "ESC - Quit"; 199 | final int w1 = g.getFontMetrics().stringWidth(s1) / 2; 200 | final int w2 = g.getFontMetrics().stringWidth(s2) / 2; 201 | final int w3 = g.getFontMetrics().stringWidth(s3) / 2; 202 | final int w4 = g.getFontMetrics().stringWidth(s4) / 2; 203 | final int w5 = g.getFontMetrics().stringWidth(s5) / 2; 204 | g.drawString(s1, (WIDTH / 2) - w1, (HEIGHT / 2) - (16 / 2) - 45); 205 | g.drawString(s2, (WIDTH / 2) - w2, (HEIGHT / 2) - (16 / 2) - 20); 206 | g.drawString(s3, (WIDTH / 2) - w3, (HEIGHT / 2) - (16 / 2)); 207 | g.drawString(s4, (WIDTH / 2) - w4, (HEIGHT / 2) - (16 / 2) + 20); 208 | g.drawString(s5, (WIDTH / 2) - w5, (HEIGHT / 2) - (16 / 2) + 40); 209 | } 210 | } 211 | --------------------------------------------------------------------------------