我编写了一个黑白棋程序,使用一个由按钮组成的二维数组填充GUI。程序中包含多个方法,其中包括validMoves()方法,该方法会将有效移动的按钮设置为setEnabled(true),并显示一个红点,以便用户知道哪些按钮是活跃的。当你选择要移动的棋子时,它还会翻转正确的棋子。我遇到的问题是GUI不会等待玩家进行移动。我编写了一个AI,它基本上与玩家完全相同,它会找到有效的移动,找出哪个移动能翻转最多的棋子,并以”x*y”的格式返回,我的代码会将这个字符串分割成x和y,然后进行移动并翻转棋子。我尝试了thread.sleep()、wait()和while (!hasMoved)以及其他许多方法,但它就是不愿意合作。使用wait()时,我遇到了无限循环,因为GUI存在但里面什么都没有。然而,当我注释掉wait()时,它显示得完全正确。
package reversi;/* * 需要检查边缘的bug // 它会超出边界 * 刷新分数不工作 * * 在某人移动后添加清除所有准备状态 */import java.util.logging.Level;import java.util.logging.Logger;import javax.swing.JApplet;import java.awt.*;import java.awt.event.*;import javax.swing.*;import java.util.Timer;/** * @author 13ponchera * 黑白棋游戏的棋盘 */public class ReversiBoard extends JApplet{ private int score1; private int score2; private int turn; private Icon p1 = new ImageIcon("images/p1.png"); private Icon p2 = new ImageIcon("images/p2.png"); private Icon bGround = new ImageIcon("images/bGround.png"); private Icon ready = new ImageIcon("images/ready.png"); // 帮助动作监听器确定轮到哪个玩家 private int x; // 按钮的数量 private JButton[][] buttons; private JPanel settingPanel = new JPanel(); private JButton newGame = new JButton("新游戏?"); private JLabel score1A; private JLabel score2A; private JPanel gameBoard = new JPanel(); private AI ai = new AI(bGround,p1,p2); private boolean hasMoved; private Timer t; /** * 初始化方法 * 在小程序加载到浏览器后调用 * 设置所有内容 */ public void init() { score1 = 0; score2 = 0; // 设置查看器显示分数 score1A = new JLabel("玩家: " + score1); score2A = new JLabel("电脑:" + score2); // 将组件添加到设置面板 settingPanel.add(newGame); newGame.addActionListener(new NewGameListener()); settingPanel.add(score1A);//初始化为0 settingPanel.add(score2A); // 创建任意大小的游戏棋盘,只要是偶数 String a = JOptionPane.showInputDialog (null, "请输入一个偶数作为x"); x = Integer.parseInt(a) + 2; if ((x % 2 == 0) && (x > 4)) { gameBoard = new JPanel(new GridLayout(x, x, 4, 4)); buttons = new JButton[x][x]; }// 在x上加2,然后将边缘设置为新图片 else { // 请求新的数字,直到输入的数字是偶数 while ((x % 2 != 0) && (x < 4)) { String responce = JOptionPane.showInputDialog (null, "必须是偶数的行和列 \n\t请输入x"); x = Integer.parseInt(responce) + 2; } gameBoard = new JPanel(new GridLayout(x, x, 4, 4)); buttons = new JButton[x][x]; } // 为每个位置创建按钮并将它们放入二维数组 for (int c = 0; c < x; c++) { for (int d = 0; d < x; d++) { buttons[c][d] = new JButton(); buttons[c][d].setIcon(bGround); // 添加默认背景图片 buttons[c][d].addActionListener (new ButtonListener(buttons[c][d],c,d)); // 添加通用按钮监听器 buttons[c][d].setEnabled(false); // 按钮不可点击,除非是有效移动 gameBoard.add(buttons[c][d]); buttons[c][d].setIcon(bGround); buttons[c][d].setDisabledIcon(bGround); // 将按钮添加到JPanel } } // 使边缘颜色不同 // 将包含所有按钮的JPanel添加到内容窗格 buttons[x/2][x/2] .setIcon(p1); buttons[x/2][x/2] .setDisabledIcon(p1); buttons[(x/2) - 1][(x/2) - 1] .setIcon(p1); buttons[(x/2) - 1][(x/2) - 1] .setDisabledIcon(p1); refreshTable(); // 使中间上左和下右的棋子为白色 buttons[(x/2) - 1][(x/2)] .setIcon(p2); buttons[x/2][(x/2) - 1] .setIcon(p2); buttons[(x/2) - 1][(x/2)] .setDisabledIcon(p2); buttons[x/2][(x/2) - 1] .setDisabledIcon(p2); refreshTable(); // 使中间上右和下左的棋子为黑色 getContentPane() .add(gameBoard, BorderLayout.CENTER); getContentPane();//刷新小程序 // 在敲击键盘两天后 getContentPane() .add(settingPanel, BorderLayout.SOUTH); // 将设置面板(新游戏按钮和分数)添加到内容窗格 refreshTable(); t = new Timer(); hasMoved = false; validMoves(); refreshTable(); synchronized(t) { while(!hasMoved) { try { t.wait(50); } catch (InterruptedException ex) { Logger.getLogger(ReversiBoard.class.getName()) .log(Level.SEVERE, null, ex); } } } refreshTable(); aiTurn(); // 使边缘图片不同 } /** * 刷新表格以显示图片 */ public void refreshTable() { for (int a = 0; a < x; a++) { for (int b = 0; b < x; b++) { buttons[a][b].repaint(); } } setScores(); score1A.setText ("玩家: " + score1); score2A.setText ("电脑: " + score2); gameBoard.repaint(); settingPanel.repaint(); } /** * 查找所有有效移动并设置按钮启用状态和可识别的图片 */ public void validMoves() { int c = 0; int d = 0; // 临时值,以便搜索可以不被打断 for(int a = 1; a < x - 1; a++) { for(int b = 1; b < x - 1; b++) { // 查看每个棋子 if (buttons[a][b].getIcon() ==bGround) { // 如果是背景棋子,看看是否有敌方棋子相邻 if (buttons[a + 1][b].getIcon() == p2) { // 继续检查那个方向 // 直到到达棋盘末端或碰到友方棋子 // c 是找到的位置的坐标 c = a + 1; while ((buttons[c][b].getIcon() == p2) && (c < x) && (c > 0)) { if (buttons[c + 1][b].getIcon() == p1) { buttons[a][b].setEnabled(true); buttons[a][b] .setIcon(ready); //JOptionPane.showMessageDialog(null,""); } c++; } } // 查看是否有敌方棋子相邻 if (buttons[a - 1][b].getIcon() == p2) { // 继续检查那个方向 // 直到到达棋盘末端或碰到友方棋子 // c 是找到的位置的坐标 c = a - 1; while ((buttons[c][b].getIcon() == p2) && (c < x) && (c > 0)) { if (buttons[c - 1][b].getIcon() == p1) { buttons[a][b].setEnabled(true); buttons[a][b] .setIcon(ready); //JOptionPane.showMessageDialog(null,""); } c--; } } if (buttons[a][b + 1].getIcon() == p2) { // 继续检查那个方向 // 直到到达棋盘末端或碰到友方棋子 // c 是找到的位置的坐标 c = b + 1; while ((buttons[a][c].getIcon() == p2) && (c < x) && (c > 0)) { if (buttons[a][c + 1].getIcon() == p1) { buttons[a][b].setEnabled(true); buttons[a][b] .setIcon(ready); //JOptionPane.showMessageDialog(null,""); } c++; } } if (buttons[a][b - 1].getIcon() == p2) { // 继续检查那个方向 // 直到到达棋盘末端或碰到友方棋子 // c 是找到的位置的坐标 c = b - 1; while ((buttons[a][c].getIcon() == p2) && (c < x) && (c > 0)) { if (buttons[a][c - 1].getIcon() == p1) { buttons[a][b].setEnabled(true); buttons[a][b] .setIcon(ready); //JOptionPane.showMessageDialog(null,""); } c--; } } // 开始检查对角线 if (buttons[a + 1][b + 1].getIcon() == p2) { // 继续检查那个方向 // 直到到达棋盘末端或碰到友方棋子 // c 是找到的位置的坐标 c = a + 1; d = b + 1; while ((buttons[c][d].getIcon() == p2) && (c < x) && (c > 0) && (d < x) && (d > 0)) { if (buttons[c + 1][d + 1].getIcon() == p1) { buttons[a][b].setEnabled(true); buttons[a][b] .setIcon(ready); // JOptionPane.showMessageDialog(null,""); } c++; d++; } } if (buttons[a - 1][b - 1].getIcon() == p2) { // 继续检查那个方向 // 直到到达棋盘末端或碰到友方棋子 // c 是找到的位置的坐标 c = a - 1; d = b - 1; while ((buttons[c][d].getIcon() == p2) && (c < x) && (c > 0) && (d < x) && (d > 0)) { if (buttons[c - 1][d -1].getIcon() == p1) { buttons[a][b].setEnabled(true); buttons[a][b] .setIcon(ready); //JOptionPane.showMessageDialog(null,""); } c--; d--; } } if (buttons[a - 1][b + 1].getIcon() == p2) { // 继续检查那个方向 // 直到到达棋盘末端或碰到友方棋子 // c 是找到的位置的坐标 c = a - 1; d = b + 1; while ((buttons[c][d].getIcon() == p2) && (c < x) && (c > 0) && (d < x) && (d > 0)) { if (buttons[c - 1][d + 1].getIcon() == p1) { buttons[a][b].setEnabled(true); buttons[a][b] .setIcon(ready); // JOptionPane.showMessageDialog(null,""); } c--; d++; } } if (buttons[a + 1][b - 1].getIcon() == p2) { // 继续检查那个方向 // 直到到达棋盘末端或碰到友方棋子 // c 是找到的位置的坐标 c = a + 1; d = b - 1; while ((buttons[c][d].getIcon() == p2) && (c < x) && (c > 0) && (d < x) && (d > 0)) { if (buttons[c + 1][d - 1].getIcon() == p1) { buttons[a][b].setEnabled(true); buttons[a][b] .setIcon(ready); // JOptionPane.showMessageDialog(null,""); } c++; d--; } } } } } refreshTable(); /*try { Thread.sleep(50); // 30秒 NA } catch (InterruptedException ex) { Logger.getLogger(ReversiBoard.class.getName()) .log(Level.SEVERE, null, ex); }*/ } /** * 计算每个玩家的分数 */ /** * * @return boolean 如果游戏结束 */ public boolean winner() { refreshTable(); if (!anyMove(1) && !anyMove(0)) return true; //joptionpan 显示赢家(如果score1 > score 2).... 和相反情况 return false; } /** * @param player 1 为玩家1,0 为电脑 * @return boolean 如果有移动 */ public void clearReady() { for (int a = 0; a < x; a++) { for (int b = 0; b < x; b++) { if (buttons[a][b].getIcon() == ready) { buttons[a][b].setIcon(bGround); buttons[a][b].setEnabled(false); buttons[a][b].setDisabledIcon(bGround); } } } } private void makeMove(String move) { int temp = 0; char[] f = move.toCharArray(); for(int i = 0; i < f.length; i++) { if (f[i] == ('*')) temp = i; } int x = Integer.parseInt(move.substring(0,temp)); int y = Integer.parseInt(move.substring(temp + 1)); buttons[x][y].setIcon(p2); buttons[x][y].setEnabled(false); buttons[x][y].setDisabledIcon(p2); flip(1,x,y); } private void aiTurn() { String move = ai.makeMove(buttons); // 字符串move是一个用*分隔的坐标字符串 makeMove(move); turn++; } class ButtonListener implements ActionListener { private JButton a; private int x; private int y; private ButtonListener(JButton jButton, int c, int d) { a = jButton; x = c; y = d; } public void actionPerformed(ActionEvent e) { // 必须区分p1和p2 switch (turn % 2) { case 0: a.setIcon(p1); a.setEnabled(false); a.setDisabledIcon(p1); flip(0,x,y); turn++; clearReady(); hasMoved = true; /*synchronized(t) { t.notifyAll(); }*/ refreshTable(); break; case 1: a.setIcon(p2); a.setEnabled(false); a.setDisabledIcon(p2); flip(1,x,y); turn++; clearReady(); hasMoved = true; /*synchronized(t) { t.notifyAll(); }*/ refreshTable(); break; } } }}
回答:
请看下面的GUI应用程序应该如何表现(或多或少):
- 创建小部件
- 向小部件注册监听器
- 等待用户操作
- 更新GUI以显示用户的移动
- 调用AI进行移动并显示
- 返回到第3点
你已经在init
方法中完成了第1和第2点。你应该删除该方法的最后一部分(包含while(!hasMoved)
循环的块):这是第3点,由init
完成后的小程序处理。你不需要做任何事情。
当用户点击一个JButton(如果按钮是启用的),ButtonListener
的actionPerformed
方法会被调用:这是第4点的开始,你应该更新你的小部件(你的代码中的refreshTable()
),并让AI移动(aiTurn()
,第5点)。当actionPerformed
方法完成后,JApplet会自动返回到第3点,你不需要做任何事情。
我对Applets不太了解,所以我不能更详细。你应该搜索与你的行为相似的JApplet示例/教程。记住,你的代码中不应该有等待用户输入的无限循环:它由JApplet处理。