0

I am doing a TicTacToe program and right now I am working on using a menu to change the size of the board(anywhere from 3x3 to 9x9) I need to remove the current buttons in use before adding the new ones. While looping through trying to remove them i get this error:

Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 3
    at TicTacToe$menuItem.actionPerformed(TicTacToe.java:84)
    at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
    at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
    at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
    at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
    at javax.swing.AbstractButton.doClick(AbstractButton.java:376)
    at javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:833)
    at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(BasicMenuItemUI.java:877)
    at java.awt.Component.processMouseEvent(Component.java:6535)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
    at java.awt.Component.processEvent(Component.java:6300)
    at java.awt.Container.processEvent(Container.java:2236)
    at java.awt.Component.dispatchEventImpl(Component.java:4891)
    at java.awt.Container.dispatchEventImpl(Container.java:2294)
    at java.awt.Component.dispatchEvent(Component.java:4713)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
    at java.awt.Container.dispatchEventImpl(Container.java:2280)
    at java.awt.Window.dispatchEventImpl(Window.java:2750)
    at java.awt.Component.dispatchEvent(Component.java:4713)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.awt.EventQueue$4.run(EventQueue.java:731)
    at java.awt.EventQueue$4.run(EventQueue.java:729)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Process finished with exit code 0

Here is the code(everything that needs to be imported has been i just didnt paste it in)

public class TicTacToe extends JFrame{

public enum Space{EMPTY, X_SPACE, O_SPACE}
public enum Winner{PLAYER, COMPUTER, NEITHER}

int size;
Button button[][];
Space board[][];
int weight[][];
menuItem item[];

ImageIcon X = new ImageIcon(this.getClass().getResource("X.png"));
ImageIcon O = new ImageIcon(this.getClass().getResource("O.png"));
JPanel panel = new JPanel();


public TicTacToe(int s) {
    // Initialize window properties
    super("Tic Tac Toe");
    boardSize(s);
    item = new menuItem[7];
    clearBoard();
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setSize(500, 500);
    setResizable(false);
    setLocationRelativeTo(null);
    panel.setLayout(new GridLayout(s, s));

    JMenuBar menubar = new JMenuBar();
    this.setJMenuBar(menubar);

    JMenu menu = new JMenu("Set Size");
    menubar.add(menu);

    for(int i=0; i<item.length; i++){
        item[i] = new menuItem(this, i+3, menu);
    }

    add(panel);
    setVisible(true);
    initWeight(); // Adds priority initialized in initWeight
}

public class Button extends JButton implements ActionListener {
    int row, col;
    TicTacToe ttt;
    public Button(TicTacToe t, int r, int c){
        ttt = t;
        row = r;
        col = c;
        this.addActionListener(this); // Adds the action listener to the buttons(this)
    }
    public void actionPerformed(ActionEvent e){
        // Displays move after event is found
        ttt.enterMove(row, col);
        displayWinner();
    }
}

public class menuItem extends JMenuItem implements ActionListener{
    int size;
    TicTacToe ttt;
    public menuItem(TicTacToe t,int s, JMenu menu){
        ttt = t;
        size = s;
        setText(s+"x"+s);
        menu.add(this);
        this.addActionListener(this);
    }
    public void actionPerformed(ActionEvent e){
        int s = size;
        for(int i=0; i < s; i++){
            for(int j=0; j < s; j++){
                ttt.panel.remove(button[i][j]);
            }
        }
        ttt.boardSize(size);
    }
}

public void addButtons(int s){
    s = size;
    for(int i = 0; i < s; i++)
    {
        for(int j = 0; j < s; j++)
        {
            board[i][j] = Space.EMPTY;
            button[i][j] = new Button(this, i, j);
            panel.add(button[i][j]);
        }
    }
}

public void displayWinner() {
    Space winner = getWinner();
    String msg;
    if(winner == Space.X_SPACE)
        msg = "Congratulations. You beat a bot smartass.\n Would you like to play again?";
    else if(winner == Space.O_SPACE)
        msg = "Wow... You just lost against a random number generator... You suck.\n Do you want redemption?";
    else
        return;

    int playAgain = JOptionPane.showConfirmDialog(this, msg, "Play Again", JOptionPane.YES_NO_OPTION); // Creates the window asking to play again

    if(playAgain == JOptionPane.YES_OPTION)
        clearBoard();
    else
        System.exit(0);
}

public void newGame(){
    for(int i = 0; i < size; i++){
        for(int j=0; j < size; j++) {
            board[i][j] = Space.EMPTY;
        }
    }
}

public boolean enterMove(int i, int j) {
    boolean goodMove = false;
    if(board[i][j] == Space.EMPTY){
        board[i][j] = Space.X_SPACE;
        goodMove = true;
    }else{
        JOptionPane.showMessageDialog(this, "Invalid Move", "Error", JOptionPane.ERROR_MESSAGE);
    }
    if(goodMove)
        updateBoard();

    if(getWinner() == Space.EMPTY && goodMove){
        computerMove();
        updateBoard();
    }
    return goodMove;
}

public void boardSize(int s) {
    size = s;
    button = new Button[size][size];
    board = new Space[size][size];
    weight = new int[size][size];
    addButtons(s);
    clearBoard();
    newGame();
}

public void initWeight(){
    //Corners
    weight[0][0] = 2;
    weight[0][size -1] = 2;
    weight[size-1][0] = 2;
    weight[size-1][size-1] = 2;

    //Edges
    for(int i = 1; i < size-1; i++) {
        weight[i][0] = 1;
        weight[0][i] = 1;
        weight[size - 1][i] = 1;
        weight[i][size - 1] = 1;
    }

    //Center(s)
    for(int i=1; i < size-1; i++){
        for(int j=1; j < size-1; j++){
            if(weight[i][j] == 0)
                weight[i][j] = 3;
            else if(weight[j][i] == 0)
                weight[j][i] = 3;
        }
    }

}

public boolean computerMove() {
    int mi = size, mj = size;
    int w = 0;

    for(int i=0; i < size; i++){
        for(int j=0; j <size; j++){
            if(board[i][j] == Space.EMPTY){
                if(weight[i][j] > w){
                    w = weight[i][j];
                    mi = i;
                    mj = j;
                }
            }
        }
    }
    if(mi < size && mj < size){
        board[mi][mj] = Space.O_SPACE;
        return true;
    }
    return false;
}

public void clearBoard(){
    for(int i=0; i < size; i++){
        for(int j=0; j < size; j++) {
            board[i][j] = Space.EMPTY;
            button[i][j].setIcon(null);
        }
    }
}

public Space getWinner(){
    Space w = Space.EMPTY;

    // Check for winner horizontally and vertically
    boolean dwii=true, dwij=true;
    for(int i=0; i < size; i++){
        boolean hw=true, vw=true;
        for(int j=1; j < size; j++){
            if(board[i][0] != board[i][j])
                hw = false;
            if(board[0][i] != board[j][i])
                vw = false;
        }
        if(hw && board[i][0] != Space.EMPTY)
            w = board[i][0];
        if(vw && board[0][i] != Space.EMPTY)
            w = board[0][i];

        if(board[0][0] != board[i][i])
            dwii = false;
        if(board[0][size-1] != board[i][size-i-1])
            dwij = false;
    }
    if(dwii && board[0][0] != Space.EMPTY)
        w = board[0][0];
    if(dwij && board[0][size-1] != Space.EMPTY)
        w = board[0][size-1];

    return w;
}

public void updateBoard() {
    for (int i = 0; i < size; i++) {
        for(int j=0; j < size; j++) {
            if (board[i][j] == Space.O_SPACE)
                button[i][j].setIcon(O);
            else if (board[i][j] == Space.X_SPACE)
                button[i][j].setIcon(X);
        }
    }
}

public static void main(String[] args) { // all this does is run the window
    new TicTacToe(3);
}
}

This is the part that is giving me trouble.

        public void actionPerformed(ActionEvent e){
    int s = size;
    for(int i=0; i < s; i++){
        for(int j=0; j < s; j++){
            ttt.panel.remove(button[i][j]);
        }
    }
    ttt.boardSize(size);
}
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Please post complete stack trace. The exception tells you that you are accessing an invalid index (3). The stack trace should give you the line number where your code makes that invalid access. Probably your loop condition is wrong .. – GhostCat Mar 12 '16 at 00:44
  • Ok I posted the full stack trace. – Carter Moore Mar 12 '16 at 00:54
  • I told you: that is the line number ... do you really need us to understand that you now have to figure what is going on ... on line 84? Hint: at least add that line to the question; or do you expect us to copy your code; hoping that you posted all of us; so that the line numbers are matching up?! – GhostCat Mar 12 '16 at 00:56
  • I already added the loop that was triggering this exception. The specific line is ttt.panel.remove(button[i][j]); – Carter Moore Mar 12 '16 at 01:02
  • See [What is a stack trace, and how can I use it to debug my application errors?](http://stackoverflow.com/q/3988788/418556) – Andrew Thompson Mar 12 '16 at 01:02
  • *"I need to remove the current buttons in use before adding the new ones."* Use a [`CardLayout`](http://download.oracle.com/javase/8/docs/api/java/awt/CardLayout.html) as shown in [this answer](http://stackoverflow.com/a/5786005/418556). – Andrew Thompson Mar 12 '16 at 01:03
  • @CarterMoore Problem is: your code is overly complex. You are constantly using some `s` and `size`. Don't do that. It is just confusing your code. There should be **one** variable in your whole program that contains the current number of elements per row/column. Especially: having an s method parameter, that is then initialized with size ... bonkers. – GhostCat Mar 12 '16 at 01:05
  • I wouldn't use a CardLayout. I don't think it makes sense to create multiple panels for each possible size of grid. The functionality of the panel remains the same, only the number of buttons changes. – camickr Mar 12 '16 at 02:15

2 Answers2

1

I need to remove the current buttons in use before adding the new ones.

You can use the removeAll() method on the Container class.

This of course assumes the you create a panel that only contains the buttons of your grid which is how I would design the GUI. It is always a good idea to break the components down into logical panels.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • Alright I tried doing this by nesting the gridlayout panel into the borderlayout panel of the menu. The panel containing the buttons doesn't show up though, how should I have that set up? (Sorry, I am a noob with Swing) – Carter Moore Mar 12 '16 at 03:07
  • It looks like your original code was already adding the "panel" to the CENTER of the BorderLayout. So I'm not sure why you changed anything. The point of my answer was that if the panel only contains the buttons, then you can use removeAll(). – camickr Mar 12 '16 at 05:28
0

It seems like you have accidentally modified the global attribute 'size' in your menuItem constructor. In this case, 'size' is > 3 after you initialized menuItems.

user3632094
  • 13
  • 2
  • 5