Create a Single Random Value to Share Across Classes Java - java

Final(?) edit:
Leonardo Pina's solution looks like what I needed. Thank you all for your input! My code is now:
public final class Roll {
private static final Random r1 = new Random();
private final int r = level();
private static final int level() {
int s = 1, e = 100;
int r = r1.nextInt((e - s) + 1) + s;
return r;
}
public int itemType() {
boolean b = r1.nextBoolean();
int a = ((b) ? 1 : 2);
return a;
}
public int getR() {
return r;
}
}
And the implementation in the other class:
public class Damage {
Roll r;
Damage(Roll r) {
this.r = r;
}
public int damageOut() {
int a = r.getR(); //roll value
return a * 2; //test math on r
}
}
Edit:
Thank you for the responses. However, it seems my use of this method was not clear. Apologies! This is what I need:
Call Roll class, get e.g. "12"
OtherClass1 receives "12" from Roll via getter
OtherClass2 also receives "12" from Roll via getter
Call Roll class again, get a new random number, e.g. "48"
OtherClass1 receives "48" from Roll via getter
OtherClass2 also receives "48" from Roll via getter
Using the solutions provided, Roll creates a single random number and never creates a new one again. I need Roll to create random numbers on demand, and then share that random number with other classes. I do not want it to only generate one number and then stop. I hope this makes sense. Thank you for the responses so far!
I have searched for and read the other threads of a similar topic (including this one, which is how I tried to solve my problem), but the methods I have tried have failed. I need to create a single random value and then use that same value across multiple classes.
My random class:
public final class Roll {
private static final Random r1 = new Random();
private final int r = level();
private static final int level() {
int s = 1, e = 100;
int r = r1.nextInt((e - s) + 1) + s;
return r;
}
public int itemType() {
boolean b = r1.nextBoolean();
int a = ((b) ? 1 : 2);
return a;
}
public int getR() {
return r;
}
}
And a class where I call for the static value from the random class:
public class OtherClass{
Roll roll = new Roll();
int r = roll.getR();
public int getR() {
return r;
}
}
When I call the getter from OtherClass, the value is different than the getter from Roll. I would expect them to be the same value.
I call the values for testing like so:
Roll roll = new Roll();
int r = roll.getR();
OtherClass other = new OtherClass();
int o = other.getR();
Any guidance would be greatly appreciated. Have a good day.

When you create a new instance of OtherClass you also create a new instance of Roll, therefore, the rolls are different.
You need to make sure you are getting the value R from the same object Roll.
This could be achieved by using a singleton pattern for the Roll class, or you could specify the Roll object you want to use to get values, this way you could have several rolls for different purposes. i.e.
Edit:
Answering your edit: to get a new number, you should update the value of r in the Roll class whenever you generate a new value, for example, instead of returning a value, the level() method could update the r variable:
public final class Roll {
private static final Random r1 = new Random();
// We now attribute a value to r uppon construction
private final int r;
public Roll() {
level();
}
// Whenever level() is called the value of r is updated
private static final int level() {
int s = 1, e = 100;
int r = r1.nextInt((e - s) + 1) + s;
this.r = r;
}
public int itemType() {
boolean b = r1.nextBoolean();
int a = ((b) ? 1 : 2);
return a;
}
public int getR() {
return r;
}
}
class Game {
public static void main(String[] args) {
// Create the Roll obj
Roll myRoll = new Roll();
// Initialize the other objs with myRoll
Board board = new Board(myRoll);
Player player = new Player(myRoll);
// Do your comparison
int b = board.getRoll();
int p = player.getRoll();
// EDIT: get a new value
myRoll.level();
// Do your comparison once more
b = board.getRoll();
p = player.getRoll();
}
class Board {
Roll r;
Board(Roll roll) {
this.r = roll;
}
public int getRoll() {
return r.getR();
}
}
class Player {
Roll r;
Player(Roll roll) {
this.r = roll;
}
public int getRoll() {
return r.getR();
}
}
}

Probably the simplest way to make it work would be to field r in class Roll as static, so that the Roll class looks like this:
ublic final class Roll {
private static final Random r1 = new Random();
private static final int r = level();
private static final int level() {
int s = 1, e = 100;
int r = r1.nextInt((e - s) + 1) + s;
return r;
}
public int itemType() {
boolean b = r1.nextBoolean();
int a = ((b) ? 1 : 2);
return a;
}
public int getR() {
return r;
}
}
Or second approach would be to make Roll class a singleton.
Edit:
Since you have changed the intent of the question, and from what I understand you should look into Observer design pattern. It may be helpful for your case.

As someone said in the comments, you should follow the singleton design pattern, meaning that your variable r should be unique across all Roll class instances. To do this, your code will have to look something like this:
public class RandomTest {
public static void main(String[] args) {
Roll roll = new Roll();
int r = roll.getR();
OtherClass other = new OtherClass();
int o = other.getR();
}
}
class Roll {
private static Random r1 = new Random();
private static int r = level();
private static final int level() {
int s = 1, e = 100;
int r = r1.nextInt((e - s) + 1) + s;
return r;
}
public int itemType() {
boolean b = r1.nextBoolean();
int a = ((b) ? 1 : 2);
return a;
}
public int getR() {
return r;
}
}
class OtherClass{
Roll roll = new Roll();
int r = roll.getR();
public int getR() {
return r;
}
}

How about sharing the Roll instance?
Roll roll = new Roll();
// roll.roll(); // Unclear how you "refresh" the data
int r = roll.getR();
OtherClass other = new OtherClass(roll);
int o = other.getR();
assert(o == r);
class OtherClass {
private final Roll roll;
public OtherClass(Roll roll) {
this.roll = roll;
}
public int getR() {
return roll.getR();
}
}
Tip: I think a Dice class with a roll() method makes more sense.

Related

Random value instance shared between classes

I have a method that returns a random value.
public int newRandom() {
Random random = new Random();
int o = random.nextInt(9 - 0 + 1) + 0;
return o;
}
After I generate that value I'm currently setting a variable inside another method to the result of this function, what I want to do is have another class know the same result that variable has, what I mean is both having the same random number, I for some reason can't figure this out on my own. What would be the best way of achieving this?
Do you mean something like that?
import java.util.Random;
public class A
{
public static Random random = new Random();
}
public class B
{
public int getRandomInt()
{
return A.random.nextInt();
}
}
Create a Base class with a public static variable and when you inherit these new classes from the Base class and create new instances they share this same variable:
import java.util.Random;
class Base_class {
public static int o;
public Base_class() {
}
public int newRandom() {
Random random = new Random();
o = random.nextInt(9 - 0 + 1) + 0;
return o;
}
public int get_randn() {
return o;
}
}
class Derived_class1 extends Base_class {
public Derived_class1() {
}
}
class Derived_class2 extends Base_class {
public Derived_class2() {
}
}
class Main{
public static void main(String [] args){
Derived_class1 obj1 = new Derived_class1();
Derived_class2 obj2 = new Derived_class2();
obj1.newRandom();
System.out.println(obj1.get_randn());
System.out.println(obj2.get_randn());
}
}
Consider using composition where multiple objects (could be different classes) share the same instance of a class which provides the random values. For example, given this class to keep track of random values:
private class RandomProvider {
private final Random random = new Random();
private int currentValue;
private int newRandom() {
currentValue = random.nextInt(10);
return currentValue;
}
private int getCurrentValue() {
return currentValue;
}
}
and another class (or classes) which use a RandomProvider:
private class A {
private final RandomProvider randomProvider;
private A(RandomProvider randomProvider) {
this.randomProvider = randomProvider;
}
private int getCurrentRandomValue() {
return randomProvider.getCurrentValue();
}
}
So then a couple instances of A (or some other class) can use the same instance of RandomProvider:
RandomProvider randomProvider = new RandomProvider();
System.out.println("new random value is " + randomProvider.newRandom());
A a1 = new A(randomProvider);
System.out.println("A1 sees current random value as " + a1.getCurrentRandomValue());
A a2 = new A(randomProvider);
System.out.println("A2 sees current random value as " + a2.getCurrentRandomValue());
randomProvider.newRandom();
System.out.println("A1 sees current random value as " + a1.getCurrentRandomValue());
System.out.println("A2 sees current random value as " + a2.getCurrentRandomValue());
Sample output:
new random value is 9
A1 sees current random value as 9
A2 sees current random value as 9
A1 sees current random value as 3
A2 sees current random value as 3

Exchange variable in non specified method

Im a newcomer :)
However, my program has a table and a RandomGen should get the highest random int by getRowCount -> checkvar1.
Now, the main class gets checkvar1 and sends it to setVariable(), then I want to exchange this checkvar1 with randomGen to limit the maximum generated integer.
So this of course doesn't work because the parameters in randomGen() aren't set and I cannot set them, because then the exchange to the onActionPerformed() method in my main class doesn't work anymore.
public final class RandomGen
{
// EXCHANGE OF CHECKVAR1 FOR RANDOM GEN
public static void setVariable(int checkvar1)
{
System.out.print(checkvar1);
}
// RANDOM GENERATOR
public static int randomGen()
{
Random rand = new Random();
int var1 = rand.nextInt(checkvar1) + 1;
return var1;
}
}
Here my main class:
public void onActionPerformed(java.awt.event.ActionEvent evt) {
//NUMBER OF LAST ROW
int checkvar1 = (Integer)jTable1.getRowCount();
//->EXCHANGE WITH setVariable()
RandomGen.setVariable(checkvar1);
if (checkvar1 >= 3) {
int recogvar1 = checkvar1 - 1;
Object checkobj1 = jTable1.getModel().getValueAt(recogvar1, 0);
if (checkobj1 == null){
//...
}
else {
int var1 = RandomGen.variable();
String result = var1 + "";
jTextField1.setText(result);
//System.out.print(result);
}
}
else {
String rule2 = "At least " + 3 + " rows should be filled";
jTextField1.setText(rule2);
}
You are doing nothing with your setVariable in your class RandomGen. So You only need to change this.
// RANDOM GENERATOR
public static int randomGen(int checkvar1)
{
Random rand = new Random();
int var1 = rand.nextInt(checkvar1) + 1;
return var1;
}
And in your main Method try this.
//NUMBER OF LAST ROW
int checkvar1 = (Integer)jTable1.getRowCount();
//->EXCHANGE WITH randomGenerator
checkvar1 = RandomGen.randomGen(checkvar1);

How to sort an array when it is filled with numbers from a get method

Arrays.sort() gives and error of:
FiveDice.java:19: error: no suitable method found for sort(int)
Arrays.sort(compNums);
If I take anything out of the for loop, it thinks thee is only 1 number or gives an error. What other sorting options would be usable?
import java.util.*;
public class FiveDice {
public static void main(String[] args) {
int x;
int compNums = 0;
int playerNums;
Die[] comp = new Die[5];
Die[] player = new Die[5];
System.out.print("The highest combination wins! \n5 of a kind, 4 of a kind, 3 of a kind, or a pair\n");
//computer
System.out.print("Computer rolled: ");
for(x = 0; x < comp.length; ++x) {
comp[x] = new Die();
compNums = comp[x].getRoll();
//Arrays.sort(compNums); <--does not work
System.out.print(compNums + " ");
}
//player
System.out.print("\nYou rolled: \t ");
for(x = 0; x < player.length; ++x) {
player[x] = new Die();
playerNums = player[x].getRoll();
System.out.print(playerNums + " ");
}
}
}
die class
public class Die {
int roll;
final int HIGHEST_DIE_VALUE = 6;
final int LOWEST_DIE_VALUE = 1;
public Die()
{ }
public int getRoll() {
roll = ((int)(Math.random() * 100) % HIGHEST_DIE_VALUE + LOWEST_DIE_VALUE);
return roll; }
public void setRoll()
{ this.roll = roll; }
}
Easiest way is to implement Comparable to Die class , set value of roll in constructor of Die not in the getter method and your problem is solved.
public class Die implements Comparable<Die> {
private int roll;
final int HIGHEST_DIE_VALUE = 6;
final int LOWEST_DIE_VALUE = 1;
public Die() {
roll = ((int)(Math.random() * 100) % HIGHEST_DIE_VALUE + LOWEST_DIE_VALUE);
}
public int getRoll() {
return roll;
}
public void setRoll(int roll) {
this.roll = roll;
}
public int compareTo(Die d) {
return new Integer(d.getRoll()).compareTo(new Integer(this.getRoll()));
}
}
now Arrays.sort(Die[]) will sort the array of Die.
You don't have to use 2 arrays for the sorting. If you implement the Comparable<T> interface, your classes can be sorted by Java Collections API. In your case, Die class can implement Comparable<T> and provide a way for the Java framework to compare dice values.
Take a look at the Java API:
http://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html
In your case:
public class Die implements Comparable<Die>{
int roll;
final int HIGHEST_DIE_VALUE = 6;
final int LOWEST_DIE_VALUE = 1;
public Die() { }
public int computeRoll() {
roll = ((int)(Math.random() * 100) % HIGHEST_DIE_VALUE + LOWEST_DIE_VALUE);
return roll;
}
// I also changed this to store the value
public int getRoll() {
return roll;
}
public void setRoll() { this.roll = roll; }
// This is the method you have to implement
public int compareTo(Die d) {
if(getRoll() < d.getRoll()) return -1;
if(getRoll() > d.getRoll()) return +1;
if(getRoll() == d.getRoll()) return 0;
}
}
Whenever you have a Collection of Dies, like an ArrayList or LinkedList, you simply sort the collection itself. Below is a sample code.
ArrayList<Die> myCollection = new ArrayList<Die>();
myCollection.add(die1);
// more array population code
// ...
Collections.sort(myCollection);
You can't perform sort() on compNums because it is a single value. You declare it as an int value, rather than as an array of integers.
Instead, you should make compNums an array, populate it with the Die roll values, and then perform a sort operation on the resultant array. I believe the following will achieve what you are after.
int[] compNums = new int[5];
...
for(x = 0; x < comp.length; ++x) {
comp[x] = new Die();
compNums[x] = comp[x].getRoll();
}
Arrays.sort(compNums);
// print results
you must pass an array to the Array.Sort(arr) not parameter you must do something like this Array.Sort(int[]compNums);
and if you are using a anonymous type like comp[]compNums you must do it like this
java.util.Arrays.sort(comp[]compNums, new java.util.Comparator<Object[]>() {
public int compare(Object a[], Object b[]) {
if(something)
return 1;
return 0;
}
});

Calling a method from a class array gives NullPointerException

I've been searching a lot for this problem and I can't find a solution. I'm trying to build a mini-game and I have a method for creating platforms. I have a class with every platform parameters, and I made a class array so i can have multiple platforms at the same time.
Problem: When I try to call the method for constructing the platform by sending the parameters I want, it gives me a NullPointerException. The method was working before, but with everything static and so i couldnt have multiple instances of that class, and now I removed the static fields from the platform class and it gives me the NullPointerException every time I call the method.
I copied the part of the code that gives me the error, the error goes the following way:
public static void main(String[] args) {
Game ex = new Game();
new Thread(ex).start();
}
In Game class:
public Load_Stage load = new Load_Stage();
public Game() {
-other variables initializatin-
Initialize_Items();
load.Stage_1(); // <--- problem this way
In Load_Stage class:
public class Load_Stage {
public Platforms plat = new Platforms();
public void Stage_1(){
Stage_Builder.Build_Platform(200, 500, 300, plat.platform1);
Stage_Builder.Build_Platform(100, 200, 100, plat.platform1);
}
}
And inside the Stage_Builder class:
public class Stage_Builder {
public static final int max_platforms = 10;
public static Platform_1[] p1 = new Platform_1[max_platforms];
public static boolean[] platform_on = new boolean[max_platforms];
public Stage_Builder() {
for (int c = 0; c < platform_on.length; c++) {
platform_on[c] = false;
}
}
public static void Build_Platform(int x, int y, int width, ImageIcon[] type) { // BUILDS A PLATFORM
for (int b = 0; b < max_platforms; b++) {
if (platform_on[b] == false) {
p1[b].Construct(x, y, width, type); // <-- NullPointerException here
platform_on[b] = true;
break;
}
}
}
}
Thanks beforehand.
EDIT: Here's the Platform_1 class (sorry for forgetting about it):
public class Platform_1 {
private int platform_begin_width = 30;
private int platform_middle_width = 20;
public int blocks_number = 0;
public ImageIcon[] platform_floors = new ImageIcon[500];
private int current_width = 0;
public int [] platform_x = new int [500];
public int platform_y = 0;
public int platform_width = 0;
public void Construct(int x, int y, int width, ImageIcon [] type) {
platform_width = width;
platform_y = y;
for (int c = 0; current_width <= platform_width; c++) {
if (c == 0) {
platform_x[c] = x;
platform_floors[c] = type[0];
current_width += platform_begin_width;
} else if ((current_width + platform_middle_width) > platform_width) {
platform_floors[c] = type[2];
blocks_number = c + 1;
platform_x[c] = current_width + x;
current_width += platform_middle_width;
} else {
platform_floors[c] = type[1];
platform_x[c] = current_width + x;
current_width += platform_middle_width;
}
}
}
}
And the Platforms class:
public class Platforms {
public ImageIcon[] platform1 = {new ImageIcon("Resources/Sprites/Stage_Objects/Platform1/begin.png"),
new ImageIcon("Resources/Sprites/Stage_Objects/Platform1/middle.png"),
new ImageIcon("Resources/Sprites/Stage_Objects/Platform1/end.png")};
}
The problem and solution are both obvious.
public static Platform_1[] p1 = new Platform_1[max_platforms];
After this line of code executes, p1 is an array of references of type Platform_1 that are all null.
Executing this line of code tells you so right away:
p1[b].Construct(x, y, width, type); // <-- NullPointerException here
The solution is to intialize the p1 array to point to non-null instances of Platform_1.
Something like this would work:
for (int i = 0; < p1.length; ++i) {
p1[i] = new Platform1();
}
I'm not seeing where you put things in the p1 array in the Stage_Builder class.
Another possibility (unlikely, but possible if you haven't shown everything) is that something in the Platform class, which you didn't show, is not initialized, and is breaking when you call Construct.
Also, the following seems problematic
public static Platform_1[] p1 = new Platform_1[max_platforms];
public static boolean[] platform_on = new boolean[max_platforms];
public Stage_Builder() {
for (int c = 0; c < platform_on.length; c++) {
platform_on[c] = false;
}
}
it appears you declare static variables p1 and platform_on, but you only populate platform_on in a constructor. So the first time you create a Stage_Builder instance, you populate one static array with all false, and don't put anything in the other static array...
Populate those static variables in a static block
// static var declarations
static {
// populate static arrays here.
}
The array you are calling a message on was never filled.
You have
public static Platform_1[] p1 = new Platform_1[max_platforms];
so p1 is
p1[0] = null
p1[1] = null
.
.
.
p1[max_platforms] = null
You try to call
p1[b].Construct(x, y, width, type);
which is
null.Construct(...);
You need to initialize that index on the array first.
p1[b] = new Platform_1();
p1[b].Construct(...);
First of all, your problem is that p1[b] is most likely null, as duffymo pointed out.
Secondly, you are using arrays in a really weird way. What about
a) delete Stage_Builder
b) Instead, have an ArrayList somewhere
c) The equivalent of Build_Platform1() look like this, then:
p1.add(new Platform1(x, y, width, type);
d) no if on[i], no max_platforms, no for loop to add a platform (the latter is a bad performance problem if you actually have a few hundret platforms)

Random value from enum with probability

I have an enum that I would like to randomly select a value from, but not truly random. I would like some of the values to be less likely of being selected so far. Here is what I have so far...
private enum Type{
TYPE_A, TYPE_B, TYPE_C, TYPE_D, TYPE_E;
private static final List<Type> VALUES =
Collections.unmodifiableList(Arrays.asList(values()));
private static final int SIZE = VALUES.size();
private static final Random RANDOM = new Random();
public static Type randomType() {
return VALUES.get(RANDOM.nextInt(SIZE));
}
}
Is there an efficient way of assigning probabilities to each of these values?
Code found from here
several ways to do it, one of them, similar to your approach
private enum Type{
TYPE_A(10 /*10 - weight of this type*/),
TYPE_B(1),
TYPE_C(5),
TYPE_D(20),
TYPE_E(7);
private int weight;
private Type(int weight) {
this.weight = weight;
}
private int getWeight() {
return weight;
}
private static final List<Type> VALUES =
Collections.unmodifiableList(Arrays.asList(values()));
private int summWeigts() {
int summ = 0;
for(Type value: VALUES)
summ += value.getWeight();
return summ;
}
private static final int SIZE = summWeigts();
private static final Random RANDOM = new Random();
public static Type randomType() {
int randomNum = RANDOM.nextInt(SIZE);
int currentWeightSumm = 0;
for(Type currentValue: VALUES) {
if (randomNum > currentWeightSumm &&
randomNum <= (currentWeightSumm + currentValue.getWeight()) {
break;
}
currentWeightSumm += currentValue.getWeight();
}
return currentValue.get();
}
}
Here's a generic approach to choosing an enum value at random. You can adjust the probabilities as suggested here.
Assuming you have a finite number of values you could have a separate array (float[] weights;) of weights for each value. These values would be between 0 and 1. When you select a random value also generate another random number between and only select the value if the second generated number is below the weight for that value.
You can create an enum with associated data bby provding a custom constructor, and use the constructor to assign weightings for the probabilities and then
public enum WeightedEnum {
ONE(1), TWO(2), THREE(3);
private WeightedEnum(int weight) {
this.weight = weight;
}
public int getWeight() {
return this.weight;
}
private final int weight;
public static WeightedEnum randomType() {
// select one based on random value and relative weight
}
}
import java.util.*;
enum R {
a(.1),b(.2),c(.3),d(.4);
R(final double p) {
this.p=p;
}
private static void init() {
sums=new double[values().length+1];
sums[0]=0;
for(int i=0;i<values().length;i++)
sums[i+1]=values()[i].p+sums[i];
once=true;
}
static R random() {
if (!once) init();
final double x=Math.random();
for(int i=0;i<values().length;i++)
if (sums[i]<=x&&x<sums[i+1]) return values()[i];
throw new RuntimeException("should not happen!");
}
static boolean check() {
double sum=0;
for(R r:R.values())
sum+=r.p;
return(Math.abs(sum-1)<epsilon);
}
final double p;
static final double epsilon=.000001;
static double[] sums;
static boolean once=false;
}
public class Main{
public static void main(String[] args) {
if (!R.check()) throw new RuntimeException("values should sum to one!");
final Map<R,Integer> bins=new EnumMap<R,Integer>(R.class);
for(R r:R.values())
bins.put(r,0);
final int n=1000000;
for(int i=0;i<n;i++) {
final R r=R.random();
bins.put(r,bins.get(r)+1);
}
for(R r:R.values())
System.out.println(r+" "+r.p+" "+bins.get(r)/(double)n);
}
}
Here is another alternative which allows the distribution to be specified at runtime.
Includes suggestion from Alexey Sviridov. Also method random() could incorporate suggestion from Ted Dunning when there are many options.
private enum Option {
OPTION_1, OPTION_2, OPTION_3, OPTION_4;
static private final Integer OPTION_COUNT = EnumSet.allOf(Option.class).size();
static private final EnumMap<Option, Integer> buckets = new EnumMap<Option, Integer>(Option.class);
static private final Random random = new Random();
static private Integer total = 0;
static void setDistribution(Short[] distribution) {
if (distribution.length < OPTION_COUNT) {
throw new ArrayIndexOutOfBoundsException("distribution too short");
}
total = 0;
Short dist;
for (Option option : EnumSet.allOf(Option.class)) {
dist = distribution[option.ordinal()];
total += (dist < 0) ? 0 : dist;
buckets.put(option, total);
}
}
static Option random() {
Integer rnd = random.nextInt(total);
for (Option option : EnumSet.allOf(Option.class)) {
if (buckets.get(option) > rnd) {
return option;
}
}
throw new IndexOutOfBoundsException();
}
}
You can use EnumeratedDistribution from the Apache Commons Math library.
EnumeratedDistribution<Type> distribution = new EnumeratedDistribution<>(
RandomGeneratorFactory.createRandomGenerator(new Random()),
List.of(
new Pair<>(Type.TYPE_A, 0.2), // get TYPE_A with probability 0.2
new Pair<>(Type.TYPE_B, 0.5), // get TYPE_B with probability 0.5
new Pair<>(Type.TYPE_C, 0.3) // get TYPE_C with probability 0.3
)
);
Type mySample = distribution.sample();

Categories