0

I made a boolean for my while loop to make it run when the boolean is equal to true, I wanted to make the play button make the boolean = true which would trigger the while loop and run the game. But this isn't working for some reason.

Can someone help with making the boolean gameRunning = true;? I just can't figure out how to change its value from false to true.

I tried using atomic booleans but that didn't work

package panda.org;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.lang.Math;
import java.util.concurrent.atomic.AtomicBoolean;

public class NumberGame implements ActionListener{

    JFrame frame;
    JLabel rules;
    JLabel rulesText;
    JLabel rulesText2;
    JButton play;
    JButton exit;

    Font myFont = new Font("Serif Plain", Font.BOLD, 15);

    NumberGame() {

        frame = new JFrame("NumberGame");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(600, 500);
        frame.setLocationRelativeTo(null);
        frame.setLayout(null);
        frame.setResizable(true);
        Image icon = Toolkit.getDefaultToolkit().getImage("C:\\Users\\Gaming MSI\\Pictures\\Saved Pictures\\download (1).png");
        frame.setIconImage(icon);

        rules = new JLabel("Rules: ");
        rules.setFont(myFont);
        rules.setBounds(50, 100, 100, 75);

        rulesText = new JLabel("We will pick a random number in the range of 1 -> 50.");
        rulesText.setBounds(100, 100, 315, 75);

        rulesText2 = new JLabel("Your job is to guess that number!");
        rulesText2.setBounds(100, 120, 315, 75);

        play = new JButton("Play");
        play.setBounds(150, 300, 100, 75);

        boolean gameRunning = false;

        play.addActionListener(e -> {
            gameRunning = true;
        });

        while(gameRunning = true) {

            JLabel label = new JLabel("Guess the number from 1 till 50");
            label.setFont(myFont);
            label.setBounds(150, 75, 315, 75);

            JLabel hints = new JLabel("");
            hints.setBounds(150, 180, 1000, 100);

            JLabel hints2 = new JLabel("");
            hints2.setBounds(150, 200, 1000, 100);

            JTextField text = new JTextField();
            text.setBounds(250, 150, 100, 25);

            JButton check = new JButton("Check");
            check.setBounds(150, 150, 75, 25);

            double randomDouble = Math.random();
            randomDouble = randomDouble * 50 + 1;

            double randomDouble2 = Math.random();
            randomDouble2 = randomDouble2 * (15 - 5 + 1) + 5 ;

            double randomDouble3 = Math.random();
            randomDouble3 = randomDouble3 * (15 - 5 + 1) + 5 ;

            int randomHint = (int) randomDouble2;
            int randomHint2 = (int) randomDouble3;
            int randomInt = (int) randomDouble;

            System.out.println("nb: " + randomInt);
            System.out.println("hint: " + randomHint);
            System.out.println("hint2: " + randomHint2);

            JLabel status = new JLabel("");
            status.setBounds(150, 160, 1000, 100);

            JLabel closeness = new JLabel("");
            closeness.setBounds(150, 220, 1000, 100);
            closeness.setForeground(Color.blue);

            final int[] failedAttempts = {0};

            check.addActionListener(e1 -> {

                String nb = text.getText();
                int change = Integer.parseInt(nb);

                frame.add(status);

                if (randomInt == change) {
                    status.setText("You chose the correct number!");
                    status.setForeground(Color.green);

                    hints.setText("");
                    hints2.setText("");
                }
                if (randomInt > change) {

                    closeness.setText("Your answer is smaller than the correct answer");

                }
                if (randomInt < change) {

                    closeness.setText("Your answer is larger than the correct answer");

                }
                if (randomInt != change) {
                    status.setText("Wrong choice! Try again.");
                    status.setForeground(Color.red);
                    failedAttempts[0]++;

                    if (failedAttempts[0] == 3) {

                        int plus = randomInt + randomHint;
                        int minus = randomInt - randomHint2;

                        hints.setText("Hint: I see you are struggling, here is a low range to make it easier!");
                        hints2.setText("The lowered range is from " + plus + " to " + minus);
                    }
                }
            });

            rules.setText("");
            rulesText.setText("");
            rulesText2.setText("");

            frame.add(hints);
            frame.add(hints2);
            frame.add(label);
            frame.add(check);
            frame.add(closeness);
            frame.add(text);
        }

        exit = new JButton("Exit");
        exit.setBounds(350, 300, 100, 75);
        exit.addActionListener(e -> {

            int result = JOptionPane.showConfirmDialog(frame,"Are you sure want to exit?", "Exit",
                    JOptionPane.YES_NO_OPTION,
                    JOptionPane.QUESTION_MESSAGE);
            if(result == JOptionPane.YES_OPTION){
                System.exit(0);
                }
        });

        frame.add(play);
        frame.add(exit);
        frame.add(rules);
        frame.add(rulesText);
        frame.add(rulesText2);

        frame.setVisible(true);
    }

    public static void main(String[] args) {

        NumberGame number = new NumberGame();

    }

    @Override
    public void actionPerformed(ActionEvent e) {

    }
}


Frakcool
  • 10,915
  • 9
  • 50
  • 89

2 Answers2

2

You're still thinking in console applications, Swing is designed to work with events...

  • The user pressed a button? An event
  • Some timer triggered something in the background? An event
  • User typed something? An event

So, with the above in mind, you cannot expect your code here:

play.addActionListener(e -> {
    gameRunning = true;
});

while(gameRunning = true) {
    ...
}

To be executed in that order, because you have no control of when the user is going to press the button, it could be either in 2 seconds, or could be in 2 hours

For this to happen, you might need to move the while loop to a method, and when the user presses the play button, you need to change gameRunning = true and then call that other method, something like this:

public void runGame() {
    while(gameRunning) {
        // Your code here
    }
}

play.addActionListener(e -> {
    if (!gameRunning) { //This validation is needed otherwise if you press the button multiple times you'll have multiple loops running
        gameRunning = true;
        runGame();
    }
});

This way, you don't start your game until the user presses the play button.

Notice how I just wrote while(gameRunning) without ==, as mentioned in the comments above, if you have gameRunning = true you're assigning it a value instead of comparing it, when using boolean variables you can simply write them like that, it reduces the possibility of typos like that one.

  • if(true) is the same as if (true == true)
  • if(!false) is the same as if (false == false)
  • if(false) is the same as if (true == false)

And as I mentioned in my previous answer avoid the use of null-layout and setBounds and why are you implementing ActionListener and never using it? It's empty, so just remove implements ActionListener

Frakcool
  • 10,915
  • 9
  • 50
  • 89
  • Thank you this is very helpful! but if the loop is in its own method how will it access the gameRunning boolean? and if I define it in the method the button will not be able to access that boolean – Ahmad Nasser Apr 29 '21 at 21:20
  • Either pass it as a parameter or make it a global variable – Frakcool Apr 29 '21 at 21:21
  • Btw doing all that `JLabel` creation and adding `ActionListeners` inside the `while` loop is going to make your game slow at some point, you'd need to remake it at some point if that happens – Frakcool Apr 29 '21 at 21:23
  • I hate to annoy you but I have one more question! so when I run the game the loop runs but it runs at insane speeds and repeats so much, I want it to just run normally like its supposed to – Ahmad Nasser Apr 29 '21 at 21:27
  • Well, your code is too big to test it, reduce it to a [mre], ask a second question where you take all the tips given in your previous and this question, and try to reproduce the problem. It shouldn't be longer than idk 100 lines (and I'm going big), remove all unnecessary code such as colors, validations not needed, multiple labels, buttons, make it as simple as possible, otherwise I'm afraid I cannot check this code for too long – Frakcool Apr 29 '21 at 21:30
  • I tried doing this ``` public void run() { //game loop long lastTime = System.nanoTime(); double amountOfTicks =60.0; double ns = 1000000000 / amountOfTicks; double delta = 0; while(true) { long now = System.nanoTime(); delta += (now -lastTime)/ns; lastTime = now; if(delta >=1) { runGame(); delta--; } } } ``` but it didnt work – Ahmad Nasser Apr 29 '21 at 21:30
  • Okay working on a minimal version as I write this – Ahmad Nasser Apr 29 '21 at 21:38
  • I made a new post with a minimal version of the code – Ahmad Nasser Apr 29 '21 at 21:46
0

You can not change the value of a local variable in a lambda function. As I am sure the compiler is telling you, they need to be final or effectively final which means they are only assigned once.

The solution is to use a type that can hold your Boolean and change the value that it is holding instead.

    AtomicBoolean gameRunning = new AtomicBoolean(false);

    play.addActionListener(e -> {
        gameRunning.set(true);
    });

    ...

    while(gameRunning.get()) {
    }
SephB
  • 617
  • 3
  • 6
  • But when I click play it still does nothing? I tried ```while(gameRunning.get())``` and ```while(gameRunning.get()) == true``` and it still didnt do anything. (yes i did change the other stuff u told me to do – Ahmad Nasser Apr 29 '21 at 21:05
  • While my answer is a solution, you may want to rethink how your main game loop works. As it is written now it will run as fast as it can and will probably do multiple loops per UI render. Trying to slow it down by calling sleep() will just make it less responsive. Instead you need to find out how to run the main loop code once per frame. – SephB Apr 29 '21 at 21:05
  • how would i do that exactly? – Ahmad Nasser Apr 29 '21 at 21:07
  • @AhmadNasser Put a break point in the debugger and you should see that the main loop is running. I am assuming you are doing something wrong with Swing, but I don't have much knowledge in that area. – SephB Apr 29 '21 at 21:07
  • I was using this code inside the play button and it worked fine, but I wanted to make it so after 10 attempts the game would be lost and a while loop is the only way to do that... but new complications rised – Ahmad Nasser Apr 29 '21 at 21:08
  • I see why your main loop is not running. The loop evaluates right after you add the listener for the start game button. By the time you set it to True you are already past the loop. – SephB Apr 29 '21 at 21:08
  • so in that case should i make the while loop inside the button? – Ahmad Nasser Apr 29 '21 at 21:10
  • It may be a bit advanced for you, but I would look into using threads and executors, specifically ScheduledExecutorService.scheduleAtFixedRate to run your game at 60FPS or whatever rate works for you. Edit: Also research how to interact with Swing from another thread, that generally takes some sort of trick in UI frameworks. In JavaFX you call Platform.runlater but I don't know on Swing. – SephB Apr 29 '21 at 21:11