Guess Number Game GUI - Infinite Loop - java

I'm making a guess the number program and I'm having trouble with my loop. When I run the program and input a number into the textfield and hit enter it freezes. I figured out that this might be happening because of an infinite loop. Feel free to correct me if I'm wrong. Basically when I enter a number into the textfield and press enter it suppose to change a label and change background color but this doesn't happen and I think its because my loop runs until win becomes true and when I type in my number it keeps running that number instead of outputting the correct label and letting me input a different number into the textfield. P.S: I know the newGame button does not work yet
import java.util.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class GuessingGame implements ActionListener
{
JFrame guessFrame;
JPanel guessPanel;
JTextField guessText;
JButton newGame;
JLabel rangeLbl, enterGuessLbl, winLbl;
Random rand = new Random();
int numToGuess = rand.nextInt(1000)+1;
int numOfTries = 0;
int guess;
public GuessingGame()
{
// Create the frame and container.
guessFrame = new JFrame("Guess the Number");
guessPanel = new JPanel();
guessPanel.setLayout(new GridLayout(5,0));
// Add the widgets.
addWidgets();
// Add the panel to the frame.
guessFrame.getContentPane().add(guessPanel, BorderLayout.CENTER);
// Exit when the window is closed.
guessFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Show the converter.
guessFrame.pack();
guessFrame.setVisible(true);
}
// Create and add the widgets for converter.
private void addWidgets()
{
// Create widgets.
guessText = new JTextField();
guessText.setHorizontalAlignment(JTextField.CENTER);
rangeLbl = new JLabel("I have a number between 1 and 1000. Can you guess my number?", SwingConstants.LEFT);
enterGuessLbl = new JLabel("Please enter your guess", SwingConstants.LEFT);
winLbl = new JLabel(" ", SwingConstants.CENTER);
newGame = new JButton("New Game");
// Listen to events from Convert textfield.
guessText.addActionListener(this);
// Add widgets to container.
guessPanel.add(rangeLbl);
guessPanel.add(enterGuessLbl);
guessPanel.add(guessText);
guessPanel.add(winLbl);
guessPanel.add(newGame);
}
// Implementation of ActionListener interface.
public void actionPerformed(ActionEvent event)
{
boolean win = false;
guess = Integer.parseInt(guessText.getText());
if ( guess == numToGuess)
{
win = true;
}
else if ( guess < numToGuess)
{
winLbl.setText("Too Low");
guessPanel.setBackground(Color.red);
guess = Integer.parseInt(guessText.getText());
}
else if ( guess > numToGuess)
{
winLbl.setText("Too High");
guessPanel.setBackground(Color.blue);
guess = Integer.parseInt(guessText.getText());
}
winLbl.setText("Correct!");
guessPanel.setBackground(Color.green);
}
public static void main(String[] args)
{
GuessingGame game = new GuessingGame();
}
}

Your while-loop is inappropriate here, because you are in a actionPerformed() Method. This method is most likely to be called on a gui action (e.g. button kicked).
It should do one action corresponding to your needs and then terminate, since this method is invoked within the EDT. Your gui will not perform any updates until this method is finished.
Thus nothing will change (e.g. your win status) until the user makes some additional action, which he can't because your gui is frozen.

You don't update the value of guess. You'll need to read guess at the end of your loop, or at least in those cases that guess and numToGuess don't have the same value.
In the current case: if the values are not equal the first iteration, it will never become.
add
guess = Integer.parseInt(guessText.getText());
as last statement to each else if block
EDIT: a better way, as Marcinek points out, would be to remove the while loop, but since I don't know your requirements, I won't go as far as to claim it's the correct sollution.

Your loop keep runing because this condition if ( guess == numToGuess) never verfied
boolean win = false;
while (win == false){
if ( guess == numToGuess){
win = true;
}
.......
}
and
win still false, and while loop go a head runing.
while (win == false)
{
.....}

Related

Updating JLabel text output for counter not working

So I am creating a number guessing game. The computer generates a random number, the user inputs a guess, then the computer lets the user know if they've won, or if the guess was less than or more than the random number. This goes on until they've got it right, or press play again (which resets it).
I want to count and display the number of guesses a user makes each round. So I am incrementing 'tallyValue' for each guess. I know it is counting and incrementing the values correctly. However for instance if the user guesses twice in a row values that are less than the random number, then the tally value doesn't update on the .setText output. It will only update when the users guesses alternate from less than the random number to more than the random number.
What am I missing? I've tried removing and then setting the text but I don't understand why this happens.
MyFrame() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //exit out of app
this.setLayout(new FlowLayout());
//add labels, buttons and input fields
button = new JButton("Submit");
button.addActionListener(this); //can just pass in this because we already pass actionlistener above
JLabel instruction = new JLabel();
instruction.setText("Input Your Guess (must be between 1 and 100");
textField = new JTextField();
textField.setPreferredSize(new Dimension(250,40));
tally = new JLabel();
tally.setText("Number of guesses: " + tallyValue);
outputMessage = new JLabel();
outputMessage.setText("Take a guess!");
playAgainButton = new JButton("Play Again");
playAgainButton.addActionListener(this);
this.add(instruction);
this.add(textField);
this.add(button);
this.add(playAgainButton);
this.add(outputMessage);
this.add(tally);
this.setTitle("Number Guessing Game");
this.setVisible(true); //make frame visible
this.pack(); //frame size adjusts to components
ImageIcon icon = new ImageIcon("res/game-icon.png"); //creates an image icon
this.setIconImage(icon.getImage());//changes icon of frame
System.out.println(randomNumber); //for testing purposes
}
//add any unimplemented methods because we are using an interface
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==button) {
int textFieldValue;
this.remove(outputMessage);
try {
textFieldValue = Integer.parseInt(textField.getText());
//if guess is correct
if(textFieldValue == randomNumber) {
outputMessage.setText("Congratulations you guessed correctly!");
this.add(outputMessage);
tallyValue++;
displayCount(tallyValue);
this.pack();
textFieldValue = 0; //reset text field val
}
//if guess is less than randomNumber
if(textFieldValue < randomNumber) {
outputMessage.setText("Incorrect - the number I am thinking of is more than that");
this.add(outputMessage);
tallyValue++;
displayCount(tallyValue);
this.pack();
textFieldValue = 0; //reset text field val
}
//if guess is more than randomNumber
if(textFieldValue > randomNumber) {
outputMessage.setText("Incorrect - the number I am thinking of is less than that");
this.add(outputMessage);
tallyValue++;
System.out.println(tallyValue);
displayCount(tallyValue);
this.pack();
textFieldValue = 0; //reset text field val
}
}
catch (NumberFormatException ex){
outputMessage.setText("You must insert a valid number");
this.add(outputMessage);
this.pack();
}
}
if(e.getSource()==playAgainButton) {
System.out.println("pa");
this.remove(outputMessage);
randomNumber = rand.nextInt(101);
outputMessage.setText("Take a guess!");
this.add(outputMessage);
tallyValue = 0;
displayCount(tallyValue);
this.pack();
}
}
private void displayCount (int tv) {
this.remove(tally);
tally.setText("Number of guesses:" + tv);
this.add(tally);
this.pack();
}
It will only update when the users guesses alternate from less than the random number to more than the random number.
There is no need to increment the tally in 3 different places. The tally should be incremented right after you Integer.parseInt(...) statement. That is it will be updated every guess, no matter what the guess is.
Then you would just update the text of your tally label with the new count. There is no need to remove/add the label from the frame. There is no need to pack() the frame. Just setting the text will cause the label to repaint. So there is no need for the displayCount() method.
You should also be learning how to use layout manager correctly. Currently you are using a FlowLayout will will just cause the components to display in a row and then wrap to the next line. This is not a very effective layout as all the components will shift if the frame is ever resized.
Read the Swing tutorial on Layout Managers. You can nest panels with different layout managers to achieve a more flexible layout.
textField = new JTextField();
textField.setPreferredSize(new Dimension(250,40));
Don't use magic numbers for the size of a component. Instead use the API properly so the text field can determine its own preferred size:
textField = new JTextField(10);
The "10" will allow the text field to size itself to display 10 "W" characters before scrolling of the text.
Also, you should not use a shared ActionListener for your buttons.
One approach is to use a lamba to separate the functionality:
//playAgainButton.addActionListener(this);
playAgainButton.addActionListener((e) -> playAgain());
Then you create a private method playAgain() in your class. Now the code is separated into methods by function.

Adding a new button after an event

Writing a program to increment a counter when a +1 button is pressed, then when the counter reaches a certain number, remove the +1 button and replace it with a +2 button and so on. I create both buttons at first but just set btnCount1 to setVisible(false). When the certain number passes, I make btnCount invisible and btnCount1 visible and increment by two from there. When it reaches 10 clicks, the btnCount disappears, but btnCount1 does not appear.
I have tried making an if(arg0.equals(btnCount1)), and incrementing by two from there. I tried putting the add(btnCount1) inside the else if statement to create it after the elseif condition is true.
public class AWTCounter extends Frame implements ActionListener
private Label lblCount;
private TextField tfCount;
private Button btnCount;
private Button btnCount1;
private int count = 0;
public AWTCounter() {
setLayout(new FlowLayout());
lblCount = new Label("Counter");
add(lblCount);
tfCount = new TextField(count + "",10);
tfCount.setEditable(false);
add(tfCount);
btnCount = new Button("Add 1");
btnCount1 = new Button("Add 2");
add(btnCount);
add(btnCount1);
btnCount1.setVisible(false);
btnCount.addActionListener(this);
btnCount1.addActionListener(this);
setTitle("AWT Counter");
setSize(500,500);
}
public static void main(String[]args) {
AWTCounter app = new AWTCounter();
}
public void actionPerformed(ActionEvent arg0) {
if(count <= 10) {
++count; //Increase the counter value
tfCount.setText(count + "");
}else if(count > 10) {
btnCount.setVisible(false);
btnCount1.setVisible(true);
count += 2;
tfCount.setText(count + "");
}
}
The better solution here is to just have one button object and a separate variable for the current increment amount. When you hit the required count, increase the increment amount and change the button's label to the new value.
There are also a few other things you could do better here.
Use String.valueOf() instead of int + "" for String representations of integers if you're not adding words before or after the integer.
Don't add obvious comments for code. (e.g. 'increment variable x', 'set textString to the new value')
Use descriptive names for method parameters and variables.
Use Labels instead of TextFields for text that doesn't need to be editable or selectable like counter displays.
I'd personally change the name of lblCount to something like lblTitle as well, since changing your tfCount to a Label would logically take up that name and lblTitle makes more sense.
Here's a better way to implement actionPerformed:
private int increment = 1;
private Label lblCount;
...
public void actionPerformed(ActionEvent ignore) {
if(count == 10) {
btnCount.setLabel("Add " + (++increment));
}
lblCount.setText(String.valueOf(count += increment));
}

Using break to stop the moving lines in java

I am completely new to java and I have one problem I am trying to resolve. I am using ACM Library for my purpose. My goal is the following:
Move Label 1
Move Label 2
Compare label 1's and label 2's position (when the text hits and its on each other) "Using 'if' statement"
Once on top, stop it by using break statement.
Restart the cycle.
If someone could help me out by explaining how this can be implemented. That would be awesome. I am trying to learn. Thank you!
import acm.graphics.GLabel;
import acm.program.CommandLineProgram;
import acm.program.GraphicsProgram;
public class Main extends GraphicsProgram {
public void run( )
{
int label1_xy = 50;
int label2_xy = 200;
GLabel label1 = new GLabel("Hello World.");
add(label1, label1_xy,label1_xy);
GLabel label2 = new GLabel("Goodbye World.");
add(label2, label2_xy,label2_xy);
while (true)
{
label1.move(10,10);
pause(500);
label2.move(-10,-10);
break;
}
}
}
Move Label 1
Move Label 2
You have done these two.
Compare label 1's and label 2's position (when the text hits and its on each other) "Using 'if' statement"
Once on top, stop it by using break statement.
You can compare the position of those two labels by trying to retrieve the "bound" of the object. example :
label1.move(10,10);
pause(500);
if (label1.getBounds().getX() == label2.getBounds().getX()
&& label1.getBounds().getY() == label2.getBounds().getY()) {
break;
} else {
label2.move(-10,-10);
}
Restart the cycle
You can restart this by putting another while block for the run() code or maybe just call the run() (beware of the StackOverflowError here).

Error when I tried to run my Java class on another computer?

I am notdoing a school project, so please do not be alarmed. I am doing some private programming to brush up. My program, a program of type .java, creates a form that asks for the boundaries and quantities of a lottery-drawing activity and acts on the generating based on the input.
Here's my problem. On the WIndows 2000 computer where I coded the program, shows itself, perfectly. That's only half the story. When I tried to put it on another computer, the program shows a blank window; it compiles and runs, but it shows a blank window. Now, I do consider version numbers to be factors, so I will provide the versions and ask for confirmation if those are the root of the evil.
On my original computer, which is Windows 2000, the version is 1.6.0_31-b05. The other computer, which is Windows 7 dual-booted with Linux Mint 17.2, is running 1.8.0_60-b27 and 1.8.0_00 respectively.
My program is not finished yet, but I'll worry about that later. What I'm hoping to do now is to get the program, such as it is, to run on the platforms of all my computers. Since Java is known for its portability, I expect it to run on all my computers. Is that a misconception?
Anyways, here's the code:
//Import class libraries
import javax.swing.*;
import javax.swing.JOptionPane;
import java.awt.*;
import java.awt.event.*;
public class Lotterygui //Begin class
{
//VARIABLES FOR DATA COLLECTION
private JTextField lowerRange; //Lowest number
private JTextField higherRange; //Highest number
private JTextField quantity; //How many numbers to generate
private JTextArea displayArea; //What to display when the program is in use
//ADD WARNING CONSTANT FOR INVALID INPUT
private final String WARNING = "Please fill out valid data "
+ "and not leave anything out. "
+ "Also,do not enter any "
+ "zeroes.";
public Lotterygui()
{
//GUI CONFIGURATION
//Frame settings
JFrame jfrFrame = new JFrame("Lottery Program");
jfrFrame.setSize(300,400);
jfrFrame.setLocationRelativeTo (null);
jfrFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jfrFrame.setVisible(true);
jfrFrame.setResizable(false);
//Panel to hold the user input controls in place
JPanel jplInputs = new JPanel();
jplInputs.setLayout(new GridLayout(4, 2));
//CREATE INPUT CONTROLS
//Lowest range
JLabel jlblLowerRange = new JLabel("Lowest");
lowerRange = new JTextField();
//Highest range
JLabel jlblHigherRange = new JLabel("Highest");
higherRange = new JTextField();
//Quantity
JLabel jlblQuantity = new JLabel("Quantity");
quantity = new JTextField();
//Buttons and their respective action associations
//Generate numbers button
JButton jbtnGenerate = new JButton("Generate");
ActionListener alGenerate = new listenGenerate();
jbtnGenerate.addActionListener(alGenerate);
//Reset all values button
JButton jbtnReset = new JButton("Reset");
ActionListener alReset = new listenReset();
jbtnReset.addActionListener(alReset);
//ADD CONTROLS TO FORM
jplInputs.add(jlblLowerRange);
jplInputs.add(lowerRange);
jplInputs.add(jlblHigherRange);
jplInputs.add(higherRange);
jplInputs.add(jlblQuantity);
jplInputs.add(quantity);
jplInputs.add(jbtnGenerate);
jplInputs.add(jbtnReset);
//CREATE DISPLAY AREA AND ADD
//The display area used for showing generated numbers
displayArea = new JTextArea();
displayArea.setLineWrap(true);
displayArea.setText(WARNING);
//The control that sets autoscrolling for the display area
JScrollPane jspDisplayArea = new JScrollPane(displayArea);
jfrFrame.add(jspDisplayArea);
//Add the JPanels to the window
jfrFrame.add(jplInputs, BorderLayout.NORTH);
jfrFrame.add(jspDisplayArea);
}//END lotteryGUI constructor
//MAIN Method
public static void main (String[] args)
{
//CALL UP lotteryGUI CLASS
new Lotterygui();
}//END Main method
//GENERATE BUTTONS ACTION
private class listenGenerate implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
//DECLARE VARIABLES
int low; //Lowest number
int high; //Highest number
int qty; //How many numbers
try //Monitor the input of above variables in the form
{
low = Integer.parseInt(lowerRange.getText());
high = Integer.parseInt(higherRange.getText());
qty = Integer.parseInt(quantity.getText());
}
catch (NumberFormatException nfe)
{
//RESET ALL FORM VALUES
reset();
//RESET VARIABLE VALUES
low = 0;
high = 0;
qty = 0;
}//END format errors try-catch
//CHECK IF PROGRAM CAN CONTINUE
if (low != 0 || high != 0 || qty != 0) //If valid
{
//Action pending
displayArea.setText("Generate here - incomplete");
}
else //If there are more one or more errors in the input
{
//ISSUE WARNING
JOptionPane.showMessageDialog(null, WARNING);
}//END IF continue CHECK
}//END actionPerformed method
}//END listenGenerate class
I've been looking up and down at the code. Can this that I did not reference any of the layouts outlined in import? I know it's not JPanel as I did try that can still the problem existed. Anything that will help me will be appreciated. Thank you.
You're calling
jfrFrame.setVisible(true);
first and then adding a bunch of components to the JFrame, and that's backwards and can result in a GUI that does not render components until it is resized or minimized and restored. In fact try this -- run your program, then minimize the blank GUI and restore it, and I'll bet you'll see your components.
I suggest that you swap this order around -- call jfrFrame.setVisible(true); last after adding everything to the GUI.

Resetting a new thread repetitively

In my program, there is a button, "Display", and another button, "Reset".
The user enters the number of prime numbers they want in the text field and then clicks the "Display" button. Then, the first x prime numbers will appear in the text area.
In the code, I have:
Declarations:
Thread go;
Thread newThread;
JLabel howManyLabel;
JTextField howMany;
JButton display;
JButton reset;
JTextArea primes;
Action Event:
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source == display) {
display.setEnabled(false);
if (go == null) {
go = new Thread(this);
go.start();
} else {
newThread = new Thread(this);
newThread.start();
}
} else if (source == reset) {
display.setEnabled(true);
howMany.setText(" ");
primes.setText(" ");
}
}
Run method:
public void run() {
int quantity = Integer.parseInt(howMany.getText());
int numPrimes = 0;
int candidate = 2; // candidate = the number that MIGHT be prime
primes.append("First " + quantity + " primes:");
while(numPrimes < quantity) {
if (isPrime(candidate)) {
primes.append(candidate + ", ");
numPrimes++;
}
candidate++;
}
}
The run() method is in the same class, and simply calculates the first x amount of prime numbers.
I am trying to create a new thread every time the "Reset" button is called. The thread runs the first time, but then does not run again after I click "Reset". Can the run() method only work once?
Thanks in advance.
The run() method is just like any other method and can be invoked any number of times. The method that cannot be invoked multiple times is start()(according to Thread).
Your explanation does not seem to fit with the code you gave. You say you want to spawn a new thread when a user clicks reset, yet you only construct or start threads if the source is Display. Did you mean, rather, that you want to cancel the last thread and enable controls for the user to start again? In that case you should use a Future, not a generic thread.
Another thing is that UI components are generally not thread-safe. Swing explicitly warns against it on every components JavaDoc. What you may be seeing is just components not updating their visible state when they are changed from a different thread. Have you tried using a debugger to see if a thread is actually not getting spawned, or it is being spawned, but not having the result you want?

Categories