Win a copy of Transfer Learning for Natural Language Processing (MEAP) this week in the Artificial Intelligence and Machine Learning forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • Paul Clapham
  • Devaka Cooray
  • Bear Bibeault
Sheriffs:
  • Junilu Lacar
  • Knute Snortum
  • Liutauras Vilda
Saloon Keepers:
  • Ron McLeod
  • Stephan van Hulst
  • Tim Moores
  • Tim Holloway
  • Piet Souris
Bartenders:
  • salvin francis
  • Carey Brown
  • Frits Walraven

Looping through multidimensional array

 
Greenhorn
Posts: 21
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hey folks!! I'm following a tutorial along to create a minesweeper game and I came across this piece of code:



This method is suppose to "get" all the gameobjects[][] neighbors (neighbors for each square) but I can't tell exactly how it's doing it. Could anyone please help me visualise?

 
Saloon Keeper
Posts: 21960
149
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
OK. The object is to create a List og GameObject's that are adjacent to a specified GameObject. This is done by scanning the following potential matrix, which is overlaid on the board matrix:


Note that the center cell is omitted, since that's where the reference GameObject itself lies.

We increment the x and y co-ordinates from relative -1 to relative +1, indexing the x co-ordinate the fastest. Or, in other words, scan by row, then column going left-to-right and top-to-bottom.

Hower, the gameboard is of finite dimensions, so co-ordinate positions that fall off the edge of the board (board co-ordinates of less than 0 or more than the board width and height) are ignored, since they don't exist and would throw an ArrayIndexException if you tried to use them.

Given those constraints, the eligible cells are each checked for the presence of another GameObject, and if present, that GameObject is added to the returned list. If there are no adjacent objects, an empty list will be returned.
 
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I would try to avoid using continue for this. Since Java 9, that code can also be written like so:

where you have this helper method:

In Java 8, you'd have to use Arrays.asList(-1, 0, 1) instead of List.of(-1, 0, 1)
 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote: the eligible cells are each checked for the presence of another GameObject, and if present, that GameObject is added to the returned list. If there are no adjacent objects, an empty list will be returned.


That's not quite what that code is doing though. The check on line 11 is basically saying "if the object in gameField[y][x] is the gameObject we're trying to get the neighbors for..." and the continue on line 12 says "...skip it." In other words, if the x offset is 0 and the y offset is 0, then you've got the coordinates of the gameObject itself, not of any of its neighbors. There is no logic in that code that says "if present, add to the returned list."

It looks like the code assumes that all cells on the gameField have gameObjects in them. Maybe an empty cell is also represented by a special kind of GameObject instance.
 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
If indeed there is a concept of an "empty" cell in the gameField and assuming being empty is represented by null, I would change the code I suggested slightly to:

and the helper method to:

If you go with the former algorithm, you should probably look into using a Null Object.
 
Rancher
Posts: 3503
37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:



Based on the original code, I think you want

since the original code had "continue" for x == SIZE or y == SIZE.
 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Mike Simmons wrote:Based on the original code, I think you want
... since the original code had "continue" for x == SIZE or y == SIZE.


Yes, you're absolutely right. My bad.
 
Tim Holloway
Saloon Keeper
Posts: 21960
149
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sorry, but my sense of symmetry does not agree with an isNotEmpty()  method that equates "empty" with both an empty cell - that is, one with nothing in it and an "empty" cell that has nothing in in because it doesn't even exist. And don't get me started on cells that have something in them but not something that qualifies.

It's my Fortran background showing, but I probably would have coded this problem exactly as initially stated. It's what I call a "filtered loop", meaning that you only act on elements that pass the leading filter conditions.

But somewhere around Java 8 we added constructs that put the filter directly into the loop heading, did we not?
 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I rather like the way it could be written in Kotlin:

Kinda wish there was a built-in way to be this expressive in Java.
 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote:Sorry, but my sense of symmetry does not agree with an isNotEmpty()  method that equates "empty" with both an empty cell - that is, one with nothing in it and an "empty" cell that has nothing in in because it doesn't even exist.


That's a very valid argument that I would definitely consider. I've been known to make poor design choices and that would be a good reminder of another such occasion

And don't get me started on cells that have something in them but not something that qualifies.


(edit) Wait, so you'd have an objection to using a Null Object here? I'd actually like to dig into this, if you don't mind.
 
Mike Simmons
Rancher
Posts: 3503
37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:I rather like the way it could be written in Kotlin:

Kinda wish there was a built-in way to be this expressive in Java.



What, you don't think

would cut it? ;)
 
Tim Holloway
Saloon Keeper
Posts: 21960
149
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:

Tim Holloway wrote:
And don't get me started on cells that have something in them but not something that qualifies.


(edit) Wait, so you'd have an objection to using a Null Object here? I'd actually like to dig into this, if you don't mind.



Actually, I was thinking more in the lines of variants where you'd be looking for certain chess pieces. marker/no-marker is OK for "isNotEmpty", although for preference, "not isEmpty()". Marker/no-marker/no-cell was just a step too far.
 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote:Actually, I was thinking more in the lines of variants


Ok, got it. Thanks. You're right about a step too far though and I was kind of thinking that as I wrote the revision but it didn't bother enough to stop me. That you objected to it raised the smell level for me, which in itself is a little interesting.
 
Tim Holloway
Saloon Keeper
Posts: 21960
149
Android Eclipse IDE Tomcat Server Redhat Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Mike's got something along the lines of what I was thinking. Though I suspect a truly evil version of this would incorporate a map method.
 
Mike Simmons
Rancher
Posts: 3503
37
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I wasn't trying to be evil - I was actually lamenting that Java never got a proper equivalent of Guava's Range class, which was the closest other equivalent I knew of to Kotlin's IntRange.  The Guava version would be

which is not too far off from the Kotlin version.  I was trying to find a modern Java equivalent, which led me to the IntStream version I posted.  Which is pretty bad.

But as long as we're turning towards evil, how's this?
 
Pablo Aguirre
Greenhorn
Posts: 21
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:It looks like the code assumes that all cells on the gameField have gameObjects in them. Maybe an empty cell is also represented by a special kind of GameObject instance.



Thanks for all the replies, I'm still trying to make sense of the code as it's slightly above my head. I think yes, the previous code assumes there are no empty cells. In a different method all the cells are filled a GameObject:

 
Pablo Aguirre
Greenhorn
Posts: 21
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote:OK. The object is to create a List og GameObject's that are adjacent to a specified GameObject. This is done by scanning the following potential matrix, which is overlaid on the board matrix:


Note that the center cell is omitted, since that's where the reference GameObject itself lies.

We increment the x and y co-ordinates from relative -1 to relative +1, indexing the x co-ordinate the fastest. Or, in other words, scan by row, then column going left-to-right and top-to-bottom.

Hower, the gameboard is of finite dimensions, so co-ordinate positions that fall off the edge of the board (board co-ordinates of less than 0 or more than the board width and height) are ignored, since they don't exist and would throw an ArrayIndexException if you tried to use them.

Given those constraints, the eligible cells are each checked for the presence of another GameObject, and if present, that GameObject is added to the returned list. If there are no adjacent objects, an empty list will be returned.



I'm still a bit confused with the names of the variables. After your explanation I understand what the loop is doing, but when trying to simulate the loop starting on int y = -1, x=-1 (if y<0 || y>=side). I'm assuming the y,x variables in the if statement are the ones in the game object, not the new ones created to run the loop? Shouldn't they have a different name to make it easier to visualize? (a,b) or whatever. Or what am I missing? Again thanks a lot for the invaluable replies.
 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Pablo Aguirre wrote:I'm still a bit confused with the names of the variables. ... I'm assuming the y,x variables in the if statement are the ones in the game object, not the new ones created to run the loop?


If you're referring to the y and x here:

for (int y = ... ) {
   for (int x = ... ) {

then yes, they are the ones referred to in the if-statements. The gameObject x and y fields are qualified: gameObject.x and gameObject.y.

Shouldn't they have a different name to make it easier to visualize? (a,b) or whatever. Or what am I missing? Again thanks a lot for the invaluable replies.


They could have different names but I doubt (a, b) would make their intent any clearer. 'neighborX' and 'neighborY' maybe but that's a little wordy. In this case, I don't actually mind y and x that much.

I could suggest at least one different way that is more object-oriented but that would take us a whole 'nother path that you might not be ready to go down right now.
 
Pablo Aguirre
Greenhorn
Posts: 21
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:

Pablo Aguirre wrote:I'm still a bit confused with the names of the variables. ... I'm assuming the y,x variables in the if statement are the ones in the game object, not the new ones created to run the loop?


If you're referring to the y and x here:

for (int y = ... ) {
   for (int x = ... ) {

then yes, they are the ones referred to in the if-statements. The gameObject x and y fields are qualified: gameObject.x and gameObject.y.

Shouldn't they have a different name to make it easier to visualize? (a,b) or whatever. Or what am I missing? Again thanks a lot for the invaluable replies.


They could have different names but I doubt (a, b) would make their intent any clearer. 'neighborX' and 'neighborY' maybe but that's a little wordy. In this case, I don't actually mind y and x that much.

I could suggest at least one different way that is more object-oriented but that would take us a whole 'nother path that you might not be ready to go down right now.



After creating this little test I finally understood that writing for (int a = gameObject.y - 1; a <= gameObject.y + 1; a++)  because the number in the matrix depends on the number of the coordinates.

 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Let's visualize it a little bit more for you, except let's just use a variation of my version where we're using the loop index as an offset instead of the actual x or y coordinate.

If you just look at the values of the loop indices, yOffset and xOffset, they would cycle through these pairs in the inner for-loop:

(-1,-1) (-1, 0) (-1, 1)
( 0,-1) ( 0, 0) ( 0, 1)
( 1,-1) ( 1, 0) ( 1, 1)

Notice that the (0, 0) pair is right in the middle of that square. Those would correspond to the offsets of the cell that the gameObject itself is in. That is gameObject.x + 0 gives you gameObject.x and gameObject.y + 0 gives you gameObject.y. So the cell with offset (0, 0) is the cell that gameObject itself occupies. That, of course, is not a neighbor cell. The neighbor cells are identified by all the other offsets.

The Top-left corner will have an offset of (-1, -1), meaning you can get its coordinates with gameObject.y + (-1) and gameObject.x + (-1)
The top-right corner will have an offset of (-1, 1), meaning you can get its coordinates with gameObject.y + (-1) and gameObject.x + 1
The bottom-right corner will have an offset of (1, 1), meaning you can get its coordinates with gameObject.y + 1 and gameObject.x + 1
and so on with the other coordinates of the rest of the neighboring cells.

Are you tracking this so far?

The only difference in the above code and the code you posted is that the code you have doesn't bother with explicitly naming the idea of an "offset" but incorporates the offset calculation in the for-loop header itself:

for (int y = gameObject.y - 1; y <= gameObject.y + 1; y++) {
   for (int x = gameObject x - 1; x <= gameObject.x + 1; x++) {
       ...

So the y loop variable will take these values: gameObject.y - 1, gameObject.y + 0, gameObject.y + 1
Likewise, the x loop variable will take these values: gameObject.x - 1, gameObject.x + 0, gameObject.x + 1

Compare the expressions I bolded in both approaches. They are identical.

Does this clear up some things for you?
 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

The message you have there doesn't quite capture the intent of the if-statement. It's really this:

 
Pablo Aguirre
Greenhorn
Posts: 21
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Junilu Lacar wrote:

Does this clear up some things for you?



I'll need a bit of time to study your post, haha. Then I'll get back to you. Thanks so much!

Question, what's a good code simulator for Java for people to work in a project simultaneously? Something equivalent to stackblitz for Angular. I think it's a great tool for study.

 
Junilu Lacar
Sheriff
Posts: 15519
263
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm not familiar with stackblitz for Angular or its capabilities but I don't see why any of the collaboration tools available out there like Zoom/WebEx/GitHub/Git wouldn't help you achieve a similar dynamic. Even if you're just screen sharing, you can always put the code up on GitHub and make whoever you're collaborating with a contributor/committer in that repo. Then as each of you changes the code, you'd just pull-commit-push.
 
Whatever. Here's a tiny ad:
Two software engineers solve most of the world's problems in one K&R sized book
https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
    Bookmark Topic Watch Topic
  • New Topic