Jump to content

VB 2010 Snake Help

- - - - -

This topic has been archived. This means that you cannot reply to this topic.
40 replies to this topic

#1
Bound

Bound

    Learning Programmer

  • Members
  • PipPipPip
  • 30 posts
Hi all,

I'm trying to program a game of Snake in VB 2010. So far it's been pretty easy, but right now I'm stuck on adding length to my snake when he eats an apple and detecting when his head runs into any other points in his body. I'm trying to work on adding length first because he starts off as only one square long and that means it's impossible for his head to intersect anything...

To create the snake I'm using an array of points and on the point with the highest index value I use a graphics class to paint a square black and for the rest of his body I paint them transparent so you can't see them. I'm trying to make it so when he eats an apple it'll paint the next square in his body black but I can't seem to figure out a way to do that. Here's my code for painting his body:

        'Create rest of snake's body.

        For i As Integer = 0 To m - 1

            g.FillRectangle(Brushes.Transparent, New Rectangle(p(i), New Size(10, 10)))

            p(i) = p(i + 1)

        Next

        'Paint snake's head. (Highest array index value = snake's head).

        g.FillRectangle(Brushes.Black, New Rectangle(p(m), New Size(10, 10)))

        g.Dispose()

I dimensioned a variable named "m" to create an array of m points so I paint everything except the head transparent in the beginning and only the head of the snake is black. I tried using a for loop like the one above to paint the next square black but I think it doesn't work because it gets in the way of the first for loop...

Is there a way to paint the next square in the body black and so on for each time the snake eats an apple? After I figure that out I can try working on detecting intersecting points... Any help would be appreciated.

Thanks,
Bound

#2
LuthfiHakim

LuthfiHakim

    Programming God

  • Members
  • PipPipPipPipPipPipPip
  • 765 posts
Hi Bound,

I've never develop a "snake" game before, but I think I could give you one or two pointers.

First I don't think it's necessary to paint "transparent" body of the snake. Wasting unnecessary cpu time, I believe. Just add a variable to store the current length of the snake and use it in FOR loop. Stop painting when reaching the snake's current length.

Second, after the snake ate an apple, you can simply increment the snake's current length variable you added for the first pointer above. There you get the new correct snake size for next painting process. The new size I believe will correctly added in the snake's tail.

Cheers!

#3
Bound

Bound

    Learning Programmer

  • Members
  • PipPipPip
  • 30 posts
Thanks for replying, LuthfiHakim. :)

Ok, I can definitely stop painting transparent points for the snake's body, but I'm not sure how to implement your suggestion of adding a variable to store the snake's length... I mean p(m) is the starting length where "m" is = to 1 square (the head) so I guess I could just dimension a variable called "iLength" or something and say:

        For i As Integer = 0 To iLength

            g.FillRectangle(Brushes.Black, New Rectangle(p(iLength), New Size(10, 10)))

            i += 1

        Next

Something like that would do it? If I'm wrong can you try to write some example code for me please? That would help a lot.

I think I can work out the eating of the apple part thanks to your suggestion. I'd just add 1 to iLength, right? :)

Thanks,
Bound

#4
Bound

Bound

    Learning Programmer

  • Members
  • PipPipPip
  • 30 posts
Ok so far I stopped painting the transparent points and tried to implement the "iLength" variable, but so far no luck :/

I think understanding how the program paints the snake might help everyone a little bit: it paints it from highest array index value to lowest... So it starts off by (let's say the array has 5 points) painting the head - p(m), then the next one p(3), then p(2), then p(1), then p(0). I don't get how the for loop I have even works sometimes because the loop goes from 0 to the highest array index value, but I'm pretty sure it paints it in the opposite direction. I found out how it painted the snake by coloring each body part a different color and remembering which point was which color and found out it painted from highest to lowest so it's very confusing for me.

Anyway, I tried using this to keep track of the snake's length and paint the rest:


    Public iLength As Integer 'Length of Snake

        'Set length of snake to 0.

        iLength = 1


        For i As Integer = iLength To 0

            g.FillRectangle(Brushes.Black, New Rectangle(p(i), New Size(10, 10)))

            p(i) = p(i - 1)

        Next


And when an apple was eaten I simply incremented iLength by 1, but still no luck. Any ideas? If you wanna take a look at more of my code just ask me and I'll post what you need to know/see. :)

Bound

Edited by Bound, 13 December 2010 - 01:38 AM.
grammar


#5
LuthfiHakim

LuthfiHakim

    Programming God

  • Members
  • PipPipPipPipPipPipPip
  • 765 posts
Yes, you have understood my points correctly. Only in implementation I found some mistakes. Like in your first reply:

Bound said:

        For i As Integer = 0 To iLength

            g.FillRectangle(Brushes.Black, New Rectangle(p(iLength), New Size(10, 10)))

            i += 1  !!!!

        Next

Note the line I marked with !!!!? You are not suppose to change the counting variable within a FOR loop. Perhaps this added some confusion when you were debugging.


Bound said:


    Public iLength As Integer 'Length of Snake

        'Set length of snake to 0.

        iLength = 1


        For i As Integer = iLength To 0

            g.FillRectangle(Brushes.Black, New Rectangle(p(i), New Size(10, 10)))

            p(i) = p(i - 1)   ???

        Next


Note the line I marked with ??? ? I cannot see why do you need this anymore. I think you can safely remove it, unless it serves another purpose beside tracking the next rectangle to be drawn.

#6
Bound

Bound

    Learning Programmer

  • Members
  • PipPipPip
  • 30 posts
Hmmm... well I took out the line which incremented the counting variable and instead of painting the snake like normal it kept painting a square in the top left corner. Here, take a look at my code:

(Declarations)

Public m As Integer = 3 'Snake's length (you can edit the snake's length here)

    Public p(m) As Point 'Snake's whole body

    Public k As Keys 'Snake's direction


(Form_Load)

'Start timer.

        tmrMove.Start()

        'Set the snake's direction to south.

        k = Keys.Down


(tmrMove_Tick)

'Create image for game.

        Dim bMap As New Bitmap(260, 260)

        'Set graphics class to game image.

        Dim g As Graphics = Graphics.FromImage(bMap)


'Move Snake in the direction it's facing.

        '(Can't understand how this moves the entire snake)

        'I thought p(m) = Snake's head, but I guess

        ' it's the whole array.

        If k = Keys.Left Then

            p(m).X -= 10

        End If

        If k = Keys.Right Then

            p(m).X += 10

        End If

        If k = Keys.Up Then

            p(m).Y -= 10

        End If

        If k = Keys.Down Then

            p(m).Y += 10

        End If


'Create rest of snake's body. (Needs work)

        For i As Integer = 0 To m - 1

            g.FillRectangle(Brushes.Black, New Rectangle(p(i), New Size(10, 10)))

            p(i) = p(i + 1)

        Next

        'Paint snake's head. (Highest array index value = snake's head?).

        g.FillRectangle(Brushes.Black, New Rectangle(p(m), New Size(10, 10)))

        g.Dispose()


'Set the background image of the form to the game image.

        Me.BackgroundImage = bMap

        'Set the form's size to the game image's size.

        Me.ClientSize = bMap.Size

        'Refresh the form.

        Me.Refresh()

I tried to comment it to the best of my knowledge... That's not all of the code though. There's still some more code for generating the apples at random locations and painting them as well as disabling the user from running the snake through itself and when the snake hits one of the borders it'll end the game, but those are irrelevant at the moment.

What that code does is paint the snake (you can edit the length by editing m's value) and then paint the head. If m = 0 then it will still paint the snake's head, if m = 1 then the snake's full length (including the head) will be 2 squares, and so on and so forth. It also tests to see which direction the snake should be heading in and then edits the X or Y values of p(m) and that ends up moving the entire snake... I guess p(m).X += 10 is really moving the entire array of points' X values. Can you see what it's doing now? Maybe you could test it out for yourself... It should work for you, but if you need the apples I can give you the code for them as well.

Bound

#7
LuthfiHakim

LuthfiHakim

    Programming God

  • Members
  • PipPipPipPipPipPipPip
  • 765 posts
You did not write the code by yourself? Ouch! My days with VB was over too many years ago. So I could not help you with immediate code. I can read, but I could not write good VB code in short time. However I can explain some of the code.

In the code, m was defining the snake's size. P(m) was an array of size m (means containing m number of values) of points. A point is a value of 2 integers, Y and Y. So actually m is what I originally suggested (variable to store the snake size).

The head (which "rectangle" was defined by p(m)) was located in the end of the array. This is different from what I had assumed (I assumed that the head was located in the start of the array). So you are right to reverse the loop from
FOR 0 to ilength
into
FOR Length to 0
.

So you don't need new variable to store the snake's size. Just increment m after the snake ate an apple and use your old code for painting, and I believe you will be fine.

#8
Bound

Bound

    Learning Programmer

  • Members
  • PipPipPip
  • 30 posts
I did write the code by myself, actually. The only part I didn't come up with was using a Bitmap and painting the snake. I was using pictureboxes in the beginning and that wasn't working out too well, so I found that idea which told me to also use a for loop and I tried to implement it. Also, I've been thinking... When I increment m after eating an apple I get an "IndexOutOfRangeException" at


g.FillRectangle(Brushes.Black, New Rectangle(p(m), New Size(10, 10)))


I think it's because p(m) is NOT a dynamic array and since I'm not dimensioning p(m + 1) when I eat an apple, it pushes p(m) to a value greater than there is in my array because I dimensioned it as p(m) which is static... Am I right?

I tried implementing this:


(Declarations)

Public m As Integer = 2 'Snake's length (you can edit the snake's length here)

    Public p() As Point 'Snake's whole body


(Eating apple)

'Add new square to snake. (Needs work)

            ReDim p(m + 1)


(Painting Snake)

'Create rest of snake's body. (Needs work)

        For i As Integer = m - 1 To 0

            ReDim p(i)

            g.FillRectangle(Brushes.Black, New Rectangle(p(i), New Size(10, 10)))

            p(i) = p(i - 1)

        Next

        'Paint snake's head. (Highest array index value = snake's head?).

        g.FillRectangle(Brushes.Black, New Rectangle(p(m), New Size(10, 10)))

        g.Dispose()


But now when I eat an apple, it stops painting the snake's head and starts painting a new head at (0,0). The process can go on indefinitely as long as I keep eating apples...

Bound

#9
LuthfiHakim

LuthfiHakim

    Programming God

  • Members
  • PipPipPipPipPipPipPip
  • 765 posts

Bound said:

I did write the code by myself, actually. The only part I didn't come up with was using a Bitmap and painting the snake. I was using pictureboxes in the beginning and that wasn't working out too well, so I found that idea which told me to also use a for loop and I tried to implement it.

Lol, sorry that i got that impression.

Bound said:

Also, I've been thinking... When I increment m after eating an apple I get an "IndexOutOfRangeException" at


g.FillRectangle(Brushes.Black, New Rectangle(p(m), New Size(10, 10)))


I think it's because p(m) is NOT a dynamic array and since I'm not dimensioning p(m + 1) when I eat an apple, it pushes p(m) to a value greater than there is in my array because I dimensioned it as p(m) which is static... Am I right?

Yes, you are right. I was talking in casual way. But sorry if it confused you in the beginning.

Bound said:

But now when I eat an apple, it stops painting the snake's head and starts painting a new head at (0,0). The process can go on indefinitely as long as I keep eating apples...

I believe that's because when you redimensioning your array, your old data did not automatically get copied to the new array. This leaving your new array with junk content. So you have to copy content of old array to new array. But wait until you read my next sentence.

I believe now you have started to question your decision of placing the snake's head data at the end of the array. With that scheme, it's harder to copy data. Not only harder in copying, but also harder in other processing. So my first recommendation is to place the head at the start of the array (i.e. at index 0) and place its tail at the last cell of the array.

Instead of redimensioning each time eating an apple, I think it's better just to use an array big enough for the max possibility (i.e. when the snake fills the whole area). If you choose to follow this second recommendation then you need to add extra variable to store the current snake size (just like I suggested in my first post). In looping you don't use the size of the array (or m in the current case), but that current size variable.

And the third and final recommendation, maybe you can stop eating apples now... lol!

#10
Bound

Bound

    Learning Programmer

  • Members
  • PipPipPip
  • 30 posts
It's alright, I can see how it seemed as if I didn't write it lol :)

Ok FINALLY I'm starting to see some progress thanks to you! :c-grin: I took your suggestion and made the array 50 points but left the points "unpainted" and I also used iLength to keep track of how long the Snake is and incremented it whenever an apple is eaten... Here's the code:


(Declarations)

Public m As Integer = 50 'Snake's length (you can edit the snake's length here)

    Public p(m) As Point 'Snake's whole body

    Public iLength As Integer = 1 'Length of Snake


(tmrMove_Tick)

'Move Snake in the direction it's facing.

        '(Can't understand how this moves the entire snake)

        'I thought p(m) = Snake's head, but I guess

        ' it's the whole array.

        If k = Keys.Left Then

            p(0).X -= 10

            For i As Integer = 1 To iLength

                p(i).X -= 10

            Next

        End If

        If k = Keys.Right Then

            p(0).X += 10

            For i As Integer = 1 To iLength

                p(i).X += 10

            Next

        End If

        If k = Keys.Up Then

            p(0).Y -= 10

            For i As Integer = 1 To iLength

                p(i).Y -= 10

            Next

        End If

        If k = Keys.Down Then

            p(0).Y += 10

            For i As Integer = 1 To iLength

                p(i).Y += 10

            Next

        End If


'Add new square to snake. (Needs work)

            iLength += 1


'Create rest of snake's body. (Needs work)

        For i As Integer = 1 To iLength

            g.FillRectangle(Brushes.Black, New Rectangle(p(i), New Size(10, 10)))

            p(i) = p(i + 1)

        Next

        'Paint snake's head. (Highest array index value = snake's head?).

        g.FillRectangle(Brushes.Black, New Rectangle(p(0), New Size(10, 10)))

        g.Dispose()


I had to edit the moving portion of the code because it needs to also move the body as well as the head. Now whenever I eat an apple it adds a new square, but the square doesn't follow the head... It stays at (0,0) but will move right a few squares when I move the head right, left when I move the head left, up when I move the head up and down when I move the head down and it even goes offscreen BUT the important thing is that length has been added! :c-grin:

I just need to figure out how to get the new squares attached to the head and then I can start working on detecting if any points are intersecting. :)

Bound

#11
LuthfiHakim

LuthfiHakim

    Programming God

  • Members
  • PipPipPipPipPipPipPip
  • 765 posts
Congrats! :)

The new square drawn at (0,0) because you did not give it proper coordinate. You should give it coordinate of previous tail. Remember that the new length was added to the end/tail of the snake.

I think to effectively attack this project, we should properly break down its functionalities into subroutines. Here is the rough subroutines I proposed (I wrote them in Pascal, but you should be able to understand it easily).


procedure Move(X, Y: Integer; Draw: Boolean);

procedure Paint;

procedure Eat;


procedure Move is responsible to update the position of the snake. This is done by placing its head in coordinate specified in X and Y, and update coordinates of its body by copying content of cell to Cell[i-1] (remember that for this we are starting from index 1 since 0 is for the head). If parameter Draw=True, in the end of this subroutine you must call Paint procedure.

Example code for updating the body

  for i := 1 to iLength-1 do

    P[i] := P[i-1]


Note that from the above sample code you can see that coordinate previously for the head now becoming the "neck", previously the "neck" now become the "chest", and so on...

Procedure Paint is responsible to draw the current position of the snake to the screen or to whatever target you want.

Procedure Eat is responsible to add size or increment the size of the snake. Note that it's very similar with move since you have to update the array content, only this time you need to increase the size. [EDIT]On second thought I think we must not update the whole array (oc in respect to the size) in this routine, let the [I]Move
routine does it. Here we only need to increase the size AND copy the previously tail coordinate to the new tail.

When you have implemented these subs properly, you will find it's easier to handle player inputs or to handle points intersecting event.

#12
Bound

Bound

    Learning Programmer

  • Members
  • PipPipPip
  • 30 posts
Thanks LuthfiHakim, I think I understand what you're suggesting...

I can't work on my project for a few hours cause I have a Linux final exam soon, but when I get home I'll try to divide the code down into sub routines... I might need some help figuring out how to update the array and such, but it seems to make much more sense when things are separated like that.

I call the Move() routine in the timer's tick event and in the Move() routine I call the Paint() routine. The Move() routine updates the array while the Paint() routine paints the rectangles and I'll call the Eat() routine in the timer's tick event when the head intersects with an apple and that will add a new point in the array, am I correct on all of that?

Perhaps after these are all solved I could make a new sub routine for detecting intersecting points that are being painted?