2D Arrays

Monkey Forums/Monkey Programming/2D Arrays

Why0Why(Posted 2011) [#1]
I need some help with 2D arrays. I know how to convert to a 1d and I have read the existing topics here. I would like to know how to get this right so that I understand it. I want to declare the array and then set the number of elements with provided parameters at runtime. This is a level class for a 2d roguelike. Thanks!


Class Level
	Field LevelWidth:Int
	Field LevelHeight:Int
	Field Tiles:Int[][]
	Field i:Int,j:Int
	Field walls:GameImage
		
	Method Generate:Void(Width:Int,Height:Int)
		LevelWidth=Width
		LevelHeight=Height
		Tiles=[New Int[Width],New Int[Height]]
		walls=game.images.Find("walls")	
		
		For i=0 To LevelWidth-1
			For j=0 To LevelHeight-1
				Tiles[i][j]=Solid
			Next
		Next
		
	End
	
	Method Draw:Void()
		For i=0 to 19 Step 32
			For j=0 to 14 Step 32
				walls.Draw(i,j,,,,Solid)
			Next
		Next
	End
End




muddy_shoes(Posted 2011) [#2]
I know you said you've read the existing topics, but this issue was covered in this thread: http://monkeycoder.co.nz/Community/posts.php?topic=748.


Why0Why(Posted 2011) [#3]
That topic isn't clear to me. None of the examples have it declared at top and sized in the code. A simple example is all that I would like. What I have looks like it should work to me, but it doesn't.


skid(Posted 2011) [#4]
the problem with your code is this line

Tiles=[New Int[Width],New Int[Height]]


It creates an array of arrays with two members, one a single dimensional array Width big, the second a single dimensional array Height big.

What you want is Height objects Width big.

Seeing as you don't know Height in advance you can't go

Tiles=[New Int[Width],New Int[Width],New Int[Width],New Int[Width],New Int[Width],New Int[Width] ....]


So you need to use a loop to create the array.


Why0Why(Posted 2011) [#5]
Yes, that is what I want. I started using Blitz 2d 10 years ago and have used Max since it was in Beta. I am just not exactly getting the syntax in Monkey. Can I please get an example where the array is declared earlier and then sized once the dimensions are known? I have experimented with it many different ways and wasted a bunch of time that could be better spent on programming. I have read the other topic multiple times.

Thanks for the help.


muddy_shoes(Posted 2011) [#6]
Okay, but I'm just going to end up repeating what was already written. It doesn't matter where the variable name and type is declared. Here's a single dimensional Array:

Function Main()
	Local Tiles:Int[]
        Tiles = New Int[10]
	Print Tiles.Length ' prints "10"
End


That declares Tiles to be an array of Ints and then initialises it with an array of length ten. You could also declare it directly:

Local Tiles:Int[] = [0,0,0,0,0,0,0,0,0,0]


Now, multidimensional arrays in Monkey follow the "Array of Arrays" model so, if you want a two-dimensional array that contains Ints, then you are going to be declaring an Array of Arrays of Ints.

Function Main()
	Local Tiles:Int[][] 
        Tiles = [New Int[2],New Int[3],New Int[4]]
	Tiles[2][3] = 99
	Print Tiles.Length '3
	Print Tiles[0].Length '2
	Print Tiles[1].Length '3
	Print Tiles[2].Length '4
	Print Tiles[2][3]  '99
End


Note that the arrays can be "ragged", i.e. the rows do not have to be the same length.

So, coming back to your code above. You seem to have made a common error in your declaration:

Tiles=[New Int[Width],New Int[Height]]


I imagine that you think this is doing something like:
Tiles = Int[Width][Height]

...and declaring a Width x Height array of Ints. If you read your declaration though, you'll see that you're actually declaring an array with two elements: one array of Ints of length Width and one array of Ints of length Height. In other words, your array only has two rows.

If you want a Width x Height array then you need to declare each row as an array of the appropriate length. In the thread I pointed you to, there are at least two given code examples of how to do this with variable array sizes.


muddy_shoes(Posted 2011) [#7]
If you're still struggling:

Function AllocateArray:Int[][]( i:Int, j:Int)
    Local arr:Int[][] = New Int[i][]
    For Local ind = 0 Until i
        arr[ind] = New Int[j]
    Next
    Return arr		
End

Function Main()
	Local Tiles:Int[][]
	Tiles = AllocateArray(3,4)
	Tiles[2][3] = 99
	Print Tiles.Length '3
	Print Tiles[0].Length '4
	Print Tiles[1].Length '4
	Print Tiles[2].Length '4
	Print Tiles[2][3]  '99
End



Why0Why(Posted 2011) [#8]
That is much more clear, thank you. I wasn't clear on when new was needed either. Much appreciated.


Chroma(Posted 2011) [#9]
Yeah but can I do a:

Local arr:Int[][] = New Int[5][5]

For Local y = 0 to 4
For Local x = 0 to 4
arr[x][y] = 1
Next
Next

?


MikeHart(Posted 2011) [#10]
...


Foppy(Posted 2011) [#11]
I guess you cannot do it in one step like that. You need to create an array of arrays in step 1 (involving one call to New), and then create the subarrays inside the big array in step 2 (involving as many calls to New as there are subarrays), as in muddy_shoes' example.


jondecker76(Posted 2012) [#12]
I'm trying to do the whole two dimensional array thing as explained above. However, I need an 2d array of class instances instead of a 2d array of ints. I have tried modifying the function, but I get null access errors trying to access the elements later in code...

Global FIELD_X:int = 5
Global FIELD_Y:int = 7

local grid:Dice[][]
grid=AllocateArray(FIELD_X,FIELD_Y)


'Randomize the grid
Local x:Int
Local y:Int
		
For x=0 To FIELD_X-1
	For y=0 To FIELD_Y-1
		grid[x][y].value=Rnd(1,6) ' I get a null access error here
	Next
Next

Class Dice
	Field posX:Float
	Field posY:Float
	Field value:Int
	
	Method New()
		posX=0
		posY=0
		value=0
	End
End

Function AllocateArray:Dice[][]( i:Int, j:Int)
    Local arr:Dice[][] = New Dice[i][]
    For Local ind = 0 Until i
        arr[ind] = New Dice[j]
    Next
    Return arr		
End



Any ideas?


Amon(Posted 2012) [#13]
@jd
Don't you have to 'new' the Class?


Shinkiro1(Posted 2012) [#14]
Amon is right, loop through the whole array and 'New" the references (each element)


jondecker76(Posted 2012) [#15]
Isn't that what the AllocateArray function is doing ?


jondecker76(Posted 2012) [#16]
I'll try looping through the array after the AllocateArray function is called and trying the "new" suggestion


muddy_shoes(Posted 2012) [#17]
The AllocateArray function above allocates the array. It doesn't initialise the contents. For objects, the array just holds object references so you have to create your objects and insert the reference for there to be a valid reference in a given array position.

By the way, with generics I now prefer this:

[monkeycode]
Class ArrayUtil<T>
Function CreateArray:T[][](rows:Int,cols:Int)
Local a:T[][] = New T[rows][]
For Local i:Int = 0 Until rows
a[i] = New T[cols]
End

Return a
End
End
[/monkeycode]

Which can be used to create any array type, e.g:

[monkeycode]
Local diceArr:Dice[][] = ArrayUtil<Dice>.CreateArray(25, 50)
[/monkeycode]


Amon(Posted 2012) [#18]
I like that! Gonna save that code; thanks muddy!


therevills(Posted 2012) [#19]
For the Diddy users out there, Diddy also has a CreateArray method in it's Array Class:

http://code.google.com/p/diddy/source/browse/trunk/src/diddy/arrays.monkey


Amon(Posted 2012) [#20]
I think this should help you out, jd! I used your code and made a working example.


Strict

Import mojo


Function Main:Int()
	New ArrayCTest
	Return 0
End Function

Class ArrayCTest Extends App
	Global FIELD_X:int = 5
	Global FIELD_Y:int = 7
	Global grid:Dice[][]
	
	Method OnCreate:Int()
		SetUpdateRate(60)
		grid = AllocateArray(FIELD_X, FIELD_Y)
		
		For Local x:int = 0 To FIELD_X - 1
			For Local y:Int = 0 To FIELD_Y - 1
				grid[x][y] = New Dice
			Next
		Next
		For Local x:Int = 0 To FIELD_X - 1
			For Local y:Int = 0 To FIELD_Y - 1
				grid[x][y].value = Rnd(1, 6)
			Next
		Next		
		Return 0
	End Method
	
	Method OnUpdate:Int()
	
		Return 0
	End Method
	
	Method OnRender:Int()
		Cls
		For Local x:Int = 0 To FIELD_X - 1
			For Local y:Int = 0 To FIELD_Y - 1
				If grid[x][y].value > 0 And grid[x][y].value < 7
					DrawText(grid[x][y].value, x * 20, y * 20)
				EndIf
			Next
		Next
		Return 0
	End Method
	
End Class

Class Dice
	Field posX:Float
	Field posY:Float
	Field value:Int
	
	Method New()
		posX=0
		posY=0
		value=0
	End
End

Function AllocateArray:Dice[][]( i:Int, j:Int)
    Local arr:Dice[][] = New Dice[i][]
    For Local ind:Int = 0 Until i
        arr[ind] = New Dice[j]
    Next
    Return arr		
End