8
\$\begingroup\$

I recently coded a program to stimulate Conway's Game of Life. Originally, my program just printed an array of 1's and 0's where the 1's represented the "live" cells, but I tried to teach myself some graphics programming to add graphics to the game. Currently, my program draws a black rectangle to represent a live cell and a white one to represent a dead cell in a grid of rectangles. I am looking to improve my implementation of the graphics and do it in a more organized and traditional way.

import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.Dimension;
import java.awt.Graphics;

public class GameofLife {
static JPanel panel;
static JFrame frame;
public static void main(String[] args) throws InterruptedException{
    int [][] array = new int [28][28];
    /*
     * Set the pattern for Conway's Game of Life by manipulating the array below.
     */

    /*Acorn
     */
    array[19][15]=1;
    array[19][16]=1;
    array[19][19]=1;
    array[19][20]=1;
    array[19][21]=1;
    array[18][18]=1;
    array[17][16]=1;

            panel = new JPanel();
    Dimension dim = new Dimension(400, 400);
    panel.setPreferredSize(dim);
    frame = new JFrame();
    frame.setSize(1000, 500);
    Container contentPane = frame.getContentPane();
    contentPane.add(panel);
    frame.setVisible(true);
    /*
     * Runs the Game of Life simulation "a" number of times.
     */
    int[][] end = new int[array.length][array[0].length];
    int a = 0;
    while (true) {
        for (int i = 1; i <= array.length - 2; i++) {
            for (int j = 1; j <= array[0].length - 2; j++) {
                int counter = surround(array, i, j);
                if (array[i][j] == 1 && counter <= 2) {
                    end[i][j] = 0;
                }
                if (array[i][j] == 1 && counter == 3) {
                    end[i][j] = 1;
                }
                if (array[i][j] == 1 && counter > 4) {
                    end[i][j] = 0;
                }
                if (array[i][j] == 0 && counter == 3) {
                    end[i][j] = 1;
                }
                if (array[i][j] == 1 && counter == 4) {
                    end[i][j] = 1;
                }
            }
        }
        Graphics g = panel.getGraphics();
        Graphics(array, g);
        a++;
        /*
         * printArray(array); System.out.println("");
         * System.out.println("");
         */
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array[0].length; j++) {
                array[i][j] = end[i][j];
                end[i][j] = 0;
            }
        }
        Thread.sleep(125);
        g.dispose();
    }
}

/*
 * Checks the cells around a specific cell in the initial array and returns
 * the number of live cells surrounding it.
 */
public static int surround(int[][] initial, int i, int j) {
    int[][] surrounding = {
            { initial[i - 1][j - 1], initial[i - 1][j],
                    initial[i - 1][j + 1] },
            { initial[i][j - 1], initial[i][j], initial[i][j + 1] },
            { initial[i + 1][j - 1], initial[i + 1][j],
                    initial[i + 1][j + 1] } };
    int counter = 0;
    for (int a = 0; a <= 2; a++) {
        for (int b = 0; b <= 2; b++) {
            if (surrounding[a][b] == 1) {
                counter++;
            }
        }
    }
    return counter;
}

/*
 * Prints the 2D array.
 */
public static void printArray(int[][] input) {
    for (int x = 0; x < input.length; x++) {
        for (int y = 0; y < input[0].length; y++) {
            System.out.print(input[x][y]);
        }
        System.out.println("");
    }
}

/*
 * Creates the graphic for Conway's game of life.
 */
public static void Graphics(int[][] array, Graphics g) {
    int BOX_DIM = 10;
    for (int i = 0; i < array.length; i++) {
        for (int j = 0; j < array[0].length; j++) {
            g.drawRect(i * BOX_DIM, j * BOX_DIM, 10, 10);
            if (array[i][j] == 0) {
                g.setColor(Color.WHITE);
                g.fillRect(i * BOX_DIM, j * BOX_DIM, 10, 10);
            }
            if (array[i][j] == 1) {
                g.setColor(Color.BLACK);
                g.fillRect(i * BOX_DIM, j * BOX_DIM, 10, 10);
            }
        }
    }

}
}
\$\endgroup\$
1
\$\begingroup\$

Flickering rectangle

Your graphics function does something strange because it draws a rectangle outline followed by a filled rectangle. First, you don't need the rectangle outline. Second, even if you did, you should set the color first. It was actually causing a flicker on the upper left corner when I tried it out because it was drawing a rectangle of the wrong color sometimes.

Also, it's strange that you defined BOX_DIM to be 10 but then didn't use BOX_DIM for your width and height.

Here is how I would change the function:

public static void Graphics(int[][] array, Graphics g) {
    int BOX_DIM = 10;
    for (int i = 0; i < array.length; i++) {
        for (int j = 0; j < array[0].length; j++) {
            g.setColor(array[i][j] == 0 ? Color.WHITE : Color.BLACK);
            g.fillRect(i * BOX_DIM, j * BOX_DIM, BOX_DIM, BOX_DIM);
        }
    }
}

A possible optimization

One thing you could do is to remember which cells changed on each generation and only draw the cells that changed instead of drawing all cells. For a 28x28 grid, it doesn't make any difference. But once you start trying larger grids like a 1000x1000 grid, it becomes noticeable if you draw just the changed cells versus all cells.

\$\endgroup\$
1
\$\begingroup\$

Things That Look Good

I think you did a number of things really well with this. Using the array's dimensions rather than just hard coding your loops with the right values, for example, is great because if you ever want different sizes, you can just change the declaration of array and you're good to go.

Also, having the Graphics() method separated out was a good idea, too. It separates the drawing logic from the game state logic. Nice work.

Naming

A lot of your variables have meaningless names, though. You have an array named array. What does it hold? In this case, it turns out to be the game state. So maybe call it currentGameState? And it looks like end is the game state of the next step, so maybe call it nextGameState. Likewise, panel and frame could be more descriptive. Maybe gamePanel or worldFrame or something like that? And same with a (which is unused and should be removed) and g. g is the graphics context or state, so at least call it something like that.

Functions

You really need to break your code up into functions that do specific things instead of having it all in one large function. Your main() has at least 3 parts:

  1. Setting up the initial game state
  2. Setting up the drawing state
  3. Running the game loop

Those should be 3 separate functions. Furthermore, your game loop does 2 things - it updates the state of the game, then displays it to the user. Those should be 2 functions, as well.

Optimizations

There are a lot of optimizations that can be made for the game of life. There are far too many to get into here, but you should look up "conway game of life optimization" on your favorite search engine for those. For now, I'll address your code specifically.

Your surround() method takes an array and a starting index. It then copies the items in the surrounding 3x3 area into a new array and checks the values in the new array. This seems wasteful. Just check the ones in the array that's passed in. Something like this:

public static int surround(int[][] initial, int i, int j) {
    int counter = 0;
    for (int a = i - 1; a <= i + 1; a++) {
        for (int b = j - 1; b <= j + 1; b++) {
            if (initial[a][b] == 1) {
                counter++;
            }
        }
    }
    return counter;
}

I concur with @Jared's comments above about the graphics routine.

Also, I notice that you calculate the new game state, draw it, then you copy the new game state into the current game state variable. It would be better to have a couple of references to 2 arrays and access the arrays through the references, then just swap the references rather than copying an entire array.

\$\endgroup\$

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.