Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Snake game algorithm that doesn't use a grid and the segments can move slower than their dimensions

I'm trying to do exactly what this guy is doing but I think he may have less requirements than me because the answers in that post don't seem like they'd work for my game. Let me inline his algorithm so my question is easier to understand:

The easiest solution is to loop from tail to head and set the position of the current to the next segment's position, ie: segment[i].position = segment[i - 1].position

I think the key difference between our requirements is I want to move each piece less than its own width/height on every tick. For example:

enter image description here

Pretend these squares are aligned horizontally (I drew it unaligned because I think it makes it easier to see what's going on). The red H is where the head currently is. The red T is where the tail currently is. The black H' is where the head should be next tick. So the snake is moving right to left. If I use the algorithm described above, won't the segments overlap or start driving apart? Let me play it out step by step:

  1. Create H'.position
  2. T.position = H.position
  3. H.position = H'.position

The result of this would become:

enter image description here

What algorithm can I use to make sure that all the pieces move at the same speed and stay the same distance apart from each other?


I'll give my idea, but I'm skeptical of it because my research doesn't show anyone else using this:

Each segment will store it's coordinates and its direction. EG: [[50, 50, LEFT], [100, 50, LEFT]]. The head is index 0, the tail is index 1. The speed at which the snake moves is 10, even though the segments are 50x50.

Each tick I'll do this:

  1. If the mouse was pressed, overwrite the nextDirection variable with the direction that was pressed. Otherwise, nextDirection is whatever it was last tick. In this example, lets assume someone pressed UP.
  2. Iterate the array and apply the direction to each tick. EG: [[40, 50, LEFT], [90, 50, LEFT]]
  3. Shift the directions from head to tail and set the head's new direction to nextDireciton. EG: [[40, 50, UP], [90, 50, LEFT]]
  4. Repeat every tick.

Does this algorithm seem like it would work? Part of the reason I'm asking is because it's more complicated than the other guy's algorithm so I feel like I'm doing something wrong. And also, the more I think about his algorithm, the less it seems like it can work.


Rewording My Problem

Pretend each segment of the snake is a 20x20 pixel rectangle. Each tick, I want the head to move 5 pixels in some direction. How do I make sure all the segments stay touching each other?

@Rafe's description below in a comment is:

Consider the ordered set of locations occupied by the snake at step t, with the leftmost being the tail and the rightmost being the head: {A, B, C, D, E}. At step t+1, the ordered set of locations occupied by the snake is {B, C, D, E, F} where the tail has moved from A to B and the head has moved from E to F.

And I don't think this algorithm works because:

Yes but if the width of A = B = C = D = E = F = 20px. Shifting {A, B, C, D, E} so that it becomes {B, C, D, E, F} just added 20px to the right side and removed 20px from the left side. Doesn't this mean he moved 20px, not 5px? I want to move 5px, not 20px If you're saying that's accomplished with your suggestion, please explain how. I don't see it.

like image 474
Daniel Kaplan Avatar asked Oct 03 '22 22:10

Daniel Kaplan


1 Answers

I know you're ignoring the grid but what about making the distance the snake moves forward some number that factors your big block size evenly?

Let's say that you're moving forward only half of the width/height of your block.

Now you can optimize a bit.

  • Start a counter, n, that runs throughout the game.
  • Display the blocks that comprise the snake (n = 0). Let's call this snake_even.
  • Next move (n = 1), create the blocks that comprise the next snake by moving them all forward 1/2 unit. Let's call this snake_odd.
  • For all subequent moves, you display either snake-even or snake_odd, but you can create snake_even(n+2) from snake_even(n), or snake_odd(n+2) from snake_odd(n) just by changing the head to the new position and writing over the tail.
  • Whenever the snake eats something, add the length to whichever snake_xxxx you're on, and then add the length to the other one.

If you want to move forward only 1/5th of the height, you'd do the same thing, but you'd have five arrays to keep track of instead of two.

Additional info based on your added example (20x20 px segments moving 5 px each step):

Take a 4-segment snake moving to the right. Segment sizes are 20x20 px, and they move 5 px per move. I'll define the snakes as a list of coordinates (x, y). I'll mark the head with 'H' and the snake will cycle by moving right in the list, cycling back to the beginning if needed.

// n = 0
snake_0 => H(60, 0), (40, 0), (20, 0), (0, 0)

// Snake is moving to the right.
// n = 1 -- construct snake_1 from snake_0 and display that one (n % 4 = 1)
snake_1 => H(65, 0), (45, 0), (25, 0), (5, 0)

// n = 2 -- construct snake_2 from snake_1 and display that one (n % 4 = 2)
snake_2 => H(70, 0), (50, 0), (30, 0), (10, 0)

// n = 3 -- construct snake_3 from snake_2 and display that one (n % 4 = 3)
snake_3 => H(75, 0), (55, 0), (35, 0), (15, 0)

// n = 4 -- Now just move the head, and re-use all but the tail of snake_0
snake_0 => (60, 0), (40, 0), (20, 0), H(80, 0)

// n = 5
snake_1 => (65, 0), (45, 0), (25, 0), H(85, 0)

// n = 6
snake_2 => (70, 0), (50, 0), (30, 0), H(90, 0)

// etc.

Now one thing I forgot to take into account was the direction each segment needs to move. That could be stored right alongside the coordinate. But I think that part you probably understand already.

like image 184
John Avatar answered Oct 12 '22 12:10

John