Javafx adding event handlers in a loop - java

I'm trying to make a method to add SetOnMousePressed-functions to multiple nodes, and i've tried using a couple different loops etc, but i always end up with the error "Local variable x defined in an enclosing scope must be final or effectively final." This is as far as i've gotten:
public static int playerSelectingCategory(int intScorecard[][], Rectangle[][] scoreboardBackground, int categoryCounter, int nrOfPlayers, boolean limitCheck)
{
int counter = 0;
int y = 0;
for(int x = 0; x<YatzyConstants.getNrCategories(); x++)
{
if(counter < nrOfPlayers)
{
if(y < YatzyConstants.getNrCategories())
{
scoreboardBackground[counter][y].setOnMousePressed(e ->
{
scoreboardBackground[counter][y].setFill(javafx.scene.paint.Color.ALICEBLUE);
});
y++;
}
counter++;
}}
return intScorecard[counter][y];
}
I originally declared everything one by one, but i have to think that there should be a more effective way of doing it. Any help is appreciated, really hit a brick wall here.

Since y is modified by your code, it's not efectively final and therefore cannot be accessed from a anonymus class or lambda expression. The same is true for counter.
I'd recommend storing scoreboardBackground[counter][y] in a variable that is (effectively) final (that is unless the array is modified and you want to color the Rectangle at the position where the original rect was stored...):
final Rectangle rect = scoreboardBackground[counter][y];
rect.setOnMousePressed(e -> {
rect.setFill(javafx.scene.paint.Color.ALICEBLUE);
});
Or alternatively simply use the Node that is the source of the event:
final EventHandler<MouseEvent> handler = event -> {
((Shape) event.getSource()).setFill(javafx.scene.paint.Color.ALICEBLUE);
};
for(int x = 0; x<YatzyConstants.getNrCategories(); x++)
{
if(counter < nrOfPlayers)
{
if(y < YatzyConstants.getNrCategories())
{
scoreboardBackground[counter][y].setOnMousePressed(handler);
y++;
}
....

Related

How do I get my set to stop adding duplicates?

I am making an implementation of the game Hexic. The game is centered around making clusters of Hexagons in order to remove them from the board. I have found a way to identify the coordinates of every hexagon that is part of a cluster in order to add them to a set. Some of these will be identified twice, but I only want every coordinate to be saved once which is why I chose a set.
The issue is that the coordinates get added to the set twice anyway.
The following is the relevant code:
Instantiating the set:
private Set<int[]> clusters = new HashSet<>();
The nested loop for identifying the clusters:
void findClusters() {
for (int i = 0; i < NOOFCOLUMNS; i++) {
for (int j = 1; j < NOOFROWS; j++) {
Color color = hexagons[i][j].color;
int row = j-i%2; // do not remove, else magic number
if ((hexagons[i][j-1].color == color)) {
if ((i>0)&&(hexagons[i-1][row].color==color)) { addCluster(i, j, -1); }
if ((i<9)&&(hexagons[i+1][row].color==color)) { addCluster(i, j, +1); }
}
}
}
}
The function for adding the coordinates to the set:
void addCluster(int i, int j, int x) {
clusters.add(new int[] { i, j });
clusters.add(new int[] { i, j-1 });
clusters.add(new int[] { i+x, j-i%2 });
}
Thanks in advance!

Find element position array in java

I need to write some methods for a game in java and one of them is int[] findStone. The method returns an array, which gives the coordinate of the element that I am searching.
The field looks like this and is defined like this: private static int[][] gamefield = new int[8][6];
So if I use the method: findStone(3)[0], it should return 0 for the x coordinate and for findStone(3)1, 2. This is the code that I wrote.
private static int[] findStone(int stone) {
int[] position = new int[2];
for(int x = 0; x < 8; x++ ){
for(int y = 0; y < 6; y++ ) {
int a = gamefield[x][y];
int i = x;
int j = y;
if(a == stone) {
position[0] = i;
position[1] = j;
}
break;
}
}
return position;
}
The problem is: The method only returns the x-coordinates for the first row corectly, for the other elements it shows me 0. Could someone explain me what I did wrong and what I should change? Please, only simple explanation. I am only at the beginning and I don't have experience in java.
Thank you :)
You probably intended to put your break clause inside the if block. The way you have it now, the break keyword has no effect. It just breaks the inner loop (with y variable), but since this block of code ends here anyway, it simply does nothing.
You're searching for a single point on your map, so when you find the stone position, you can immediately return it, as there's nothing more to do.
Moreover, you don't need additional variables, a, i and j. Using them is not wrong, but code looks clearer and is more concise without them. Have a look at this code:
private static int[] findStone(int stone) {
int[] position = new int[2];
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 6; y++) {
if (gamefield[x][y] == stone) {
position[0] = x;
position[1] = y;
return position;
}
}
}
return null; // if there's no given stone
}

Why am I getting a varriable not declared error?

I keep getting this error cannot find symbol - variable minDist even though I know it has been declared and initialized. I feel like it's staring me straight in the face. Does anyone know why this is happening?
There's another class file that goes along with this, but I don't think the error is in there.
I get it on the third to last line, when I get to minDist, but if I remove minDist I also get it on minCost and minMPG as well.
public class AnnualFuelUseTester
{
public static void main(String[] args)
{
int sMiles1, sMiles2, sMiles3, sMiles4;
int eMiles1, eMiles2, eMiles3, eMiles4;
int[] dist = new int[4];
double gals1, gals2, gals3, gals4;
double[] MPG = new double[4];
double price1, price2, price3, price4;
double[] cost = new double[4];
AnnualFuelUse[] fillUps = {new AnnualFuelUse(108438, 108725, 13.9, 2.98),
new AnnualFuelUse(108738, 109023, 15.3, 3.02),
new AnnualFuelUse(109023, 109232, 10.3, 3.05),
new AnnualFuelUse(109564, 109854, 13.1, 3.03)};
for(int i = 0; i < fillUps.length; i++)
{
dist[i] = fillUps[i].calcDistance();
MPG[i] = fillUps[i].calcMPG();
cost[i] = fillUps[i].calcCost();
}
for (int i = 0; i < dist.length; i++)
{
int maxDist = 0;
int minDist = dist[0];
if (dist[i] > maxDist)
{
maxDist = dist[i];
}
if (dist[i] < minDist)
{
minDist = dist[i];
}
}
for (int i = 0; i < dist.length; i++)
{
double maxMPG = 0;
double minMPG = MPG[0];
if (MPG[i] > maxMPG)
{
maxMPG = MPG[i];
}
if (MPG[i] < minMPG)
{
minMPG = MPG[i];
}
}
for (int i = 0; i < dist.length; i++)
{
double maxCost = 0;
double minCost = cost[0];
if (cost[i] > maxCost)
{
maxCost = cost[i];
}
if (cost[i] < minCost)
{
minCost = dist[i];
}
}
System.out.printf("%15s%15s%15s%15s%15s%15s%15s%15s%15s\n\n"
,"Fill Up", "Days", "Start Miles", "End Miles"
,"Distance", "Gallons Used", "MPG", "Price", "Cost");
for(int i = 0; i < fillUps.length; i++)
{
System.out.printf("%15s%15s%15s%15s%15s%15s%15.2f%15s%15.2f\n"
,(i+1),(int)(1 + i *(i*1.1)), fillUps[i].getmySMiles()
,fillUps[i].getmyEMiles(), dist[i]
,fillUps[i].getmyGals(), MPG[i]
,fillUps[i].getmyPrice(), cost[i]);
}
System.out.printf("%10s%10s%30s%30s","Minimum",minDist,minMPG,minCost);
}
}
You declared minDist inside of a for loop, so it only exists in there, and you cannot use it outside of the loop.
You're declaring it within the scope of the for loop. You need to move the declaration of int minDist outside of that loop, to the same level that you're doing your printf.
Always consider the scope in which you are declaring your variables because it determines the visibility of your variable.
You declare your variable within a for-block which is a scope. Then your are trying to reference these variable from outside the scope where you have declared them. That won't work.
public void foo () {
while (someBool) {
int someVariable = 0;
someVariable = 1 // works because using and declaring takes place in the same scope.
}
someVariable = 2; // that won't work because variable is not existent in this scope.
}
Also consider that scopes can be hierarchically structured meaning a variable declared in some scope is also visible within all nested scopes:
public void foo () {
while (someBool) {
int aVariable = 0;
if (anotherBool) {
aVariable = 1; // works because this scope is a nested scope inside the scope where the variable has been declared.
}
}
}
You will find plenty of information about the well known concept of scopes which is used not only in C# but in most programming languages.
A point to start you research might be the MSDN documentation:
http://msdn.microsoft.com/en-us/library/aa691132(v=vs.71).aspx
you declared minDist variable inside of loop so scope of that variable limited to that particular for loop.
so you cant access that variable outside.
Basically, since you said
int minDist = dist[0];
in your for loop, it only exists in your loop. For example,
for(int i = 0; i < 10; i++) {
int x = 0;
}
System.out.println(x);
would return an error as x isn't there outside of that loop. It's known as scope and is basically the different levels of invisibility. Think like inception, the movie - the people in the 2nd dream level know what the dream in the 1st level is, but the 1st level can't see the 2nd level. Therefore :
int x = 5;
for(int i = 0; i < 1; i++) {
x = 3;
int y = 10;
}
System.out.println(x);
System.out.println(y);
will print out 3 successfully but crash when it tries to print out y because simply put, they can't see y outside of the for loop.
To fix your problem: simply declare minDist outside of your loop, somewhere near the start and it should work.

Java: "Local variable may not have been initialized" not intelligent enough?

Consider the following method:
void a ()
{
int x;
boolean b = false;
if (Math.random() < 0.5)
{
x = 0;
b = true;
}
if (b)
x++;
}
On x++ I get the "Local variable may not have been initialized" error. Clearly x will never be used uninitialized. Is there any way to suppress the warning except by initializing x? Thanks.
No, there is no way Java can examine all possible code paths for a program to determine if a variable has been initialized or not, so it takes the safe route and warns you.
So no, you will have to initialize your variable to get rid of this.
There is one :
void a () {
if (Math.random() < 0.5) {
int x = 1;
}
}
The compiler isn't responsible for devising and testing the algorithm. You are.
But maybe you should propose a more practical use case. Your example doesn't really show what's your goal.
Why don't you simply use
void a ()
{
int x;
boolean b = false;
if (Math.random() < 0.5)
{
x = 0;
b = true;
x++;
}
if (b) {
//do something else which does not use x
}
}
In the code why do you want to use x outside the first if block, all the logic involving x can be implemented in the first if block only, i don't see a case where you would need to use the other if block to use x.
EDIT: or You can also use:
void a ()
{
int x;
boolean b = (Math.random() < 0.5);
if (b) {
x=1
//do something
}
}
You can and should be defining the value of x unconditionally if it will be used later in your code.
There are a few ways to do this:
On initialization
int x = 0;
Because this is outside the conditional (if), Java won't complain.
Add else clause to conditional
if (Math.random() < 0.5)
{
x = 0;
b = true;
} else
{
x = 1;
}
Because there is an else to this if, and both code paths initialize x, Java will also be happy with this.
Move your usage of the variable into the conditional block
Clearly the question has a minimally-reproducible example, not a full one, but if you only ever want to use the variable conditionally, then it belongs in the conditional block.
if (Math.random() < 0.5)
{
x = 0;
x++;
}
If you don't aren't conditionally using the variable, then you need to provide an integer value to use in case Math.random() >= 0.5, using one of the solutions above.

Are there any techniques to split a method with a flag argument?

I have a method with a flag argument. I think that passing a boolean to a method is a bad practice (complicates the signature, violates the "each method does one thing" principle). I think splitting the method into two different methods is better. But if I do that, the two methods would be very similar (code duplication).
I wonder if there are some general techniques for splitting methods with a flag argument into two separate methods.
Here's the code of my method (Java):
int calculateNumOfLiveOrDeadNeighbors(Cell c, int gen, boolean countLiveOnes) {
int x = c.getX();
int y = c.getY();
CellState state;
int aliveCounter = 0;
int deadCounter = 0;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if (i == x && j == y)
continue;
state = getCell(i, j).getCellState(gen);
if (state == CellState.LIVE || state == CellState.SICK){
aliveCounter++;
}
if(state == CellState.DEAD || state == CellState.DEAD4GOOD){
deadCounter++;
}
}
}
if(countLiveOnes){
return aliveCounter;
}
return deadCounter;
}
If you don't like the boolean on your signature, you could add two different methods without it, refactoring to private the main one:
int calculateNumOfLiveNeighbors(Cell c, int gen) {
return calculateNumOfLiveOrDeadNeighbors(c, gen, true);
}
int calculateNumOfDeadNeighbors(Cell c, int gen) {
return calculateNumOfLiveOrDeadNeighbors(c, gen, false);
}
OR
you could code a Result Class or int array as output parameter for storing both the results; this would let you get rid of the annoying boolean parameter.
I guess it depends on every single case.
In this example you have two choices, in my opinion.
Say you want to split the call calculateNumOfLiveOrDeadNeighbors()
in two:
calculateNumOfLiveNeighbors()
and
calculateNumOfDeadNeighbors()
You can use Template Method to move the loop to another method.
You can use it to count dead / alive cells in the two methods.
private int countCells(Cell c, int gen, Filter filter)
{
int x = c.getX();
int y = c.getY();
CellState state;
int counter = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if (i == x && j == y)
continue;
state = getCell(i, j).getCellState(gen);
if (filter.countMeIn(state))
{
counter++;
}
}
}
return counter;
}
private interface Filter
{
boolean countMeIn(State state);
}
public int calculateNumOfDeadNeighbors(Cell c, int gen)
{
return countCells(c, gen, new Filter()
{
public boolean countMeIn(CellState state)
{
return (state == CellState.DEAD || state == CellState.DEAD4GOOD);
}
});
}
public int calculateNumOfLiveNeighbors(Cell c, int gen)
{
return countCells(c, gen, new Filter()
{
public boolean countMeIn(CellState state)
{
return (state == CellState.LIVE || state == CellState.SICK);
}
});
}
It's cumbersome, maybe not even worth the pain. You can, alternatively, use a monad to store the results of your statistics calculation and then use getDeadCounter() or getLiveCounter() on the monad, as many suggested already.
you can try to extract the common functionality in a single method and only use the specific functionality
you can create a private method with that flag, and invoke it from the two public methods. Thus your public API will not have the 'complicated' method signature, and you won't have duplicated code
make a method that returns both values, and choose one in each caller (public method).
In the example above I think the 2nd and 3rd options are more applicable.
Seems like the most semantically clean approach would be to return a result object that contains both values, and let the calling code extract what it cares about from the result object.
Like Bozho said: But but combine point 2 and 3 in the other way arround:
Create a (possible private method) that returns both (living and dead) and (only if you need dead or alive seperate in the most cases) then add two methods that pick dead or both out of the result:
DeadLiveCounter calcLiveAndDead(..) {}
int calcLive(..) { return calcLiveAndDead(..).getLive; }
int calcDead(..) { return calcLiveAndDead(..).getDead; }
IMO, this so-called "each method does one thing" principle needs to be applied selectively. Your example is one where, it is probably better NOT to apply it. Rather, I'd just simplify the method implementation a bit:
int countNeighbors(Cell c, int gen, boolean countLive) {
int x = c.getX();
int y = c.getY();
int counter = 0;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if (i == x && j == y)
continue;
CellState s = getCell(i, j).getCellState(gen);
if ((countLive && (s == CellState.LIVE || s == CellState.SICK)) ||
(!countLive && (s == CellState.DEAD || s == CellState.DEAD4GOOD))) {
counter++;
}
}
}
return counter;
}
In terms of using refactoring, some things you can do are;
copy the method and create two version, one with true hard coded and the other false hard coded. Your refactoring tools should help you inline this constant and remove code as required.
recreate the method which calls the right true/false method as above for backward compatibility. You can then inline this method.
I would be inclined here to keep a map from the CellState enum to count, then add the LIVE and the SICK or the DEAD and the DEAD4GOOD as needed.
int calculateNumOfLiveOrDeadNeighbors(Cell c, int gen, boolean countLiveOnes) {
final int x = c.getX();
final int y = c.getY();
final HashMap<CellState, Integer> counts = new HashMap<CellState, Integer>();
for (CellState state : CellState.values())
counts.put(state, 0);
for (int i = x - 1; i < x + 2; i++) {
for (int j = y - 1; j < y + 2; j++) {
if (i == x && j == y)
continue;
CellState state = getCell(i, j).getCellState(gen);
counts.put(state, counts.get(state) + 1);
}
}
if (countLiveOnes)
return counts.get(CellState.LIVE) + counts.get(CellState.SICK);
else
return counts.get(CellState.DEAD) + counts.get(CellState.DEAD4GOOD);
}
have a private method which is an exact copy and paste of what you currently have.
Then create two new methods, each with a more descriptive name that simply call your private method with appropriate boolean

Categories