3
\$\begingroup\$

I've made this program here (~240 lines) in which the user has to match their RGB Panel(right) with a randomized color on the left. It's a pretty fun program, and I suggest you try it out! It's fully runnable as is.

But a lot of the code seems repetitive and I was wondering how it could be condensed and made any more efficient if possible. Any other tips would be greatly appreciated. This is my first java program in a few years so I may have broken some unwritten rules.

My biggest concern with this program is the repetitiveness of the JButtons and the functions which they run. There are 6 buttons, one that adds red, one that subtracts red, one that adds green, one that subtracts green, one that adds blue, and one that subtracts blue. The functionality is very similar because they all either increase or decrease the color by 15, so I was wondering if there was a way to condense them all into just one function. Thanks!

I added some comments to help explain what's going on

package guessColor;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;

public class GuessColor extends JFrame {

    private static final long serialVersionUID = 1L;

    Font font = new Font("Times New Roman", Font.BOLD, 30);
    
    static Random rand = new Random();
    
    static int randRed = (rand.nextInt(17)+1)*15; //randomized red green and blue values, multiples of 15
    static int randGrn = (rand.nextInt(17)+1)*15;
    static int randBlu = (rand.nextInt(17)+1)*15;
    
    static int userRed = 0;
    static int userGrn = 0;
    static int userBlu = 0;
    
    Color randColor = new Color(randRed, randGrn, randBlu);
    static Color userColor = new Color(userRed, userGrn, userBlu);
    
    Dimension d = new Dimension(500, 500); //color panel size
    Dimension b = new Dimension(50,50); //button size

    public GuessColor() {
        initGUI();
        System.out.println("SOLUTION: " + randRed + " " + randGrn + " " + randBlu); // This is just to show what the RGB values are so you can easily solve
        setTitle("Match the color!");
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
        setResizable(false);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
    private void initGUI() {  //sets up the frame and functionality of UI
        JLabel title = new JLabel("Match The Color!", JLabel.CENTER);
        title.setFont(font);
        title.setBackground(Color.BLACK);
        title.setForeground(Color.WHITE);
        title.setOpaque(true);
        add(title, BorderLayout.NORTH);
        
        JPanel center = new JPanel();
        center.setBackground(Color.CYAN);
        add(center, BorderLayout.CENTER);
        
        JPanel randPan = new JPanel(); //random color panel
        randPan.setBackground(randColor);
        randPan.setPreferredSize(d);
        center.add(randPan, BorderLayout.EAST);
        
        JPanel userPan = new JPanel(); //adjustable color panel
        userPan.setBackground(userColor);
        userPan.setPreferredSize(d);
        center.add(userPan, BorderLayout.WEST);
        
        
        /**BUTTONS**/
        
        JPanel butPan = new JPanel();
        add(butPan, BorderLayout.SOUTH);
        
        JButton addRed = new JButton("+");
        addRed.setBackground(Color.RED);
        addRed.setPreferredSize(b);
        addRed.setFocusPainted(false);
        addRed.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                increaseRed();
                userPan.setBackground(userColor);
                repaint();
                check();
            }
        });
        butPan.add(addRed);
        
        
        JButton subRed = new JButton("-");
        subRed.setBackground(Color.RED);
        subRed.setPreferredSize(b);
        subRed.setFocusPainted(false);
        subRed.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                decreaseRed();
                userPan.setBackground(userColor);
                repaint();
                check();
            }
        });
        butPan.add(subRed);

        
        JButton addGrn = new JButton("+");
        addGrn.setBackground(Color.GREEN);
        addGrn.setPreferredSize(b);
        addGrn.setFocusPainted(false);
        addGrn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                increaseGrn();
                userPan.setBackground(userColor);
                repaint();
                check();
            }
        });
        butPan.add(addGrn);

        
        JButton subGrn = new JButton("-");
        subGrn.setBackground(Color.GREEN);
        subGrn.setPreferredSize(b);
        subGrn.setFocusPainted(false);
        subGrn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                decreaseGrn();
                userPan.setBackground(userColor);
                repaint();
                check();
            }
        });
        butPan.add(subGrn);


        JButton addBlu = new JButton("+");
        addBlu.setBackground(Color.BLUE);
        addBlu.setPreferredSize(b);
        addBlu.setFocusPainted(false);
        addBlu.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                increaseBlu();
                userPan.setBackground(userColor);
                repaint();
                check();
            }
        });
        butPan.add(addBlu);

        
        JButton subBlu = new JButton("-");
        subBlu.setBackground(Color.BLUE);
        subBlu.setPreferredSize(b);
        subBlu.setFocusPainted(false);
        subBlu.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                decreaseBlu();
                userPan.setBackground(userColor);
                repaint();
                check();
            }
        });
        butPan.add(subBlu); 
    }
    

    //function names say it all...

    private static void increaseRed() {
        if (userRed < 255) {
            userRed += 15;  
            userColor = new Color(userRed, userGrn, userBlu);
        }
    }
    
    private static void increaseGrn() {
        if (userGrn < 255) {
            userGrn += 15;  
            userColor = new Color(userRed, userGrn, userBlu);
        }
    }
    
    private static void increaseBlu() {
        if (userBlu < 255) {
            userBlu += 15;  
            userColor = new Color(userRed, userGrn, userBlu);
        }
    }
    
    private static void decreaseRed() {
        if (userRed > 0) {
            userRed -= 15;
            userColor = new Color(userRed, userGrn, userBlu);
        }
    }
    
    private static void decreaseGrn() {
        if (userGrn > 0) {
            userGrn -= 15;
            userColor = new Color(userRed, userGrn, userBlu);
        }
    }
    
    private static void decreaseBlu() {
        if (userBlu > 0) {
            userBlu -= 15;
            userColor = new Color(userRed, userGrn, userBlu);
        }
    }
    

    //checks if the color panels are the same and displays winning message if they are

    private static void check() {
        if (userRed == randRed && userGrn == randGrn && userBlu == randBlu) {
            int choose = JOptionPane.showConfirmDialog(null, "You win! Play again?");
            if(choose == JOptionPane.YES_OPTION) {
                reset();
            } else if(choose == JOptionPane.NO_OPTION) {
                System.exit(0);
            }           
        }   
    }
    
    //resets game for when user wins and wants to play again
    private static void reset() {
        randRed = (rand.nextInt(17)+1)*15;
        randGrn = (rand.nextInt(17)+1)*15;
        randBlu = (rand.nextInt(17)+1)*15;
        userRed = 0;
        userGrn = 0;
        userBlu = 0;
        userColor = new Color(userRed, userGrn, userBlu);
        
        new GuessColor();
    }

    //main method
    public static void main(String[] args) {
        try {
            String laf = UIManager.getCrossPlatformLookAndFeelClassName();
            UIManager.setLookAndFeel(laf);
        } 
        catch (Exception e) {}

        SwingUtilities.invokeLater(new Runnable(){
            public void run() {
                new GuessColor();
            }
        });
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ no black possible? \$\endgroup\$ – Hagen von Eitzen Oct 30 '20 at 7:44
  • \$\begingroup\$ @HagenvonEitzen what do you mean? \$\endgroup\$ – CodingNinja Oct 30 '20 at 8:06
2
\$\begingroup\$

My version isn't any shorter, but hopefully, it's clearer and provides a firmer foundation to build more complex games.

When developing a Swing game, it helps to separate the model, view, and controller. This is called the model / view / controller pattern.

So, I created a model class, GameModel. Here's the code for the GameModel class.

public class GameModel {
    
    private Color randomColor;
    private Color userColor;
    
    private final Random random;
    
    public GameModel() {
        this.random = new Random();
    }
    
    public void createColors() {
        setRandomColor();
        setUserColor();
    }

    public Color getUserColor() {
        return userColor;
    }

    public void setUserColor() {
        int userRed = 0;
        int userGrn = 0;
        int userBlu = 0;
        this.userColor = new Color(userRed, userGrn, userBlu);
    }
    
    public void setUserColor(int red, int green, int blue) {
        this.userColor = new Color(red, green, blue);
    }

    public Color getRandomColor() {
        return randomColor;
    }

    public void setRandomColor() {
        int randRed = (random.nextInt(17) + 1) * 15;
        int randGrn = (random.nextInt(17) + 1) * 15;
        int randBlu = (random.nextInt(17) + 1) * 15;
        this.randomColor = new Color(randRed, randGrn, randBlu);
    }
    
}

GameModel is a plain Java class that holds the data for the game. In this simple game, we have two color fields, one for the random color and one for the user to adjust using the GUI buttons.

We moved the color initiation code to this class.

We have two setRandomColor methods, one to initialize the user color, and one to set the user color based on the GUI button actions.

Now that we've created a working game model, we can focus on the view. Here's the view code.

public class GuessColor {
    
    private GameModel model;
    
    private JFrame frame;
    
    private JPanel userPanel;

    public GuessColor() {
        this.model = new GameModel();
        this.model.createColors();
        
        frame = new JFrame("Match the color!");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                
        frame.add(createMainPanel(), BorderLayout.CENTER);
        
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(true);
        
        printSolution();
    }

    // sets up the frame and functionality of UI
    private JPanel createMainPanel() { 
        JPanel panel = new JPanel(new BorderLayout());
        
        JLabel title = new JLabel("Match The Color!", JLabel.CENTER);
        Font font = new Font("Times New Roman", Font.BOLD, 30);
        title.setFont(font);
        title.setBackground(Color.BLACK);
        title.setForeground(Color.WHITE);
        title.setOpaque(true);
        panel.add(title, BorderLayout.NORTH);

        JPanel center = new JPanel(new BorderLayout());
        center.setBackground(Color.CYAN);
        panel.add(center, BorderLayout.CENTER);
        
        Dimension d = new Dimension(500, 500); // color panel size

        JPanel randPan = new JPanel(); // random color panel
        randPan.setBackground(model.getRandomColor());
        randPan.setPreferredSize(d);
        center.add(randPan, BorderLayout.WEST);

        userPanel = new JPanel(); // adjustable color panel
        userPanel.setBackground(model.getUserColor());
        userPanel.setPreferredSize(d);
        center.add(userPanel, BorderLayout.EAST);

        /** BUTTONS **/

        JPanel buttonPanel = new JPanel();
        panel.add(buttonPanel, BorderLayout.SOUTH);
        
        // This Object array makes it possible to create the JButtons in a loop
        // buttonObject[0] - JButton labels
        // buttonObject[1] - JButton action commands
        // buttonObject[2] - JButton background colors
        // buttonObject[3] - JButton foreground colors
        Object[][] buttonObject = new Object[][] { { "+", "-", "+", "-", "+", "-" },
                { "red", "red", "green", "green", "blue", "blue" },
                { Color.RED, Color.RED, Color.GREEN, 
                        Color.GREEN, Color.BLUE, Color.BLUE },
                { Color.WHITE, Color.WHITE, Color.BLACK, 
                        Color.BLACK, Color.WHITE, Color.WHITE } };
        Dimension b = new Dimension(50, 50); // button size
        ButtonListener listener = new ButtonListener();
        
        for (int i = 0; i < buttonObject[0].length; i++) {
            JButton button = new JButton((String) buttonObject[0][i]);
            button.setActionCommand((String) buttonObject[1][i]);
            button.setBackground((Color) buttonObject[2][i]);
            button.setForeground((Color) buttonObject[3][i]);
            button.setPreferredSize(b);
            button.setFocusPainted(false);
            button.addActionListener(listener);
            buttonPanel.add(button);
        }

        return panel;   
    }
    
    public void setUserPanelColor() {
        userPanel.setBackground(model.getUserColor());
    }
    
    public void printSolution() {
        // This is just to show what the RGB
        // values are so you can easily solve
        System.out.println("SOLUTION: " + model.getRandomColor());

    }

    // main method
    public static void main(String[] args) {
        try {
            String laf = UIManager.getCrossPlatformLookAndFeelClassName();
            UIManager.setLookAndFeel(laf);
        } catch (Exception e) {
            e.printStackTrace();
        }

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new GuessColor();
            }
        });
    }

}

I made quite a few changes to your code. Here are the major changes I want to emphasize.

  1. In the main method, I added an e.printStackTrace(); to the catch block. You should always print or log errors.

  2. I separated the code to construct the JFrame from the code to construct the main JPanel. This allows me to focus on one part of the GUI at a time.

  3. I used a JFrame. The only time you should extend a Swing component, or any Java class, is when you want to override one or more of the class methods.

  4. I moved almost all of the GuessColor class variables to their respective methods. The only class variables that remain as class variables are the variables used in more than one method.

  5. I created an admittedly complex Object array so I could create the JButtons in a loop.

Finally, I created the ActionListener. I was able to move the check method in the ActionListener.

public class ButtonListener implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent event) {
        JButton button = (JButton) event.getSource();
        String text = button.getText();
        String action = event.getActionCommand();
        
        Color color = model.getUserColor();
        int red = color.getRed();
        int green = color.getGreen();
        int blue = color.getBlue();
        
        if (action.equals("red")) {
            if (text.equals("+")) {
                red += 15;
                red = Math.min(255, red);
                model.setUserColor(red, green, blue);
            } else {
                red -= 15;
                red = Math.max(0, red);
                model.setUserColor(red, green, blue);
            }
        } else if (action.equals("green")) {
            if (text.equals("+")) {
                green += 15;
                green = Math.min(255, green);
                model.setUserColor(red, green, blue);
            } else {
                green -= 15;
                green = Math.max(0, green);
                model.setUserColor(red, green, blue);
            }
        } else if (action.equals("blue")) {
            if (text.equals("+")) {
                blue += 15;
                blue = Math.min(255, blue);
                model.setUserColor(red, green, blue);
            } else {
                blue -= 15;
                blue = Math.max(0, blue);
                model.setUserColor(red, green, blue);
            }
        }
        
        setUserPanelColor();
        System.out.println(model.getUserColor());
        check();
    }
    
    // checks if the color panels are the same and displays 
    // winning message if they are the same

    private void check() {
        if (model.getRandomColor().equals(model.getUserColor())) {
            int choose = JOptionPane.showConfirmDialog(frame, 
                    "You win!  Play again?");
            if (choose == JOptionPane.YES_OPTION) {
                model.createColors();
                setUserPanelColor();
                printSolution();
            } else if (choose == JOptionPane.NO_OPTION) {
                System.exit(0);
            }
        }
    }
    
}

Here's the complete runnable code. I hope this explanation helps you.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class GuessColor {
    
    private GameModel model;
    
    private JFrame frame;
    
    private JPanel userPanel;

    public GuessColor() {
        this.model = new GameModel();
        this.model.createColors();
        
        frame = new JFrame("Match the color!");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                
        frame.add(createMainPanel(), BorderLayout.CENTER);
        
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(true);
        
        printSolution();
    }

    // sets up the frame and functionality of UI
    private JPanel createMainPanel() { 
        JPanel panel = new JPanel(new BorderLayout());
        
        JLabel title = new JLabel("Match The Color!", JLabel.CENTER);
        Font font = new Font("Times New Roman", Font.BOLD, 30);
        title.setFont(font);
        title.setBackground(Color.BLACK);
        title.setForeground(Color.WHITE);
        title.setOpaque(true);
        panel.add(title, BorderLayout.NORTH);

        JPanel center = new JPanel(new BorderLayout());
        center.setBackground(Color.CYAN);
        panel.add(center, BorderLayout.CENTER);
        
        Dimension d = new Dimension(500, 500); // color panel size

        JPanel randPan = new JPanel(); // random color panel
        randPan.setBackground(model.getRandomColor());
        randPan.setPreferredSize(d);
        center.add(randPan, BorderLayout.WEST);

        userPanel = new JPanel(); // adjustable color panel
        userPanel.setBackground(model.getUserColor());
        userPanel.setPreferredSize(d);
        center.add(userPanel, BorderLayout.EAST);

        /** BUTTONS **/

        JPanel buttonPanel = new JPanel();
        panel.add(buttonPanel, BorderLayout.SOUTH);
        
        // This Object array makes it possible to create the JButtons in a loop
        // buttonObject[0] - JButton labels
        // buttonObject[1] - JButton action commands
        // buttonObject[2] - JButton background colors
        // buttonObject[3] - JButton foreground colors
        Object[][] buttonObject = new Object[][] { { "+", "-", "+", "-", "+", "-" },
                { "red", "red", "green", "green", "blue", "blue" },
                { Color.RED, Color.RED, Color.GREEN, 
                        Color.GREEN, Color.BLUE, Color.BLUE },
                { Color.WHITE, Color.WHITE, Color.BLACK, 
                        Color.BLACK, Color.WHITE, Color.WHITE } };
        Dimension b = new Dimension(50, 50); // button size
        ButtonListener listener = new ButtonListener();
        
        for (int i = 0; i < buttonObject[0].length; i++) {
            JButton button = new JButton((String) buttonObject[0][i]);
            button.setActionCommand((String) buttonObject[1][i]);
            button.setBackground((Color) buttonObject[2][i]);
            button.setForeground((Color) buttonObject[3][i]);
            button.setPreferredSize(b);
            button.setFocusPainted(false);
            button.addActionListener(listener);
            buttonPanel.add(button);
        }

        return panel;   
    }
    
    public void setUserPanelColor() {
        userPanel.setBackground(model.getUserColor());
    }
    
    public void printSolution() {
        // This is just to show what the RGB
        // values are so you can easily solve
        System.out.println("SOLUTION: " + model.getRandomColor());

    }

    // main method
    public static void main(String[] args) {
        try {
            String laf = UIManager.getCrossPlatformLookAndFeelClassName();
            UIManager.setLookAndFeel(laf);
        } catch (Exception e) {
            e.printStackTrace();
        }

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new GuessColor();
            }
        });
    }
    
    public class ButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent event) {
            JButton button = (JButton) event.getSource();
            String text = button.getText();
            String action = event.getActionCommand();
            
            Color color = model.getUserColor();
            int red = color.getRed();
            int green = color.getGreen();
            int blue = color.getBlue();
            
            if (action.equals("red")) {
                if (text.equals("+")) {
                    red += 15;
                    red = Math.min(255, red);
                    model.setUserColor(red, green, blue);
                } else {
                    red -= 15;
                    red = Math.max(0, red);
                    model.setUserColor(red, green, blue);
                }
            } else if (action.equals("green")) {
                if (text.equals("+")) {
                    green += 15;
                    green = Math.min(255, green);
                    model.setUserColor(red, green, blue);
                } else {
                    green -= 15;
                    green = Math.max(0, green);
                    model.setUserColor(red, green, blue);
                }
            } else if (action.equals("blue")) {
                if (text.equals("+")) {
                    blue += 15;
                    blue = Math.min(255, blue);
                    model.setUserColor(red, green, blue);
                } else {
                    blue -= 15;
                    blue = Math.max(0, blue);
                    model.setUserColor(red, green, blue);
                }
            }
            
            setUserPanelColor();
            System.out.println(model.getUserColor());
            check();
        }
        
        // checks if the color panels are the same and displays 
        // winning message if they are the same

        private void check() {
            if (model.getRandomColor().equals(model.getUserColor())) {
                int choose = JOptionPane.showConfirmDialog(frame, 
                        "You win!  Play again?");
                if (choose == JOptionPane.YES_OPTION) {
                    model.createColors();
                    setUserPanelColor();
                    printSolution();
                } else if (choose == JOptionPane.NO_OPTION) {
                    System.exit(0);
                }
            }
        }
        
    }
    
    public class GameModel {
        
        private Color randomColor;
        private Color userColor;
        
        private final Random random;
        
        public GameModel() {
            this.random = new Random();
        }
        
        public void createColors() {
            setRandomColor();
            setUserColor();
        }

        public Color getUserColor() {
            return userColor;
        }

        public void setUserColor() {
            int userRed = 0;
            int userGrn = 0;
            int userBlu = 0;
            this.userColor = new Color(userRed, userGrn, userBlu);
        }
        
        public void setUserColor(int red, int green, int blue) {
            this.userColor = new Color(red, green, blue);
        }

        public Color getRandomColor() {
            return randomColor;
        }

        public void setRandomColor() {
            int randRed = (random.nextInt(17) + 1) * 15;
            int randGrn = (random.nextInt(17) + 1) * 15;
            int randBlu = (random.nextInt(17) + 1) * 15;
            this.randomColor = new Color(randRed, randGrn, randBlu);
        }
        
    }
}

Edited to add: I made the game more accessible to younger players by adding an RGB display for each color.

Here's the GUI.

Match The Color GUI

Here's the revised complete runnable code.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class GuessColor {
    
    private DisplayPanel randomDisplayPanel;
    private DisplayPanel userDisplayPanel;
    
    private GameModel model;
    
    private JFrame frame;
    
    private JPanel randomPanel;
    private JPanel userPanel;

    public GuessColor() {
        this.model = new GameModel();
        this.model.createColors();
        
        frame = new JFrame("Match the color!");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                
        frame.add(createMainPanel(), BorderLayout.CENTER);
        
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(true);
    }

    // sets up the frame and functionality of UI
    private JPanel createMainPanel() { 
        JPanel panel = new JPanel(new BorderLayout());
        
        JLabel title = new JLabel("Match The Color!", JLabel.CENTER);
        Font font = new Font("Times New Roman", Font.BOLD, 30);
        title.setFont(font);
        title.setBackground(Color.BLACK);
        title.setForeground(Color.WHITE);
        title.setOpaque(true);
        panel.add(title, BorderLayout.NORTH);

        JPanel center = new JPanel(new BorderLayout());
        center.setBackground(Color.CYAN);
        panel.add(center, BorderLayout.CENTER);
        
        Dimension d = new Dimension(500, 500); // color panel size

        randomPanel = new JPanel(new BorderLayout()); // random color panel
        randomPanel.setBackground(model.getRandomColor());
        randomPanel.setPreferredSize(d);
        randomDisplayPanel = new DisplayPanel(model.getRandomColor());
        randomPanel.add(randomDisplayPanel.getPanel(), BorderLayout.NORTH);
        center.add(randomPanel, BorderLayout.WEST);

        userPanel = new JPanel(new BorderLayout()); // adjustable color panel
        userPanel.setBackground(model.getUserColor());
        userPanel.setPreferredSize(d);
        userDisplayPanel = new DisplayPanel(model.getUserColor());
        userPanel.add(userDisplayPanel.getPanel(), BorderLayout.NORTH);
        center.add(userPanel, BorderLayout.EAST);

        /** BUTTONS **/

        JPanel buttonPanel = new JPanel();
        panel.add(buttonPanel, BorderLayout.SOUTH);
        
        // This Object array makes it possible to create the JButtons in a loop
        // buttonObject[0] - JButton labels
        // buttonObject[1] - JButton action commands
        // buttonObject[2] - JButton background colors
        // buttonObject[3] - JButton foreground colors
        Object[][] buttonObject = new Object[][] { { "+", "-", "+", "-", "+", "-" },
                { "red", "red", "green", "green", "blue", "blue" },
                { Color.RED, Color.RED, Color.GREEN, 
                        Color.GREEN, Color.BLUE, Color.BLUE },
                { Color.WHITE, Color.WHITE, Color.BLACK, 
                        Color.BLACK, Color.WHITE, Color.WHITE } };
        Dimension b = new Dimension(50, 50); // button size
        ButtonListener listener = new ButtonListener();
        
        for (int i = 0; i < buttonObject[0].length; i++) {
            JButton button = new JButton((String) buttonObject[0][i]);
            button.setActionCommand((String) buttonObject[1][i]);
            button.setBackground((Color) buttonObject[2][i]);
            button.setForeground((Color) buttonObject[3][i]);
            button.setPreferredSize(b);
            button.setFocusPainted(false);
            button.addActionListener(listener);
            buttonPanel.add(button);
        }

        return panel;   
    }
    
    public void setRandomPanelColor() {
        randomPanel.setBackground(model.getRandomColor());
        randomDisplayPanel.setColor(model.getRandomColor());
    }
    
    public void setUserPanelColor() {
        userPanel.setBackground(model.getUserColor());
        userDisplayPanel.setColor(model.getUserColor());
    }
    
    // main method
    public static void main(String[] args) {
        try {
            String laf = UIManager.getCrossPlatformLookAndFeelClassName();
            UIManager.setLookAndFeel(laf);
        } catch (Exception e) {
            e.printStackTrace();
        }

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new GuessColor();
            }
        });
    }
    
    public class DisplayPanel {
        
        private JPanel panel;
        
        private JTextField redField;
        private JTextField greenField;
        private JTextField blueField;
        
        public DisplayPanel(Color color) {
            createJPanel();
            setColor(color);
        }
        
        private void createJPanel() {
            panel = new JPanel(new GridBagLayout());
            panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
            
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.anchor = GridBagConstraints.LINE_START;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.insets = new Insets(5, 5, 5, 5);
            gbc.gridx = 0;
            gbc.gridy = 0;
            
            JLabel redLabel = new JLabel("Red:");
            redLabel.setForeground(Color.WHITE);
            panel.add(redLabel, gbc);
            
            gbc.gridx++;
            redField = new JTextField(3);
            redField.setEditable(false);
            redField.setHorizontalAlignment(JTextField.TRAILING);
            panel.add(redField, gbc);
            
            gbc.gridx = 0;
            gbc.gridy++;
            JLabel greenLabel = new JLabel("Green:");
            greenLabel.setForeground(Color.WHITE);
            panel.add(greenLabel, gbc);
            
            gbc.gridx++;
            greenField = new JTextField(3);
            greenField.setEditable(false);
            greenField.setHorizontalAlignment(JTextField.TRAILING);
            panel.add(greenField, gbc);
            
            gbc.gridx = 0;
            gbc.gridy++;
            JLabel blueLabel = new JLabel("Blue:");
            blueLabel.setForeground(Color.WHITE);
            panel.add(blueLabel, gbc);
            
            gbc.gridx++;
            blueField = new JTextField(3);
            blueField.setEditable(false);
            blueField.setHorizontalAlignment(JTextField.TRAILING);
            panel.add(blueField, gbc);
        }

        public JPanel getPanel() {
            return panel;
        }

        public void setColor(Color color) {
            panel.setBackground(color);
            redField.setText(Integer.toString(color.getRed()));
            greenField.setText(Integer.toString(color.getGreen()));
            blueField.setText(Integer.toString(color.getBlue()));
        }
        
    }
    
    public class ButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent event) {
            JButton button = (JButton) event.getSource();
            String text = button.getText();
            String action = event.getActionCommand();
            
            Color color = model.getUserColor();
            int red = color.getRed();
            int green = color.getGreen();
            int blue = color.getBlue();
            
            if (action.equals("red")) {
                if (text.equals("+")) {
                    red += 15;
                    red = Math.min(255, red);
                } else {
                    red -= 15;
                    red = Math.max(0, red);
                }
            } else if (action.equals("green")) {
                if (text.equals("+")) {
                    green += 15;
                    green = Math.min(255, green);
                } else {
                    green -= 15;
                    green = Math.max(0, green);
                }
            } else if (action.equals("blue")) {
                if (text.equals("+")) {
                    blue += 15;
                    blue = Math.min(255, blue);
                } else {
                    blue -= 15;
                    blue = Math.max(0, blue);
                }
            }
            
            model.setUserColor(red, green, blue);
            setUserPanelColor();
            check();
        }
        
        // checks if the color panels are the same and displays 
        // winning message if they are the same

        private void check() {
            if (model.getRandomColor().equals(model.getUserColor())) {
                int choose = JOptionPane.showConfirmDialog(frame, 
                        "You win!  Play again?");
                if (choose == JOptionPane.YES_OPTION) {
                    model.createColors();
                    setRandomPanelColor();
                    setUserPanelColor();
                } else if (choose == JOptionPane.NO_OPTION) {
                    frame.dispose();
                    System.exit(0);
                }
            }
        }
        
    }
    
    public class GameModel {
        
        private Color randomColor;
        private Color userColor;
        
        private final Random random;
        
        public GameModel() {
            this.random = new Random();
        }
        
        public void createColors() {
            setRandomColor();
            setUserColor();
        }

        public Color getUserColor() {
            return userColor;
        }

        public void setUserColor() {
            int userRed = 135;
            int userGrn = 135;
            int userBlu = 135;
            this.userColor = new Color(userRed, userGrn, userBlu);
        }
        
        public void setUserColor(int red, int green, int blue) {
            this.userColor = new Color(red, green, blue);
        }

        public Color getRandomColor() {
            return randomColor;
        }

        public void setRandomColor() {
            int randRed = (random.nextInt(17) + 1) * 15;
            int randGrn = (random.nextInt(17) + 1) * 15;
            int randBlu = (random.nextInt(17) + 1) * 15;
            this.randomColor = new Color(randRed, randGrn, randBlu);
        }
        
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Great answer, very helpful. I like the idea of the object array for the buttons! \$\endgroup\$ – CodingNinja Nov 2 '20 at 17:17
1
\$\begingroup\$

Most of what I would change at the first pass is about correctly defining and limiting the scope of your objects to make it easier to read and think about the code. (I would change more too, but I just wanted to offer you feedback in this area for now)

You can skip the whole initGui() thing and just define the top level items (title, center, butPan) as members of the class, and then using initializer blocks so that they are created and set up how you want, including their children. Initializer blocks and the constructor are called in file order. (see my version below)

When you have a long method, that's not always a sign of a problem. But when you do, it can be useful to use scope blocks to limit where things are visible. It just makes it easier to tell at a glance that whole sections of the code don't define variables that are going to matter later on in the long method. (So I used this in the initializer block for butPan below).

Other than that, you've got static on a lot of things that don't feel static as they belong to the particular game being played. That's why you have a reset method which primarily just sets everything back - but it's normally easier and less prone to errors if you just make a whole new instance for a new game.

Here's where I got to so far. I hope it helps, even though I guess I haven't actually answered your question about how to better handle creating the buttons.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class GuessColor extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final Font font = new Font("Times New Roman", Font.BOLD, 30);
    private static final Dimension d = new Dimension(500, 500); // color panel size
    private static final Dimension b = new Dimension(50, 50); // button size
    private static final Random rand = new Random();

    private Color userColor = new Color(0, 0, 0);
    private Color goalColor = randomColor();

    private JLabel title = new JLabel("Match The Color!", JLabel.CENTER);
    {
        this.add(title, BorderLayout.NORTH);
        title.setFont(font);
        title.setBackground(Color.BLACK);
        title.setForeground(Color.WHITE);
        title.setOpaque(true);
    }

    private JPanel center = new JPanel();
    {
        this.add(center, BorderLayout.CENTER);
        center.setBackground(Color.CYAN);
    }

    private JPanel randPan = new JPanel(); // random color panel
    {
        center.add(randPan, BorderLayout.EAST);
        randPan.setBackground(goalColor);
        randPan.setPreferredSize(d);
    }

    private JPanel userPan = new JPanel(); // adjustable color panel
    {
        center.add(userPan, BorderLayout.WEST);
        userPan.setBackground(userColor);
        userPan.setPreferredSize(d);
    }

    private JPanel butPan = new JPanel();
    {
        this.add(butPan, BorderLayout.SOUTH);

        {
            JButton addRed = new JButton("+");
            addRed.setBackground(Color.RED);
            addRed.setPreferredSize(b);
            addRed.setFocusPainted(false);
            addRed.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    increaseRed();
                    userPan.setBackground(userColor);
                    repaint();
                    check();
                }
            });
            butPan.add(addRed);
        }

        {
            JButton subRed = new JButton("-");
            subRed.setBackground(Color.RED);
            subRed.setPreferredSize(b);
            subRed.setFocusPainted(false);
            subRed.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    decreaseRed();
                    userPan.setBackground(userColor);
                    repaint();
                    check();
                }
            });
            butPan.add(subRed);
        }

        {
            JButton addGrn = new JButton("+");
            addGrn.setBackground(Color.GREEN);
            addGrn.setPreferredSize(b);
            addGrn.setFocusPainted(false);
            addGrn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    increaseGrn();
                    userPan.setBackground(userColor);
                    repaint();
                    check();
                }
            });
            butPan.add(addGrn);
        }

        {
            JButton subGrn = new JButton("-");
            subGrn.setBackground(Color.GREEN);
            subGrn.setPreferredSize(b);
            subGrn.setFocusPainted(false);
            subGrn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    decreaseGrn();
                    userPan.setBackground(userColor);
                    repaint();
                    check();
                }
            });
            butPan.add(subGrn);
        }

        {
            JButton addBlu = new JButton("+");
            addBlu.setBackground(Color.BLUE);
            addBlu.setPreferredSize(b);
            addBlu.setFocusPainted(false);
            addBlu.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    increaseBlu();
                    userPan.setBackground(userColor);
                    repaint();
                    check();
                }
            });
            butPan.add(addBlu);
        }

        {
            JButton subBlu = new JButton("-");
            subBlu.setBackground(Color.BLUE);
            subBlu.setPreferredSize(b);
            subBlu.setFocusPainted(false);
            subBlu.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    decreaseBlu();
                    userPan.setBackground(userColor);
                    repaint();
                    check();
                }
            });
            butPan.add(subBlu);
        }
    }

    public GuessColor() {
        System.out.println("SOLUTION: " + goalColor);
        this.setTitle("Match the color!");
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
        this.setResizable(false);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private Color randomColor() {
        return new Color((rand.nextInt(17) + 1) * 15, (rand.nextInt(17) + 1) * 15,
                (rand.nextInt(17) + 1) * 15);
    }

    private void increaseRed() {
        if (userColor.getRed() < 255) {
            userColor = new Color(userColor.getRed() + 15, userColor.getGreen(),
                    userColor.getBlue());
        }
    }

    private void increaseGrn() {
        if (userColor.getGreen() < 255) {
            userColor = new Color(userColor.getRed(), userColor.getGreen() + 15,
                    userColor.getBlue());
        }
    }

    private void increaseBlu() {
        if (userColor.getBlue() < 255) {
            userColor = new Color(userColor.getRed(), userColor.getGreen() + 15,
                    userColor.getBlue() + 15);
        }
    }

    private void decreaseRed() {
        if (userColor.getRed() > 0) {
            userColor = new Color(userColor.getRed() - 15, userColor.getGreen() + 15,
                    userColor.getBlue());
        }
    }

    private void decreaseGrn() {
        if (userColor.getGreen() > 0) {
            userColor = new Color(userColor.getRed(), userColor.getGreen() - 15,
                    userColor.getBlue());
        }
    }

    private void decreaseBlu() {
        if (userColor.getBlue() > 0) {
            userColor = new Color(userColor.getRed(), userColor.getGreen(),
                    userColor.getBlue() - 15);
        }
    }

    private void check() {
        if (userColor.equals(goalColor)) {
            int choose = JOptionPane.showConfirmDialog(null, "You win! Play again?");
            if (choose == JOptionPane.YES_OPTION) {
                this.dispose();
                new GuessColor();
            } else if (choose == JOptionPane.NO_OPTION) {
                System.exit(0);
            }
        }
    }

    public static void main(String[] args)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new GuessColor();
            }
        });
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Thanks! although it didn't answer about the buttons it was still a very useful. upvoted :) \$\endgroup\$ – CodingNinja Nov 2 '20 at 17:07
1
\$\begingroup\$

Building on my previous answer, now I'll address simplifying the creation of the buttons.

The main thing here is that you have some code which is repeated, so we can pull that logic out to a function and parameterise the parts that change. (createGameButton)

The ActionListeners you create just call their respective methods -- that's a little unnecessary as we can just put those instructions for the ActionListener directly where we define them. It also helps that we don't need to name the buttons when we only refer to them once. (butPan initializer block)

Lastly, the bits that are called every time afterwards can go into a method, and just call that method. (update)

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class GuessColor extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final Font font = new Font("Times New Roman", Font.BOLD, 30);
    private static final Dimension d = new Dimension(500, 500); // color panel size
    private static final Dimension b = new Dimension(50, 50); // button size
    private static final Random rand = new Random();

    private Color userColor = new Color(0, 0, 0);
    private Color goalColor = randomColor();

    private JLabel title = new JLabel("Match The Color!", JLabel.CENTER);
    {
        this.add(title, BorderLayout.NORTH);
        title.setFont(font);
        title.setBackground(Color.BLACK);
        title.setForeground(Color.WHITE);
        title.setOpaque(true);
    }

    private JPanel center = new JPanel();
    {
        this.add(center, BorderLayout.CENTER);
        center.setBackground(Color.CYAN);
    }

    private JPanel randPan = new JPanel(); // random color panel
    {
        center.add(randPan, BorderLayout.EAST);
        randPan.setBackground(goalColor);
        randPan.setPreferredSize(d);
    }

    private JPanel userPan = new JPanel(); // adjustable color panel
    {
        center.add(userPan, BorderLayout.WEST);
        userPan.setBackground(userColor);
        userPan.setPreferredSize(d);
    }

    private JPanel butPan = new JPanel();
    {
        this.add(butPan, BorderLayout.SOUTH);
        butPan.add(createGameButton(Color.RED, "+", new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (userColor.getRed() < 255) {
                    userColor = new Color(userColor.getRed() + 15, userColor.getGreen(),
                            userColor.getBlue());
                }
                update();
            }
        }));
        butPan.add(createGameButton(Color.RED, "-", new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (userColor.getRed() > 0) {
                    userColor = new Color(userColor.getRed() - 15, userColor.getGreen(),
                            userColor.getBlue());
                }
                update();
            }
        }));
        butPan.add(createGameButton(Color.GREEN, "+", new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (userColor.getGreen() < 255) {
                    userColor = new Color(userColor.getRed(), userColor.getGreen() + 15,
                            userColor.getBlue());
                }
                update();
            }
        }));
        butPan.add(createGameButton(Color.GREEN, "-", new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (userColor.getGreen() > 0) {
                    userColor = new Color(userColor.getRed(), userColor.getGreen() - 15,
                            userColor.getBlue());
                }
                update();
            }
        }));
        butPan.add(createGameButton(Color.BLUE, "+", new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (userColor.getBlue() < 255) {
                    userColor = new Color(userColor.getRed(), userColor.getGreen(),
                            userColor.getBlue() + 15);
                }
                update();
            }
        }));
        butPan.add(createGameButton(Color.BLUE, "-", new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (userColor.getBlue() > 0) {
                    userColor = new Color(userColor.getRed(), userColor.getGreen(),
                            userColor.getBlue() - 15);
                }
            }
        }));

    }

    public GuessColor() {
        System.out.println("SOLUTION: " + goalColor);
        this.setTitle("Match the color!");
        this.pack();
        this.setLocationRelativeTo(null);
        this.setVisible(true);
        this.setResizable(false);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private Color randomColor() {
        return new Color((rand.nextInt(17) + 1) * 15, (rand.nextInt(17) + 1) * 15,
                (rand.nextInt(17) + 1) * 15);
    }

    private JButton createGameButton(Color color, String label, ActionListener listener) {
        JButton button = new JButton(label);
        button.setBackground(color);
        button.setPreferredSize(b);
        button.setFocusPainted(false);
        button.addActionListener(listener);
        return button;
    }

    private void update() {
        userPan.setBackground(userColor);
        repaint();
        check();
    }

    private void check() {
        if (userColor.equals(goalColor)) {
            int choose = JOptionPane.showConfirmDialog(null, "You win! Play again?");
            if (choose == JOptionPane.YES_OPTION) {
                this.dispose();
                new GuessColor();
            } else if (choose == JOptionPane.NO_OPTION) {
                System.exit(0);
            }
        }
    }

    public static void main(String[] args)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException,
            UnsupportedLookAndFeelException {
        UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new GuessColor();
            }
        });
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Yeah this is a good answer but it doesn't really shorten the code a whole lot. I guess its not really possible considering the buttons all perform different functions. Maybe if you added a increase/decrease boolean and color into the createGameButton function I could cut it down some more? \$\endgroup\$ – CodingNinja Nov 2 '20 at 17:14

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.