Java TicTacToe程序在未获胜的组合中触发



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

当我使用同样的方案时,我遇到了一些我自己无法解决的问题,所以请帮帮我。

该程序由两个类组成,主类TTTMain:

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游戏场地由按钮阵列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部分。这里,intbuttonCounter的值大于1。无论buttonCounter是偶数还是奇数,被点击的btnPlayButton都会被重新标记为"0";X〃;或";O〃;。由于buttonCounter每次点击都会得到+1,因此X和O是交替的。

这是所说的部分:

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();
}

点击按钮的ActionListener被移除以防止作弊。每次按下按钮,比赛场地都会被检查是否有获胜的组合。这种情况发生在CheckField()中。

CheckField()中,或者更准确地说,在ButtonsWithIdenticalLabels(x, y, z)中,取btnPlayButtons[x]btnPlayButtons[y]btnPlayButtons[z]的标签并进行比较,如果它们相同,则返回true。

由于btnPlayButton的顺序如下:

0 1 2
3 4 5
6 7 8

获胜组合为:012345678036147258045和246

因此,例如,当btnPlayButton[0]btnPlayButton[1]btnPlayButton[2]都具有相同的标签时。ButtonsWithIdenticalLabels为真,程序跳到Deactivatebuttons(),在那里所有的btnPlayButton都被禁用,这意味着找到了获胜的组合,游戏结束。如果CCD_ 24的标签是"0";X〃;int xScore加1,btnPlayButton[0]btnPlayButton[1]btnPlayButton[2]也被涂成红色。

使用Restart按钮,您可以跳转到一个for循环,该循环再次重新标记btnPlayButton,并将其添加到TTTFrame中实现的ActionListenerbuttonCounter也被重新设置为0。重新标记与课程开头的相同:

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个O,有时甚至像这样的字段也被认为是获胜的

图片

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

提前感谢

Fihdi

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