2
\$\begingroup\$

Link to github Link to github: https://github.com/zombi3123/Game-Of-Life/

Source code:

Window.Java

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
//JFrame Class
public class Window extends JFrame {

public Window(int x, int y) {
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setSize(x, y);
    this.setResizable(true);
    this.setLayout(null);
    getContentPane().setBackground(Color.BLACK);
}

}

Main.Java

public class Main {
//Main class
public static void main(String[] args){
    Window w=new Window(1800,1000);
    Frame f=new Frame(w.getWidth(),w.getHeight());
    w.add(f);
    w.setVisible(true);

}
}

Frame.Java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
//JFrame class
public class Frame extends JPanel implements MouseListener, ActionListener, MouseMotionListener {
    private ArrayList<Cell> columns;
    private ArrayList<ArrayList<Cell>> rows;
    private int cellLength,JFrameWidth,JFrameHeight;
    private Color dead,killDefaultColor;
    private Timer tm;
    private int border;
    private JButton Start,stop,randomize,kill,step,reset,drawLine;
    private Random randNum;
    private boolean killCells,drawLineMode;
    public Frame(int JFrameWidth,int JFrameHeight){
        tm=new Timer(1,this);
        rows=new ArrayList<>();
        columns= new ArrayList<>();
        this.cellLength=3;
        this.dead=Color.BLACK;
        this.border=1;
        this.JFrameWidth=JFrameWidth;
        this.JFrameHeight=JFrameHeight;
        this.killCells=false;
        this.drawLineMode=false;
        this.setSize(new Dimension(this.JFrameWidth, this.JFrameHeight));
        randNum=new Random();
        Start=new JButton("Start");
        stop=new JButton("Stop");
        randomize=new JButton("Randomize");
        kill=new JButton("Kill Cells");
        step=new JButton("Step");
        reset=new JButton("Reset");
        drawLine =new JButton("Line");
        ArrayList<JButton> JB = new ArrayList<>();
        JB.add(Start);
        JB.add(stop);
        JB.add(randomize);
        JB.add(kill);
        JB.add(step);
        JB.add(reset);
        JB.add(drawLine);
        int i=50;
        for (JButton j:JB){
            j.setBounds(i*2,0,50,20);
            this.add(j);
            i+=10;
        }
        killDefaultColor=kill.getBackground();
        Color killActiveColour=Color.green;
        Start.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                tm.start();
                Start.setVisible(false);
               // Start.setEnabled(false);
            }
        });
        stop.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                tm.stop();
                Start.setEnabled(true);
                Start.setVisible(true);
            }
        });
        randomize.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                for (int i = 1; i < rows.size()-1; i++) {
                    for (int j = 1; j < rows.get(0).size()-1; j++){
                        Cell c=rows.get(i).get(j);
                        float a=randNum.nextFloat();
                        if (a>0.90) {
                            c.setAlive();
                        }
                    }
                }
                repaint();
            }
        });
        kill.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
              killCells=!killCells;
              if (!killCells) {
                    kill.setBackground(killDefaultColor);
                }
              else kill.setBackground(killActiveColour);
            }
        });
        step.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                ArrayList<Cell> deadCells=new ArrayList<Cell>();
                ArrayList<Cell> bornCells=new ArrayList<Cell>();
                for (int i = 1; i < rows.size()-1; i++) {
                    for (int j = 1; j < rows.get(0).size()-1; j++){
                        Cell c=rows.get(i).get(j);
                        Cell cE=rows.get(i).get(j+1);
                        Cell cW=rows.get(i).get(j-1);
                        Cell cN=rows.get(i-1).get(j);
                        Cell cS=rows.get(i+1).get(j);
                        Cell cNE=rows.get(i-1).get(j+1);
                        Cell cNW=rows.get(i-1).get(j-1);
                        Cell cSE=rows.get(i+1).get(j+1);
                        Cell cSW=rows.get(i+1).get(j-1);
                        if(c.isAlive()){
                            if(c.totalNeighbours(cE,cW,cN,cS,cNE,cNW,cSE,cSW)>=4){deadCells.add(c);}
                            if(c.totalNeighbours(cE,cW,cN,cS,cNE,cNW,cSE,cSW)<2){deadCells.add(c);}
                        }
                        else{
                            if(c.totalNeighbours(cE,cW,cN,cS,cNE,cNW,cSE,cSW)==3){bornCells.add(c);}
                        }
                        repaint();
                    }
                }
                for(Cell c:deadCells){c.setDead();}
                for(Cell c:bornCells){c.setAlive();}
            }
        });
        reset.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                for (int i = 0; i < rows.size()-1; i++) {
                    for (int j = 0; j < rows.get(0).size()-1; j++){
                        Cell c=rows.get(i).get(j);
                        c.setDead();
                    }
                }
                repaint();
            }
        });
        drawLine.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                drawLineMode = !drawLineMode;
                if (drawLineMode) {
                    killCells=false;
                    drawLine.setBackground(killActiveColour);
                }
                else{
                    drawLine.setBackground(killDefaultColor);
                }
            }
        });
        repaint();
        createMap();
        addMouseListener(this);
        addMouseMotionListener(this);
        setFocusable(true);
    }

    private void initComponents() {
    }

    public void createMap(){
        //Draw grid. Initialize all cells to dead
            for(int columsi=0;columsi<getHeight()/cellLength;columsi++){
                ArrayList<Cell> columns = new ArrayList<>();
                for(int rowsi=0;rowsi<getWidth()/cellLength;rowsi++){

                    Cell c=new Cell(rowsi*(cellLength+border),columsi*(cellLength+border),cellLength,false);
                    columns.add(c);
                    repaint();
                }
                rows.add(columns);
        }

        System.out.println(rows.size()*rows.get(0).size());
    }
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (int i = 0; i < rows.size(); i++) {
            for (int j = 0; j < rows.get(0).size(); j++){
                Cell c=rows.get(i).get(j);
                if (c.isAlive()) {
                    g.setColor(Color.black);
                } else {
                    g.setColor(Color.white);
                }
            c.draw(g);
        }
    }
    }

    @Override
    public void mouseClicked(MouseEvent e) {}

    @Override
    public void mousePressed(MouseEvent e) {
        if (killCells){
        for (int i = 1; i < rows.size()-1; i++) {
            for (int j = 1; j < rows.get(0).size()-1; j++){
                Cell c=rows.get(i).get(j);
                if(e.getX()>=c.getTlx()&&e.getX()<c.getTlx()+c.getCellLength()&&e.getY()>= c.getTly()&&e.getY()<c.getTly()+c.getCellLength()) {
                    c.setDead();
                    repaint();
                }
                }
            }
        }
        else if(!killCells && !drawLineMode){
            outerLoop:
            for (int i = 1; i < rows.size()-1; i++) {
                for (int j = 1; j < rows.get(0).size()-1; j++){
                    Cell c=rows.get(i).get(j);
                    if(e.getX()>=c.getTlx()&&e.getX()<c.getTlx()+c.getCellLength()&&e.getY()>= c.getTly()&&e.getY()<c.getTly()+c.getCellLength()) {
                        c.setAlive();
                        repaint();
                        break outerLoop;
                    }
                }
            }
        }
        else if(drawLineMode=true){
            outerLoop:
            for (int i = 0; i < rows.size()-1; i++) {
                for (int j = 0; j < rows.get(0).size()-1; j++){
                    Cell c=rows.get(i).get(j);
                    if(e.getX()>=c.getTlx()&&e.getX()<c.getTlx()+c.getCellLength()&&e.getY()>= c.getTly()&&e.getY()<c.getTly()+c.getCellLength()) {
                        for (int k = rows.indexOf(c); k < rows.get(0).size()-1; k++){
                            if(k+j>rows.get(0).size()-1){break outerLoop;}
                            else{rows.get(i).get(j+k).setAlive();}
                            repaint();

                        }
                        //break outerLoop;
                    }
                }
            }

        }
        }



    @Override
    public void mouseReleased(MouseEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}

    @Override
    //When the timer ticks, action is performed
    public void actionPerformed(ActionEvent e) {
        ArrayList<Cell> deadCells=new ArrayList<Cell>();
        ArrayList<Cell> bornCells=new ArrayList<Cell>();
        for (int i = 1; i < rows.size()-1; i++) {
            for (int j = 1; j < rows.get(0).size()-1; j++){
                Cell c=rows.get(i).get(j);
                Cell cE=rows.get(i).get(j+1);
                Cell cW=rows.get(i).get(j-1);
                Cell cN=rows.get(i-1).get(j);
                Cell cS=rows.get(i+1).get(j);
                Cell cNE=rows.get(i-1).get(j+1);
                Cell cNW=rows.get(i-1).get(j-1);
                Cell cSE=rows.get(i+1).get(j+1);
                Cell cSW=rows.get(i+1).get(j-1);
                if(c.isAlive()){
                if(c.totalNeighbours(cE,cW,cN,cS,cNE,cNW,cSE,cSW)>=4){deadCells.add(c);}
                if(c.totalNeighbours(cE,cW,cN,cS,cNE,cNW,cSE,cSW)<2){deadCells.add(c);}
            }
                else{
                    if(c.totalNeighbours(cE,cW,cN,cS,cNE,cNW,cSE,cSW)==3){bornCells.add(c);}
                }
                repaint();
            }
        }
        for(Cell c:deadCells){c.setDead();}
        for(Cell c:bornCells){c.setAlive();}
    }

    @Override
    //When mouse dragged across screen, draw cells according to mouses position
    public void mouseDragged(MouseEvent e) {
    if(killCells){
        outerloop:
        for (int i = 1; i < rows.size()-1; i++) {
            for (int j = 1; j < rows.get(0).size()-1; j++){
                Cell c=rows.get(i).get(j);
                if(c.isAlive()){
                if(e.getX()>=c.getTlx()&&e.getX()<c.getTlx()+c.getCellLength()&&e.getY()>= c.getTly()&&e.getY()<c.getTly()+c.getCellLength()) {
                    c.setDead();
                    repaint();
                    break outerloop;
                }
                }
            }
        }
    }
          else{
            for (int i = 1; i < rows.size()-1; i++) {
                for (int j = 1; j < rows.get(0).size()-1; j++){
                    Cell c=rows.get(i).get(j);
                    if(e.getX()>=c.getTlx()&&e.getX()<c.getTlx()+c.getCellLength()&&e.getY()>= c.getTly()&&e.getY()<c.getTly()+c.getCellLength()) {
                        c.setAlive();
                        repaint();
                    }
                }
            }
        }
    }

    @Override
    public void mouseMoved(MouseEvent e) {}
}

Cell.Java

import java.awt.*;
import java.util.ArrayList;
//Cell class
public class Cell {
    private int cellLength,tlx,tly;
    private Color color;
    private boolean alive;
    public Cell(int tlx,int tly,int cellLength,boolean alive){
        this.cellLength=cellLength;
        this.alive=alive;
        this.color=color;
        this.tlx=tlx;
        this.tly=tly;
    }
    public int getTlx(){return this.tlx;}

    public int getTly(){return this.tly;}

    public void setAlive() {
        this.alive = true;
    }

    public void setDead() {
        this.alive = false;
    }

    public boolean isAlive() {
        return alive;
    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    public int getCellLength() {
        return cellLength;
    }

    public int totalNeighbours(Cell c1,Cell c2,Cell c3,Cell c4,Cell c5,Cell c6,Cell c7,Cell c8){
        int liveNeighbours=0;
        ArrayList<Cell> neighborslist=new ArrayList();
        neighborslist.add(c1);
        neighborslist.add(c2);
        neighborslist.add(c3);
        neighborslist.add(c4);
        neighborslist.add(c5);
        neighborslist.add(c6);
        neighborslist.add(c7);
        neighborslist.add(c8);
        for(Cell c:neighborslist){
            if(c.isAlive()){
                liveNeighbours+=1;
            }
        }
        return liveNeighbours;
    }

    public void draw(Graphics g){
        g.setColor(color);
        g.fillRect(tlx,tly,cellLength,cellLength);
    }
}

I'd like advice on how I can crunch this program down to fewer lines.

\$\endgroup\$
  • \$\begingroup\$ If this code is in multiple files (repo seems to show that) then it might be better if you broke the code block into multiple blocks, one for each file with the file name above the code block. \$\endgroup\$ – pacmaninbw May 13 at 14:25
  • \$\begingroup\$ Keeps giving me format errors. People are just going to have to use GitHub if they find the pasted source code a pain \$\endgroup\$ – jamie May 13 at 14:46
  • \$\begingroup\$ Take a look at how I did it. \$\endgroup\$ – pacmaninbw May 13 at 14:55
  • \$\begingroup\$ @pacmaninbw thank you! \$\endgroup\$ – jamie May 13 at 14:57
1
\$\begingroup\$

The number of lines is seldom a measure of quality. It is always better to use more lines if it makes the code more readable or more efficient. Why do you want fewer lines?

Anyway, since this is a bit of "a big code dump," I'm just going to skim over it and point the most obvious things. It's not an exhaustive list. You can start improving the code from these.

Window.Java

Pararameters names x and y are reserved for indexing a two dimensional map, e.g. pinpointing a pixel in an image. The code uses them for dimension, where width and height would be better.

Main.Java

I don't remember much of Swing anymore, but shouldn't the window resize itself to fit the frame? So passing the frame size to window would be redundant as it's the Frame you want to be exactly 1800x1000 pixels.

Frame.java

You can see from the implemented interfaces that this class loads on responsibilities. Refactor the listeners into standalone classes so the frame doesn't need to be responsible for interpreting mouse movement (single responsibility principle) and such. The frame should only be reponsible for displaying the graphics.

This class is a bit of a mess. Cells are stored in a redundant array of rows and columns. A two dimensional array, or a one dimensional array with index calculated from x and y location would be easier to understand as they are common constructs in this kind of application.

General

There is a separate class for Cell but in Game of Life the relation of the Cells is just as important. There should be a separate class for the Grid that contains the cells. Not just a bunch of ArrayLists. The rules that control the life and death of cells should be in yet another class. That class would be the game engine.

The Frame should not be controlling the game. The timer should be a separate class which tells the game engine to update the game model and send events to the Frame to let it know that the visual representation needs to be updated. Look at the Model-View-Controller design pattern.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your in-depth comment. It means a lot to me I'll take all of your points into consideration for further improvements/programs. \$\endgroup\$ – jamie May 14 at 17:35
  • \$\begingroup\$ I will say that I did indeed create a two-dimensional arraylist for the cells \$\endgroup\$ – jamie May 14 at 17:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.