-1

I am making a tic tac toe game and I want to be able to have the button alternate x and o when clicked. Now they just are all x on the first click and all o on the second click. I tried with and without keyword this as well.

Here is the button class

public class Toebuttons extends JButton implements ActionListener
{
boolean x = true; // if true x's turn if false o's turn
int count = 0;
public Toebuttons()
{
   super("blank");
   this.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
   if(this.x == true)
   {
       count++;
       System.out.println(count);
       setText("X");
       this.x = false;
   }
   else if(this.x == false)
   {
       count++;
       System.out.println(count);
       setText("O");
       this.x = true;
   }
  }
}

Here is the board class

public class ticTacBoard extends JFrame
{
Toebuttons toe[] = new Toebuttons[9];
public ticTacBoard()
{
    super("Tic tac board");
    setSize(500,500);
    setLayout(new GridLayout(3,3));
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    toFront();
    for(int i = 0; i<toe.length; i++)
    {
        toe[i] = new Toebuttons();
        add(toe[i]);
    }
    setVisible(true);
 }
}
Ben
  • 31
  • 8

2 Answers2

4

Your problem is one of ineffective object-design: Each button has its own boolean x state which is independent from all the others, and this you should not do. In fact, you should have a Game object, one that holds the buttons and that is separate from the buttons, and that holds this state and controls the button's response to press based on this state.

Side note: I wouldn't extend JButton but rather would use JButtons.

For example, you could give all the JButtons the same ActionListener:

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;    
import javax.swing.*;

public class TicTacFoo extends JPanel {
    private static final long serialVersionUID = 1L;
    private static final int ROWS = 3;
    private static final Font BTN_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 80);
    private JButton[][] buttons = new JButton[ROWS][ROWS];
    private boolean xTurn = true;
    private int count = 0;

    public TicTacFoo() {

        JPanel buttonPanel = new JPanel(new GridLayout(ROWS, ROWS));
        setLayout(new GridLayout(ROWS, ROWS));

        // single ActionListener for all buttons
        ButtonListener buttonListener = new ButtonListener();

        // create buttons in nested for loop
        for (int row = 0; row < buttons.length; row++) {
            for (int col = 0; col < buttons[row].length; col++) {
                buttons[row][col] = new JButton("   ");
                buttons[row][col].setFont(BTN_FONT);
                buttons[row][col].addActionListener(buttonListener);
                buttonPanel.add(buttons[row][col]);
            }
        }

        setLayout(new BorderLayout());
        add(buttonPanel);
        add(new JButton(new ResetAction()), BorderLayout.PAGE_END);
    }

    private class ButtonListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {

            // get the button that was pressed
            JButton buttonPressed = (JButton) e.getSource();
            String text = buttonPressed.getText().trim();
            if (!text.isEmpty()) {
                // button already has been pressed
                // so exit from the listener
                return;
            }

            if (xTurn) {
                buttonPressed.setText("X");
            } else {
                buttonPressed.setText("O");
            }

            int rowPressed = -1;
            int colPressed = -1;
            // which button pressed?
            for (int row = 0; row < buttons.length; row++) {
                for (int col = 0; col < buttons[row].length; col++) {
                    if (buttons[row][col] == buttonPressed) {
                        rowPressed = row;
                        colPressed = col;
                        break;
                    }
                }
            }

            // TODO: here code where you would test for win
            // ......

            // swap turn
            xTurn = !xTurn;
            // increment count:
            count++;
            System.out.printf("count: %d, [row, col]: [%d, %d]%n", count, rowPressed, colPressed);
        }
    }

    // resets program back to initial state
    @SuppressWarnings("serial")
    class ResetAction extends AbstractAction {
        public ResetAction() {
            super("Reset");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            // loop through all buttons, resetting state back to initial
            for (JButton[] buttonRow : buttons) {
                for (JButton button : buttonRow) {
                    button.setText("   ");
                }
            }
            xTurn = true;
            count = 0;
        }
    }

    private static void createAndShowGui() {
        TicTacFoo mainPanel = new TicTacFoo();

        JFrame frame = new JFrame("TicTacFoo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Could a JPanel be a game object? How would it hold the buttons' state? Why would the button's have their own boolean x state when the boolean x is a global variable? – Ben Aug 12 '18 at 18:48
  • @Ben: yes, but don't think in terms of components but rather in terms of logical objects. – Hovercraft Full Of Eels Aug 12 '18 at 18:50
  • @Ben: please see comments in code that explain things – Hovercraft Full Of Eels Aug 12 '18 at 19:08
  • When I try to make a single button listener using I get an error using "ButtonListener name = new ButtonListener();" I have imported java.awt.*; javax.swing.*; java.awt.event.*; java.awt.event.ActionListener; – Ben Aug 16 '18 at 18:58
  • @Ben: the key to the problem will be in the complete error message, something you're not showing us -- you're not telling us what the error actually is, and again, that is key to solving the problem. Search this site on that key message to learn how to solve it. – Hovercraft Full Of Eels Aug 17 '18 at 00:39
  • The error message is "cannot find symbol - class ButtonListener" – Ben Aug 18 '18 at 00:10
  • And if you searched on this error, you would quickly find [What does a “Cannot find symbol” compilation error mean?](https://stackoverflow.com/questions/25706216/what-does-a-cannot-find-symbol-compilation-error-mean). If you haven't read the answer yet, I suggest you do so. If you already have read it, then re-look at it and study it in depth. – Hovercraft Full Of Eels Aug 18 '18 at 00:27
  • ButtonListener was a class that you created in your code. That's why it couldn't find it. – Ben Aug 18 '18 at 13:06
0

This is not C++ and this one

boolean x = true;

is not a global in Java. To mimic variable that can be understood as "global" (common for all class instances) in Java you need to declare it as static like

static boolean x = true;
m.antkowicz
  • 13,268
  • 18
  • 37
  • 2
    I strongly object to this solution as this isn't C either and you're throwing the OOP baby out with the bath water. Let's avoid telling a newbie to use static when this is neither needed nor recommended. Another object should hold the boolean state. – Hovercraft Full Of Eels Aug 12 '18 at 18:46
  • @HovercraftFullOfEels I totally agree with you however teaching OP and explaining the whole scope of OOP and why Java supports this approach and why it should be followed goes far beyond this question. Because we don't know how whole application looks and what is the context and requirements I'm providing this answer that resolving OP's issue – m.antkowicz Aug 12 '18 at 18:49
  • I really like the use of static before the variables.@HovercraftFullOfEels I don't understand how an object would hold a boolean state or how the count could continue to increase? – Ben Aug 12 '18 at 18:55
  • @Ben: see edit for an example. And no, don't use static for bad reasons, such as this one – Hovercraft Full Of Eels Aug 12 '18 at 18:58
  • @HovercraftFullOfEels I'm not at the level you are at in java and don't yet understand all your code. I do understand why the static works in my code now, but don't understand why it is bad to use it. – Ben Aug 12 '18 at 19:02
  • @Ben: please read Jon Skeet's answer to [Why are static variables considered evil?](https://stackoverflow.com/a/7026563/522444). The other answers to this question are pretty decent as well. – Hovercraft Full Of Eels Aug 12 '18 at 19:05
  • @Ben: the bottom line is -- don't have each button hold its own `x` boolean variable. Kick the variable up a level to the game level, your JFrame class for instance, not the JButton level. – Hovercraft Full Of Eels Aug 12 '18 at 19:06
  • @HovercraftFullOfEels If you move the x boolean variable up a level, would you have to move the action listener up a level as well? – Ben Aug 12 '18 at 19:26
  • @Ben: yes, but the action listener deserves to be up a level. Again, you're extending JButton inappropriately and burying the listener where it doesn't belong. The game itself is interested in what button is pressed, not the button itself. – Hovercraft Full Of Eels Aug 12 '18 at 19:27
  • @HovercraftFullOfEels Your code is very helpful, but why would you choose to use the JPanel instead of a JFrame? – Ben Aug 12 '18 at 19:50
  • @Ben: You are painting yourself in a corner by having your class extend JFrame, forcing you to create and display JFrames, when often more flexibility is called for. In fact, I would venture that most of the Swing GUI code that I've created and that I've seen does **not** extend JFrame, and in fact it is rare that you'll ever want to do this. More commonly your GUI classes will be geared towards creating JPanels, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. This will greatly increase the flexibility of your GUI coding. – Hovercraft Full Of Eels Aug 12 '18 at 19:54