Java TicTacToe 程序在非获胜组合时触发

Java TicTacToe program triggers at non winning combinations

几年前我做了一个 GUI TicTacToe 游戏,现在想重做,因为我现在有更多的编程技能。我能够将代码从 600 行缩减到大约 150 行。

虽然我用的是同样的方案,但我运行遇到了一些自己无法解决的问题,请大家帮帮我。

程序由两个class组成,主要classTTTMain:

public class TTTMain {

public static void main(String[] args) {
    TTTFrame tttf = new TTTFrame(0,0);
    
    /*Tic Tac Toe Field:
     *  0 1 2
     *  3 4 5
     *  6 7 8
    */
    
}}

TTTFrame

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

public class TTTFrame extends JFrame implements ActionListener {

    private Button[] btnPlayButton;
    private Button btnRestart;
    private int buttonCounter;
    private int xScore;
    private int oScore;
    private Label Olabel, Xlabel;

    TTTFrame(int xScore, int oScore) {

        this.xScore = xScore;
        this.oScore = oScore;

        btnPlayButton = new Button[9];
        for (int i = 0; i < 9; i++) {
            btnPlayButton[i] = new Button("" + i);
            btnPlayButton[i].setBackground(Color.white);
            btnPlayButton[i].setForeground(Color.white);
            btnPlayButton[i].addActionListener(this);
            this.add(btnPlayButton[i]);
        }

        Xlabel = new Label("X: " + this.xScore);
        Xlabel.setFont(new Font("Arial", Font.BOLD, 24));
        Xlabel.setForeground(Color.white);
        Xlabel.setBackground(Color.black);
        this.add(Xlabel);

        btnRestart = new Button("Restart");
        btnRestart.setActionCommand("Restart");
        btnRestart.setFont(new Font("Arial", Font.PLAIN, 18));
        btnRestart.addActionListener(this);
        this.add(btnRestart);

        Olabel = new Label("O: " + this.oScore);
        Olabel.setFont(new Font("Arial", Font.BOLD, 24));
        Olabel.setForeground(Color.white);
        Olabel.setBackground(Color.black);
        this.add(Olabel);

        this.setLayout(new GridLayout(4, 3));
        this.pack();
        this.setResizable(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setTitle("Tic Tac Toe");
        this.setSize(300, 400);
        this.getContentPane().setBackground(Color.black);
        this.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        if (e.getActionCommand().equals("Restart")) {
            System.out.println("Restarted");
            for (int i = 0; i < 9; i++) {

                btnPlayButton[i].setLabel("" + i);
                btnPlayButton[i].setForeground(Color.white);
                btnPlayButton[i].setBackground(Color.white);
                btnPlayButton[i].addActionListener(this);

                this.buttonCounter = 0;
            }
        } else {

            ((Button) e.getSource()).setFont(new Font("Arial", Font.BOLD, 48));
            ((Button) e.getSource()).setForeground(Color.black);
            System.out.println(buttonCounter);
            if (buttonCounter % 2 == 0) {
                ((Button) e.getSource()).setLabel("X");
                ((Button) e.getSource()).removeActionListener(this);
            } else {
                ((Button) e.getSource()).setLabel("O");
                ((Button) e.getSource()).removeActionListener(this);
            }
            buttonCounter++;
            CheckField();
        }

    }

    private void CheckField() {

        if (ButtonsWithIdenticalLabels(0, 1, 2)) {

            Deactivatebuttons();
        }
        if (ButtonsWithIdenticalLabels(3, 4, 5)) {

            Deactivatebuttons();
        }
        if (ButtonsWithIdenticalLabels(6, 7, 8)) {

            Deactivatebuttons();
        }
        if (ButtonsWithIdenticalLabels(0, 3, 6)) {

            Deactivatebuttons();
        }
        if (ButtonsWithIdenticalLabels(1, 4, 7)) {

            Deactivatebuttons();
        }
        if (ButtonsWithIdenticalLabels(2, 5, 8)) {

            Deactivatebuttons();
        }
        if (ButtonsWithIdenticalLabels(0, 4, 8)) {

            Deactivatebuttons();
        }
        if (ButtonsWithIdenticalLabels(2, 4, 6)) {

            Deactivatebuttons();
        }
    }

    private boolean ButtonsWithIdenticalLabels(int i, int j, int k) {
        if (btnPlayButton[i].getLabel() == btnPlayButton[j].getLabel()
                && btnPlayButton[j].getLabel() == btnPlayButton[k].getLabel()) {
    
            btnPlayButton[i].setBackground(Color.red);
            btnPlayButton[j].setBackground(Color.red);
            btnPlayButton[k].setBackground(Color.red);

            if (btnPlayButton[i].getLabel().equals("X")) {
                xScore++;
                Xlabel.setText("X: " + xScore);
            } else {
                oScore++;
                Olabel.setText("O: " + oScore);
            }

            return true;
        } else {
            return false;
        }
    }

    private void Deactivatebuttons() {
        for (int i = 0; i < 9; i++) {
            btnPlayButton[i].removeActionListener(this);
        }
    }
}

现在让我解释一下这个程序是如何工作的。 3x3 运动场由 ButtonArray btnPlayButton 组成。这些按钮 与它们的标签 进行了比较,因此为了在游戏开始时没有匹配的标签,按钮在创建时就被标记为 1 到 9。这里:

for (int i = 0; i < 9; i++) {
        btnPlayButton[i] = new Button("" + i); // Right here
        btnPlayButton[i].setBackground(Color.white);
        btnPlayButton[i].setForeground(Color.white);
        btnPlayButton[i].addActionListener(this);
        this.add(btnPlayButton[i]);
    }

每当您单击 btnPlayButton 时,程序都会跳转到 actionPerformed 方法。由于 btnPlayButtons 没有 ActionCommand ,它直接跳转到方法的 else 部分。在这里,int buttonCounter 增加了 1。无论 buttonCounter 是偶数还是奇数,被点击的 btnPlayButton 都会重新标记为“X”或“O”。由于 buttonCounter 每次点击都会获得 +1,因此 X 和 Os 交替出现。

这里说的部分:

else {

    ((Button) e.getSource()).setFont(new Font("Arial", Font.BOLD, 48));
    ((Button) e.getSource()).setForeground(Color.black);
    System.out.println(buttonCounter);
    if (buttonCounter % 2 == 0) {
        ((Button) e.getSource()).setLabel("X");
        ((Button) e.getSource()).removeActionListener(this);
    } else {
        ((Button) e.getSource()).setLabel("O");
        ((Button) e.getSource()).removeActionListener(this);
    }
    buttonCounter++;
    CheckField();
}

被点击的Button的ActionListener被移除,防止作弊。每次按下按钮,都会检查比赛场地以寻找获胜组合。这发生在 CheckField().

CheckField() 中,或者更准确地说,ButtonsWithIdenticalLabels(x, y, z) btnPlayButtons[x]btnPlayButtons[y]btnPlayButtons[z] 的标签被获取并比较,如果它们完全相同 returns 正确。

因为 btnPlayButton 是这样排列的:

0 1 2
3 4 5
6 7 8

中奖组合为:012,345,678,036,147,258,045 和 246

所以,例如,当 btnPlayButton[0]btnPlayButton[1]btnPlayButton[2] 都具有相同的标签时。 ButtonsWithIdenticalLabels 为真,程序跳转到 Deactivatebuttons(),其中所有 btnPlayButton 都被禁用,这意味着找到了获胜组合并且游戏结束了。如果 btnPlayButton[1] 的标签是“X”,那么 int xScore 会被加 1。 btnPlayButton[0]btnPlayButton[1]btnPlayButton[2] 也为了美观而涂成红色。

使用“重新启动”按钮,您将跳入一个 for 循环,再次重新标记 btnPlayButton 并将它们添加到 TTTFrame 中实现的 ActionListenerbuttonCounter 也被重置为 0。重新标记与class开头的相同:

if (e.getActionCommand().equals("Restart")) {
            System.out.println("Restarted");
            for (int i = 0; i < 9; i++) {

                btnPlayButton[i].setLabel("" + i);
                btnPlayButton[i].setForeground(Color.white);
                btnPlayButton[i].setBackground(Color.white);
                btnPlayButton[i].addActionListener(this);

                this.buttonCounter = 0;
            }

现在的问题是,在几次重新启动后,X 和 O 的标签不再交替出现。有时连续有 3 个 Os,有时甚至像这样的字段也被认为是胜利

Picture

如果有人知道如何修复这个错误,我会很高兴。

提前致谢,

菲赫迪

这里的问题是:当您重新启动游戏时,每个按钮都会添加一个新的ActionListener。但是,只有当您单击它或有人赢得游戏时,它才会被删除。这意味着当你在任何人获胜之前重新开始游戏时,每个未点击的按钮都会获得第二个 ActionListener,因此点击将被注册两次并出现此错误。在重置电路板之前尝试调用 DeactivateButtons()