Some help appreciated

For an implementation of the good old Snake game (Nokia) I run into problems when checking coordinates against each other.
I use a cartesian model with X/Y coordinates.

Part of the game is placing a cookie somewhere in the playing field at random.
The snake coordinates are held in a 2 dimensional array for X and Y. For placing the cookie I check the random coordinates against all of the snake elements so that the cookie is not placed in a spot where the snake is. Code:

[code]'this method calculates a random spot to place the cookie
'and checks wether the coordinates are valid

While Placefood = False
'give a random coordinate

Dim j As Integer = App.Randomizer.InRange(0,BlockCount-1)
Dim k As Integer = App.Randomizer.InRange(0,BlockCount-1)

'check wether this coordinate is in range of the snake itself
For n as Integer = 0 To Ubound(TheSnake.Elements)-1
If (j = TheSnake.Elements(n,0)) AND (k = TheSnake.Elements(n,1)) Then
'exit and try new randoms
Exit for
End if
Next

'if we reach this then the coordinates are valid
FoodX = j
FoodY = k
PlaceFood = True
Return
Wend
[/code]

Unfortunately sometimes the cookie (green) is placed within the snake. Can’t find where that happens in the above code…

If you assume the pieces of snake are an array of x,y co-ordinates, then you are better off turning the array of co-ords into a dictionary

Say the snake is 3 squares long, at 10,3 10,4 and 10,5
Create a dictionary

Untested code:

[code]
dim thiskey as string
dim snakebody as new dictionary

thiskey = format(10)+"|"+format(3)
Snakebody.value(thiskey) = thiskey
thiskey = format(10)+"|"+format(4)
Snakebody.value(thiskey) = thiskey
thiskey = format(10)+"|"+format(5)
Snakebody.value(thiskey) = thiskey

//now the snake dictionary has 3 elements in it

Dim j As Integer = App.Randomizer.InRange(0,BlockCount-1) //blockcount assumes a square grid??
Dim k As Integer = App.Randomizer.InRange(0,BlockCount-1)

//blockcount assumes a square grid??
//maybe this should be App.Randomizer.InRange(0,gridwidth-1)
// and App.Randomizer.InRange(0,gridheight-1)

'check wether this coordinate is in range of the snake itself
//exists, try again
j = App.Randomizer.InRange(0,BlockCount-1)
k = App.Randomizer.InRange(0,BlockCount-1)
Next[/code]

//by this line, we have a co-ordinate which doesnt hit a snake

Hi Jeff,

the BlockCount value gets expanded to pixels in the paint event of the Canvas. In this way I can create a playing field of different format rectangles.

But WHY is a dictionary better than a 2dim array?

Faster to search , because it uses hashes.

Using an array you need to consider all the possible array elements until you find a hit.
That might be 0,0 - great!
But it might be element 100,000 … not so great… it took you 10000 tries to find a hit

using dictionary.haskey literally goes straight there… it either exists already or it doesnt.
The rest of the ‘array’ doesnt even exist.

Im using that technique for a much bigger area and it makes an incredible difference to the speed.

In the loop above, it could be a dictionary of actual Points, but I wasnt sure if adding a point as a key to a dictionary would allow comparison (maybe the key was a pointer to a Point rather than a value)

[quote=400815:@Jeff Tullin]Faster to search , because it uses hashes.

Using an array you need to consider all the possible array elements until you find a hit.
That might be 0,0 - great!
But it might be element 100,000 … not so great… it took you 10000 tries to find a hit

using dictionary.haskey literally goes straight there… it either exists already or it doesnt.
The rest of the ‘array’ doesnt even exist.

Im using that technique for a much bigger area and it makes an incredible difference to the speed.

In the loop above, it could be a dictionary of actual Points, but I wasnt sure if adding a point as a key to a dictionary would allow comparison (maybe the key was a pointer to a Point rather than a value)[/quote]

Fair enough, so you use the ‘|’ (thought it was the ‘pipe’ sign - good old DOS) as an delimiter?

No, Jeff uses ‘space+|+space’ as a delimiter.

No, Jeff uses anything at all as a delimiter. All we need is something derived from x,y values which is unique.
If we didnt keep the numbers apart, then 10,10 would be ‘1010’ yet so would 101,0
Putting something non numeric between stops that.

Ok but that is arbitrary I understand. You could use anything as long as it is non numeric

My ESP decoder is broken.

Thanks for all the replies. Have to wrap my head around this dictionary stuff.

Your comment says exit and try new randoms but your flow just uses that value and returns.

Hello,
I recently did a basic snake in Xojo. Source here.

Ah ah! The ‘wend’ command should be higher up.

[code]'this method calculates a random spot to place the cookie
'and checks wether the coordinates are valid

While Placefood = False
'give a random coordinate

Dim j As Integer = App.Randomizer.InRange(0,BlockCount-1)
Dim k As Integer = App.Randomizer.InRange(0,BlockCount-1)

'check wether this coordinate is in range of the snake itself
For n as Integer = 0 To Ubound(TheSnake.Elements)-1
If (j = TheSnake.Elements(n,0)) AND (k = TheSnake.Elements(n,1)) Then
'exit and try new randoms
Exit for
End if
Next
Wend

'if we reach this then the coordinates are valid
FoodX = j
FoodY = k
PlaceFood = True
Return
[/code]

[quote=400831:@Christian Mézes]Hello,
I recently did a basic snake in Xojo. Source here.[/quote]

I see your cookie routine looks very similar, almost exact to mine. You’re using a Do…Loop instead of a While…Wend.

[code]

Do

onSnake = FALSE

// randomly generate new food position
foodXPos = RandomNumber.InRange(0, FieldSegmentsX - 1)
foodYPos = RandomNumber.InRange(0, FieldSegmentsY - 1)

// make sure the position of the food does not lie on the snake,
// we loop through every segment of the snake and compare it’s position with the position of the food
for i = 0 to theSnake.Ubound
if ((theSnake(i).xPos = foodXPos) AND (theSnake(i).yPos = foodYPos)) then
// ok, so we hit the snake! this food position is not suitable
onSnake = TRUE
Exit for
end if
next

Loop Until (onSnake = FALSE)

// set the new food position
theFood = new Food
theFood.xPos = foodXPos
theFood.yPos = foodYPos [/code]