create random mines for a minesweeper game - java

JButton[][] buttons = new JButton[20][20];
public void mines(){
ArrayList<Integer> x = new ArrayList<>();
ArrayList<Integer> y = new ArrayList<>();
for(int a=0;a<20;a++){
x.add(a);
y.add(a);
}
for(int i=0;i<30;i++){
int random_x = x.get(new Random().nextInt(x.size()));
int random_y = y.get(new Random().nextInt(y.size()));
x.remove(random_x);
y.remove(random_y);
buttons[random_x][random_y].setText("X");
}
}
I Want to create random mines for a minesweeper game..can anyone tell what I am doing wrong ?If i run the program it won't show me 30 random mines

You have chosen an unusual model for holding information on where mines are located. While you will likely resolve the immediate problem through some judicious debugging I expect it will cause you further problems down the track.
I would suggest changing your model to be something more direct such as:
class Cell {
private final JButton button = new JButton();
private boolean mine = false;
private boolean hidden = true;
public Cell() {
button.setText(" ");
}
public void setMine() {
assert hidden;
mine = true;
}
public boolean hasMine() {
return mine;
}
public void reveal() {
hidden = false;
button.setText(mine ? "X" : "-");
}
public boolean isHidden() {
return hidden;
}
}
class Field {
public static final int SIZE = 20;
private final Cell[][] cells = new Cell[SIZE][SIZE];
public Field(int minesToAdd) {
for (int x = 0; x < SIZE; ++) {
for (int y = 0; y < SIZE; y++) {
cells[x][y] = new Cell();
}
}
Random random = new Random();
while (minesToAdd > 0) {
Cell cell = cells[random.nextInt(SIZE)][random.nextInt(SIZE)];
if (!cell.hasMine()) {
cell.setMine();
minesToAdd--;
}
}
}
public JPanel getButtonPanel() {
....
}
}
I believe that would make your intention clearer. There are a few issues with this such as the tight link between the model and presentation (JButton) but that's entirely fixable with various design patterns.

Try this method:
public static void mines(JButton[][] buttons)
{
Random rand = new Random();
int mineCount = 0;
while (mineCount < 30)
{
int randomInteger = (int) (rand.nextDouble() * buttons.length);
int randomInteger2 = (int) (rand.nextDouble() * buttons[0].length);
if (buttons[randomInteger][randomInteger2].getText().equals("X"))
continue;
else
{
buttons[randomInteger][randomInteger2].setText("X");
mineCount++;
}
}
}
.nextDouble() method returns a Double value less than 1.0. But we need a random integer between 0 and 19(which is buttons.length-1).
So we multiply this random double value with the size of the button list, which is 20, and we cast it to int. So we can get values between 0 and 19.
Difference between buttons.length and buttons[0].length is, with the first one you get the length of first dimension(it's a two dimension array, as you know) and the second one gives the length of the second dimension. So you get the number dynamically and multiply with the random number, to avoid ArrayIndexOutOfBounds exception.
You can use this method with any size of two dimensional buttons array, it will work. But warning, if you use an array which has less than 30 buttons, while loop will continue forever, as you can't get 30 mines :)
One way to improve this method would be parameterizing mineCount, so you can change the method like public static void mines(JButton[][] buttons, int mineCount) so you can set mine count on method call.

This here:
for (int i = 0; i < 30; i++) {
int random_x = x.get(new Random().nextInt(x.size()));
int random_y = y.get(new Random().nextInt(y.size()));
x.remove(random_x);
y.remove(random_y);
....
is going to generate a IndexOutOfBoundsException some when....
Your List have 20 elements and you are removing 30 times element from it...

Your random coordinates may be larger than your grid size. If the size of x is 20, and your random_x is also 20, you will have an IndexOutOfBoundsException.
Change it to:
int random_x = x.get(new Random().nextInt(x.size() - 1));
int random_y = y.get(new Random().nextInt(y.size() - 1));
Which will give you a random number between 0 and 19, if the size is 20.
Additionally, because your List is of type Integer, the x.remove(random_x) call is interpreting it as the index in the list, not the object itself.
To fix this, use an Integer, not an int when calling remove(). Such as:
x.remove(Integer.valueOf(random_x));
Edit:
To further improve/fix the random generator, change it to:
int random_x = x.get(new Random().nextInt(x.size() == 1 ? 1 : x.size() - 1));
When tested with the following snippet:
Test t = new Test();
t.mines();
for (int i = 0; i < 20; i++)
{
for (int j = 0; j < 20; j++)
{
if ("X".equals(t.buttons[i][j].getText())) {
System.out.println("x:" + i + ", y:" + j);
}
}
}
Gives the output:
x:0, y:18
x:1, y:5
x:2, y:1
x:3, y:3
x:4, y:9
x:5, y:15
x:6, y:14
x:7, y:10
x:8, y:8
x:9, y:11
x:10, y:16
x:11, y:17
x:12, y:0
x:13, y:7
x:14, y:4
x:15, y:12
x:16, y:2
x:17, y:6
x:18, y:13
x:19, y:19

You can try with this:
int x = 20;
int y = 20;
JButton[][] buttons = new JButton[x][y];
public void mines(){
int mines = 30;
Random rand = new Random()
while(mines>0){
int random_x = rand.nextInt(x);
int random_y = rand.nextInt(y);
if(buttons[random_x][random_y]!=null){
buttons[random_x][random_y] = new Button();
buttons[random_x][random_y].setText("X")
mines--;
}
}
}

Related

I am trying to avoid duplicates in my lottery rows ie if number 35 is in the first lottery row then it must not be in the second row

I am trying to avoid duplicates in my lottery rows , ie if number 35 is in the first lottery row then it must not be in the second row
I am confused about why I keep on getting duplicates I have added a image that explains my problem better?
What is the best way to go about this probelem?
I have tried to change nt lottoTal = rand.nextInt(35) + 1 to nt lottoTal = rand.nextInt(34) + 1
public class LottoCanvas extends BorderPane{
private Canvas canvas;
private GraphicsContext gc;
private Text text;
private Text textExtra; ///
private Set<Number> lottorad;
private Set<Number> lottoradExtra;//
Random rand;
public LottoCanvas() { //constructor
lottorad = new HashSet();
lottoradExtra = new HashSet();
rand = new Random();
canvas = new Canvas(250, 250);
gc = canvas.getGraphicsContext2D();
Button button = new Button("Rita om");
button.setOnAction(new EventHandler() {
#Override
public void handle(Event event) {
drawLotto(gc);
}
});//end EventHandler
VBox vbox = new VBox();
vbox.setPadding(new Insets(5,5,5,5));
vbox.setAlignment(Pos.CENTER);
text = new Text();
textExtra = new Text(); //
vbox.getChildren().addAll(text,textExtra,button);
drawLotto(gc);
setCenter(canvas);
setBottom(vbox);
}//end constructor
private void createLottoRad(){
lottorad.clear();
lottoradExtra.clear();
while (lottorad.size()<7) {
int lottoTal = rand.nextInt(35) + 1;
lottorad.add(lottoTal);
}
while (lottoradExtra.size()<4) {
int lottoTal = rand.nextInt(35) + 1;
lottoradExtra.add(lottoTal);
}
}//end metoden createLottoRad
private void drawLotto(GraphicsContext gc) {
createLottoRad();
int x = 0;
int y = 0;
int bredd=30;
int hojd=30;
for (int i = 0; i < 35; i++) {
x += 35;
if (i % 7 == 0){
y += 35;
x = 0;
}
if (lottorad.contains(i+1)){
gc.setFill(Color.RED);
}
if (lottoradExtra.contains(i+1)){
gc.setFill(Color.GREEN);
}
gc.fillRoundRect(x, y, bredd, hojd , 10, 10);
gc.setFill(Color.WHITE);
gc.fillText("" + (i + 1), x + 10, y + 20);
gc.setFill(Color.BLACK);
}
List myList = new ArrayList(lottorad);
Collections.sort(myList);
text.setText(myList.toString());
List myListExtra = new ArrayList(lottoradExtra);
Collections.sort(myListExtra);
textExtra.setText(myListExtra.toString());
}//end metoden drawLotto
}//enter piture of what i am trying to avoid
I am trying to avoid duplicates in my lottery rows , ie if number 35 is in the first lottery row then it must not be in the second row I am confused about why I keep on getting duplicates I have added a image that explains my problem better? What is the best way to go about this probelem?
Your current approach is something like this:
Add random numbers into lottorad until there are 7 unique values in it.
Add random numbers into lottoradExtra until there are 4 unique values in it.
The two sets are independent, so there is nothing to prevent from having the same numbers in both. You could add an explicit condition when populating lottoradExtra to skip over the number if it's already in lottorad, for example:
while (lottoradExtra.size() < 4) {
int lottoTal = rand.nextInt(35) + 1;
if (lottorad.contains(lottoTal)) {
continue;
}
lottoradExtra.add(lottoTal);
}
However, the current approach is flawed, since it's a bit of a waste to do extra loops when the same number keeps coming up repeatedly. In theory, your program might not terminate.
An alternative approach is the build a list of numbers, shuffle it, and just take the first 7 values, and then the first 4 values. That way it's guaranteed there will be no duplicates. For example:
List<Integer> numbers = IntStream.rangeClosed(1, 35).boxed().collect(Collectors.toList());
Collections.shuffle(numbers);
lottorad.clear();
lottorad.addAll(numbers.subList(0, 7));
lottoradExtra.clear();
lottoradExtra.addAll(numbers.subList(7, 11));

Java storing objects in a multidimensional array

I'm attempting to store objects in a multidimensional array in an attempt to save each position in a game boards 'state', however after the loop which is supposed to set each instance to it's own parameters they all end up the same variables. Did I not link the correct array or set it up wrong?
There's also a lot of "square.something should be accessed in a static way".
Is square.var or World[x][y].var the correct way for referencing the objects variables?
public static void generateMap() {
MapSquare[][] World = new MapSquare[10][10]; //2D array init.
//Choose a square for the home position.
Random homeRandom = new Random();
int HomeX = homeRandom.nextInt(10);
int HomeY = homeRandom.nextInt(10);
//Chooses a key room.
int KeyX = homeRandom.nextInt(10);
int KeyY = homeRandom.nextInt(10);
//Loop through the objects and set each's parameters.
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
MapSquare square = new MapSquare();
//World[i][j] = square;
//Calculate the level of the square from the distance to home.
int distX = HomeX - i;
int distY = HomeY - j;
int CalcX = Math.abs(distX); //Convert to positive if negative.
int CalcY = Math.abs(distY); //Convert to positive if negative.
//Generate contents of the square.
int newRandom = random.nextInt(5) + 1;
switch(newRandom) {
case 1: // Spawn a monster only.
{
square.monster = true;
square.treasure = false;
square.trap = false;
square.home = false;
square.peekable = false;
square.key = false;
square.mapLevel = CalcX + CalcY;
//Generate the monsters stats.
Monster monster = new Monster();
monster.setLevel(square.mapLevel);
monster.setMaxHealth(monster.getLevel() * 5);
monster.setHealth(monster.getMaxHealth());
monster.setDamage(monster.getLevel() * 2);
break;
}
}
}
}
//Generate home square.
World[HomeX][HomeY].monster = false;
World[HomeX][HomeY].treasure = false;
World[HomeX][HomeY].trap = false;
World[HomeX][HomeY].home = true;
World[HomeX][HomeY].peekable = true;
World[HomeX][HomeY].key = false;
World[HomeX][HomeY].mapLevel = 0;
World[HomeX][HomeY].visited = true;
}
The static keyword basically means that a method can be used independently of a specific instance of the class it is in. Such as the Math.abs(int i) function you are using, you don’t have to create an instance of the Math class to use it.
If you intend to use a class to store data it is generally a bad idea to include static methods as they would not be able to access that data.

Using Arrays Java [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Closed 8 years ago.
Improve this question
My question is:
Is this code correct and is it properly assigned to the Wheel speeds and duration
What do I need to do in order to add the main method to run the code
use a for loop to iterate through each row, instructing the Finch robot to move according to the numbers in each row. Pause after each movement using the code JOptionPane.showMessageDialog(null,"Click OK to continue...");
public class FinchArray {
public static final int LEFT_WHEEL = 0;
public static final int RIGHT_WHEEL = 1;
public static final int DURATION = 2;{
int[][] x = new int[10][3]; // 2D array filled with zeros
int time = 25;
for (int[] row : x) {
row[2] = time;
time += 25;
}
for (int[] row : x) {
row[DURATION] = time;
time += 25;
}
for (int[] row : x){
row[LEFT_WHEEL] = time;
time += 25;
}
for (int[] row : x){
row[RIGHT_WHEEL] = time;
time += 25;
}
Finch fRobot = new Finch();
fRobot.setWheelVelocities(LEFT_WHEEL,RIGHT_WHEEL,DURATION);
long before = System.currentTimeMillis();
while (System.currentTimeMillis() - before < 5000){
if(fRobot.isObstacle())break; {
}
fRobot.stopWheels();
fRobot.quit();
}
}
You can initialize your 2D array (which, in Java, is really just an array of 1D arrays) like this:
int[][] x = new int[10][3]; // 2D array filled with zeros
int time = 25;
for (int[] row : x) {
row[2] = time;
time += 25;
}
This uses an enhanced for loop and is equivalent to this:
int[][] x = new int[10][3]; // array filled with zeros
int time = 25;
for (int i = 0; i < x.length; ++i) {
int[] row = x[i];
row[2] = time;
time += 25;
}
Note that this leaves the left and right wheel speeds at their default values of zero.
Your current code is first creating a 10x3 2D array, but then your loop is reassigning each row to be a new 10-element array, so you end up with a 10x10 array (which is not what you want).
P.S. Using a 3-element array to represent three distinct pieces of data is not a good programming style (although it will work just fine). A slight improvement can be obtained by using symbolic constants for the subscripts. Declare at the top of your class:
public static final int LEFT_WHEEL = 0;
public static final int RIGHT_WHEEL = 1;
public static final int DURATION = 2;
Then use these to index into each row:
for (int[] row : x) {
row[DURATION] = time;
time += 25;
}
Since each row represents three different attributes, a better approach would be to define a separate class to contains the data:
public class Step {
public int leftWheelSpeed;
public int rightWheelSpeed;
public int duration;
}
Then instead of a 2D array of int values, you can declare a 1D array of Step objects:
Step[] x = new Step[10];
int time = 25;
for (Step step : x) {
step.duration = time;
time += 25;
}
I'm not sure, but is this maybe what you are looking for?
import java.util.Arrays;
public class FinchArray {
public static void main(final String[] args) {
int[][] array = new int[3][10]; // initializing the array
for(int i = 0; i < array.length; i++) { // outer for loop loops over first dimension
int n = 25; // initializing the counter from 25 to 250
for(int j = 0; j < array[i].length; j++) { // inner for loop loops over second dimension
array[i][j] = n; // access the slots with the two indices i and j and give them the value of n
n += 25;
}
n = 25; // reset n to 25 after finishing the inner for loop
}
System.out.println(Arrays.deepToString(array));
}
}
Output: [[25, 50, 75, 100, 125, 150, 175, 200, 225, 250], [25, 50, 75, 100, 125, 150, 175, 200, 225, 250], [25, 50, 75, 100, 125, 150, 175, 200, 225, 250]]

Java Loops and Random numbers

Hi I am having some problems with using random numbers inside of loops.
private void SetMines()
{
Random randRowGen = new Random();
Random randColGen = new Random();
int mineCount = 0;
int numMines = (ROWS * COLUMNS)* (int)0.156;
while(mineCount <= numMines)
{
int randRow = randRowGen.nextInt(ROWS)+1;
int randCol = randColGen.nextInt(COLUMNS)+1;
grid[randRow][randCol] = new Character('*');
mineCount++;
}
}
Here is my method it is going through an array size 25 * 25 and picking random spots and putting "mines" there. The only problem is it only selects one location to put a "mine" in and it needs to put 97 mines in random spots.
Any help will be appreciated thanks!!
Your numMines calculation will always return 0, because when you cast a double that is less than 1 to an int, it will be set to 0, which means that the statement in your while loop will only be run a single time, hence only a single mine being placed.
The problem isn't Random, it's int numMines = (ROWS * COLUMNS)* (int)0.156;. Have you checked what that value is? It's 0, because (int) 0.156 equals 0.
Perhaps you want int numMines = (int) ((double) 0.156 * ROWS * COLUMNS);. The problem with integer maths is that you can lose a LOT of precision.
Make your computer suffer and make it drop all those mines.
Remember. The definition of "computation" is "to force a machine do a boring job that nobody else would like to do" :-)
public static void main(String[] args) {
int rows = 25;
int cols = 25;
boolean[][] mines = new boolean[rows][cols];
while(mineCount(mines) < 97){
dropMine(mines);
}
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
System.out.print("[");
if (mines[i][j]){
System.out.print("*");
}else{
System.out.print(" ");
}
System.out.print("] ");
}
System.out.println();
}
}
private static void dropMine(boolean[][] mines) {
int x = (int)(Math.random()*25);
int y = (int)(Math.random()*25);
mines[x][y] = true;
}
private static int mineCount(boolean[][] mines) {
int count = 0;
for(int i=0;i<25;i++){
for(int j=0;j<25;j++){
if (mines[i][j]){
count++;
}
}
}
return count;
}

How to create a number generator that will only pick a number 1 time?

I am creating a concentration game.
I have an buffered image array where I load in a 25 image sprite sheet.
public static BufferedImage[] card = new BufferedImage[25];
0 index being the card back. and 1 - 24 being the values for the face of the cards to check against if the cards match.
What I am tying to do is this I will have 4 difficulties Easy, Normal, Hard, and Extreme. Each difficulty will have a certain amount of cards it will need to draw and then double the ones it chosen. for example the default level will be NORMAL which is 12 matches so it need to randomly choose 12 unique cards from the Buffered Image array and then double each value so it will only have 2 of each cards and then shuffle the results.
This is what I got so far but it always seems to have duplicates about 99% of the time.
//generate cards
Random r = new Random();
int j = 0;
int[] rowOne = new int[12];
int[] rowTwo = new int[12];
boolean[] rowOneBool = new boolean[12];
for(int i = 0; i < rowOneBool.length; i++)
rowOneBool[i] = false;
for(int i = 0; i < rowOne.length; i++){
int typeId = r.nextInt(12)+1;
while(rowOneBool[typeId]){
typeId = r.nextInt(12)+1;
if(rowOneBool[typeId] == false);
}
rowOne[i] = typeId;
j=0;
}
the 3 amounts I will be needing to generate is Easy 6, Normal 12, and Hard 18 extreme will use all of the images except index 0 which is the back of the cards.
This is more or less in the nature of random numbers. Sometimes they are duplicates. You can easily factor that in though if you want them to be more unique. Just discard the number and generate again if it's not unique.
Here's a simple method to generate unique random numbers with a specified allowance of duplicates:
public static void main(String[] args) {
int[] randoms = uniqueRandoms(new int[16], 1, 25, 3);
for (int r : randoms) System.out.println(r);
}
public static int[] uniqueRandoms(int[] randoms, int lo, int hi, int allowance) {
// should do some error checking up here
int range = hi - lo, duplicates = 0;
Random gen = new Random();
for (int i = 0, k; i < randoms.length; i++) {
randoms[i] = gen.nextInt(range) + lo;
for (k = 0; k < i; k++) {
if (randoms[i] == randoms[k]) {
if (duplicates < allowance) {
duplicates++;
} else {
i--;
}
break;
}
}
}
return randoms;
}
Edit: Tested and corrected. Now it works. : )
From what I understand from your question, the answer should look something like this:
Have 2 classes, one called Randp and the other called Main. Run Main, and edit the code to suit your needs.
package randp;
public class Main {
public static void main(String[] args) {
Randp randp = new Randp(10);
for (int i = 0; i < 10; i++) {
System.out.print(randp.nextInt());
}
}
}
package randp;
public class Randp {
private int numsLeft;
private int MAX_VALUE;
int[] chooser;
public Randp(int startCounter) {
MAX_VALUE = startCounter; //set the amount we go up to
numsLeft = startCounter;
chooser = new int[MAX_VALUE];
for (int i = 1; i <= chooser.length; i++) {
chooser[i-1] = i; //fill the array up
}
}
public int nextInt() {
if(numsLeft == 0){
return 0; //nothing left in the array
}
int a = chooser[(int)(Math.random() * MAX_VALUE)]; //picking a random index
if(a == 0) {
return this.nextInt(); //we hit an index that's been used already, pick another one!
}
chooser[a-1] = 0; //don't want to use it again
numsLeft--; //keep track of the numbers
return a;
}
}
This is how I would handle it. You would move your BufferedImage objects to a List, although I would consider creating an object for the 'cards' you're using...
int removalAmount = 3; //Remove 3 cards at random... Use a switch to change this based upon difficulty or whatever...
List<BufferedImage> list = new ArrayList<BufferedImage>();
list.addAll(Arrays.asList(card)); // Add the cards to the list, from your array.
Collections.shuffle(list);
for (int i = 0; i < removalAmount; i++) {
list.remove(list.size() - 1);
}
list.addAll(list);
Collections.shuffle(list);
for (BufferedImage specificCard : list) {
//Do something
}
Ok, I said I'd give you something better, and I will. First, let's improve Jeeter's solution.
It has a bug. Because it relies on 0 to be the "used" indicator, it won't actually produce index 0 until the end, which is not random.
It fills an array with indices, then uses 0 as effectively a boolean value, which is redundant. If a value at an index is not 0 we already know what it is, it's the same as the index we used to get to it. It just hides the true nature of algorithm and makes it unnecessarily complex.
It uses recursion when it doesn't need to. Sure, you can argue that this improves code clarity, but then you risk running into a StackOverflowException for too many recursive calls.
Thus, I present an improved version of the algorithm:
class Randp {
private int MAX_VALUE;
private int numsLeft;
private boolean[] used;
public Randp(int startCounter) {
MAX_VALUE = startCounter;
numsLeft = startCounter;
// All false by default.
used = new boolean[MAX_VALUE];
}
public int nextInt() {
if (numsLeft <= 0)
return 0;
numsLeft--;
int index;
do
{
index = (int)(Math.random() * MAX_VALUE);
} while (used[index]);
return index;
}
}
I believe this is much easier to understand, but now it becomes clear the algorithm is not great. It might take a long time to find an unused index, especially when we wanted a lot of values and there's only a few left. We need to fundamentally change the way we approach this. It'd be better to generate the values randomly from the beginning:
class Randp {
private ArrayList<Integer> chooser = new ArrayList<Integer>();
private int count = 0;
public Randp(int startCounter) {
for (int i = 0; i < startCounter; i++)
chooser.add(i);
Collections.shuffle(chooser);
}
public int nextInt() {
if (count >= chooser.size())
return 0;
return chooser.get(count++);
}
}
This is the most efficient and extremely simple since we made use of existing classes and methods.

Categories

Resources