Event driven input vs turn based - java

I am trying to recreate console game with JTextArea as console/output and JTextField as user input. Since the GUI is event driven I do not understand how could I stop the code from executing, to wait for user input before proceeding with opponents turn. The only solution I can think of is While(userTurn) and userTurn would be changed to false upon actionlistener is there a better way?
My console solution
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;
}
I just called this getInput method and I proceeded with opponents turn.
What I want to accomplish is:
Opponents turn
Game waits for user
User types text into JtextField and presses enter
Game executes players command
Opponents turn again ..

I've written an example game so you can observe the difference. The computer and the user try to guess a random number between 0-2 inclusive. Who ever gets it right wins. If both get it right or both get it wrong its a draw.
EDIT: Updated GUI version
Here's the console program:
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();
}
}
Here's the GUI program:
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;
}
}

Well, I think this:
The ruler of the game is that who owns the turn. As long as the ruler make its move, the other must wait. How to implement this?
If the user owns the turn, he/she may enter text in the JTextField.
When he/she presses ENTER, the command must be validated. If it is OK, the turn must be transferred to the program, and meanwhile, the user shall not even be able to enter text in the JTextField. For example, disabling it:
private void switchTurnToTheProgram()
{
jTextField.setEnabled(false);
}
When the program finalizes its move, the turn must be transferred to the user again, and so, the jTextField must be enabled:
private void switchTurnToTheUser()
{
jTextField.setEnabled(true);
}
Last, you must determine in each case whose's first turn (to make the jTextField appear enabled or disabled).
The complete algorithm:
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 ...
}
}
To enhance the user's experience, maybe it will be useful to enable/disable the button along with the textField. In that case, you'll have to modify just the two methods switchTurnToTheProgram and switchTurnToTheUser.

Related

Trying to update a Java Swing JLabel text field to display new text once an IF condition is met

I'm working on a simple GUI project to get the foundations of Java Swing.
I created a Rock Paper Scissors game which you play against the computer which i implemented a GUI for
My text based GUI Form for Rock, Paper,Scissors
My problem is that once either my score or the computers score reach a value of 3, i want the text on the frame to change. I've tried to implement the code to check each time the variable increases in the button function but it still does not works, neither does implementing the function in the main, the game doesn't change any text or stop once the scores reach 3.
chooseRock.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String cc = getCompChoice();
if (cc.equalsIgnoreCase("Paper")) {
compcount++;
if(compcount == 3){
winner.setText("Computer Wins");
}
compChoice.setText("Computers Choice: " + cc);
This code shows the GUI object and the listener for selecting "Rock", the code is the same for both paper and Scissors. Both compchoice and playchoice are declared with the other attributes at the top.
gameScreen(String title){
super(title);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setContentPane(gameBoard);
this.pack();
chooseRock.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String cc = getCompChoice();
if (cc.equalsIgnoreCase("Paper")) {
compcount++;
compChoice.setText("Computers Choice: " + cc);
compScore.setText("Computer Score: " + compcount);
} else if (cc.equalsIgnoreCase("Scissors")) {
playcount++;
compChoice.setText("Computers Choice: " + cc);
playerScore.setText("Your Score: " + playcount);
} else {
compChoice.setText("Computers Choice: " + cc + " Its a DRAW!");
}
}
});
This is a function I've written to check the scores and display the winner, the 'winner' text is displayed at the top of the panel and has a placeholder.
public void checkScore(){
if(playcount == 3 ){
winner.setText("GAME OVER - PLAYER WINS");
chooseRock.setEnabled(false);
chooseScissors.setEnabled(false);
choosePaper.setEnabled(false);
}else if(compcount == 3 ){
winner.setText("GAME OVER - COMPUTER WINS! BETTER LUCK NEXT TIME");
chooseRock.setEnabled(false);
chooseScissors.setEnabled(false);
choosePaper.setEnabled(false);
}
}
Is there any way to take the variable written inside the listener and use it to change the text field or is there some way conditions like this should be implemented?
You should call checkScore() within the ActionListeners actionPerformed method so that the score is calculated and acted upon with each button press.
Interesting aside:
One way to simplify your listeners is to create an enum, one that checks for win, something like:
public enum RPS {
ROCK("Rock"), PAPER("Paper"), SCISSORS("Scissors");
private String text;
private RPS(String text) {
this.text = text;
}
public String getText() {
return text;
}
#Override
public String toString() {
return text;
}
This way, your check for win can be as simple as adding a comparing method to the enum:
// returns 1 for win, -1 for loss, 0 for tie
public int compare(RPS other) {
int length = values().length;
int delta = (length + ordinal() - other.ordinal()) % length;
return delta != 2 ? delta : -1;
}
}
Each enum value has an ordinal() method that returns its order of declaration, 0 for ROCK, 1 for PAPER, and 2 for SCISSORS. The delta equation simply gets the difference between these ordinal values, adds 3 (the "size" of the enum) and checks the remainder of the calculation. If it is equal to 1, then that enum "wins" the battle, if 0, then its a tie and if 2, then this enum "loses". This can simplify your listeners.
For example, here I create a single listener class for all buttons:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class RockPaperScissors extends JPanel {
public static final int MAX_SCORE = 3;
private static final int PANEL_WIDTH = 600;
private int myScore;
private int compScore;
private JLabel statusLabel = new JLabel(" ");
private JLabel myChoiceLabel = new JLabel();
private JLabel compChoiceLabel = new JLabel();
private JLabel myScoreLabel = new JLabel();
private JLabel compScoreLabel = new JLabel();
private List<Action> actionList = new ArrayList<>();
public RockPaperScissors() {
JPanel statusPanel = new JPanel();
statusPanel.setBorder(BorderFactory.createTitledBorder("Status"));
statusPanel.add(statusLabel);
JPanel scorePanel = new JPanel();
scorePanel.setBorder(BorderFactory.createTitledBorder("Score"));
scorePanel.add(new JLabel("My Score:"));
scorePanel.add(myScoreLabel);
scorePanel.add(Box.createHorizontalStrut(15));
scorePanel.add(new JLabel("Comp Score:"));
scorePanel.add(compScoreLabel);
JPanel selectionPanel = new JPanel();
selectionPanel.setBorder(BorderFactory.createTitledBorder("Selections"));
selectionPanel.add(new JLabel("My Choice:"));
selectionPanel.add(myChoiceLabel);
selectionPanel.add(Box.createHorizontalStrut(15));
selectionPanel.add(new JLabel("Comp Choice:"));
selectionPanel.add(compChoiceLabel);
JPanel btnPanel = new JPanel(new GridLayout(1, 0, 3, 0));
for (RPS rps : RPS.values()) {
Action action = new ButtonAction(rps);
actionList.add(action);
JButton button = new JButton(action);
btnPanel.add(button);
}
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(statusPanel);
add(scorePanel);
add(selectionPanel);
add(btnPanel);
}
#Override
public Dimension getPreferredSize() {
Dimension superSize = super.getPreferredSize();
int height = superSize.height;
int width = Math.max(superSize.width, PANEL_WIDTH);
return new Dimension(width, height);
}
private class ButtonAction extends AbstractAction {
private RPS rps;
public ButtonAction(RPS rps) {
super(rps.getText());
this.rps = rps;
}
#Override
public void actionPerformed(ActionEvent e) {
int randomValue = (int) (RPS.values().length * Math.random());
RPS compChoice = RPS.values()[randomValue];
myChoiceLabel.setText(rps.getText());
compChoiceLabel.setText(compChoice.getText());
if (rps.compare(compChoice) > 0) {
statusLabel.setText("I Win");
myScore++;
} else if (rps.compare(compChoice) < 0) {
statusLabel.setText("Computer Wins");
compScore++;
} else {
statusLabel.setText("Draw");
}
myScoreLabel.setText(String.valueOf(myScore));
compScoreLabel.setText(String.valueOf(compScore));
if (myScore >= MAX_SCORE) {
statusLabel.setText("I Win the Game");
} else if (compScore >= MAX_SCORE) {
statusLabel.setText("Computer Wins the Game");
}
if (myScore >= MAX_SCORE || compScore >= MAX_SCORE) {
for (Action action : actionList) {
action.setEnabled(false);
}
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
RockPaperScissors mainPanel = new RockPaperScissors();
JFrame frame = new JFrame("RockPaperScissors");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
enum RPS {
ROCK("Rock"), PAPER("Paper"), SCISSORS("Scissors");
private String text;
private RPS(String text) {
this.text = text;
}
public String getText() {
return text;
}
#Override
public String toString() {
return text;
}
public int compare(RPS other) {
int length = values().length;
int delta = (length + ordinal() - other.ordinal()) % length;
return delta != 2 ? delta : -1;
}
}

How can i stop the program from skipping my check the second time around?

I am creating a program to take in sets of binary digits and convert them into hammingcodes (Effectively take in 8 digits, turn into 12, print out) but i am having trouble. Currently, i am using a JTextField for the user to enter their number, then they press a JButton to enter the data. I then do funky shit with that number to put it into a list and confirm that this is the last of the numbers they wish to enter. If they click a JButton called yes (New text in button, but same button) if goes on to do what i need. But if they click the other JButton called no, it goes back and repeats the same process. My problem is after clicking no once, the program stops allowing you to press no at the step to check if you want to add another list of numbers. IT appears to skip the check all together and assume they pressed yes as it does the rest of the working out thats done after all entry is finished.
My code is a tad messy due to messing with it for a few hours.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.*;
public class MainProgram extends JFrame
{
public MainProgram()
{
}
public static void main(String[] args)
{
MainProgram mp = new MainProgram();
mp.run();
}
private void run()
{
java.util.List<Integer> streamSplit = new ArrayList<>();
java.util.List<Integer> tempEight = new ArrayList<>();
java.util.List<Integer> finalStream = new ArrayList<>();
yes.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
checkYes = true;
}
});
no.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
checkNo = true;
}
});
init();
yesChange("Enter");
boolean confirm = false;
int aCheck = 0;
while (aCheck == 0)
{
confirm = false;
while (!confirm)
{
setTopText("<html>Please enter your next 8 bits. Do not enter more than 8 bits.<br> Press Enter when done</html>");
yesChange("Enter");
confirm = checkYes();
}
confirm = false;
setTopText("Digits Successfully added.");
int stream = checkInput();
do
{
streamSplit.add(stream % 10);
stream /= 10;
} while (stream != 0);
setYesNo();
setTopText("<html>Are you finished entering streams?</html>");
yesChange("YES");
noChange("NO");
aCheck = 2;
checkYes();
checkNo();
while (aCheck == 2)
{
if ( checkNo())
{
aCheck = 0;
System.out.println("CrapNo");
}
else if (checkYes())
{
aCheck = 1;
System.out.println("CrapYes");
}
}
}
int arrayLength = streamSplit.size();
int bufferLength = 8 - arrayLength % 8;
int numberOfStreams = 0;
if (bufferLength != 8)
{
numberOfStreams = arrayLength / 8 + 1;
} else
{
numberOfStreams = arrayLength / 8;
}
int tempStreams = numberOfStreams;
System.out.println(numberOfStreams + "<Streams Buffer>" + bufferLength);
while (bufferLength > 0 && bufferLength != 8)
{
streamSplit.add(0);
bufferLength--;
}
while (tempStreams > 0)
{
for (int i = 0; i < 8; i++)
{
tempEight.add(streamSplit.get(i));
}
if ((tempEight.get(0) + tempEight.get(1) + tempEight.get(3) + tempEight.get(4) + tempEight.get(6)) % 2 == 0)
{
tempEight.add(0, 0);
} else
{
tempEight.add(0, 1);
}
if ((tempEight.get(1) + tempEight.get(3) + tempEight.get(5) + tempEight.get(6) + tempEight.get(7)) % 2 == 0)
{
tempEight.add(1, 0);
} else
{
tempEight.add(1, 1);
}
if ((tempEight.get(3) + tempEight.get(4) + tempEight.get(5) + tempEight.get(8) + tempEight.get(9)) % 2 == 0)
{
tempEight.add(3, 0);
} else
{
tempEight.add(3, 1);
}
if ((tempEight.get(7) + tempEight.get(8) + tempEight.get(9) + tempEight.get(10)) % 2 == 0)
{
tempEight.add(7, 0);
} else
{
tempEight.add(7, 1);
}
tempStreams--;
for (int i = 0; i < 12; i++)
{
finalStream.add(tempEight.get(0));
tempEight.remove(0);
}
}
Collections.reverse(streamSplit);
System.out.print("Your original bit-stream was: ");
for (int i = 0; i < numberOfStreams * 2; i++)
{
for (int j = 0; j < 4; j++)
{
System.out.print(streamSplit.get(j + (i * 4)));
}
System.out.print(" ");
}
System.out.println();
System.out.print("Your new HammingCode bit-stream is: ");
for (int i = 0; i < numberOfStreams * 3; i++)
{
for (int j = 0; j < 4; j++)
{
System.out.print(finalStream.get(j + (i * 4)));
}
System.out.print(" ");
}
System.out.println();
}
public Boolean checkYes = false;
public Boolean checkNo = false;
private JFrame frame = new JFrame("Absolute Layout Example");
private JPanel contentPane = new JPanel();
private JLabel topText = new JLabel("Welcome to my Hamming Code Generator", JLabel.CENTER);
private JTextField inputText = new JTextField();
private JButton yes = new JButton("YES");
private JButton no = new JButton("NO");
public void init()
{
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane.setOpaque(true);
contentPane.setBackground(Color.WHITE);
contentPane.setLayout(null);
topText.setLocation(0, 0);
topText.setSize(400, 50);
topText.setBorder(BorderFactory.createLineBorder(Color.black));
inputText.setLocation(0,50);
inputText.setSize(400,75);
inputText.setBorder(BorderFactory.createLineBorder(Color.black));
yes.setSize(80, 40);
yes.setLocation(60, 135);
no.setSize(80, 40);
no.setLocation(260, 135);
contentPane.add(topText);
contentPane.add(inputText);
contentPane.add(yes);
contentPane.add(no);
frame.setContentPane(contentPane);
frame.setSize(400, 225);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public void setTopText(String s)
{
topText.setText(s);
}
public void setYesNo()
{
checkYes = false;
checkNo = false;
}
public Boolean checkYes() {return checkYes;}
public Boolean checkNo() {return checkNo;}
public int checkInput()
{
String temp1 = inputText.getText();
int temp = Integer.parseInt(temp1);
return temp;
}
public void yesChange(String s)
{
yes.setText(s);
}
public void noChange(String s)
{
no.setText(s);
}
}
I find it tough to answer this question not fully knowing what your code is doing, especially the part where you "... do funky #### with that number..."
But I do know that you have significant issues with your program structure, especially within your lengthy run() method where you have numerous nested while (...) loops and do-while loops, code constructs that might seem at home within a linear processing console program but which seems out of place within an event-driven Swing GUI.
What I suggest that you do is try to use some state-dependent coding. For instance, you could give your class the boolean variables, enteringData and dataValidYet, to represent two key states: whether the user is now entering data into the JTextField, and whether that data has yet been validated yet. And then within your JButton ActionListeners, use if and if/else blocks to decide what to do on button push depending on the state of these boolean fields, and likely other key fields of the class.
For a code "skeleton" example, one that doesn't yet do anything, but hopefully will show you the structure I'm talking about:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class StateMachine extends JPanel {
private static final String INITIAL_TITLE = "Please enter your next 8 bits. "
+ "Do not enter more than 8 bits.\n"
+ "Press Enter when done";
private static final String ARE_YOU_FINISHED = "Are you finished entering streams?";
private static final String YES = "Yes";
private static final String ENTER = "Enter";
private static final String NO = "No";
private static int GAP = 8;
private static final int COLUMNS = 30;
// this is a JTextArea built to look like a JLabel
private JTextArea topTextArea = new JTextArea(2, COLUMNS);
private JTextField dataEntryField = new JTextField(COLUMNS);
private JButton yesEnterButton = new JButton(ENTER);
private JButton noButton = new JButton(NO);
private boolean enteringData = true;
private boolean dataValidYet = false;
public StateMachine() {
yesEnterButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yesEnterButtonActionPerfromed(e);
}
});
noButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
noButtonActionPerfromed(e);
}
});
topTextArea.setWrapStyleWord(true);
topTextArea.setLineWrap(true);
topTextArea.setFocusable(false);
topTextArea.setEditable(false);
topTextArea.setOpaque(false);
topTextArea.setText(INITIAL_TITLE);
JPanel innerButtonPanel = new JPanel(new GridLayout(1, 0, GAP, 0));
innerButtonPanel.add(yesEnterButton);
innerButtonPanel.add(noButton);
JPanel outerButtonPanel = new JPanel();
outerButtonPanel.add(innerButtonPanel);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout(GAP, GAP));
add(topTextArea, BorderLayout.PAGE_START);
add(dataEntryField, BorderLayout.CENTER);
add(outerButtonPanel, BorderLayout.PAGE_END);
}
protected void noButtonActionPerfromed(ActionEvent e) {
// TODO depending on state of enteringData and dataValidYet booleans
// change text in buttons, do things with JTextField data
// set state of enteringData and dataValidYet booleans
if (enteringData) {
// a no press is meaningless if entering data
return;
}
// .... more
}
private void yesEnterButtonActionPerfromed(ActionEvent e) {
// TODO depending on state of enteringData and dataValidYet booleans
// change text in buttons, do things with JTextField data
// set state of enteringData and dataValidYet booleans
if (enteringData) {
topTextArea.setText(ARE_YOU_FINISHED);
yesEnterButton.setText(YES);
yesEnterButton.setActionCommand(YES);
enteringData = false;
return;
}
// .... more
}
private static void createAndShowGui() {
StateMachine mainPanel = new StateMachine();
JFrame frame = new JFrame("State Machine");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Also, as a "side" recommendation, one unrelated to your main problem, understand that null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Note that if this were my program, I would use more indirection including creating separate classes to separate out the GUI portion of the program from the logic portion.

arithmetic expression Math.random in java

the purpose of this program is to let the user try to guess a number up to 20 randomly chosen by the computer. The user inputs their guess. The computer then tells the user if their guess is too high or too low. The user keeps inputting their guess until theu guess correctly. Then they are told how many guesses it took them. The problem is that I am not understanding whether the arithmetic expression in the program generates random numbers from 1 to 20 because when I am inputting numbers from 1 to 20, the pop-up says 'number is too big'.
package pkTopic6Exercise13;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.Font;
public class Topic6Exercise13
{
private GuessingGame MyGuessingGame = new GuessingGame();
private Topic6Exercise13()
{//constructor
}
public static void main(String[] args)
{
Topic6Exercise13 myTopic6Exercise13 = new Topic6Exercise13();
myTopic6Exercise13.go();
}
private void go()
{
T6Ex13GUIin myT6Ex13GUIin = new T6Ex13GUIin();
}
private class GuessingGame
{
private int NumToGuess=0;
private int Guess=0;
private int NumGuesses=0;
private GuessingGame()
{//constructor
}
private void GenerateNum()
{
NumToGuess = 1+(int)(20*Math.random());
}
private String CheckNum()
{
NumGuesses = NumGuesses + 1;
if (Guess > NumToGuess)
{
JOptionPane.showMessageDialog(null, Guess + " is too BIG!");
return "NotGuessed";
}
else if (Guess < NumToGuess)
{
JOptionPane.showMessageDialog(null, Guess + " is too SMALL!");
return "NotGuessed";
}
else
{
JOptionPane.showMessageDialog(null, "Well done! " + Guess +" is correct, it took " + NumGuesses + " goes.");
return "Guessed";
}
}
}
private class T6Ex13GUIin extends JFrame
{
private JLabel lblGuess;
private JTextField txfGuess;
private JButton btnPickNumber;
private JButton btnQuit;
private T6Ex13GUIin()
{//constructor
Font fontDialog = new Font("Dialog", Font.BOLD,24);
this.setSize(1000,500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocation(100,0);
this.setTitle("Guessing Game");
this.setLayout(null);
lblGuess = new JLabel("Type Guess:");
lblGuess.setFont(fontDialog);
lblGuess.setBounds(100,200,200,50);
lblGuess.setVisible(false);
this.add(lblGuess);
txfGuess = new JTextField("");
txfGuess.setBounds(300,200,100,50);
txfGuess.setFont(fontDialog);
txfGuess.setVisible(false);
ReturnListener MyReturnListener = new ReturnListener();
txfGuess.addKeyListener(MyReturnListener);
this.add(txfGuess);
ClickListener MyClickListener = new ClickListener();
btnPickNumber = new JButton("Pick Number");
btnPickNumber.setBounds(100,100,750,50);
btnPickNumber.setFont(fontDialog);
btnPickNumber.addActionListener(MyClickListener);
this.add(btnPickNumber);
btnQuit = new JButton("Quit");
btnQuit.setBounds(750,300,100,50);
btnQuit.setFont(fontDialog);
btnQuit.addActionListener(MyClickListener);
this.add(btnQuit);
this.setVisible(true);
}
private class ClickListener implements ActionListener
{
private ClickListener()
{//constructor
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == btnPickNumber)
{
MyGuessingGame.GenerateNum();
MyGuessingGame.NumGuesses=0;
btnPickNumber.setVisible(false);
lblGuess.setVisible(true);
txfGuess.setVisible(true);
txfGuess.setText("");
}
if (e.getSource() == btnQuit)
{
System.exit(0);
}
}
}
private class ReturnListener implements KeyListener
{
private ReturnListener()
{//constructor
}
public void keyPressed(KeyEvent e)
{
}
public void keyReleased(KeyEvent e)
{
}
public void keyTyped(KeyEvent e)
{
if (e.getKeyChar() == '\n')
{
MyGuessingGame.Guess =
Integer.parseInt(txfGuess.getText());
if (MyGuessingGame.CheckNum().equals("Guessed"))
{
lblGuess.setVisible(false);
txfGuess.setVisible(false);
btnPickNumber.setVisible(true);
}
else
{
txfGuess.setText("");
}
}
}
}
}
}
Have you tried any testing? Like, debugging and seeing what value is getting set for NumToGuess? Or perhaps simply displaying the value being set? Then if you know what the random number is, you can test out your logic.
Based on your core logic, I ran this simple code below and didn't experience any problems.
public static void main(String[] args) {
int NumToGuess = 1+(int)(20*Math.random());
int myGuess = Integer.parseInt("10");
System.out.println("Value is: " + NumToGuess);
System.out.println("My Guess: " + myGuess);
if (myGuess > NumToGuess) {
System.out.println("Too Big");
}
else if (myGuess < NumToGuess) {
System.out.println("Too Small");
}
}
Math.rand() generates a random number between 0 and 1 exclusive. So by multiplying by 20 you get a value between 0 and 20 exclusive. By rounding down with the (int) cast it becomes between 0-19 inclusive and by adding one it becomes between 1-20 inclusive. So yes, your number is being generated correctly. However, you do add 1 again at the beginning of the checkNum() method so that could be a problem. Hope this helps.

JApplet crashing because of a for loop for an Array

this is my first post so go easy on me.
I'm doing some uni work and im not asking for any help or advice for the whole project.
Basically the whole thing does work kinda when i comment out a for loop:
for(int i=0;i<words.length;i++)
{
textArea.append(words[i] + "\n");
}
this is designed to work its way through an array but NetBeans keeps telling me to change it into an enhanced for loop but when i do it crashes. The whole program is designed to take an input from a JAplet interface, send it to stringAnalyser in a separate java file, record the length of words and how often words of that length occur, then send them back to the interface and print them onscreen in the centre console.
Here are both files i'm using if it helps:
package Assignment2;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowListener;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.JPanel;
public class GUIAssign extends JApplet
{
private JPanel pnorth,psouth,pcentre,pwest,peast;
private JButton btnEnter,btnReset,btnSave,btnLoad;
private JTextField txtEnter;
private JLabel label1,label2,header;
private JTextArea textArea;
private Container c;
private paintPanel panelpaint;
private String inputString,shape;
private int x,y,size;
private stringAnalyser tim;
#Override
public void init()
{
this.setVisible(true);
this.setSize(800,600);
initComponents();
initPanels();
initButtons();
initText();
initLabels();
tim=new stringAnalyser();
}
public void initComponents()
{
x=0;
y=0;
size=0;
this.panelpaint=new paintPanel();
}
public void initPanels()
{
pnorth= new JPanel();
pnorth.setBackground(Color.CYAN);
this.psouth = new JPanel();
psouth.setBackground(Color.BLUE);
this.peast =new JPanel();
peast.setBackground(Color.CYAN);
this.pwest = new JPanel();
pwest.setBackground(Color.CYAN);
this.pcentre = new JPanel();
pcentre.setBackground(Color.yellow);
}
String words[];
public void initButtons()
{
this.btnEnter=new JButton("Enter");
btnEnter.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
inputString=txtEnter.getText();
tim.setText(inputString);
words=tim.sendBack();
for(int i=0;i<words.length;i++)
{
textArea.append(words[i] + "\n");
}
tim.output();
}
});
this.btnReset=new JButton("Reset");
btnReset.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent ae) {
txtEnter.setText("");
}
});
this.btnSave=new JButton("Save");
this.btnLoad=new JButton("Load");
}
public void initText()
{
txtEnter=new JTextField();
textArea=new JTextArea();
}
public void initLabels()
{
this.label1=new JLabel("");
this.label2=new JLabel("");
}
//structure
#Override
public void start()
{
panelEast();
panelWest();
panelNorth();
panelSouth();
panelCentre();
c=this.getContentPane();
c.setBackground(Color.LIGHT_GRAY);
c.setLayout(new BorderLayout());
c.add(pnorth,BorderLayout.NORTH);
c.add(peast,BorderLayout.EAST);
c.add(psouth,BorderLayout.SOUTH);
c.add(pwest,BorderLayout.WEST);
c.add(pcentre,BorderLayout.CENTER);
}
public void panelNorth()
{
Font f=new Font("Jokerman",Font.BOLD,16);
pnorth.setLayout(new GridLayout(1,3));
pnorth.add(new JLabel(" "));
pnorth.add(new JLabel("Tim's String Lenght Tester"));
pnorth.add(new JLabel(" "));
}
public void panelWest()
{
pwest.add(new JLabel(" "));
//pwest.add(new JLabel(new ImageIcon("src/tim.jpg")));
}
public void panelEast()
{
peast.add(new JLabel(" "));
}
public void panelSouth()
{
psouth.setLayout(new FlowLayout());
JPanel psouth1=new JPanel();
JPanel psouth2=new JPanel();
psouth1.setLayout(new GridLayout(1,3));
psouth1.setBackground(Color.CYAN);
psouth1.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED,Color.cyan, Color.black, Color.black, Color.cyan));
psouth1.add(this.txtEnter);
psouth1.add(this.btnEnter);
psouth1.add(this.btnReset);
psouth2.setLayout(new GridLayout(1,2));
psouth2.setBackground(Color.LIGHT_GRAY);
psouth2.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED,Color.cyan, Color.black, Color.black, Color.cyan));
psouth2.add(this.btnSave);
psouth2.add(this.btnLoad);
psouth.add(psouth1);
psouth.add(psouth2);
}
public void panelCentre()
{
pcentre.setLayout(new GridLayout(1,2));
JPanel pcentre1=new JPanel();
pcentre.setBackground(Color.GREEN);
pcentre.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED,Color.green,Color.green));
pcentre.add(textArea);
textArea.setBackground(Color.LIGHT_GRAY);
textArea.setForeground(Color.YELLOW);
pcentre.add(pcentre1);
}
#Override
public void setName(String n) {
this.inputString=n;
}
public String getNameString() {
return inputString;
}
#Override
public void stop(){}
#Override
public void destroy(){}
#Override
public void paint(Graphics g){
}
}//close project`
file 2, this is the analysing java file:
package Assignment2;
public class stringAnalyser{
//static String inputArray[]= {"Tim","John","Craig", "Andrew", "Bob", "Tom", "Phillipa","Billie-Bob"};
private String inputString = "";
private String removedCharString;//will store the string with removed chars
private String array3="",array4="", array5="", array6="", array7="", array8="", array9="", array10="",array11="",
array12="",array13="",array14="",array15="";
private String[] sendString;
private int count=0;
//will add the words with the appropriate length to a string for printing
public void setText(String input)
{
this.inputString=input;
}
public String showme()
{
return "Think is not difficult believe is the word and look at me I am the great evidence!!!";
}
public void output()
{
int three=0, four=0, five=0, six=0, seven=0, eight=0, nine=0, ten=0,eleven=0,twelve=0,thirt=0,fort=0,fift=0;
// these ints will count how many times the words relevent length have accoured in a string
String w[];
removedCharString = inputString.replaceAll("[^a-zA-Z\\s]", "");//remove chars
w=removedCharString.split(" ");
this.sendString=new String[w.length];
for (String retval: removedCharString.split(" "))//split at any spaces
{
if (retval.length()==3){
three++;
array3 = array3 + retval + ", ";
}//if 3
if (retval.length()==4){
four++;
array4 = array4 + retval + ", ";
}//if 4
if (retval.length()==5){
five++;
array5 = array5 + retval + ", ";
}//if 5
if (retval.length()==6){
six++;
array6 = array6 + retval + ", ";
}//if 6
if (retval.length()==7){
seven++;
array7 = array7 + retval + ", ";
}//if 7
if (retval.length()==8){
eight++;
array8 = array8 + retval + ", ";
}//if 8
if (retval.length()==9){
nine++;
array9 = array9 + retval + ", ";
}//if 9
if (retval.length()==10){
ten++;
array10 = array10 + retval + ", ";
}//if 10
if (retval.length()==11){
eleven++;
array11 = array11 + retval + ", ";
}//if 10
if (retval.length()==12){
twelve++;
array12 = array12 + retval + ", ";
}//if 10
if (retval.length()==13){
thirt++;
array13 = array13 + retval + ", ";
}//if 10
if (retval.length()==14){
fort++;
array14 = array14 + retval + ", ";
}//if 10
if (retval.length()==15){
fift++;
array15 = array15 + retval + ", ";
}//if 10
}//for string retval
// print the results
System.out.println(inputString);
if (three!=0){
count++;
sendString[count]="There are: "+three+" three letter word/s; "+array3;
System.out.println("There are: "+three+" three letter word/s; "+array3);}
if (four!=0){
count++;
sendString[count]="There are: "+four+" four letter word/s; "+array4;
System.out.println("There are: "+four+" four letter word/s; "+array4);}
if (five!=0){
System.out.println("There are: "+five+" five letter word/s; "+array5);}
if (six !=0){
System.out.println("There are: "+six+" six letter word/s; "+array6);}
if (seven !=0){
System.out.println("There are: "+seven+" seven letter word/s; "+array7);}
if (eight!=0){
System.out.println("There are: "+eight+" eigth letter word/s; "+array8);}
if (nine!=0){
System.out.println("There are: "+nine+" nine letter word/s; "+array9);}
if (ten!=0){
System.out.println("There are: "+ten+" ten letter word/s;"+array10);}
if (eleven!=0){
System.out.println("There are: "+eleven+" eleven letter word/s;"+array11);}
if (twelve!=0){
System.out.println("There are: "+twelve+" twelve letter word/s;"+array12);}
if (thirt!=0){
System.out.println("There are: "+thirt+" thirteen letter word/s;"+array13);}
if (fort!=0){
System.out.println("There are: "+fort+" forteen letter word/s;"+array14);}
if (fift!=0){
System.out.println("There are: "+fift+" fifteen letter word/s;"+array15);}
}//output
public String[] sendBack()
{
return this.sendString;
}
public static void main(String []args)
{
stringAnalyser a=new stringAnalyser();
a.output();
}//close main
}//class`
thank you in advance for any and all input.
It seems that in btnEnter.addActionListener you only invoke tim.setText(inputString); which sets the input. But you never call output() or any other required methods on the analyzer that supposed to process the input. As a result, tim.sendBack() returns null. stringAnalyser.sendString is initialized only inside output(). Until then it remains null. So words.length triggers NullPointerException because words array is null.
The indentation and naming of the posted code is bit hard to follow at times. See Java Code Conventions, Naming Conventions section in particular. Most tools can auto indent the code for you, ie ctrl+shift+f in Eclipse.

Java guessing game

I am trying to write a program in Java that takes a random number from 1-1000 and then as the guess it the background color changes to blue(cold) or red(warm) if they are in the number. I am new to java GUI, but I think the rest of the logic is right, not sure. It compiles, but the guess button doesn't work. Any guidance will be appreciated.
package guessGame;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.color.*;
import java.util.Random;
import java.util.Random;
import java.util.logging.FileHandler;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class GuessGame extends JFrame
{
private JFrame mainFrame;
private JButton GuessButton;
private JButton QuitButton;
private JLabel prompt1, prompt2;
private JTextField userInput;
private JLabel comment = new JLabel("What is your destiny?");
private JLabel comment2 = new JLabel (" ");
//private int number, guessCount;
//private int lastGuess;
private int randomNumber;
private Color background;
public GuessGame()
{
mainFrame = new JFrame ("Guessing Game!");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Creates components
GuessButton = new JButton("Guess");
QuitButton = new JButton("Quit");
prompt1 = new JLabel("I have a number between 1 and 1000.");
prompt2 = new JLabel("Can you guess my number? Enter your Guess:");
comment = new JLabel ("What is your destiny?");
comment2 = new JLabel (" ");
userInput = new JTextField(5);
//userInput.addActionListener(new GuessHandler());
//content pane
Container c = mainFrame.getContentPane();
c.setLayout(new FlowLayout());
//adding component to the pane
c.add(prompt1);
c.add(prompt2);
c.add(userInput);
c.add(comment2);
c.add(GuessButton);
c.add(QuitButton);
c.add(comment);
GuessButton.setMnemonic('G');
QuitButton.setMnemonic('Q');
mainFrame.setSize(300,200);
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
mainFrame.setResizable(false);
// define and register window event handler
// mainFrame.addWindowListener(new WindowAdapter() {
// public void windowClosing(WindowEvent e)
// { System.exit(0); }
// });
//creating the handler
GuessButtonHandler ghandler = new GuessButtonHandler(); //instantiate new object
GuessButton.addActionListener(ghandler); // add event listener
QuitButtonHandler qhandler = new QuitButtonHandler();
QuitButton.addActionListener(qhandler);
}
public void paint (Graphics g)
{
super.paint(g);
setBackground(background);
}
class QuitButtonHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
}
class GuessButtonHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
int getUserInput=0;
int diff;
int Difference;
randomNumber = new Random().nextInt(1001);
try {
getUserInput = Integer.parseInt(
userInput.getText().trim());
} catch (NumberFormatException ex){
comment.setText("Enter a VALID number!");
return;
}
if (getUserInput == randomNumber){
JOptionPane.showMessageDialog(null, "CONGRATULATIONS! You got it!!",
"Random Number: " + randomNumber,
JOptionPane.INFORMATION_MESSAGE);
randomNumber = new Random().nextInt(1000) + 1;
return;
}
if (getUserInput > randomNumber){
comment.setText( "Too High. Try a lower number." );
diff=getUserInput - randomNumber;
Difference=Math.abs(diff);
} else {
comment.setText( "Too Low. Try a higher number." );
diff=randomNumber - getUserInput;
Difference=Math.abs(diff);
}
if(Difference<=25){
comment2.setText("Cold");
setBackgroundColor(Color.blue);
}
if(Difference<=10){
comment2.setText("Warm");
setBackgroundColor(Color.red);
}
else {
}
}
private void setBackgroundColor(Color color) {
setBackgroundColor(color);
}
}
public static void main(String args[]) {
//instantiate gueesgame object
GuessGame app = new GuessGame();
}
}
The colors aren't changing because your setBackgroundColor always uses Color.black. Change it to:
private void setBackgroundColor(Color color) {
setBackground(color);
}
As for the number always being zero. You do not instantiate the randomNumber field. Add this to your constructor:
randomNumber = new Random().nextInt(1001);
Another problem I noticed was you added a window listener to ensure the program exits when you close the window. This is implemented in JFrame. In the constructor add:
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Instead of using the deprecated method:
mainFrame.show();
use the not deprecated:
mainFrame.setVisible(true);
Furthermore you have a field, which is never queried:
private Color background;
It's best to do the logic before connecting it to the gui. It's a lot easier to test and find the worst bugs.
Refactored code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class GuessGame extends JFrame {
private JTextField userInput;
private JLabel comment = new JLabel("What is your destiny?");
private JLabel comment2 = new JLabel(" ");
private int randomNumber;
public GuessGame() {
super("Guessing Game!");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Creates components
JButton guessButton = new JButton("Guess");
JButton quitButton = new JButton("Quit");
JLabel prompt1 = new JLabel("I have a number between 1 and 1000.");
JLabel prompt2 = new JLabel("Can you guess my number? Enter your Guess:");
comment = new JLabel("What is your destiny?");
comment2 = new JLabel(" ");
userInput = new JTextField(5);
//content pane
Container c = getContentPane();
setLayout(new FlowLayout());
//adding component to the pane
c.add(prompt1);
c.add(prompt2);
c.add(userInput);
c.add(comment2);
c.add(guessButton);
c.add(quitButton);
c.add(comment);
guessButton.setMnemonic('G');
quitButton.setMnemonic('Q');
setSize(300, 200);
setLocationRelativeTo(null);
setVisible(true);
setResizable(false);
initializeNumber();
//creating the handler
GuessButtonHandler ghandler = new GuessButtonHandler(); //instantiate new object
guessButton.addActionListener(ghandler); // add event listener
QuitButtonHandler qhandler = new QuitButtonHandler();
quitButton.addActionListener(qhandler);
}
private void initializeNumber() {
randomNumber = new Random().nextInt(1000) + 1;
}
class QuitButtonHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
class GuessButtonHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
int getUserInput;
int diff;
int Difference;
try {
getUserInput = Integer.parseInt(userInput.getText().trim());
if (getUserInput == randomNumber) {
JOptionPane.showMessageDialog(null, "CONGRATULATIONS! You got it!!",
"Random Number: " + randomNumber,
JOptionPane.INFORMATION_MESSAGE);
initializeNumber();
return;
}
if (getUserInput > randomNumber) {
comment.setText("Too High. Try a lower number.");
diff = getUserInput - randomNumber;
Difference = Math.abs(diff);
} else {
comment.setText("Too Low. Try a higher number.");
diff = randomNumber - getUserInput;
Difference = Math.abs(diff);
}
if (Difference <= 25) {
comment2.setText("Cold");
GuessGame.this.setBackgroundColor(Color.blue);
}
if (Difference <= 10) {
comment2.setText("Warm");
GuessGame.this.setBackgroundColor(Color.red);
}
} catch (NumberFormatException ex) {
comment.setText("Enter a VALID number!");
}
}
}
private void setBackgroundColor(Color color) {
getContentPane().setBackground(color);
}
public static void main(String args[]) {
//instantiate gueesgame object
GuessGame app = new GuessGame();
}
}
You have more Swing components than you need, and you seem to be adding one set to the frame while manipulating another set. For example, you have two JTextFields, fieldBox and userInput. You add userInput to the frame, but check fieldBox for input in the Guess button handler. Since fieldBox is always empty, the NumberFormatException is caught by your exception handler (which should really just catch NumberFormatException, not Exception), and comment is updated with "Enter a VALID number!". However, just like with the double text area, comment isn't actually added to the frame, prompt1 and prompt2 are, so you can't see the change
I would write your logic without a UI first and test it until it was 100% correct. Just use a command line, text UI at first. Once that's done, put a GUI in front of it. It'll help to isolate your problems: once the text-driven logic is right, you'll know that future problems are due to UI.
It makes your MVC separation cleaner as well.

Categories