0

I have been only coding for a couple of months and I am trying to create a very simple guessing game. The game consists of a 5x5 square grid where the user will need to click on the square buttons and hope/pray that it is one of the correct squares. Now when I say correct squares they are randomly generated and stored in an array. However, whenever a set amount of random integers are generated, a few of them will generate duplicate numbers (this is no good or else it will be impossible to win my game). I am so close to finishing my code but my very last step is to find a way to solve this question.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;

public class Display implements ActionListener {

    int lives = 10;
    int correct_counter = 0;

    Random random_int = new Random();
    int[] correct_squares = new int[10];

    JFrame frame = new JFrame();
    JPanel tile_panel = new JPanel();
    JButton[] button = new JButton[25];
    JButton reset_button = new JButton();
    JTextField title = new JTextField();
    JTextField motto = new JTextField();
    JTextField failure = new JTextField();
    JTextField success = new JTextField();
    JTextField life_label = new JTextField();
    JLabel life_counter = new JLabel();
   

    public Display(){

      for (int i = 0; i < correct_squares.length; i++){
        correct_squares[i] = random_int.nextInt(25);
      }
      Collections.shuffle(Arrays.asList(correct_squares));
      System.out.println(Arrays.toString(correct_squares));

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(600,700);
        frame.getContentPane().setBackground(Color.GRAY);
        frame.setLayout(null);

        title.setBounds(0,0,600,50);
        title.setBackground(Color.GRAY);
        title.setForeground(Color.GREEN);
        title.setFont(new Font("Ink Free", Font.BOLD, 50));
        title.setEditable(false);
        title.setHorizontalAlignment(JTextField.CENTER);
        title.setBorder(null);
        title.setText("Guessing Game");

        motto.setBounds(0,50,600,75);
        motto.setBackground(Color.GRAY);
        motto.setForeground(Color.GREEN);
        motto.setFont(new Font("Ink Free", Font.BOLD, 25));
        motto.setEditable(false);
        motto.setHorizontalAlignment(JTextField.CENTER);
        motto.setBorder(null);
        motto.setText("Test your luck!");

        life_label.setBounds(480,260,100,25);
        life_label.setBackground(Color.GRAY);
        life_label.setForeground(Color.GREEN);
        life_label.setFont(new Font("Ink Free",Font.PLAIN,25));
        life_label.setText("Lives: ");
        life_label.setBorder(null);
        life_label.setEditable(false);

        life_counter.setBounds(500,300,100,25);
        life_counter.setForeground(Color.GREEN);
        life_counter.setFont(new Font("Ink Free",Font.PLAIN,25));
        life_counter.setText(String.valueOf(lives));

        tile_panel.setBounds(25,120,400,400);
        tile_panel.setBackground(Color.GRAY);
        tile_panel.setLayout(new GridLayout(5,5, 1,1));

        for (int i = 0; i < button.length; i++){
            button[i] = new JButton();
            button[i].setBackground(Color.WHITE);
            button[i].setSize(80,80);
            button[i].addActionListener(this);

            tile_panel.add(button[i]);
        }

        // Create Reset Button
        reset_button.setBounds(480,400,75,25);
        reset_button.addActionListener(this);
        reset_button.setBackground(Color.WHITE);
        reset_button.setForeground(Color.GREEN);
        reset_button.setFont(new Font("Times New Roman", Font.PLAIN,25));
        reset_button.setBorder(BorderFactory.createLineBorder(Color.GREEN, 2));
        reset_button.setText("Restart");

        frame.add(motto);
        frame.add(reset_button);
        frame.add(tile_panel);
        frame.add(life_label);
        frame.add(life_counter);
        frame.add(title);
        frame.setVisible(true);
    }

    @Override
    public void actionPerformed(ActionEvent e) {

        boolean correctanswer = false;

        if (e.getSource() == reset_button){
          
          if(lives == 0){
            failure.setVisible(false);
            frame.remove(failure);
          }
          else if(correct_counter == correct_squares.length){
            success.setVisible(false);
            frame.remove(success);
          }

          for (int i = 0; i < correct_squares.length; i++){
            correct_squares[i] = random_int.nextInt(26);
          }
          Collections.shuffle(Arrays.asList(correct_squares));
          System.out.println(Arrays.toString(correct_squares));

          for(int i = 0; i < button.length; i++){
            button[i].setBackground(Color.WHITE);
            button[i].setEnabled(true);
          }          

          correct_counter = 0;
          lives = 10;
          life_counter.setText(String.valueOf(lives));
    
        }

        for(int i = 0; i < button.length; i++){
            if (e.getSource() == button[i]){
                for (int k = 0; k < correct_squares.length; k++){
                    if (correct_squares[k] == i){
                      button[i].setBackground(Color.GREEN);
                      button[i].setEnabled(false);
                      correctanswer = true;
                      k = correct_squares.length;
                      correct_counter++;
                    } 
                }
                if (correctanswer == false){
                    button[i].setBackground(Color.RED);
                    button[i].setEnabled(false);
                    lives--;
                    life_counter.setText(String.valueOf(lives));
                    i = button.length;
                }
                else if (correct_counter == correct_squares.length){
                  i = button.length;
                }
            }
        }
        
        gameOver();

    }

    public void gameOver(){

      if(lives == 0){

        for(int i = 0; i < button.length; i++){
          button[i].setEnabled(false);
          button[i].setBackground(Color.RED);
        }

        // Create Game Over Label
        failure.setBounds(100,550,250,50);
        failure.setBackground(Color.BLACK);
        failure.setForeground(Color.RED);
        failure.setFont(new Font("Times New Roman",Font.PLAIN,50));
        failure.setBorder(null);
        failure.setEditable(false);
        failure.setText("YOU DIED");
        failure.setVisible(true);

        frame.add(failure);

      }
      else if (correct_counter == correct_squares.length){

        for(int i = 0; i < button.length; i++){
          button[i].setEnabled(false);
          button[i].setBackground(Color.GREEN);
        }

        success.setBounds(100,550,250,50);
        success.setBackground(Color.WHITE);
        success.setForeground(Color.GREEN);
        success.setFont(new Font("Free Ink",Font.PLAIN,50));
        success.setBorder(null);
        success.setEditable(false);
        success.setText("YOU WON");
        success.setVisible(true);

        frame.add(success);
      }
    }
} 

Now if you can find where I generated the random numbers, you may also find a Collection.shuffle() method being called. Other post similar to what my question was told to do this Fisher–Yates shuffle. However, it doesn't seem to work and I don't understand how that shuffle method helps let alone what it does (other than shuffle numbers).

public class Main {
  public static void main(String[] args){
      new Display();
  }
}
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 4
    Does this answer your question? [Generating Unique Random Numbers in Java](https://stackoverflow.com/questions/8115722/generating-unique-random-numbers-in-java) – Doktor OSwaldo Jun 23 '22 at 06:58
  • If you need a 5x5 filled with numbers between 0 to 24, just fill it incrementally, then shuffle it. – akarnokd Jun 23 '22 at 07:01
  • 2
    "I don't understand how that shuffle method helps let alone what it does (other than shuffle numbers)." Think about the problem carefully. You have 25 places that you want to put a number, each of which should range from 1 to 25 inclusive, and there should not be any duplicates. Right? Therefore, there should be *exactly one of each* of those numbers. Right? So, what we're **really** trying to do is, take a sequence of numbers, from 1 up to 25 (so that we have exactly one of each of those numbers), and then randomly decide where each one goes. Right? But **that's what shuffling is**... right? – Karl Knechtel Jun 23 '22 at 07:01
  • 1
    The thing about `Collections.shuffle()` you probably got from [this stackoverflow answer](https://stackoverflow.com/a/8115744/11441011), however you are implementing it incorrectly. The idea behind this solution is to create a set of unique numbers (a sequence in this case), to shuffle this set, and then to take the first x (as many as you need) numbers from that shuffled set. However, you create random numbers (which _might_ give you duplicates, and you don't want that), and you also don't store and use the shuffled result. – maloomeister Jun 23 '22 at 07:01
  • What do you mean by "taking the first x"? – AskingLotsOfQuestions Jun 23 '22 at 08:05
  • just to clarify as well, i wanted to randomly generate 10 numbers between 0 <= correct_sqaures < 25 and set them to be the squares that the user needs to find. The user then needs to guess and find each 10 correct squares without clicking on the wrong squares which will lose a life. – AskingLotsOfQuestions Jun 23 '22 at 08:18
  • 1
    `int[] array = ThreadLocalRandom.current().ints(0, 25).distinct().limit(10) .toArray();` As a side note, `Collections.shuffle(Arrays.asList(correct_squares));` has no effect at all, because you have a primitive array. So `Arrays.asList(correct_squares)` will create a `List` with a single element, the array. Shuffling a single element list obviously doesn’t change anything. – Holger Jun 23 '22 at 09:09
  • @AskingLotsOfQuestions I don't understand what you mean by "10 numbers between 0 <= correct_sqaures < 25" . The operator "between" can only take 2 arguments (low and high) but you supply three: 0, correct_squares, and 25. – k314159 Jun 23 '22 at 09:21
  • Also, you're using `random_int.nextInt(25)` which gives a value between 0 and 24. Is that what you want, or do you want a value between 1 and 25? Or perhaps you want a value between 1 and 10, or between 0 and 9? Give us a graphical example of what your matrix should look like. – k314159 Jun 23 '22 at 09:24

3 Answers3

0

To generate a pseudorandom permutation without keeping all items in a data structure you can build a block cipher of appropriate block size (e.g. 16bits for values in the 0 - (216-1) range) and operate it counter mode. For non-power-of-two ranges you can use rejection sampling.

This is primarily for very long sequences where the memory overhead would be significant. For short sequences shuffling a collection is easier to implement.

the8472
  • 40,999
  • 5
  • 70
  • 122
0

I suggest you separate the matrix generator code from the code that displays your matrix.

This small method can generate a matrix with the given size:

private List<Integer>[] generateMatrix(int size) {
    List<Integer>[] matrix = new List[size];
    IntStream.range(0, matrix.length).forEach(i -> {
        matrix[i] = IntStream.rangeClosed(1, size).boxed().collect(Collectors.toList());
        Collections.shuffle(matrix[i]);
    });
    return matrix;
}

It creates an array (your matrix) with the proper size, fills in the matrix items with ordered numbers, and shuffles that sequence.

Then this way you can show your generated matrix:

for (List l : generateMatrix(5)) {
   System.out.println(l);
}

Hope that this can help you.

Result:

[5, 1, 4, 2, 3]
[1, 5, 2, 4, 3]
[4, 3, 2, 1, 5]
[4, 1, 3, 5, 2]
[5, 4, 2, 3, 1]
zappee
  • 20,148
  • 14
  • 73
  • 129
-1
/**
 * @param count how many random numbers you want to generate
 * @implNote if you don't care about extreme performance or something like that, this implementation just works
 * */
static int[] genRandomNumbers(int count)
{
    var rand = new Random();
    var set = new HashSet<Integer>(); // use hash set to store unique numbers
    while(set.size() < count) // fill the set with random numbers
        set.add(rand.nextInt());

    // move numbers into array
    var ret = new int[count];
    var step = 0;
    for(var number : set)
    {
        ret[step] = number;
        step++;
    }
    return ret;
}
Firok
  • 269
  • 1
  • 6