2

我有一个应用程序,在该应用程序中,让鼠标单击事件模拟用户在标准输入中键入某个字符串会非常方便。所以我想知道,有没有办法将代码写入标准?我意识到这有点像黑客,但它对于我需要做的事情非常有效。

编辑(响应气垫船的 EDIT2):

谢谢。我了解该代码如何阻止人类玩家在不应该移动的情况下移动棋子。我不明白你是如何设想主游戏循环的。你介意写代码来告诉我你在想什么吗?您是否只是在想象一个忙碌的等待循环,它不断生成新的计算机动作并调用您的Move()方法(大部分时间在等待人类玩家移动时被拒绝)?像这样的东西?

public void gameLoop()
{
   while (gameNotOver)
   {
      Move compMove = generateComputersNextMove();
      move(blackPlayer, compMove); // where the blackPlayer is the computerPlayer 
   }
}
4

3 回答 3

4

你说:

基本上,我希望我的代码在某个点阻塞并等待用户单击我的 JFrame 中的特定 JLabel。用户单击 JLabel 后,我希望在获取所单击的 JLabel 的 id 后恢复代码。我不知道我会怎么做。我之前的方式是在代码中的那个点,用户只需输入一个 id (和 Scanner 的 next() 方法块)。

您不想阻止您的程序(不是在事件驱动程序中),而是更改您的 GUI 的状态,并将您对用户输入的响应基于此状态。您尝试的解决方案是一种线性思维方式,不适用于 Swing 或其他事件驱动的 GUI。

例如,作为一个简单的例子,考虑给你的程序一个布尔变量,

private boolean labelClicked = false;

单击 JLabel 后,在 JLabel 的 MouseListener 中将其更改为 true。然后仅在布尔值为真时才响应其他用户输入。

或者另一种可能的解决方案:如果您想在按下 JLabel 后激活 JButton,则禁用 JButton,并从 JLabel 的 MouseListener 中调用 JButton 上的 setEnabled(true)。

关键是要思考事件驱动编程
在这个论坛中问这样的问题时,另一个关键不是问我们一个特定于代码的问题,而是一个特定于行为的问题。这里真正的问题不是如何写入标准,而是如何让你的程序暂停并等待用户输入——这是一个很大的区别。

最后,如果您需要更具体和更好的建议,请向我们填写您的问题的详细信息,如果其中有任何令人困惑的地方,请务必提出问题。祝你好运!

编辑 1
您可能会有几个类,包括管理此应用程序的 GUI 和非 GUI 类,非 GUI 类可以包括:

  • 播放器类:可以是人或计算机
  • 棋盘类:棋盘的逻辑(非gui)表示
  • Piece 类:所有具体 Pieces 派生自的抽象类
  • ChessAI 课:这是你的 AI 课,它为计算机计算出下一个最佳棋步应该是什么。
  • 游戏类:也许是本次讨论中最重要的类,因为它将控制游戏流程并决定轮到谁移动。

Game 类将包含一个名为 Player 的字段turn,该字段将包含对轮到谁的引用。假设你的 Game 类包含两个 Player 变量,humanPlayer 和 computerPlayer。turn 变量将持有这些对象中的一个或另一个,而 Player 的响应方式将取决于 turn 持有的对象。

Game 类将有一个循环,一个游戏循环,它在告诉humanPlayer 对象和computerPlayer 对象轮到他们移动之间交替。

所以说轮到计算机了,计算下一个最佳动作需要一段时间。如果同时人类玩家试图在 GUI 上拖动棋子,GUI 将通知 Game 类人类试图移动,Game 类将检查是否轮到人类(通过检查if (turn == humanPlayer))以及是否轮到人类, 游戏会通知 GUI,GUI 会将棋子设置回原来的位置,并且可能会弹出警告消息。

在计算机请求 Game 移动其棋子后,Game 将告诉 GUI 移动计算机的棋子,然后 Game 将设置 turn = humanPlayer,现在允许人类玩家移动。在humanPlayer 尝试移动后,GUI 将告诉 Game 人类尝试移动。游戏将检查它是否有效,如果是,将告诉 computerPlayer 它现在应该采取行动。

请注意,当我说“告诉”时,我的意思是它将调用该对象的公共方法。

编辑2
我的意思是这样的:

public class Game {
   private Player whitePlayer;
   private Player blackPlayer;
   private Player turn;
   private Board board = new Board(); // non-GUI logical board
   private Gui gui; // the Swing GUI that displays all

   public Game(String humanPlayerName, boolean humanWhite) {
      if (humanWhite) {
         whitePlayer = new HumanPlayer(humanPlayerName, this);
         blackPlayer = new ComputerPlayer("Computer", this);
      } else {
         whitePlayer = new ComputerPlayer("Computer", this);
         blackPlayer = new HumanPlayer(humanPlayerName, this);
      }
   }

   public void start() {
      whitePlayer.setMyTurn(true); // tell white player to move
   }

   public void move(Player playerMakingMove, Move move) {

      // only respond if the right player is making the move
      if (turn == playerMakingMove) {
         // check if its a valid move
         // if so, tell GUI to make move
         // check if game over

         turn.setMyTurn(false); // current player's turn is over
         turn = (turn == blackPlayer) ? whitePlayer : blackPlayer; // swap players
         turn.setMyTurn(true); // tell other player, it's his turn

         turn.makeMove(); // *** added
      } else {
         // send message that it's not their turn to move
      }
   }
}

因此,如果人类玩家尝试移动 GUI 将为人类玩家移动(...),但 Game 对象不会响应,除非实际上轮到人类了。

添加到 Player 的Edit 3
makeMove()方法 请注意,Game 的move(...)方法可能是所有需要的游戏循环,特别是如果 ComputerPlayer 的makeMove(...)方法告诉 AI 引擎创建下一个最佳移动,然后再次调用 Game 的 move(...) 方法:

abstract class Player {
   private String name;
   private boolean myTurn = false;
   protected Game game;

   public Player(String name, Game game) {
      this.name = name;
      this.game = game;
   }

   public abstract void makeMove();

   public boolean isMyTurn() {
      return myTurn;
   }

   public void setMyTurn(boolean myTurn) {
      this.myTurn = myTurn;
   }

   public String getName() {
      return name;
   }

}

class HumanPlayer extends Player {

   public HumanPlayer(String name, Game game) {
      super(name, game);
   }

   @Override
   public void makeMove() {
      // TODO: ask GUI to inform player that it's his turn to move and accept the move

   }


}

class ComputerPlayer extends Player {
   private ChessAi chessAi = new ChessAi();

   public ComputerPlayer(String name, Game game) {
      super(name, game);
   }

   @Override
   public void makeMove() {
      game.move(this, chessAi.calcBestMove());
   }

}

两个玩家game.move(...)都知道移动后会打电话,这使得游戏提示下一个玩家移动......直到游戏结束。

请注意,这不是一个正在运行的程序,也不是故意的,而是更多的模型来说明可能的游戏逻辑。我没有做过这样的事情,并且可能有更好和更清洁的方法来做到这一点,但我只想了解在事件驱动的 GUI 程序中实现基于回合的逻辑的方法。

编辑 4
编译并运行以下简单的事件驱动 GUI:

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

public class EventDrivenGui extends JPanel {
   private static final Color GO_COLOR = Color.green;
   private static final Color STOP_COLOR = Color.red;
   private static final int SIDE = 300;
   private static final int TIMER_DELAY = 4 * 1000; // change light every 4 seconds
   private boolean go = false;
   private JButton button = new JButton("Button");


   public EventDrivenGui() {
      add(button);
      button.addActionListener(new ButtonListener());
      new Timer(TIMER_DELAY, new TimerListener()).start();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D) g;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

      Color color = go ? GO_COLOR : STOP_COLOR;
      g2.setColor(color);
      g2.fillOval(0, SIDE / 3, SIDE, SIDE);
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(SIDE, (4 * SIDE) / 3);
   }

   private class ButtonListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent arg0) {
         if (go) {
            JOptionPane.showMessageDialog(EventDrivenGui.this, "Button is Active");
         }
      }
   }

   private class TimerListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         // toggle go. if true, now false, and visa versa
         go = !go;
         repaint(); // redraw oval
      }
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            EventDrivenGui mainPanel = new EventDrivenGui();

            JFrame frame = new JFrame("EventDrivenGui");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
         }
      });
   }

}

您会看到在 JPanel 中有一个 JButton。作为用户,您可以随时按下按钮,但它只会在灯为绿色时响应(当 go 布尔变量为 true 时)。这是一个示例,其中按钮的行为(是否响应按下)取决于类的状态,无论 go 是否为真。一旦设置了 GUI——构造函数被调用,它被放置在一个 JFrame 中,并被显示出来,没有代码阻塞或类似的东西。有一个 Swing Timer,它唯一的工作就是切换 go 的值并重新绘制 JFrame,但仅此而已。

于 2012-05-19T03:10:01.030 回答
1

看看 JDialog http://docs.oracle.com/javase/6/docs/api/javax/swing/JDialog.html。它可以在 Swing 应用程序中阻塞,而不会造成不必要的麻烦。

于 2012-05-19T03:33:44.107 回答
0

你必须重新考虑你的解决方案。考虑标准已经连接到您的键盘,因此已经有一些东西写入标准输入。抱歉,标准客栈没有空间。

于 2012-05-19T03:11:11.037 回答