Need help with a logic problem, as well as understanding recursive functions

Godot Version

4.4.1

Question

Hello, I’m having some trouble with a recursive function.

The code below consists of two (different) get functions that return dictionaries with indexes of cells in a grid.
in a perfect I would have the second function repeat itself to keep getting and adjusting cells, but this dosen’t work. I think it’s because instead of getting two copies of the dictionary values the second time the get_neighbors_recursive is called, it overwrites the first instance of the function.

	for idx in neighbor_idx.values(): # on cell body entered get cell idx and its neighbors - this dict is a bit more constant and should not be called more than once
		if idx != null:
			neighbor_tex = cell_dict[idx]["cell_texture"]
			neighbor_ray = cell_dict[idx]["cell_ray"] 
			neighbor_ray.target_position = mouse_vel
			update_shader_param(neighbor_tex, neighbor_ray)

			for idx_rec in get_neighbors_recursive(idx).values():
				if not idx_rec == null:
					neighbor_tex = cell_dict[idx_rec]["cell_texture"]
					neighbor_ray = cell_dict[idx_rec]["cell_ray"] 
					neighbor_ray.target_position = mouse_vel * 0.75
					update_shader_param(neighbor_tex, neighbor_ray)

				for idx_rec2 in get_neighbors_recursive(idx_rec).values():
					if not idx_rec2 == null:
						neighbor_tex = cell_dict[idx_rec2]["cell_texture"]
						neighbor_ray = cell_dict[idx_rec2]["cell_ray"] 
						neighbor_ray.target_position = mouse_vel * 0.5
						update_shader_param(neighbor_tex, neighbor_ray)

The function is supposed to grab neighboring cells and modify cell_data (havent made this yet)
function in question:

func get_neighbors_recursive(cell_index: int) -> Dictionary: 
	var neighbors_rec_idx :  Dictionary = {}
	var cell_data = cell_dict[cell_index]
	var cell_ray = cell_data["cell_ray"]
	var cell_ray_angle = cell_ray.target_position.angle() # in rad

	var angle_check_arr = [-1.55, -0.80, -2.35, 3.15, 0.0, 2.35, 0.8, 1.55,]
	var closest_numb = closest(cell_ray_angle, angle_check_arr) # get closest number


	@warning_ignore("integer_division")
	var row = cell_index / columns
	var column = cell_index % columns

#gets cell dict key (idx)
#gets cell dict key (idx)
	var get_above 
	var get_top_left 
	var get_top_right 
	var get_bottom_left
	var get_bottom_right 
	var get_left 
	var get_below 
	var get_right

# check if idx exceeds rows or columns (e.g. prevents values from returning index in nonexisting row or column)
	if row > 0:
		get_above = cell_dict.get((row - 1) * columns + column)["cell_idx"]
		if column > 0:
			get_top_left = cell_dict.get((row - 1) * columns + (column - 1))["cell_idx"]
		if column < columns - 1:
			get_top_right = cell_dict.get((row - 1) * columns + (column + 1))["cell_idx"]
	if row < rows - 1:
		get_below = cell_dict.get((row + 1) * columns + column)["cell_idx"]
		if column > 0:
			get_bottom_left = cell_dict.get((row + 1) * columns + (column - 1))["cell_idx"]
		if column < columns - 1:
			get_bottom_right = cell_dict.get((row + 1) * columns + (column + 1))["cell_idx"]

	if column > 0:
		get_left = cell_dict.get(row * columns + (column - 1))["cell_idx"]
	if column < columns - 1:
		get_right = cell_dict.get(row * columns + (column + 1))["cell_idx"]


	match closest_numb: # match ray angle to closest direction (cardinal and intercardinal)
		-1.55: # above
			neighbors_rec_idx["above"] = get_above
			neighbors_rec_idx["top_left"] = get_top_left
			neighbors_rec_idx["top_right"] = get_top_right
		-0.80: # top right
			neighbors_rec_idx["above"] = get_above
			neighbors_rec_idx["top_right"] = get_top_right
			neighbors_rec_idx["right"] = get_right
		-2.35: # top left
			neighbors_rec_idx["above"] = get_above
			neighbors_rec_idx["top_left"] = get_top_left
			neighbors_rec_idx["left"] = get_left
		3.15: #left
			neighbors_rec_idx["top_left"] = get_top_left
			neighbors_rec_idx["left"] = get_left
			neighbors_rec_idx["bottom_left"] = get_bottom_left
		0.0: #right
			neighbors_rec_idx["top_right"] = get_top_right
			neighbors_rec_idx["right"] = get_right
			neighbors_rec_idx["bottom_right"] = get_bottom_right
		2.35: #bottom left
			neighbors_rec_idx["left"] = get_left
			neighbors_rec_idx["bottom_left"] = get_bottom_left
			neighbors_rec_idx["below"] = get_below
		0.8: # bottom right 
			neighbors_rec_idx["right"] = get_right
			neighbors_rec_idx["bottom_right"] = get_bottom_right
			neighbors_rec_idx["below"] = get_below
		1.55: #below
			neighbors_rec_idx["bottom_left"] = get_bottom_left
			neighbors_rec_idx["below"] = get_below
			neighbors_rec_idx["bottom_right"] = get_bottom_right



	return neighbors_rec_idx

This problem is pretty easily solved by making a duplicate of the function and calling that instead of re-calling the same function. But that doesn’t seem like good practice to copy and paste existing functions?

Is there a way to circumvent this?

I don’t see any functions in the code you posted.

Whatever you can do with a recursive call, you can as well do by pushing/popping state to/from a stack. So practice implementing things in that way, without recursion.

EDIT: ok you posted the code after I wrote the above. Still, implement a recursion-less version. You’ll learn a lot.

1 Like

What does the function get_neighbours_recursive() write into? If you have a global array then i am sure the function would over write the contents. Perhaps you should pass the array into the function …

func get_neighbours_recursive(index, return_array)

If thats not working, make a seperate gdscript file and create a class with the function defined. Then make another instance of the class to call the function again, this way the memory the function writes to is local to the instance of the class.

I wouldnt say recursive calls are wrong but maybe the recursion stack isnt too deep in gdscript. For example I once made a binary tree search that caused a stack overflow (in C++) on a binary sort of 3 million names in a data table - the problem occurred when they were already sorted. I dont know how deep gdscript will go anyway.

1 Like

What does the function get_neighbours_recursive() write into?

im not 100% sure what you mean, but the function returns 3 values (and keys). The values are indexes that are used to get the cells around the start cell. the reason I want it to be recursive is so that it can get some neighboring cells (and modify them) and then get those neighboring cells and modify them differently
The start cell is just wherever the mouse is moving to.

get_neighbors_recursive returns for example:
{ "top_right": 196, "right": 216, "bottom_right": 236 }

I don’t think it “writes into” anything, but maybe I’m misunderstanding what you’re saying.

If thats not working, make a seperate gdscript file and create a class with the function defined. Then make another instance of the class to call the function again, this way the memory the function writes to is local to the instance of the class.

Wouldn’t this be the same as copying, pasting, renaming and calling the function again

I’m not very seasoned in using godot, so I may (again) be misunderstanding

Sorry for being slow on giving all the information :slight_smile:

If I understand right would this be akin to saving the returned values from the first instance of the function and then later using them as the new indexes?

how would i go about pushing a state to a stack and then later calling the state from the stack.
Or rather is there a “technical term” that i can google myself?

Let’s start from the beginning. Can you describe what exactly is this function supposed to get? Also, never use operator == on floating point numbers.

I have a 20x20 grid thats filled with cells. This grid is created at runtime and each cell is assigned an index. The get_neighbors_recursive(cell_index) function returns a dictionary with 3 values (and keys) This is gotten by looking at a start cell (the callable : cell_index). the start cell is wherever the mouse is moving. It wont let me show video

later (this havent been implented yet) I want to modify the returned neighbors

What I was really asking is the description of the problem, not how you attempted to solve it.

My bad. When I try to call the function for the second time it starts returning null values when at the edge of the grid

I have tried to fix this with the if statements where the get_above (and so on) is set

No, the problem you’re attempting to solve with this implementation. What are you making, what’s the final purpose of this?

A visual effect. each grid has a direction (this is what decides which neighbors neighbors are gotten ) And then later i will make the visual effect follow the directions the cells have

Like so:

The arrows are set as an object (the player) moves through the grid
For example I’d like to use this as a dynamic smoke effect

1 Like

Why just not store cell coordinates into a dictionary (or a list) as the player moves through them?

How do you mean? I’m not sure I understand what you are envisioning

If the player is creating those arrows as they move, just store those in a dictionary as the player is creating them. Then you won’t need to retrieve anything from anywhere. You’ll always have them in that dictionary.

The vectors are made with cells they just turn and get larger or smaller based on how the player moves around. It’s the get_neighbors function thats gets called as the player moves around in the grid and later it will also calculate vector direction and length/size
Vectors will decide smoke direction and velocity

Could you elaborate on :

Whatever you can do with a recursive call, you can as well do by pushing/popping state to/from a stack. So practice implementing things in that way, without recursion.

That sounded like it could work

Why do you need to store this in a tilemap? Just put this data in a dictionary as it’s being generated. When player enters a cell (1,1) put that cell into a dictionary with key (1,1) and value of all needed arrow data. Do this whenever the player enters a new cell. That way you maintain a dictionary of all relevant data at all times. You don’t need anything else. Nothing to retrieve from anywhere. It’s all there :smiley:

1 Like

Ok so i see you have replied to my comment above …

When i asked what the function was writing into i was enquiring about the dictionary …

func get_neighbors_recursive(cell_index: int) -> Dictionary: 
	var neighbors_rec_idx :  Dictionary = {}

so you said the result seemed to be overwritten with successive calls, so the “neighbours_rec_idx” dictionary returned from the next calls to the function seems to break the original data … without going into too much depth (fully understanding) its intuitive to suggest that the dictionary should be ouside the function…

class_name CellNeighbourGetter # or whatever
extends ref_counted

var neighbors_rec_idx :  Dictionary = {}

func get_neighbors_recursive(cell_index: int) -> Dictionary: 

#... define function here writing to the dictionary as normal

call that file ‘CellNeighbourGetter.gd’ then in the file where you call the function …

@onready var cell_getter :CellNeighbourGetter = preload("/res/scriptsCellNeighbourGetter.gd")

# you can use the cell_getter var, then instantiate another

var cell_getter_2 : CellNeighbourGetter = CellNeighbourGetter.new() 

and they each return unique dictionary’s
and then you should be able to call them in the way you intended to.

1 Like

Ah, I think i get it. Do you think this can be used for different situations? The smoke isnt just being moved behind the player. in other situations I’d like for the smoke to move ahead of the player and around the player

The get_neighbors functions purpose is to calculate ahead of the player and around the player (e.g. whereever the player isn’t but where smoke needs to be)

I’d go ahead with what normalized has suggested …

1 Like

@normalized and @pizza_delivery_man
I’m gonna mark the thread solved for now :slight_smile:
I’m gonna try both things and see what sticks to the wall

Thanks for the help! I feel more prepared to fix it :slight_smile:

1 Like