简体   繁体   中英

Trouble with generating randomly dispersed true and false values across list

I am designing a minesweeper game in Java, and I'm having trouble with the generation of what tiles are mines / not mines. Here is my code so far:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Random;
import java.util.Scanner;

public class MineSweeper {
    private final boolean[][] mines;
    public char[][] field;

    public MineSweeper(int x, int y, int numMines) {
        field = new char[x][y];

        boolean[][] tmpMines = new boolean[x][y];

        Random rand = new Random();

        // Here is where I need to use rand to evenly disperse mines across 
        // the `tmpMines` array...

        for (int i = 0; i < x; i++)
            for (int j = 0; j < y; j++) {
                field[i][j] = 'X';
            }

        mines = tmpMines;
    }

    public void showFor(int x, int y) {
        int count = 0;

        for (int[] i : new int[][]{ /* Sides */ {x + 1, y}, {x - 1, y}, {x, y - 1}, {x, y + 1}, /* Corners */ {x + 1, y + 1}, {x - 1, y -1}, {x + 1, y - 1}, {x - 1, y + 1}}) {
            try {
                if (mines[i[0]][i[1]] == true)
                    count++;
            } catch (ArrayIndexOutOfBoundsException ex) {
                // Easiest way to handle overflow.
            }
        }

        field[x][y] = Integer.toString(count).charAt(0);
    }

    private static void printCharMatrix(char[][] matrix) {
        for (char[] a : matrix) {
            for (char c : a) {
                System.out.print(c + " ");
            }
            System.out.println();
        }
    }

    private static void printBoolMatrix(boolean[][] matrix) {
        for (boolean[] a : matrix) {
            for (boolean b : a) {
                if (b == true)
                    System.out.print("X ");
                else
                    System.out.print("O ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String input;

        Pattern patt = Pattern.compile("^\\s*(\\d+)\\s*,\\s*(\\d+)\\s*$"); // Matches any number of spaces, a digit, spaces, a comma, spaces, and a digit and extracts the 2 digits

        System.out.println("*** Welcome to MineSweeper(tm)!!! ***");
        System.out.println();
        System.out.print("Enter the number of columns: ");
        int x = scan.nextInt();
        System.out.print("Enter the number of rows: ");
        int y = scan.nextInt();
        System.out.print("Enter the number of mines: ");
        int mines = scan.nextInt();
        MineSweeper ms = new MineSweeper(x, y, mines);

        scan.nextLine();

        while (true) {
            System.out.println("Board:");
            printCharMatrix(ms.field);
            System.out.print("Type an array index (ex: 1,1). 'quit' to quit: ");
            input = scan.nextLine().toLowerCase();

            if (input.equalsIgnoreCase("quit"))
                System.exit(0);

            Matcher match = patt.matcher(input);

            if (match.find()) {
                x = Integer.parseInt(match.group(1));
                y = Integer.parseInt(match.group(2));
                if (ms.mines[x][y] == true) {
                    System.out.println("You failed!");
                    System.out.println("The board was: ");
                    System.out.println();
                    printBoolMatrix(ms.mines);
                    System.exit(0);
                } else {
                    ms.showFor(x, y);
                }
            } else {
                System.out.println("Invalid input: " + input);
            }
        }
    }
}

What I need to do is evenly disperse mines across mines ( true if it is a mine, false if it's not) based on a provided number of mines and the size of the matrix ( x * y ). I have tried several strategies, but none of them have worked. Thanks!

You can fill your tmpMines array with numMines true and x*y-numMines false, and use shuffle algorithm on it.

There are several shuffle algorithms to achieve this, for example, you may want to use this answer to shuffle your 2-dimensional array like this.

// fill tmpMines array
for(int i = 0; i < x; i++) {
    for (int j = 0; j < y; j++) {
        if (numMines > 0) {
            numMines--;
            tmpMines[i][j] = true;
        } else {
            tmpMines[i][j] = false;
        }
    }
}
// shuffle tmpMines array
for(int i = 0; i < x; i++) {
    for (int j = 0; j < y; j++) {
        // int swapPos = rand.nextInt(x*y);  this swapPos selection is not correct, please use the code next line.
        int swapPos = x*y - rand.nextInt(x*y-i*y+j);
        int swapPosY = swapPos / x;
        int swapPosX = swapPos % x;

        boolean tmp = tmpMines[i][j];
        tmpMines[i][j] = tmpMines[swapPosX][swapPosY];
        tmpMines[swapPosX][swapPosY] = tmp;
    }
}

I directly use and modify numMines here because it will not be used after this code. If you want numMines to not be modified, use a temporary variable instead.

ps Your column & row are mixed up in your code (I got 7 columns when my input says 7 rows). Please be aware of them when you are using 2-dimensional array.

Try this:

  • Create a list big enough to hold all values
  • Populate the list with exactly the quantity of true/false needed
  • Shuffle the list
  • populate the final array with values from the list

You don't need much code to do all that:

public MineSweeper(int x, int y, int numMines) {
    mines = new boolean[y][x];
    List<Boolean> values = IntStream.range(0, x * y)
      .mapToObj(i -> i < numMines).collect(Collectors.CotoList());
    Collections.shuffle(values);
    IntStream.range(0, y).forEach(i -> IntStream.range(0, x)
      .forEach(j -> mines[i][j] = values.get(i * x + j)));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM