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.
Related
So I have created this program to randomly place objects in a room with a number of constraints. One such constraint is that every object has to be at least dis distance away from every other object already placed in the room. My entire code works well, however I often have to problem that the code stays stuck in the while loop. Here is that part of the code:
// distance vector to check whether the distance is kept
double[] dis2 = new double[k+1];
// distance vector to check whether the distance to the input/output is kept
double[] dis4 = new double[2];
dis4[0] = Math.abs(NewObjectX - inputX) + Math.abs(NewObjectY - inputY);
dis4[1] = Math.abs(NewObjectX - outputX) + Math.abs(NewObjectY - outputY);
// Check the distance constraints
int l = 0;
while (l<k+1) {
dis2[l] = Math.abs(NewObjectX - PlacedX[l]) + Math.abs(NewObjectY - PlacedY[l]);
if (dis2[l]<dis || dis3>2.5*dis || dis4[0]<dis || dis4[1]<dis) {
NewObjectX = Math.random()*(dim[1]-dim[0]) + dim[0]*0.5;
NewObjectY = Math.random()*(dim[3]-dim[2]) + dim[2]*0.5;
dis3 = Math.abs(NewObjectX - PlacedX[k]) + Math.abs(NewObjectY - PlacedY[k]);
dis4[0] = Math.abs(NewObjectX - inputX) + Math.abs(NewObjectY - inputY);
dis4[1] = Math.abs(NewObjectX - outputX) + Math.abs(NewObjectY - outputY);
l=0;
} else {
l++;
}
}
What happens: I randomly place a machine in the room and then I check the distance constraints with every already placed object in the while loop:
In the while loop, I check the distance constraint with the first, then second and so on objects that have already been placed.
If the distance constraints are not met, then a new randomly selected spot is selected and I restart the while loop l=0
I am not sure why my code sometimes stays stuck in that loop and most of the time works perfectly.
Could someone help me? Did I make an error?
Thank you so much :)
Sam
EDIT:
Here is a copy of the simplified code with only 1 constraint instead of 4 in the while loop:
public static double[][] initialPop(double[] dim, double dis, int WSNr, double[] ioPlace) {
int[] WStoPlace = new int[WSNr-2];
for (int i=1; i<WSNr-1; i++) {
WStoPlace[i-1] = (i);
}
double[][] placed = new double[WSNr-2][3];
double ObjectX;
double ObjectY;
// now place the first object
ObjectX = dim[1]/2;
ObjectY = dim[3]/2;
placed[0][0] = WStoPlace[0];
placed[0][1] = ObjectX ;
placed[0][2] = ObjectY;
for (int i=1; i<WSNr-2; i++) {
//place the ith object
ObjectX = Math.random()*(dim[1]-dim[0]) + dim[0]*0.5;
ObjectY = Math.random()*(dim[3]-dim[2]) + dim[2]*0.5;
int l=0;
while (l<i) {
double dis2 = Math.abs(ObjectX - placed[l][1]) + Math.abs(ObjectY - placed[l][2]);
if (dis2<dis) {
ObjectX = Math.random()*(dim[1]-dim[0]) + dim[0]*0.5;
ObjectX = Math.random()*(dim[3]-dim[2]) + dim[2]*0.5;
l=0;
} else {
l++;
}
}
// Add the newly placed object
placed[i][0] = WStoPlace[i];
placed[i][1] = ObjectX;
placed[i][2] = ObjectY;
}
return placed;
}
This code is then called by my main program in a for-loop:
public static void main(String[] args) {
// define all the variables ...
int popFlow = 5;
double[] dim = new double [4];
dim[0] = 3; // length of WS (x)
dim[2] = 3; // width of WS (y)
dim[1] = 100; // length of facility (x)
dim[3] = 40;
double dis = 8;
int WSNr = 22;
double[] ioPlace = new double[4];
ioPlace[0] = 0; // int xi = 0;
ioPlace[1] = 5; // int yi = 2;
ioPlace[2] = 100; // int xo = 50;
ioPlace[3] = 35;
double[][] TotalPop = new double[popFlow][2*WSNr];
// Flow-related placed Workstations
for (int i=0; i<popFlow; i++) {
double [][] Pos = initialPop(dim, dis, WSNr, ioPlace);
for (int j=0; j<WSNr-2; j++) {
int Value = (int) Pos[j][0];
TotalPop[i][Value] = Pos[j][1];
TotalPop[i][Value+WSNr] = Pos[j][2];
}
}
}
As mentioned in the question's comments, you need to limit the loop. As a quick solution, you can have a counter that limits the while loop functionality and reset the entire logic which its probability to NOT go into same situation again is high. But that really should be handled better and check in which case exactly the loop would not end.
If the reason found and could be handled by the breaking conditions then it would be better as it would be more definite solution. Nevertheless, limiting the iteration for emergency is almost a must have. Here is simple sample on your code that would do something similar
public static double[][] initialPop(double[] dim, double dis, int WSNr, double[] ioPlace) {
boolean reset = false;
double[][] placed = null;
while (!reset) {
int[] WStoPlace = new int[WSNr - 2];
for (int i = 1; i < WSNr - 1; i++) {
WStoPlace[i - 1] = (i);
}
placed = new double[WSNr - 2][3];
double ObjectX;
double ObjectY;
// now place the first object
ObjectX = dim[1] / 2;
ObjectY = dim[3] / 2;
placed[0][0] = WStoPlace[0];
placed[0][1] = ObjectX;
placed[0][2] = ObjectY;
for (int i = 1; i < WSNr - 2; i++) {
//place the ith object
ObjectX = Math.random() * (dim[1] - dim[0]) + dim[0] * 0.5;
ObjectY = Math.random() * (dim[3] - dim[2]) + dim[2] * 0.5;
// Problem is the while, not the for so we define the limit here
int limit = 100;
int counter = 0;
// Make sure it's false as it might be changed in some iteration before!
// I promised, check the comments below !
reset = false;
int l = 0;
while (l < i) {
double dis2 = Math.abs(ObjectX - placed[l][1]) + Math.abs(ObjectY - placed[l][2]);
if (dis2 < dis) {
ObjectX = Math.random() * (dim[1] - dim[0]) + dim[0] * 0.5;
ObjectX = Math.random() * (dim[3] - dim[2]) + dim[2] * 0.5;
l = 0;
} else {
l++;
}
if (counter >= limit) { // Now that's enough !
reset = true;
break;
}
counter++;
}
if (reset) { // I said it's enough, I want full reset so ...
reset = false; // going to break to the while-loop that checking this, and we want to enter again !
break;
} else {
// Not sure at this point this will be the last execution
// So just make it true, so we do not enter the entire logic again
// after finally settling to our result !!!!
// If it's not the last execution which means we are entering another for-loop iteration then
// we will reset this to false, i promise !
reset = true;
// Add the newly placed object
placed[i][0] = WStoPlace[i];
placed[i][1] = ObjectX;
placed[i][2] = ObjectY;
}
}
}
return placed;
}
You need a breakout. Add a counter or timer or check off coords as you try them until they are all checked. The way it is now there is no way to end the loop if your conditions in the "if" statement are always meet, it will just keep setting 'l' to 0 and your while loop just keeps looping.
I'm trying to use this array I create in printGrid method throughout my program. Currently, I have to run this method, but the values change because of the math.random method nested in this method and I don't want it to. I just want to run this once and use the output throughout my method, is this possible?
What I've tried - I've tried to return the value of the mapArray (seen in code) and printing that out, I'm having issue with that. I've also tried isolating the math.random method in it's own method to calculate the value of the cookies, but my issue with that is that it only returns one value and I need a dynamic amount depending on the input of the x / y vars. Any suggestions?
public static char[][] printGrid(int x, int y) {
// int [][] mapArray = new int [x][y]; // Gets the information to print the array
mapArray = new char[x][y]; // Gets the information to print the array
double cookies = (x * y) * (.1); // Calculates the number of cookies per x / y input
// Storing '.' in all values of the array
for (int d = 0; d < x; d++) // Moved from below.
{
for (int j = 0; j < y; j++) {
mapArray[d][j] = '.';
}
System.out.println();
}
// Storing '0' in random values of the array and '<' at [0][0]
int c = 0;
for (c = 1; c <= cookies; c++) {
int cookiesPrintColumn = (int)(cookies * Math.random());
int cookiesPrintRow = (int)(cookies * Math.random());
mapArray[cookiesPrintColumn][cookiesPrintRow] = 'O';
mapArray[0][0] = '>';
}
// Copy loop from above to print the grid.
for (int d = 0; d < x; d++) {
for (int j = 0; j < y; j++) {
System.out.print(mapArray[d][j]);
return mapArray;
}
System.out.println();
}
return mapArray;
} // printGrid method close
If you are trying to create an array in a method, and then need to use in throughout your program, such as in main, you aren't going to be able to directly use that exact array because it will be out of scope. You can however create and then initialize the array in main equal to the return statement of your method. For example:
main() {
int[][] example;
example[][] = method(...);
}
int [][] method(...){
int[][] exampleArray = {1,2,3} // your random values here
return exampleArray;
}
Because you are creating the actual array inside of the method, it will not be in scope in main. This however will have the exact values the same, so the random numbers will be identical, and you can continue to use this for the rest of your program.
Use return value:
import package.ClassNane;
class Example {
int x = 1;
int y = 2;
char[][] grid = ClassName.printGrid(x, y);
}
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--;
}
}
}
i would like to know if it is possible in java,to create an ID for an element inside an object, so if another object is generated with the same element i can check if it was created priviously.
Example1
`
{
int[][] cha = new int[3][3];
cha[0][0] = 8;
cha[0][1] = 1;
cha[0][2] = 3;
cha[1][0] = 4;
cha[1][1] = 0;
cha[1][2] = 2;
cha[2][0] = 7;
cha[2][1] = 6;
cha[2][2] = 5;
int[][] hol = new int[3][3];
hol[0][0] = 8;
hol[0][1] = 1;
hol[0][2] = 3;
hol[1][0] = 4;
hol[1][1] = 0;
hol[1][2] = 2;
hol[2][0] = 7;
hol[2][1] = 6;
hol[2][2] = 5;
HashSet<int[][]> k = new HashSet();
k.add(cha);
System.out.println(k.contains(cha));
System.out.println(k.contains(hol));
}`
In this case, I wil get the values "true, false" even though both matrix are the same ( I know it is because HashSet does reference to the memory address and not the object.)
I want to be able to create the matrix a second time and determinate if it was already created.
Thanks.
Wrap it in a class and define your own equals() and hashcode()
I know it is because HashSet does reference to the memory address and not the object
No, that's not the reason. The reason is that an array is only equal to itself, because arrays don't override the Object.equals() and Object.hashCode() methods, that HashSet uses to check which elements it already contains.
To get the behavior you want, you need to wrap the matrix into your own class which overrides equals() and hashCode(), in order to tell when two matrices are equal. Make sure to never modify any element of a matrix once it has been stored into the HashSet:
public final class Matrix {
private int[][] elements;
public Matrix(int[][] elements) {
this.elements = elements;
}
#Override
public boolean equals(Object o) {
if (!(o instanceof Matrix)) {
return false;
}
Matrix m = (Matrix) o;
return Arrays.deepEquals(this.elements, m.elements);
}
#Override
public int hashCode() {
return Arrays.deepHashCode(elements);
}
}
And now you can do
HashSet<Matrix> set = new HashSet<>();
Matrix m1 = new Matrix(cha);
k.add(m1);
System.out.println(set.contains(m1));
Matrix m2 = new Matrix(hol);
System.out.println(set.contains(m2));
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.