-1

Another problem, same program:

The following is MainGUI.java

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

public class MainGUI extends JFrame implements ActionListener
{

    private static final long   serialVersionUID    = 4149825008429377286L;

    private final double version = 8;

    public static int rows;
    public static int columns;
    private int totalCells;
    private MainCell[] cell;

    public static Color userColor;
    JTextField speed = new JTextField("250");
    Timer timer = new Timer(250,this);
    String generationText = "Generation: 0";
    JLabel generationLabel = new JLabel(generationText);
    int generation = 0;

    public MainGUI(String title, int r, int c)
    {   
        rows = r;
        columns = c;
        totalCells = r*c;
        System.out.println(totalCells);
        cell = new MainCell[totalCells];

        setTitle(title);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Timer set up
        timer.setInitialDelay(Integer.parseInt(speed.getText()));
        timer.setActionCommand("timer");


        //set up menu bar
        JMenuBar menuBar = new JMenuBar();
        JMenu optionsMenu = new JMenu("Options");
        JMenu aboutMenu = new JMenu("About");
        menuBar.add(optionsMenu);
        menuBar.add(aboutMenu);

        JMenuItem helpButton = new JMenuItem("Help!");
        helpButton.addActionListener(this);
        helpButton.setActionCommand("help");
        aboutMenu.add(helpButton);

        JMenuItem aboutButton = new JMenuItem("About");
        aboutButton.addActionListener(this);
        aboutButton.setActionCommand("about");
        aboutMenu.add(aboutButton);

        JMenuItem colorSelect = new JMenuItem("Select a Custom Color");
        colorSelect.addActionListener(this);
        colorSelect.setActionCommand("colorSelect");
        optionsMenu.add(colorSelect);

        JMenuItem sizeChooser = new JMenuItem("Define a Custom Size");
        sizeChooser.addActionListener(this);
        sizeChooser.setActionCommand("sizeChooser");
        optionsMenu.add(sizeChooser);


        //Create text field to adjust speed and its label
        JPanel speedContainer = new JPanel();
        JLabel speedLabel = new JLabel("Enter the speed of a life cycle (in ms):");
        speedContainer.add(speedLabel);
        speedContainer.add(speed);
        speedContainer.add(generationLabel);
        Dimension speedDim = new Dimension(100,25);
        speed.setPreferredSize(speedDim);


        //Create various buttons
        JPanel buttonContainer = new JPanel();

        JButton randomizerButton = new JButton("Randomize");
        randomizerButton.addActionListener(this);
        randomizerButton.setActionCommand("randomize");
        buttonContainer.add(randomizerButton);

        JButton nextButton = new JButton("Next"); //forces a cycle to occur
        nextButton.addActionListener(this);
        nextButton.setActionCommand("check");
        buttonContainer.add(nextButton);

        JButton startButton = new JButton("Start");
        startButton.addActionListener(this);
        startButton.setActionCommand("start");
        buttonContainer.add(startButton);

        JButton stopButton = new JButton("Stop");
        stopButton.addActionListener(this);
        stopButton.setActionCommand("stop");
        buttonContainer.add(stopButton);

        JButton clearButton = new JButton("Clear");
        clearButton.addActionListener(this);
        clearButton.setActionCommand("clear");
        buttonContainer.add(clearButton);


        //holds the speed container and button container, keeps it neat
        JPanel functionContainer = new JPanel();
        BoxLayout functionLayout = new BoxLayout(functionContainer, BoxLayout.PAGE_AXIS);
        functionContainer.setLayout(functionLayout);
        functionContainer.add(speedContainer);
        speedContainer.setAlignmentX(CENTER_ALIGNMENT);
        functionContainer.add(buttonContainer);
        buttonContainer.setAlignmentX(CENTER_ALIGNMENT);


        //finish up with the cell container
        GridLayout cellLayout = new GridLayout(rows,columns);
        JPanel cellContainer = new JPanel(cellLayout);
        cellContainer.setBackground(Color.black);
        int posX = 0;
        int posY = 0;
        for(int i=0;i<totalCells;i++)
        {
            MainCell childCell = new MainCell();
            cell[i] = childCell;
            childCell.setName(String.valueOf(i));
            childCell.setPosX(posX);
            posX++;
            childCell.setPosY(posY);
            if(posX==columns)
            {
                posX = 0;
                posY++;
            }
            cellContainer.add(childCell);
            childCell.deactivate();
            Graphics g = childCell.getGraphics();
            childCell.paint(g);
        }


        //make a default color
        userColor = Color.yellow;


        //change icon
        URL imgURL = getClass().getResource("images/gol.gif");
        ImageIcon icon = new ImageIcon(imgURL);
        System.out.println(icon);
        setIconImage(icon.getImage());


        //add it all up and pack
        JPanel container = new JPanel();
        BoxLayout containerLayout = new BoxLayout(container, BoxLayout.PAGE_AXIS);
        container.setLayout(containerLayout);
        container.add(cellContainer);
        container.add(functionContainer);
        add(menuBar);
        setJMenuBar(menuBar);
        add(container);
        pack();
    }

    private void checkCells()
    {
        //perform check for every cell
        for(int i=0;i<totalCells;i++)
        {
            cell[i].setNeighbors(checkNeighbors(i));
        }
        //use value from check to determine life
        for(int i=0;i<totalCells;i++)
        {
            int neighbors = cell[i].getNeighbors();
            if(cell[i].isActivated())
            {
                System.out.println(cell[i].getName()+" "+neighbors);
                if(neighbors==0||neighbors==1||neighbors>3)
                {
                    cell[i].deactivate();
                }
            }
            if(cell[i].isActivated()==false)
            {
                if(neighbors==3)
                {
                    cell[i].activate();
                }
            }
        }
    }

    public void actionPerformed(ActionEvent e)
    {
        if(e.getActionCommand().equals("randomize"))
        {
            Random rn = new Random();
            for(int i=0;i<totalCells;i++)
            {
                cell[i].deactivate();
                if(rn.nextInt(6)==0)
                {
                    cell[i].activate();
                }
            }
        }



        //help button, self-explanatory
        if(e.getActionCommand().equals("help"))
        {
            JOptionPane.showMessageDialog(this, "The game is governed by four rules:\nFor a space that is 'populated':"
                    + "\n     Each cell with one or no neighbors dies, as if by loneliness."
                    + "\n     Each cell with four or more neighbors dies, as if by overpopulation."
                    + "\n     Each cell with two or three neighbors survives."
                    + "\nFor a space that is 'empty' or 'unpopulated':"
                    + "\n     Each cell with three neighbors becomes populated."
                    + "\nLeft click populates cells. Right click depopulates cells.","Rules:",JOptionPane.PLAIN_MESSAGE);
        }


        //shameless self promotion
        if(e.getActionCommand().equals("about"))
        {
            JOptionPane.showMessageDialog(this, "Game made and owned by *****!"
                    + "\nFree usage as see fit, but give credit where credit is due!\nVERSION: "+version,"About:",JOptionPane.PLAIN_MESSAGE);
        }


        //clears all the cells
        if(e.getActionCommand().equals("clear"))
        {
            timer.stop();
            generation = 0;
            generationText = "Generation: "+generation;
            generationLabel.setText(generationText);
            for(int i=0;i<totalCells;i++)
            {
                cell[i].deactivate();
            }
        }


        //starts timer
        if(e.getActionCommand().equals("start"))
        {

            if(Integer.parseInt(speed.getText())>0)
            {
                timer.setDelay(Integer.parseInt(speed.getText()));
                timer.restart();
            }
            else
            {
                JOptionPane.showMessageDialog(this, "Please use a value greater than 0!","Rules:",JOptionPane.ERROR_MESSAGE);
            }

        }


        //stops timer
        if(e.getActionCommand().equals("stop"))
        {
            timer.stop();
        }


        //run when timer
        if(e.getActionCommand().equals("timer"))
        {
            generation++;
            generationText = "Generation: "+generation;
            generationLabel.setText(generationText);
            timer.stop();
            checkCells();
            timer.setInitialDelay(Integer.parseInt(speed.getText()));
            timer.restart();
        }


        //see checkCells()
        if(e.getActionCommand().equals("check"))
        {
            generation++;
            generationText = "Generation: "+generation;
            generationLabel.setText(generationText);
            checkCells();
        }


        //color select gui
        if(e.getActionCommand().equals("colorSelect"))
        {
            userColor = JColorChooser.showDialog(this, "Choose a color:", userColor);
            if(userColor==null)
            {
                userColor = Color.yellow;
            }
        }


        //size chooser!
        if(e.getActionCommand().equals("sizeChooser"))
        {
            SizeChooser size = new SizeChooser();
            size.setLocationRelativeTo(null);
            size.setVisible(true);
        }
    }

    private int checkNeighbors(int c)
    {
        //if a LIVE neighbor is found, add one
        int neighbors = 0;

        if(cell[c].getPosX()!=0&&cell[c].getPosY()!=0)
        {
            if(c-columns-1>=0)
            {
                if(cell[c-columns-1].isActivated())
                {
                    System.out.println(cell[c].getName()+" found "+cell[c-columns-1].getName());
                    neighbors++;
                }
            }
        }

        if(cell[c].getPosY()!=0)
        {
            if(c-columns>=0)
            {
                if(cell[c-columns].isActivated())
                {
                    System.out.println(cell[c].getName()+" found "+cell[c-columns].getName());
                    neighbors++;
                }
            }
        }

        if(cell[c].getPosX()!=columns-1&&cell[c].getPosY()!=0)
        {
            if(c-columns+1>=0)
            {
                if(cell[c-columns+1].isActivated())
                {
                    System.out.println(cell[c].getName()+" found "+cell[c-columns+1].getName());
                    neighbors++;
                }
            }
        }

        if(cell[c].getPosX()!=0)
        {
            if(c-1>=0)
            {
                if(cell[c-1].isActivated())
                {
                    System.out.println(cell[c].getName()+" found "+cell[c-1].getName());
                    neighbors++;
                }
            }
        }

        if(cell[c].getPosX()!=columns-1)
        {
            if(c+1<totalCells)
            {
                if(cell[c+1].isActivated())
                {
                    System.out.println(cell[c].getName()+" found "+cell[c+1].getName());
                    neighbors++;
                }
            }
        }

        if(cell[c].getPosX()!=0&&cell[c].getPosY()!=rows-1)
        {
            if(c+columns-1<totalCells)
            {
                if(cell[c+columns-1].isActivated())
                {
                    System.out.println(cell[c].getName()+" found "+cell[c+columns-1].getName());
                    neighbors++;
                }
            }
        }

        if(cell[c].getPosY()!=rows-1&&cell[c].getPosY()!=rows-1)
        {
            if(c+columns<totalCells)
            {
                if(cell[c+columns].isActivated())
                {
                    System.out.println(cell[c].getName()+" found "+cell[c+columns].getName());
                    neighbors++;
                }
            }
        }

        if(cell[c].getPosX()!=columns-1&&cell[c].getPosY()!=rows-1)
        {
            if(c+columns+1<totalCells)
            {
                if(cell[c+columns+1].isActivated())
                {
                    System.out.println(cell[c].getName()+" found "+cell[c+columns+1].getName());
                    neighbors++;
                }
            }
        }
        return neighbors;
    }
}

The following is MainCell.java:

public class MainCell extends JPanel implements MouseListener
{

    //everything here should be self-explanatory

    private static final long   serialVersionUID    = 1761933778208900172L;

    private boolean activated = false;

    public static boolean leftMousePressed;
    public static boolean rightMousePressed;

    private int posX = 0;
    private int posY = 0;
    private int neighbors = 0;

    private URL cellImgURL_1 = getClass().getResource("images/cellImage_1.gif");
    private ImageIcon cellImageIcon_1 = new ImageIcon(cellImgURL_1);
    private Image cellImage_1 = cellImageIcon_1.getImage();

    private URL cellImgURL_2 = getClass().getResource("images/cellImage_2.gif");
    private ImageIcon cellImageIcon_2 = new ImageIcon(cellImgURL_2);
    private Image cellImage_2 = cellImageIcon_2.getImage();

    private URL cellImgURL_3 = getClass().getResource("images/cellImage_3.gif");
    private ImageIcon cellImageIcon_3 = new ImageIcon(cellImgURL_3);
    private Image cellImage_3 = cellImageIcon_3.getImage();

    public MainCell()
    {   
        Dimension dim = new Dimension(17, 17);
        setPreferredSize(dim);
        addMouseListener(this);
    }

    public void activate()
    {
        setBackground(MainGUI.userColor);
        System.out.println(getName()+" "+posX+","+posY+" activated");
        setActivated(true);
    }

    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if(getPosX()==MainGUI.columns-1&&getPosY()==0)
        {
            //do nothing
        }
        else if(getPosY()!=0&&getPosX()!=MainGUI.columns-1)
        {
            g.drawImage(cellImage_1,0,0,null);
        }
        else if(getPosY()==0)
        {
            g.drawImage(cellImage_2,0,0,null);
        }
        else if(getPosX()==MainGUI.columns-1)
        {
            g.drawImage(cellImage_3,0,0,null);
        }
    }

    public void setActivated(boolean b)
    {
        activated = b;  
    }

    public void deactivate()
    {
        setBackground(Color.gray);
        System.out.println(getName()+" "+posX+","+posY+" deactivated");
        setActivated(false);
    }

    public boolean isActivated()
    {
        return activated;
    }

    public void setNeighbors(int i)
    {
        neighbors = i;
    }

    public int getNeighbors()
    {
        return neighbors;
    }

    public int getPosX()
    {
        return posX;
    }

    public void setPosX(int x)
    {
        posX = x;
    }

    public int getPosY()
    {
        return posY;
    }

    public void setPosY(int y)
    {
        posY = y;
    }

    public void mouseClicked(MouseEvent e)
    {

    }

    public void mouseEntered(MouseEvent e)
    {
        if(leftMousePressed&&SwingUtilities.isLeftMouseButton(e))
        {
            activate();
        }
        if(rightMousePressed&&SwingUtilities.isRightMouseButton(e))
        {
            deactivate();
        }
    }

    public void mouseExited(MouseEvent e)
    {

    }

    public void mousePressed(MouseEvent e)
    {
        if(SwingUtilities.isRightMouseButton(e)&&!leftMousePressed)
        {
            deactivate();
            rightMousePressed = true;
        } 
        if(SwingUtilities.isLeftMouseButton(e)&&!rightMousePressed)
        {
            activate();
            leftMousePressed = true;
        }   
    }

    public void mouseReleased(MouseEvent e)
    {
        if(SwingUtilities.isRightMouseButton(e))
        {
            rightMousePressed = false;
        }
        if(SwingUtilities.isLeftMouseButton(e))
        {
            leftMousePressed = false;
        }
    }

}

The following is SizeChooser.java:

import java.awt.*;
import java.awt.event.*;
import java.net.URL;

import javax.swing.*;

public class SizeChooser extends JFrame
{
    private static final long   serialVersionUID    = -6431709376438241788L;

    public static MainGUI GUI;

    private static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    JTextField rowsTextField = new JTextField(String.valueOf((screenSize.height/17)-15));
    JTextField columnsTextField = new JTextField(String.valueOf((screenSize.width/17)-10));

    private static int rows = screenSize.height/17-15;
    private static int columns = screenSize.width/17-10;

    public SizeChooser()
    {
        setResizable(false);
        setTitle("Select a size!");

        JPanel container = new JPanel();
        BoxLayout containerLayout = new BoxLayout(container, BoxLayout.PAGE_AXIS);
        container.setLayout(containerLayout);
        add(container);

        JLabel rowsLabel = new JLabel("Rows:");
        container.add(rowsLabel);
        container.add(rowsTextField);

        JLabel columnsLabel = new JLabel("Columns:");
        container.add(columnsLabel);
        container.add(columnsTextField);

        JButton confirmSize = new JButton("Confirm");
        confirmSize.addActionListener(new ActionListener()
        {

            public void actionPerformed(ActionEvent e)
            {
                GUI.setVisible(false);
                GUI = null;
                if(Integer.parseInt(rowsTextField.getText())>0)
                {
                    rows = Integer.parseInt(rowsTextField.getText());
                }
                else
                {
                    JOptionPane.showMessageDialog(rootPane, "Please use a value greater than 0!","Rules:",JOptionPane.ERROR_MESSAGE);
                }
                if(Integer.parseInt(columnsTextField.getText())>0)
                {
                     columns = Integer.parseInt(columnsTextField.getText());
                }
                else
                {
                    JOptionPane.showMessageDialog(rootPane, "Please use a value greater than 0!","Rules:",JOptionPane.ERROR_MESSAGE);
                }
                GUI = new MainGUI("The Game of Life!", rows, columns);
                GUI.setLocationRelativeTo(null);
                GUI.setVisible(true);
                setVisible(false);
            }           
        });
        container.add(confirmSize);

        URL imgURL = getClass().getResource("images/gol.gif");
        ImageIcon icon = new ImageIcon(imgURL);
        System.out.println(icon);
        setIconImage(icon.getImage());
        pack();
    }


    public static void main(String[]args)
    {
        GUI = new MainGUI("The Game of Life!", rows, columns);
        GUI.setLocationRelativeTo(null);
        GUI.setVisible(true);
    }

}

So the problem now is, when the randomize button is pressed, or a large number of cells exist and then the timer is started, the cells aren't as snappy as they would be with less active cells. For example, with 100 columns and 50 rows, when the randomize button is pressed, one cell activates, then the next, then another, and so forth. Can I have them all activate at exactly the same time? Is this just a problem with too many things calculated at once? Would concurrency help?

QUICK EDIT: Is the swing timer the best idea for this project?

PM 77-1
  • 12,933
  • 21
  • 68
  • 111

2 Answers2

0

I havent read through your code completely but I am guessing that your listener methods are fairly computationally intensive and hence the lag in updating the display.

An SO User
  • 24,612
  • 35
  • 133
  • 221
0

This simple test reveals that your randomize operation should be fairly quick:

public static void main(String args[]) {
    Random rn = new Random();
    boolean b[] = new boolean[1000000];

    long timer = System.nanoTime();
    for (int i = 0; i < b.length; i++) {
        b[i] = rn.nextInt(6) == 0;
    }
    timer = System.nanoTime() - timer;
    System.out.println(timer + "ns / " + (timer / 1000000) + "ms");
}

The output for me is:

17580267ns / 17ms

So this leads me into thinking activate() or deactivate() is causing your UI to be redrawn.


I am unable to run this because I don't have your graphical assets, but I would try those changes to see if it works:

In MainGUI#actionPerformed, change:

if(e.getActionCommand().equals("randomize"))
{
    Random rn = new Random();
    for(int i=0;i<totalCells;i++)
    {
        cell[i].deactivate();
        if(rn.nextInt(6)==0)
        {
            cell[i].activate();
        }
    }
}

to:

if(e.getActionCommand().equals("randomize"))
{
    Random rn = new Random();
    for(int i=0;i<totalCells;i++)
    {
        // This will not cause the object to be redrawn and should
        // be a fairly cheap operation
        cell[i].setActivated(rn.nextInt(6)==0);
    }
    // Cause the UI to repaint
    repaint();
}

Add this to MainCell

// You can specify those colors however you like
public static final Color COLOR_ACTIVATED = Color.RED;
public static final Color COLOR_DEACTIVATED = Color.GRAY;

And change:

protected void paintComponent(Graphics g)
{
    super.paintComponent(g);
    if(getPosX()==MainGUI.columns-1&&getPosY()==0)
    {
        //do nothing
    }

to:

protected void paintComponent(Graphics g)
{
    // We now make UI changes only when the component is painted
    setBackground(activated ? COLOR_ACTIVATED : COLOR_DEACTIVATED);
    super.paintComponent(g);
    if(getPosX()==MainGUI.columns-1&&getPosY()==0)
    {
        //do nothing
    }
Ben Barkay
  • 5,473
  • 2
  • 20
  • 29