I've made a game that randomly generates mazes, the maze is stored in a 2d array.
each integer in the array represents the number of walls that cell has.
It's based on the java example here: http://rosettacode.org/wiki/Maze
To allow people to share a unique maze i'm trying to find a way to convert the array into a string or an integer that can be generated by one user, copied and then pasted into another game which will then load the same maze.
The user can select the size of the maze up to 25x25 so simply printing each value (2|16|4|20...) would be incredibly long.
If converting it to a 'code' isn't possible are there any other ways it can be done without using a file?
Store the seed for the random number generator. The seed fully determines the output of the random number generator.
Assuming you are using java.util.Random to generate the random numbers, instead of using the default constructor new Random() use
long seed = System.currentTimeMillis();
// store the seed somewhere
// so you can generate the same sequence of random numbers again
Random rng = new Random(seed);
To make a short "code" that can be given to users for sharing, you can convert the number to hex or base 36:
String code = Long.toString(seed, 36); // codes like heeho82h
If you're randomly creating the values for that array, you only need the state of the PRNG that produced those values to re-generate them. That's at the same time the biggest weakness and strength of all pseudo-random number generators.
As most PRNGs don't allow retrieving and setting the state, or have a pretty huge state (a Mersenne Twister has a few kilobyte of state internally), you may want to use the seed instead. Of course, then you must create a new PRNG (or reset an existing one) for level generation.
To make the number human-readable, you should just render it in some number base. Base 10 makes it obvious it's a number and is easiest to generate and parse. Base 16 (hexadecimal) and base 64 yield shorter, more obscure "codes". Padding it to be fixed-length regardless of the actual value is probably a good idea.
You could use Serialization concept to save the state of 2-D array and then retrieve it back using deserialization. Here is the simple Demo to save a 2-D array state and then read it back . I hope it would be of your help:
import java.io.*;
class ArraySerialization
{
ByteArrayOutputStream baos;
ByteArrayInputStream bins;
public void saveState(Object obj)throws Exception
{
baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
}
public int[][] readState()throws Exception
{
bins = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream oins = new ObjectInputStream(bins);
Object obj = oins.readObject();
oins.close();
return (int[][])obj;
}
public static void main(String[] args) throws Exception
{
int arr[][]= {
{1,2,3},
{4,5,7}
};
ArraySerialization ars = new ArraySerialization();
System.out.println("Saving state...");
ars.saveState(arr);
System.out.println("State saved..");
System.out.println("Retrieving state..");
int j[][] = ars.readState();
System.out.println("State retrieved..And the retrieved array is:");
for (int i =0 ; i < j.length ; i++ )
{
for (int k = 0 ; k < j[i].length ; k++)
{
System.out.print(j[i][k]+"\t");
}
System.out.print("\n");
}
}
}
Although it uses a random generator, it is in fact a deterministic process. You can repeat it by knowing the random seed. Just share the random seed, as mentioned by Joni. Anyway, if you want to share the data: a NxN mesh has NxN inner walls, and considering it takes 1 bit to store presence/absence of a wall, you will need NxN bits to store the maze. In this way you can store a 90x90 maze in 1kb.
Related
I am trying to procedurally generate a world on a 2D grid. For the random numbers generation I am using a single global java.utils.Random Instance and a seed.
Like this:
public class Game {
private static final int SEED = 1111;
private static final Random RANDOM = new Random(SEED);
private static final int roomsCount = generateRandomNumberOfRooms();
}
Everything worked just fine untill I wrote this method:
public ArrayList<XYCoords> generateRandomCoordinates(){
ArrayList<XYCoords> coords = new ArrayList<>(roomsCount);
for(int i = 0; i < roomsCount; i+=1) {
XYCoords xy = new XYCoords(RANDOM.nextInt(WIDTH), RANDOM.nextInt(HEIGHT));
coords.add(xy);
}
return coords;
}
When I execute it I get the list of XYCoordinates but they all have the same two X and Y values, for example (11,20) or (12, 5)... etc. and all the rooms land on the same spot. It looks to me like the call to RANDOM.nextInt() in the for loop doesn't update the state of the instance RANDOM.
In all other functions that I call RANDOM.nextInt() it works fine. Some of them use for loops too. The problem is only with this function. It is used once directly in main() and not nested anywhere.
Does anyone have any sense of where the problem might be? I can upload more code if you need, but I think it is irrelevant.
Unwanted behaviour 1: each application run generates the same values in the same order. The reason is your RANDOM object which is instantiated with a seed. Try the following adaption: (Removing the seed, allowing Random to give you random values):
public class Game {
private static final Random RANDOM = new Random();
private static final int roomsCount = generateRandomNumberOfRooms();
}
If you need the seed for some reason, you could try to update the seed at each startup.
Clarification what the seed actually does:
(Note that the seed is also set from the constructor if you provide a seed)
setSeed
public void setSeed(long seed)
Sets the seed of this random
number generator using a single long seed. The general contract of
setSeed is that it alters the state of this random number generator
object so as to be in exactly the same state as if it had just been
created with the argument seed as a seed. The method setSeed is
implemented by class Random by atomically updating the seed to (seed ^
0x5DEECE66DL) & ((1L << 48) - 1) and clearing the haveNextNextGaussian
flag used by nextGaussian(). The implementation of setSeed by class
Random happens to use only 48 bits of the given seed. In general,
however, an overriding method may use all 64 bits of the long argument
as a seed value.
Parameters: seed - the initial seed
Unwanted behaviour 2: the cords are all the same after the loop. The XYCoords class probably declares the x and y as static variables. Solution: Remove the static declaration. As the values are static, the objects share these values as they are bound to the class. The last values which are set are the values you will get if you iterate over all of your XYCoords objects.
In general it is a mixture of both things which lead to the result that you get the same values all the time.
Firstly I am not the greatest with Math, so please excuse any ignorance relating to that. I am trying to maintain probability based randomness across multiple executions but I am failing. I have this input in a JSONObject
{
"option1": 25,
"option2":25,
"option3" :10,
"option4" :40
}
This is my function that selects a value from the above JSONObject based on the probability assigned:
public static String selectRandomoptions(JSONObject options) {
String selectedOption = null;
if (options != null) {
int maxChance = 0;
for (String option : options.keySet()) {
maxChance += options.getInt(option);
}
if (maxChance < 100) {
maxChance = 100;
}
Random r = new Random();
Integer randomValue = r.nextInt(maxChance);
int chance = 0;
for (String option : options.keySet()) {
chance += options.getInt(option);
if (chance >= randomValue) {
selectedOption = options.toLowerCase();
break;
}
}
}
}
the function behaves within a reasonable error margin if I call it x amount of times in a single execution ( tested 100+ calls), the problem is that I am running this every hour to generates some sample data in an event-driven app to verify our analytics process/data but we need it to be somewhat predictable, at least within a reasonable margin?
Has anyone any idea how I might approach this? I would rather not have to persist anything but I am not opposed to it if it makes sense or reduces complexity/time.
The values returned by Random.nextInt() are uniformly distributed, so that shouldn't be a problem.
I you would like to make random results repeatable, then you may want to use Random with seed.
Rather than create a new Random() object each time you want a new random number, just create the Random object once per run, and use the Random.nextInt() object once per run.
Looking at the documentation of Random() constructor,
This constructor sets the seed of the random number generator to a
value very likely to be distinct from any other invocation of this
constructor.it only guarantees it to be different
that's a bit of a weaker contract than the number you get from nextInt().
If you want to get the same sequence of numbers on each run, use the Random(long seed) or the setSeed(long seed) method of the random object. Both these methods set the seed of the generator. If you used the same seed for each invocation it's guaranteed that you will get the same sequence of numbers from the generator.
Random.setSeed(long).
I'm writing this golf program for my class that takes a .txt that has 5 numbers per row and 18 rows. The first number in each row is the par for that hole. The other four numbers are for each player.
I kind of have my own spin on this program, though. I wrote another program to create the .txt file with random numbers. The output is dependent on how many players there are which is inputted by the user.
Anyway, I've gotten the .txt generator just fine and I've gotten the Golf program to accurately count how many players there are. What I can't figure out is how to create that number of arrays with different names. I want each array to be int player1[18], player2[18], player3[18], etc.
Here's my code up to that point:
import java.util.Scanner;
import java.io.*;
public class Golf
{
public static void main(String[] args) throws java.io.IOException
{
Scanner countScan = new Scanner(new File("golfscores.txt"));
Scanner file = new Scanner(new File("golfscores.txt"));
//------------------------------------------------------------
// Counting the number of players
//
// This takes the number of integers in the file, divides it by
// the 18 holes in the course and subtracts 1 for the par.
//
// I needed to count the players because it's a variable that
// can change depending on how many players are entered in the
// java program that creates a random scorecard.
//------------------------------------------------------------
int players = 0;
for (int temp = 0; countScan.hasNextInt(); players++)
temp = countScan.nextInt();
players = players/18-1;
//------------------------------------------------------------
//Creating necessary arrays
//------------------------------------------------------------
}
}
EDIT: I must use an array for each player and I am not allowed to use ArrayLists. At this point it looks like I will be using an array of arrays as suggested by some in the comments. Didn't know this was a thing (obviously I'm very noob).
Well you can use a HashMap to store your arrays. Or if you don't care about using strings to get to the array just use 2D Arrays like this:
int[][] players = new int[playerCount][18];
If you then use for example player 2 and want to see hole 12, you'd call players[1][11]
You should not go that way.
Instead create a class named Player to hold each player properties, then create a list of players: List<Player> players = new ArrayList<>();
Add each new player to that list.
Let's say I want to generate 20 random numbers on a 8 by 6 grid.(8 columns, 6 rows) . Based on the answer from here:Creating random numbers with no duplicates, I wrote my code like this:
Random randomNumGenerator = new Random();
Set<Integer[][]> generated = new LinkedHashSet<Integer[][]>();
while (generated.size() < 20) {
int randomRows = randomNumGenerator.nextInt(6);
int randomColumns = randomNumGenerator.nextInt(8);
generated.add(new Integer[][]{{randomRows,randomColumns}});
}
In reality what happens is the Set see Integer[][]{{5,5}}; and Integer[][]{{5,5}};as NOT duplicate.Why? Even tho my purpose is to get 20 non-duplicate pair of numbers, this does not work. How do I fix this?
The Set checks for duplicates using the equals method (and also the hashCode method) of its inner type, but the Integer[][]'s equals method compares the memory addresses and not the contents.
Why do you use a Set of Integer[][] if you just want to store pairs?
Unfortunately, in Java there is no Pair class, but if you do not want to create your own, you can use the Map.Entry for that.
Random randomNumGenerator = new Random();
Set<Map.Entry<Integer, Integer>> generated = new LinkedHashSet<>();
while (generated.size() < 20) {
int randomRows = randomNumGenerator.nextInt(6);
int randomColumns = randomNumGenerator.nextInt(8);
generated.add(new AbstractMap.SimpleEntry<>(randomRows,randomColumns));
}
System.out.println(generated);
Array equals is == in Java, so an array is only equal to itself. Normaly you use Arrays.equals(array1, array2) to compare them by content, but in this case, arrays are simply the wrong choice. You can either create a bean, as rafalopez79 suggested of use an array of Collections (List in your case), as a List will compare the content on equals, see the documentation. Choice is pretty much yours, a bean would probably be a bit cleaner.
How about this code. I ran it through the debugger, it works nicely and yes, the contains() method checks the value of the Integer, not the reference. You can change the range of the random number as needed, I used 5 to facilitate testing. Yes I know it's not very robust, as written this will be an endless loop (because of the limited range of 5) but it's a simple example to make the point.
UPDATE: Actually this has a bug in that it won't check for uniqueness across all the rows, but that's easily fixed as well. I just re-read the original question and looking at the original code I'm not sure I know what you want exactly. If you just want a grid with 48 unique Intergers arranged 8 by 6 this will do it, but there are several ways to do this.
final int rows = 6;
final int cols = 8;
Random randomGenerator = new Random();
ArrayList[] grid = new ArrayList[rows];
for(int i=0; i<rows; i++)
{
grid[i] = new ArrayList<Integer>();
for(int j=0; j<cols; j++)
{
for(;;)
{
Integer newInt = new Integer(randomGenerator.nextInt(5));
if(!grid[i].contains(newInt))
{
grid[i].add(newInt);
break;
}
}
}
}
Hello I am trying to create a method in Java that Accepts an integer from the user. Calculate and display how many occurences of the integer are in the array(i'm Creating a random array) as well as what percentage of the array values is the entered integer.
This is how i create my Array:
public void fillVector ( )
{
int myarray[] = new int [10];
for (int i = 0 ; i < 10 ; i++)
{
myarray [i] = (int) (Math.random () * 10);
}
}
Any sugestions how can i do to accomplish this ?
This seems like a homework to you so I am not gonna give you the full solution but I will break down the steps of what you need to do in order to solve your problem. You have to find out how to code those steps yourself, or at least provide some code and your specific problem because your question is too vague right now.
Ask the user to input the number.
Store that number somewhere.
Check each cell of the array for that number. If you find one appearance
increase the counter and continue until the end of your index.
Print out the appearances of the given number.
Print out the percentage of the cells containing the given value to the total amount of cells.
As I can see from your code (if it's yours) you are capable to pull this off on your own. It shouldn't be too hard.