How to update a dictionary with movements in a snake game

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Akubra

I’m trying to use a dictionary to keep track of the movements of the snake in a snake game. So far, I’ve managed to keep track of the head of the snake, but I fail to see how I should keep track of the body movements.

What I’m doing is this: When the head of the snake moves one cell I fill the gap with the last segment of the body (when the snake doesn’t grow) or with a new body segment (when the snake grows). How should I keep track of that movement? What should I erase?

My code for the head movement (GridData.Content is the dictionary):

func _on_Timer_timeout():
	GridData.previousHeadPosition = GridData.GetCellPosition(self.position)
	GridData.Content[GridData.previousHeadPosition] = GridData.Item.SNAKE
	match direction:
		Here I check which direction the head should go (this works fine)
	GridData.currentHeadPosition = GridData.GetCellPosition(self.position)
	GridData.Content[GridData.currentHeadPosition] = GridData.Item.SNAKE

My code for the body movement so far (There are a total of 90 body segments, but I’m only using 10 of them):

var noOfBodySegments := 90
var bodySegmentArray := []
var startCell := Vector2(7, 7)
var counterStart := 0
var counterEnd := 9
var counter := counterStart
 
onready var timer = get_node("Timer")


func _ready():
	var bodySegment
	for _i in range(noOfBodySegments):
		bodySegment = Sprite.new()
		bodySegment.texture = load("res://Sprites/Body.png")
		add_child(bodySegment)
		bodySegment.position = GridData.GetPixelPosition(Vector2(-1, -1))
		bodySegmentArray.append(bodySegment)

func _on_Timer_timeout():
	bodySegmentArray[counter].position = GridData.GetPixelPosition(GridData.currentHeadPosition)
	counter += 1
	if counter > counterEnd:
		counter = counterStart

Thanks for having a look at this.

:bust_in_silhouette: Reply From: Inces

I can’t really see full use of this dictionary we were talking about in previous question.
You could just do something like this with it once every snake grid move :

for i in segments:
      GridData[i.position] = enum.SNAKE

Alternatively, You could keep track of all past positions of a head, since body segments always follow head anyway. For example - current position of 3rd segment of the body is the same as position of the head 3 turns ago. This would even make things like spawning new segments easier - You won’t risk spawning new segment into a wall, they will just spawn at positions head were before.

Thanks for your reply. Body segments do indeed follow the head, but not indefinitely. The body has a certain length and once the whole body is past a certain cell, that cell has to be unlocked again. Anyway, your comments have me thinking about it thoroughly. I hope to find a solution soon.

Akubra | 2022-09-30 13:04

:bust_in_silhouette: Reply From: Akubra

I’m answering this question myself, because I think I’ve found a solution. I’m using an array and a dictionary. First I push the new head position to the front of the array Snake.segments. Then I add the same new head position as a key to the dictionary Grid.content. I set the matching value to the enum value Grid.item.SNAKE. If the array size is longer than the snake length I remove the last element from the array and erase it from the dictionary.

func _on_Timer_timeout():
    match direction:
        Checking which direction the head should go
    Snake.headPosition = Grid.GetCellPosition(self.position)
	Snake.segments.push_front(Snake.headPosition)
	Grid.content[Snake.headPosition] = Grid.item.SNAKE
	if Snake.segments.size() > Snake.length:
		var freedCell = Snake.segments.pop_back()
		Grid.content.erase(freedCell)