BlitzCoder Essentials
•
Home Page
•
About BlitzCoder
•
Contributors
•
Terms of Use
•
Email Us
Main Areas
•
BlitzCoder Chat
•
Discussions
•
Articles/Tutorials
•
Code Database
•
Link Database
•
Showcase Area
•
Worklogs
•
Competitions
Special Areas
•
Undocumented
Other Blitz Sites
•
Blitz Basic Home
•
Blitz Showcase
•
BlitzPlay Library
Forum Login
Username:
Password:
•
Register Now!
BlitzCoder Code Archives Page
Main Codes Page
"Array iteration techniques"
, by thechange
Demonstrating iteration/looping through a 1 and 2 dimensional array using 1 or 2 loops starting from either 0 or 1.
Code
;-> Introduction ; ; There are people who can 'shake this out of their sleeve' like ; there is nothing to it. In fact it takes quite some thinking and ; practice before one can understand what on earth is going on :P ; ;-> Usefulness ; ; When there's either a one or two dimensional array, you will always ; have to loop through it one way or another to retrieve or store the ; contents. Sometimes you have only 1 variable to index the items and ; when you're lucky you'll have two :o) ; ; In the below code a 2D tilemap is used as the example, but anything ; similar could be used. There are 16 different loops which, in fact, ; do the exact same thing. ; ; There are times when your tiles (or values) are stored starting ; from 1 or 0 (the offset). Usually you have to switch a lot between ; these two to get the right results (imagine multiplication). ; ;-> Fatalities ; ; (!) Take care of how you define your X and Y ordering, as ; ; TileMap( CounterX , CounterY ) ; ; instead of ; ; TileMap( CounterY , CounterX ) ; ; can lead to disastrous results. You can swap them ofcourse, so ; long as you're keep a consequent order, e.g. swapping them even ; in the index equations. ; ; In the code below the Y has higher priority than the X, e.g. ; ; TileMap( CounterY , CounterX ) ; ; (!) Also make sure not to mix 0-based with 1-based maths, like ; counting from 0 to 9, or from 1 to 10, both being 10 elements. ; ; If you were drawing the tiles to the screen using 1-based ; values in the tilemap, you'd have to subtract 1 before, for ; example, multiplying by the size of the tiles, or your map ; would be slightly shifted. Or worse, the collision detection ; could seem not to work, just because what you're seeing isn't ; what you're getting :P ; ;-> Definition and Initialization ; ; First some example sizes for the arrays and array declaration. ;= Horizontal and vertical sizes ; ; You can adjust one of these to see if the (X,Y) ordering is ; correct and the formula etc. ; Const SizeY = 10 Const SizeX = 10 ;= Single (1) dimensional array ; ; Index ; ; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 .. -> .. 100 ; Dim TileMap1( SizeY * SizeX ) ;= Double (2) dimensional array ; ; (IndexY,IndexX) ; ; ( 0, 0) ( 0, 1) ( 0, 2) ( 0, 3) ( 0, 4) (.. -> ..) ( 0,10) ; ( 1, 0) ( 1, 1) ( 1, 2) ( 1, 3) ( 1, 4) (.. -> ..) ( 1,10) ; ( 2, 0) ( 2, 1) ( 2, 2) ( 2, 3) ( 2, 4) (.. -> ..) ( 2,10) ; ( 3, 0) ( 3, 1) ( 3, 2) ( 3, 3) ( 3, 4) (.. -> ..) ( 3,10) ; ( 4, 0) ( 4, 1) ( 4, 2) ( 4, 3) ( 4, 4) (.. -> ..) ( 4,10) ; (.. ..) ; | ; v ; (.. ..) ; (10, 0) (10, 1) (10, 2) (10, 3) (10, 4) (.. -> ..) (10,10) ; Dim TileMap2( SizeY , SizeX ) ;-> The loops ; ; It doesn't really matter what is inside the array. The main issue ; is accessing the array correctly using any array and any looping ; variables, either of those being either 0-based or 1-based. ; ; Starting simple and slowly progressing towards the hardest way of ; iteration/looping. ;== One dimension, one loop ; ; A good way to see (and hopefully understand) the main difference ; between 0-based and 1-based values. ;= 1-based loop -> 1-based array ; ; Counter Index ; ; 1 2 3 .. -> .. 100 1 2 3 .. -> .. 100 ; For Counter = 1 To ( SizeY * SizeX ) Index = Counter TileNum = TileMap1( Index ) Next ;= 0-based loop -> 0-based array ; ; Counter Index ; ; 0 1 2 .. -> .. 99 0 1 2 .. -> .. 99 ; For Counter = 0 To ( SizeY * SizeX )-1 Index = Counter TileNum = TileMap1( Index ) Next ;= 1-based loop -> 0-based array ; ; Counter Index ; ; 1 2 3 .. -> .. 100 0 1 2 .. -> .. 99 ; For Counter = 1 To ( SizeY * SizeX ) Index = Counter - 1 TileNum = TileMap1( Index ) Next ;= 0-based loop -> 1-based array ; ; Counter Index ; ; 0 1 2 .. -> .. 99 1 2 3 .. -> .. 100 ; For Counter = 0 To ( SizeY * SizeX )-1 Index = Counter + 1 TileNum = TileMap1( Index ) Next ;== Two dimensions, two loops ; ; The same as before, only using an additional loop and array. ;= 1-based loops -> 1-based array ; ; (CounterY,CounterX) (IndexY,IndexX) ; ; ( 1, 1) ( 1, 2) ( 1, 3) (.. ( 1, 1) ( 1, 2) ( 1, 3) (.. ; ( 2, 1) ( 2, 2) ( 2, 3) (.. ( 2, 1) ( 2, 2) ( 2, 3) (.. ; ( 3, 1) ( 3, 2) ( 3, 3) (.. ( 3, 1) ( 3, 2) ( 3, 3) (.. ; (.. (.. ; For CounterY = 1 To SizeY For CounterX = 1 To SizeX IndexY = CounterY IndexX = CounterX TileNum = TileMap2( IndexY , IndexX ) Next Next ;= 0-based loops -> 0-based array ; ; (CounterY,CounterX) (IndexY,IndexX) ; ; ( 0, 0) ( 0, 1) ( 0, 2) (.. ( 0, 0) ( 0, 1) ( 0, 2) (.. ; ( 1, 0) ( 1, 1) ( 1, 2) (.. ( 1, 0) ( 1, 1) ( 1, 2) (.. ; ( 2, 0) ( 2, 1) ( 2, 2) (.. ( 2, 0) ( 2, 1) ( 2, 2) (.. ; (.. (.. ; For CounterY = 0 To SizeY-1 For CounterX = 0 To SizeX-1 IndexY = CounterY IndexX = CounterX TileNum = TileMap2( IndexY , IndexX ) Next Next ;= 1-based loops -> 0-based array ; ; (CounterY,CounterX) (IndexY,IndexX) ; ; ( 1, 1) ( 1, 2) ( 1, 3) (.. ( 0, 0) ( 0, 1) ( 0, 2) (.. ; ( 2, 1) ( 2, 2) ( 2, 3) (.. ( 1, 0) ( 1, 1) ( 1, 2) (.. ; ( 3, 1) ( 3, 2) ( 3, 3) (.. ( 2, 0) ( 2, 1) ( 2, 2) (.. ; (.. (.. ; For CounterY = 1 To SizeY For CounterX = 1 To SizeX IndexY = CounterY - 1 IndexX = CounterX - 1 TileNum = TileMap2( IndexY , IndexX ) Next Next ;= 0-based loops -> 1-based array ; ; (CounterY,CounterX) (IndexY,IndexX) ; ; ( 0, 0) ( 0, 1) ( 0, 2) (.. ( 1, 1) ( 1, 2) ( 1, 3) (.. ; ( 1, 0) ( 1, 1) ( 1, 2) (.. ( 2, 1) ( 2, 2) ( 2, 3) (.. ; ( 2, 0) ( 2, 1) ( 2, 2) (.. ( 3, 1) ( 3, 2) ( 3, 3) (.. ; (.. (.. ; For CounterY = 0 To SizeY-1 For CounterX = 0 To SizeX-1 IndexY = CounterY + 1 IndexX = CounterX + 1 TileNum = TileMap2( IndexY , IndexX ) Next Next ;== One dimension, two loops ; ; The single dimensional index is calculated using a formula which ; is more straight-forward than you might think. ; ; If the vertical axis/counter has higher priority (for example ; used in the outer loop), the values are stored, first from left ; to right, and then, from top to bottom. ; ; The index number is simply the current horizontal position ; (like an offset) PLUS the size of a horizontal line (think of a ; line in a text file) times the current vertical line starting ; from 0 (0-based) for the right result - see the grid below. ; ; Drawing it on paper helps too :o) ;= 0-based loops -> 0-based array ; ; (CounterY,CounterX) Index ; ; ( 0, 0) ( 0, 1) ( 0, 2) (.. 0 1 2 .. ; ( 1, 0) ( 1, 1) ( 1, 2) (.. 10 11 12 .. ; ( 2, 0) ( 2, 1) ( 2, 2) (.. 20 21 22 .. ; (.. .. ; ; Index calculations ; ; (0*10+0) (0*10+1) (0*10+2) (.. ; (1*10+0) (1*10+1) (1*10+2) (.. ; (2*10+0) (2*10+1) (2*10+2) (.. ; (.. ; For CounterY = 0 To SizeY-1 For CounterX = 0 To SizeX-1 Index = CounterY * SizeX + CounterX TileNum = TileMap1( Index ) Next Next ;= 1-based loops -> 1-based array ; ; (CounterY,CounterX) Index ; ; ( 1, 1) ( 1, 2) ( 1, 3) (.. 1 2 3 .. ; ( 2, 1) ( 2, 2) ( 2, 3) (.. 11 12 13 .. ; ( 3, 1) ( 3, 2) ( 3, 3) (.. 21 22 23 .. ; (.. .. ; ; Index calculations ; ; ((1-1)*10+1) ((1-1)*10+2) ((1-1)*10+3) (.. ; ((2-1)*10+1) ((2-1)*10+2) ((2-1)*10+3) (.. ; ((3-1)*10+1) ((3-1)*10+2) ((3-1)*10+3) (.. ; (.. ; For CounterY = 1 To SizeY For CounterX = 1 To SizeX ; Some people prefer using 0-based values everywhere because ; maths like calculating the index below require them so it ; would make for more consistent coding. ; However, 1-based values are easier to read/understand for ; most people so it's an endless fight between the two :) Index = (CounterY-1) * SizeX + CounterX TileNum = TileMap1( Index ) Next Next ;= 0-based loops -> 1-based array ; ; (CounterY,CounterX) Index ; ; ( 0, 0) ( 0, 1) ( 0, 2) (.. 1 2 3 .. ; ( 1, 0) ( 1, 1) ( 1, 2) (.. 11 12 13 .. ; ( 2, 0) ( 2, 1) ( 2, 2) (.. 21 22 23 .. ; (.. .. ; ; Index calculations ; ; (0*10+(0+1)) (0*10+(1+1)) (0*10+(2+1)) (.. ; (1*10+(0+1)) (1*10+(1+1)) (1*10+(2+1)) (.. ; (2*10+(0+1)) (2*10+(1+1)) (2*10+(2+1)) (.. ; (.. ; For CounterY = 0 To SizeY-1 For CounterX = 0 To SizeX-1 Index = CounterY * SizeX + (CounterX+1) TileNum = TileMap1( Index ) Next Next ;= 1-based loops -> 0-based array ; ; (CounterY,CounterX) Index ; ; ( 1, 1) ( 1, 2) ( 1, 3) (.. 0 1 2 .. ; ( 2, 1) ( 2, 2) ( 2, 3) (.. 10 11 12 .. ; ( 3, 1) ( 3, 2) ( 3, 3) (.. 20 21 22 .. ; (.. .. ; ; Index calculations ; ; ((1-1)*10+(1-1)) ((1-1)*10+(2-1)) ((1-1)*10+(3-1)) (.. ; ((2-1)*10+(1-1)) ((2-1)*10+(2-1)) ((2-1)*10+(3-1)) (.. ; ((3-1)*10+(1-1)) ((3-1)*10+(2-1)) ((3-1)*10+(3-1)) (.. ; (.. ; For CounterY = 1 To SizeY For CounterX = 1 To SizeX Index = (CounterY-1) * SizeX + (CounterX-1) TileNum = TileMap1( Index ) Next Next ;== Two dimensions, one loop ; ; This is probably the most difficult loop->array construction ; which, in the code below, uses the Modulo operator. There are ; ofcourse various ways to do this but this one is the shortest :) ; ; The vertical index is calculated by doing exactly the reversed of ; what was done last time, which is dividing the iterator/counter ; by the size of the horizontal 'line', of which the result is ; automatically rounded down when put into an integer variable. ; ; This time, not only the vertical index but also the horizontal ; index makes use of the horizontal 'line' size. In fact, we have ; to keep subtracting the horizontal 'line' from the counter until ; we have a number below the size of the horizontal 'line'. This ; is exactly what Mod does. It's like removing a number and ; keeping the remainder. ; ; Although, the remainder is non-inclusive, which means, when ; specifying the horizontal 'line' size, the result will always be ; smaller than that size. ;= 0-based loop -> 0-based array ; ; Counter (IndexY,IndexX) ; ; 0 1 2 .. ( 0, 0) ( 0, 1) ( 0, 2) (.. ; 10 11 12 .. ( 1, 0) ( 1, 1) ( 1, 2) (.. ; 20 21 22 .. ( 2, 0) ( 2, 1) ( 2, 2) (.. ; .. (.. ; ; IndexY (division=rounded) ; ; (0.0= 0) (0.1= 0) (0.2= 0) (.. -> ..) (0.9= 0) ; (1.0= 1) (1.1= 1) (1.2= 1) (.. -> ..) (1.9= 1) ; (2.0= 2) (2.1= 2) (2.2= 2) (.. -> ..) (2.9= 2) ; (.. ..) ; | ; v ; (.. ..) ; (9.0= 9) (9.1= 9) (9.2= 9) (.. -> ..) (9.9= 9) ; ; IndexX (counter=remainder) ; ; ( 0= 0) ( 1= 1) ( 2= 2) (.. -> ..) ( 9= 9) ; (10= 0) (11= 1) (12= 2) (.. -> ..) (19= 9) ; (20= 0) (21= 1) (22= 2) (.. -> ..) (29= 9) ; (.. ..) ; | ; v ; (.. ..) ; (90= 0) (91= 1) (92= 2) (.. -> ..) (99= 9) ; For Counter = 0 To ( SizeY * SizeX )-1 ; IndexY ranges from 0 to whereever Counter goes to IndexY = Counter / SizeX ; IndexX ranges from 0 to SizeX-1 IndexX = Counter Mod SizeX TileNum = TileMap2( IndexY , IndexX ) Next ;= 0-based loop -> 1-based array ; ; Counter (IndexY,IndexX) ; ; 0 1 2 .. ( 1, 1) ( 1, 2) ( 1, 3) (.. ; 10 11 12 .. ( 2, 1) ( 2, 2) ( 2, 3) (.. ; 20 21 22 .. ( 3, 1) ( 3, 2) ( 3, 3) (.. ; .. (.. ; ; IndexY and IndexX calculations are the same as last time, apart ; from adding one to the result (1-based conversion). ; ; IndexY examples ; ; 0 / 10 + 1 = 0.0 + 1 = 0 + 1 = 1 ; 8 / 10 + 1 = 0.8 + 1 = 0 + 1 = 1 ; 9 / 10 + 1 = 0.9 + 1 = 0 + 1 = 1 ; 10 / 10 + 1 = 1.0 + 1 = 1 + 1 = 2 ; 98 / 10 + 1 = 9.8 + 1 = 9 + 1 = 10 ; 99 / 10 + 1 = 9.9 + 1 = 9 + 1 = 10 ; ; IndexX examples (% = Mod) ; ; 0 % 10 + 1 = 0 + 1 = 1 ; 8 % 10 + 1 = 8 + 1 = 9 ; 9 % 10 + 1 = 9 + 1 = 10 ; 10 % 10 + 1 = 0 + 1 = 1 ; 98 % 10 + 1 = 8 + 1 = 9 ; 99 % 10 + 1 = 9 + 1 = 10 ; For Counter = 0 To ( SizeY * SizeX )-1 ; IndexY starts at 1 IndexY = Counter / SizeX + 1 ; IndexX ranges from 1 to SizeX IndexX = Counter Mod SizeX + 1 TileNum = TileMap2( IndexY , IndexX ) Next ;= 1-based loop -> 0-based array ; ; Counter (IndexY,IndexX) ; ; 1 2 3 .. ( 0, 0) ( 0, 1) ( 0, 2) (.. ; 11 12 13 .. ( 1, 0) ( 1, 1) ( 1, 2) (.. ; 21 22 23 .. ( 2, 0) ( 2, 1) ( 2, 2) (.. ; .. (.. ; ; IndexY and IndexX calculations are again, exactly the same as ; the first time, only now subtracting one from the counter ; before the rest of the equation. ; ; IndexY examples ; ; ( 1 - 1 ) / 10 = 0 / 10 = 0.0 = 0 ; ( 9 - 1 ) / 10 = 8 / 10 = 0.8 = 0 ; ( 10 - 1 ) / 10 = 9 / 10 = 0.9 = 0 ; ( 11 - 1 ) / 10 = 10 / 10 = 1.0 = 1 ; ( 99 - 1 ) / 10 = 98 / 10 = 9.8 = 9 ; (100 - 1 ) / 10 = 99 / 10 = 9.9 = 9 ; ; IndexX examples (% = Mod) ; ; ( 1 - 1 ) % 10 = 0 % 10 = 0 ; ( 9 - 1 ) % 10 = 8 % 10 = 8 ; ( 10 - 1 ) % 10 = 9 % 10 = 9 ; ( 11 - 1 ) % 10 = 10 % 10 = 0 ; ( 99 - 1 ) % 10 = 98 % 10 = 8 ; (100 - 1 ) % 10 = 99 % 10 = 9 ; For Counter = 1 To ( SizeY * SizeX ) ; IndexY starts at 0 IndexY = (Counter-1) / SizeX ; IndexX ranges from 0 to SizeX-1 IndexX = (Counter-1) Mod SizeX TileNum = TileMap2( IndexY , IndexX ) Next ;= 1-based loop -> 1-based array ; ; Counter (IndexY,IndexX) ; ; 1 2 3 .. ( 1, 1) ( 1, 2) ( 1, 3) (.. ; 11 12 13 .. ( 2, 1) ( 2, 2) ( 2, 3) (.. ; 21 22 23 .. ( 3, 1) ( 3, 2) ( 3, 3) (.. ; .. (.. ; ; This time the IndexY and IndexX calculations are a combination ; of the previous two ways, subtracting one before and adding one ; after applying division and modulo. ; ; IndexY examples ; ; ( 1 - 1 ) / 10 + 1 = 0 / 10 + 1 = 0.0 + 1 = 0 + 1 = 1 ; ( 9 - 1 ) / 10 + 1 = 8 / 10 + 1 = 0.8 + 1 = 0 + 1 = 1 ; ( 10 - 1 ) / 10 + 1 = 9 / 10 + 1 = 0.9 + 1 = 0 + 1 = 1 ; ( 11 - 1 ) / 10 + 1 = 10 / 10 + 1 = 1.0 + 1 = 1 + 1 = 2 ; ( 99 - 1 ) / 10 + 1 = 98 / 10 + 1 = 9.8 + 1 = 9 + 1 = 10 ; (100 - 1 ) / 10 + 1 = 99 / 10 + 1 = 9.9 + 1 = 9 + 1 = 10 ; ; IndexX examples (% = Mod) ; ; ( 1 - 1 ) % 10 + 1 = 0 % 10 + 1 = 0 + 1 = 1 ; ( 9 - 1 ) % 10 + 1 = 8 % 10 + 1 = 8 + 1 = 9 ; ( 10 - 1 ) % 10 + 1 = 9 % 10 + 1 = 9 + 1 = 10 ; ( 11 - 1 ) % 10 + 1 = 10 % 10 + 1 = 0 + 1 = 1 ; ( 99 - 1 ) % 10 + 1 = 98 % 10 + 1 = 8 + 1 = 9 ; (100 - 1 ) % 10 + 1 = 99 % 10 + 1 = 9 + 1 = 10 ; For Counter = 1 To ( SizeY * SizeX ) ; IndexY starts at 1 IndexY = (Counter-1) / SizeX + 1 ; IndexX ranges from 1 to SizeX IndexX = (Counter-1) Mod SizeX + 1 TileNum = TileMap2( IndexY , IndexX ) Next ;-> Combinations ; ; Ofcourse you can make things more complicated than they should be, ; by, for example, using a somewhat elaborate combination of the ways ; above. ; ; Imagine a single 1-based iterator, having to loop through a one ; dimensional array by first converting to two 0-based iterators and ; back to a single 0-based iterator by assuming to be using a 1-based ; iterator instead. Ehh, say what? :P ; ; It's in fact not very hard if you simply combine all of the above ; into one overly complex equation. ; ; Here's the mindbreaker: For Counter = 1 To ( SizeY * SizeX ) Index = (((Counter-1) / SizeX + 1)-1) * SizeX + (((Counter-1) Mod SizeX + 1)-1) TileNum = TileMap1( Index ) Next ; By combining the above techniques you can also use an array with 3 ; or more dimensions. For example converting between 2 and 3 ; dimensions which would also make for an interesting type of ; encryption or encoding. Or simply a 3D drawing technique on a 2D ; screen without the use of preprogrammed (Blitz3D) functions. ;-> Help ; ; If you run into any problems with the above, let me know ;) ; ; Have fun =)
Copyright(c) 2000-2004, BlitzCoder. All Rights Reserved.
Code software created by Krylar's Kreations