事件驱动输入与回合制
Event driven input vs turn based
我正在尝试使用 JTextArea 作为 console/output 并使用 JTextField 作为用户输入来重新创建控制台游戏。由于 GUI 是事件驱动的,我不明白如何停止执行代码,等待用户输入,然后再继续对手回合。我能想到的唯一解决方案是 While(userTurn) 并且 userTurn 将在 actionlistener 上更改为 false 是否有更好的方法?
我的控制台解决方案
String getInput(String prompt){
String inputLine = null;
console.setTextConsole(prompt + " ");
try{
BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
inputLine = is.readLine();
if(inputLine.length() == 0) return null;
}catch(IOException e){
console.setTextConsole("IOException "+e);
}
return inputLine;
}
我刚刚调用了这个 getInput 方法,然后开始了对手回合。
我想要完成的是:
- 对手轮到
- 游戏等待用户
- 用户在 JtextField 中键入文本并按下回车键
- 游戏执行玩家命令
- 对手再次转向..
嗯,我认为是这样的:
游戏的统治者是谁拥有回合。只要统治者采取行动,其他人就必须等待。如何实现?
如果用户拥有回合,he/she 可以在 JTextField 中输入文本。
当 he/she 按下 ENTER 时,命令必须被验证。如果OK,则必须转给程序,同时,用户甚至不能在JTextField中输入文本。例如禁用它:
private void switchTurnToTheProgram()
{
jTextField.setEnabled(false);
}
- 当程序完成移动时,必须再次将回合转移给用户,因此必须启用 jTextField:
private void switchTurnToTheUser()
{
jTextField.setEnabled(true);
}
- 最后,您必须在每种情况下确定谁的第一个回合(以使 jTextField 显示为启用或禁用)。
完整算法:
public void startGame(boolean userOwnsTheFirstTurn)
{
if (userOwnsTheFirstTurn)
{
switchTurnToTheUser();
}
else
{
switchTurnToTheProgram();
calculateNextMove();
switchTurnToTheUser();
}
}
public void userHasEnteredSomeCommand(String command)
{
// This must be called from the correspondant actionListener.
if (validateCommand())
{
switchTurnToTheProgram();
calculateNextMove();
switchTurnToTheUser();
}
else
{
... log an error to the user ...
}
}
为了增强用户体验,enable/disable 将按钮与 textField 一起使用可能会有用。在这种情况下,您只需修改 switchTurnToTheProgram
和 switchTurnToTheUser
.
这两个方法
我写了一个示例游戏,您可以观察其中的区别。计算机和用户尝试猜测 0-2 之间的随机数(含 0 和 2)。谁做对了谁就赢了。如果双方都做对或都做错,则平局。
编辑:更新的 GUI 版本
控制台程序如下:
import java.util.Random;
import java.util.Scanner;
public class ConsoleGame {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
Random rand = new Random();
boolean playAgain = false;
int wins = 0, losses = 0, draw = 0;
do {
int num = rand.nextInt(3); // 0-2 inclusive
System.out.println("Guess the number [0-2]: ");
int guess = Integer.parseInt(console.nextLine());
int computerGuess = rand.nextInt(3);
System.out.println("You: " + guess + "\tComputer: " + computerGuess + "\tNumber: " + num);
if (guess == num && computerGuess == num || guess != num && computerGuess != num) {
draw++;
System.out.println("Draw!");
} else if (guess == num) {
wins++;
System.out.println("You win!");
} else if (computerGuess == num) {
losses++;
System.out.println("Computer wins :(");
}
System.out.println("Play again [y/n]? ");
playAgain = console.nextLine().startsWith("y");
} while (playAgain);
System.out.println("Wins: " + wins + "\nLosses: " + losses + "\nDraw: " + draw);
console.close();
}
}
这是 GUI 程序:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class GUIGame extends JFrame {
private JPanel contentPane;
private JTextField textField;
private JTextArea textArea;
private boolean textReceived;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
GUIGame frame = new GUIGame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public GUIGame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
setContentPane(contentPane);
textField = new JTextField();
textField.addActionListener(new ActionListener() {
@Override
// user pressed 'enter' key,
public void actionPerformed(ActionEvent e) {
textReceived = true;
synchronized (textField) {
// notify game loop thread which is waiting on this event
textField.notifyAll();
}
}
});
contentPane.add(textField, BorderLayout.SOUTH);
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
textArea = new JTextArea();
textArea.setFont(new Font("Consolas", Font.PLAIN, 12));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setForeground(Color.LIGHT_GRAY);
textArea.setBackground(Color.BLACK);
textArea.setEditable(false);
scrollPane.setViewportView(textArea);
// Start game loop in new thread since we block the thread when
// waiting for input and we don't want to block the UI thread
new Thread(new Runnable() {
@Override
public void run() {
playGame();
}
}).start();
}
private void playGame() {
Random rand = new Random();
boolean playAgain = false;
int wins = 0, losses = 0, draw = 0;
do {
int num = rand.nextInt(3); // 0-2 inclusive
textArea.append("Guess the number [0-2]: \n");
int guess = Integer.parseInt(requestInput());
int computerGuess = rand.nextInt(3);
textArea.append("You: " + guess + "\tComputer: " + computerGuess + "\tNumber: " + num + "\n");
if (guess == num && computerGuess == num || guess != num && computerGuess != num) {
draw++;
textArea.append("Draw!\n");
} else if (guess == num) {
wins++;
textArea.append("You win!\n");
} else if (computerGuess == num) {
losses++;
textArea.append("Computer wins :(\n");
}
textArea.append("Play again [y/n]? \n");
playAgain = requestInput().startsWith("y");
} while (playAgain);
textArea.append("Wins: " + wins + "\nLosses: " + losses + "\nDraw: " + draw + "\n");
}
private String requestInput() {
textField.setEnabled(true);
textField.requestFocus();
// wait on text field till UI thread signals a user input event
synchronized (textField) {
while (!textReceived) {
try {
textField.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
String input = textField.getText();
textField.setText("");
textField.setEnabled(false);
textReceived = false;
return input;
}
}
我正在尝试使用 JTextArea 作为 console/output 并使用 JTextField 作为用户输入来重新创建控制台游戏。由于 GUI 是事件驱动的,我不明白如何停止执行代码,等待用户输入,然后再继续对手回合。我能想到的唯一解决方案是 While(userTurn) 并且 userTurn 将在 actionlistener 上更改为 false 是否有更好的方法?
我的控制台解决方案
String getInput(String prompt){
String inputLine = null;
console.setTextConsole(prompt + " ");
try{
BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
inputLine = is.readLine();
if(inputLine.length() == 0) return null;
}catch(IOException e){
console.setTextConsole("IOException "+e);
}
return inputLine;
}
我刚刚调用了这个 getInput 方法,然后开始了对手回合。
我想要完成的是:
- 对手轮到
- 游戏等待用户
- 用户在 JtextField 中键入文本并按下回车键
- 游戏执行玩家命令
- 对手再次转向..
嗯,我认为是这样的:
游戏的统治者是谁拥有回合。只要统治者采取行动,其他人就必须等待。如何实现?
如果用户拥有回合,he/she 可以在 JTextField 中输入文本。
当 he/she 按下 ENTER 时,命令必须被验证。如果OK,则必须转给程序,同时,用户甚至不能在JTextField中输入文本。例如禁用它:
private void switchTurnToTheProgram() { jTextField.setEnabled(false); }
- 当程序完成移动时,必须再次将回合转移给用户,因此必须启用 jTextField:
private void switchTurnToTheUser() { jTextField.setEnabled(true); }
- 最后,您必须在每种情况下确定谁的第一个回合(以使 jTextField 显示为启用或禁用)。
完整算法:
public void startGame(boolean userOwnsTheFirstTurn)
{
if (userOwnsTheFirstTurn)
{
switchTurnToTheUser();
}
else
{
switchTurnToTheProgram();
calculateNextMove();
switchTurnToTheUser();
}
}
public void userHasEnteredSomeCommand(String command)
{
// This must be called from the correspondant actionListener.
if (validateCommand())
{
switchTurnToTheProgram();
calculateNextMove();
switchTurnToTheUser();
}
else
{
... log an error to the user ...
}
}
为了增强用户体验,enable/disable 将按钮与 textField 一起使用可能会有用。在这种情况下,您只需修改 switchTurnToTheProgram
和 switchTurnToTheUser
.
我写了一个示例游戏,您可以观察其中的区别。计算机和用户尝试猜测 0-2 之间的随机数(含 0 和 2)。谁做对了谁就赢了。如果双方都做对或都做错,则平局。
编辑:更新的 GUI 版本
控制台程序如下:
import java.util.Random;
import java.util.Scanner;
public class ConsoleGame {
public static void main(String[] args) {
Scanner console = new Scanner(System.in);
Random rand = new Random();
boolean playAgain = false;
int wins = 0, losses = 0, draw = 0;
do {
int num = rand.nextInt(3); // 0-2 inclusive
System.out.println("Guess the number [0-2]: ");
int guess = Integer.parseInt(console.nextLine());
int computerGuess = rand.nextInt(3);
System.out.println("You: " + guess + "\tComputer: " + computerGuess + "\tNumber: " + num);
if (guess == num && computerGuess == num || guess != num && computerGuess != num) {
draw++;
System.out.println("Draw!");
} else if (guess == num) {
wins++;
System.out.println("You win!");
} else if (computerGuess == num) {
losses++;
System.out.println("Computer wins :(");
}
System.out.println("Play again [y/n]? ");
playAgain = console.nextLine().startsWith("y");
} while (playAgain);
System.out.println("Wins: " + wins + "\nLosses: " + losses + "\nDraw: " + draw);
console.close();
}
}
这是 GUI 程序:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class GUIGame extends JFrame {
private JPanel contentPane;
private JTextField textField;
private JTextArea textArea;
private boolean textReceived;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
GUIGame frame = new GUIGame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public GUIGame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setLayout(new BorderLayout());
setContentPane(contentPane);
textField = new JTextField();
textField.addActionListener(new ActionListener() {
@Override
// user pressed 'enter' key,
public void actionPerformed(ActionEvent e) {
textReceived = true;
synchronized (textField) {
// notify game loop thread which is waiting on this event
textField.notifyAll();
}
}
});
contentPane.add(textField, BorderLayout.SOUTH);
JScrollPane scrollPane = new JScrollPane();
contentPane.add(scrollPane, BorderLayout.CENTER);
textArea = new JTextArea();
textArea.setFont(new Font("Consolas", Font.PLAIN, 12));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setForeground(Color.LIGHT_GRAY);
textArea.setBackground(Color.BLACK);
textArea.setEditable(false);
scrollPane.setViewportView(textArea);
// Start game loop in new thread since we block the thread when
// waiting for input and we don't want to block the UI thread
new Thread(new Runnable() {
@Override
public void run() {
playGame();
}
}).start();
}
private void playGame() {
Random rand = new Random();
boolean playAgain = false;
int wins = 0, losses = 0, draw = 0;
do {
int num = rand.nextInt(3); // 0-2 inclusive
textArea.append("Guess the number [0-2]: \n");
int guess = Integer.parseInt(requestInput());
int computerGuess = rand.nextInt(3);
textArea.append("You: " + guess + "\tComputer: " + computerGuess + "\tNumber: " + num + "\n");
if (guess == num && computerGuess == num || guess != num && computerGuess != num) {
draw++;
textArea.append("Draw!\n");
} else if (guess == num) {
wins++;
textArea.append("You win!\n");
} else if (computerGuess == num) {
losses++;
textArea.append("Computer wins :(\n");
}
textArea.append("Play again [y/n]? \n");
playAgain = requestInput().startsWith("y");
} while (playAgain);
textArea.append("Wins: " + wins + "\nLosses: " + losses + "\nDraw: " + draw + "\n");
}
private String requestInput() {
textField.setEnabled(true);
textField.requestFocus();
// wait on text field till UI thread signals a user input event
synchronized (textField) {
while (!textReceived) {
try {
textField.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
String input = textField.getText();
textField.setText("");
textField.setEnabled(false);
textReceived = false;
return input;
}
}