Search code examples
javamenuactionlistenertic-tac-toe

Calling menuitems to trigger a separate block of code


I am developing a simple TicTacToe game which has a 'player vs player' mode.

I decided to add a menu in which the player could select whether he wants to play with other player or with the computer.

But I could not figure out how to properly call the menu items to trigger the specified code.

I tried Action listener but ActionListener inside an ActionListener doesn't seem to work.

I am new to programming and I searched various websites but Could not seem to find a solution. I just want the code of 1-vs-1 to be triggered, when the user clicks 1-vs-1 and 1-vs-computer (this part is not yet written, as you probably got the idea) to be triggered, when user selects 1-vs-computer from the menu.

Your little time to solve this problem will be a huge help for me!

package tictacpractice;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;


public class Frame implements ActionListener {
    int value=0;
    JButton reset;
    JButton exit;
    JLabel p1;
    JLabel p2;
    JButton []board;
    int turn;
    String Checker= "";
    String []Checkers= new String[9];
    boolean winner=false;
    JMenuBar menuBar;
    JMenu Menu;
    JMenuItem item1,item2;

    Frame(){

        turn=1;
        JFrame frm = new JFrame("TicTacThuss");
        frm.setSize(400,400);

        menuBar = new JMenuBar();

        Menu = new JMenu("Mode");
        Menu.setFont(new Font("Ariel", Font.BOLD, 32));
        item1 = new JMenuItem("1v1");
        item1.setFont(new Font("Ariel", Font.BOLD, 32));
        item2 = new JMenuItem("1vc");
        item2.setFont(new Font("Ariel", Font.BOLD, 32));
        Menu.add(item1);
        Menu.add(item2);
        menuBar.add(Menu);
        frm.setJMenuBar(menuBar);
        item2.addActionListener(this);

        reset = new JButton("reset");
        exit = new  JButton ("exit");
        p1 = new JLabel("Player X");
        p2 = new JLabel("Player O");
        board = new JButton[9];

        reset.addActionListener(this);
        exit.addActionListener(this);

        JPanel Lowerpanel= new JPanel();
        Lowerpanel.add(reset);
        Lowerpanel.add(exit);

        JPanel Centralpanel= new JPanel();
        Centralpanel.setLayout(new GridLayout(3,3));

        //String initiallizer
        for(int c=0; c<9; c++){
            Checkers[c]="";
        }

        for(int i=0; i<9; i++)
        {
            board[i]=new JButton();
            Centralpanel.add(board[i]);
            board[i].addActionListener(this);
            board[i].setFont(new Font("Ariel", Font.BOLD, 72));
        }

        frm.add(Centralpanel, BorderLayout.CENTER);
        frm.add(Lowerpanel, BorderLayout.SOUTH);
        frm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frm.setVisible(true);
    }

    @Override
    public  void actionPerformed(ActionEvent e){

         value++;
         if(turn==1){

             Checker=" X";
         }
         if(turn==0){

             Checker=" O";
         }

         if(e.getSource()==reset){
             for(int i=0; i<9; i++)
             {
                 board[i].setText("");
                 board[i].setEnabled(true);
                 winner=false;
                 Checkers[i]="";
                 value=0;
                 turn=1;
             }
        }

        if(e.getSource()==exit){

            System.exit(0);

        }

        for(int i=0; i<9; i++)
        {
            if(e.getSource()==board[i])
            {
                if(turn==1)
                {  
                    board[i].setText("X");
                    board[i].setEnabled(false);
                    Checkers[i]=Checker;

                }
                else{
                    if(turn==0)
                    {    
                        board[i].setText("O");
                        board[i].setEnabled(false);
                        Checkers[i]=Checker;

                    }
                }

                turn=(turn+1)%2;

            }
        }

        //horizontal
        if (Checkers[0].equals(Checkers[1]) && 
            Checkers[1].equals(Checkers[2]) && 
            !Checkers[0].equals("") ) {
            winner = true;
        }
        else if (Checkers[3].equals(Checkers[4]) && 
                 Checkers[4].equals(Checkers[5]) &&
                 !Checkers[3].equals("") ) {
            winner = true;
        }
        else if (Checkers[6].equals(Checkers[7]) && 
                 Checkers[7].equals(Checkers[8]) &&
                 !Checkers[6].equals("") ) {
            winner = true;
        }
        //Verticle

        if (Checkers[0].equals(Checkers[3]) && 
            Checkers[3].equals(Checkers[6]) &&
            !Checkers[0].equals("") ) {
            winner = true;
        }
        else if (Checkers[1].equals(Checkers[4]) && 
                 Checkers[4].equals(Checkers[7]) &&
                 !Checkers[1].equals("") ) {
            winner = true;
        }
        else if (Checkers[2].equals(Checkers[5]) && 
                 Checkers[5].equals(Checkers[8]) &&
                 !Checkers[2].equals("") ) {
            winner = true;
        }
        //Digonals
        if (Checkers[0].equals(Checkers[4]) && 
            Checkers[4].equals(Checkers[8]) 
            && !Checkers[0].equals("") ) {
            winner = true;
        }
        else if (Checkers[2].equals(Checkers[4]) && 
                 Checkers[4].equals(Checkers[6]) &&
                 !Checkers[2].equals("") ) {
            winner = true;
        }


         //Checker
         if(winner){

             JOptionPane.showMessageDialog(null, "Player"+Checker+" Wins");

             for (JButton i : board) {
                 i.setEnabled(false);
             }

         }
         else if(!winner && value==9)
         {
             JOptionPane.showMessageDialog(null," This game is draw ") ;     
         }

    }

}

Solution

  • Before answering your question, let me give some comment on your current code. Currently, by doing

     item1.addActionListener(this);
     item2.addActionListener(this);
     reset.addActionListener(this);
     exit.addActionListener(this);
    

    you handle all the ActionEvents in the same actionPerformed method (the one within your Frame class). As you already noticed, this makes this actionPerformed method very lengthy and hard to maintain when it comes to adding more buttons and menu-items.

    Therefore I suggest you should give separate actionPerformed methods to your buttons. This has the advantage, that each actionPerformed method would be rather short and simple. And you don't need code-lines like if (e.getSource() == reset) anymore.

    reset.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
           // handle click on the "Reset" button
           for (int i = 0; i < 9; i++) {
               board[i].setText("");
               board[i].setEnabled(true);
               winner = false;
               Checkers[i] = "";
               value = 0;
               turn = 1;
           }
        }
    });
    exit.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
           // handle click on the "Exit" button
           System.exit(0);
        }
    });
    

    (The syntax may look strange at first glance because it uses so-called anonymous classes.)

    And now to your actual question: You should use the same pattern also for the menu-items:

    item1.addActionListener(new ActionListener()  {
        @Override
        public void actionPerformed(ActionEvent e) {
            // handle click on the "1-vs-1" menu item
            ...
        }
    });
    item2.addActionListener(new ActionListener()  {
        @Override
        public void actionPerformed(ActionEvent e) {
            // handle click on the "1-vs-computer" menu item
            ...
        }
    });
    

    By the way: Since Java 8 you can rewrite the ActionListener code from above in an even more concise and better readable way by using so-called lambda-expressions instead of anonymous classes:

    reset.addActionListener(e -> {
        // handle click on the "Reset" button
        for (int i = 0; i < 9; i++) {
            board[i].setText("");
            board[i].setEnabled(true);
            winner = false;
            Checkers[i] = "";
            value = 0;
            turn = 1;
        }
    });
    exit.addActionListener(e -> {
        // handle click on the "Exit" button
        System.exit(0);
    });
    item1.addActionListener(e -> {
        // handle click on the "1-vs-1" menu item
        ...
    });
    item2.addActionListener(e -> {
        // handle click on the "1-vs-computer" menu item
        ...
    });
    

    Finally, you would not need the actionPerformed in your Frameclass anymore, and can remove implements ActionListener from that class.