• 2D
  • Maximum 2D Array size and saved file size

Hello everyone ;)

I'm working now on my own Godot version of John Conway's Game Of Life and I use a 2D array to set or change the state (living or dead) of every cell in a 2D grid.

Everything seems to work fine for a 100x100 array but each time I try to increase either dimension even slightly, I have an error message and the project refuses to launch.

the message is Invalid get index '100' (on base: 'Array')

I have tested it on Godot 3.1.2 and 3.3.2 and I have the same problem for both. Any idea to help me ?

Thank you :)

I just created a 1000x1000 array in Godot 3.3.2-stable, and it works correctly.

I used this function, found at https://godotengine.org/qa/5122/how-do-i-create-a-2d-array?show=27370#a27370

# To be used as array[y][x].
func create_2d_array(width: int, height: int, value: int) -> Array:
	var a: Array = []

	for y in range(height):
		a.append([])
		a[y].resize(width)

		for x in range(width):
			a[y][x] = value

	return a

Test code:

var big_a: Array = create_2d_array(1000, 1000, 0)
print_debug("big_a size=", big_a.size()) # outputs 1000
print_debug("big_a[555] size=", big_a[555].size()) # outputs 1000
big_a[373][733] = 42
print_debug("big_a[373][733]=", big_a[373][733]) # outputs 42

Wow, thanks a lot for your post =) I will try to see what's wrong with my code ;)

Still not sure why I got a error but never mind, I'll stick to 100x100 for the moment...

One more question I have is the growing size of a saved file I generate when I save thecurrent state of my 100x100 array. It works fine for saving and reloading but each time I save it (same name, the new file replaces the previous one), its size keeps growing. More curious : if I delete the file, the starts the application and save again, the generated file is still bigger than the one I deleted !

Can you please help me to solve that mystery ? :o

Does the state of the array change between saves? Or maybe you're adding something to the file. Try comparing two successive saved files to see what the difference is. You could temporarily reduce the dimensions of the array to make it easier to compare them.

The only content of the file is the 100x100 array, I did not add anything else to it as I was only interested in saving the state of a 100x100 board which every cell can be alive or dead. Would the use of a software like Hex help me ?

How are you determining the size of the saved file?

I just read Windows file properties, after window update (F5) to make sure the indicated size is the current one.

Does that tells you the exact file size? Windows might be allocating space in blocks. Try checking the size with the File class's get_len() method.

I have a strong feeling you're accessing the array incorrectly but I can't say for certain without seeing the code.

To make my case clearer, here are some parts of my code. I only save and load the content of a 100x100 array.

(sorry, I don't know yet how to correctly display lines of code in the forum just like DaveTheCoder did, I did type them with indentations)

My new_world function to set a new starting state (depending on its type : random, empty or load)

func new_world(type):
	for v in range (n_grid):
		var cells = []
		for u in range (n_grid):
			if type == "random":
				randomize()
				if randf() <= 0.25: set_cell(u,v,1)
				else: set_cell(u,v,0)
			if type == "empty": set_cell(u,v,0)
			if type == "load": set_cell(u,v,world[u][v])
			cells.append(0)
		world.append(cells)

My save function

func save_world():
	var dir = Directory.new()
	dir.remove("user://save.dat")
	var file = File.new()
	var error = file.open("user://save.dat", File.WRITE)
	if error == OK:
		file.store_var(world)
		file.close()

My load function

func load_world():
	var file = File.new()
	if file.file_exists("user://save.dat"):
		var error = file.open("user://save.dat", File.READ)
		if error == OK:
			world = file.get_var()
			new_world("load")
			file.close()

If n_grid=100, then every time the function new_world runs, it appends 100 arrays filled with 100 zeroes each to wathever is already inside the world variable (lines 11 and 12). So, when you run load_world, the world variable is assigned the contents of the file on line 6, and then the 10000 zeroes on line 7. Thus each time you save the new value, the world variable gets bigger and bigger. I think you'd need to first empty world if not loaded, and then append the new values together with the set_cell calls (I guess that was the idea behind the cells variable on line 3).

Unfortunately, I still just can't get what the problem is...

Here's my whole code if it helps. The scene contains only a tilemap called "world".

extends TileMap

var cell_side = 8
var n_grid = 800/cell_side
var world = []
var pause = true

func _ready():
	new_world("random")
	toggle_pause()

func _process(_delta):
	if Input.is_action_just_pressed("pause"): pause = !pause ; toggle_pause()
	if Input.is_action_just_pressed("fullscreen"): OS.window_fullscreen = !OS.window_fullscreen
	if Input.is_action_just_pressed("quit"): get_tree().quit()
	if pause:
		if Input.is_action_pressed("revive"):
			set_cellv((get_local_mouse_position()/cell_side).floor(), 1)
			read_world()
		if Input.is_action_pressed("kill"):
			set_cellv((get_local_mouse_position()/cell_side).floor(), 0)
			read_world()
		if Input.is_action_just_pressed("next"): update_world()
		if Input.is_action_just_pressed("clear"): new_world("empty")
		if Input.is_action_just_pressed("random"): new_world("random")
		if Input.is_action_just_pressed("save_world"):
			new_world("load")
			save_world()
		if Input.is_action_just_pressed("load_world"): load_world()
	else: update_world()

func neighbors(x,y):
	var x1 = x-1; if x1 < 0: x1 = n_grid-1
	var x2 = x+1; if x2 > n_grid-1: x2 = 0
	var y1 = y-1; if y1 < 0: y1 = n_grid-1
	var y2 = y+1; if y2 > n_grid-1: y2 = 0
	var neighbors = get_cell(x1,y) + get_cell(x2,y) + get_cell(x,y1) + get_cell(x,y2) + get_cell(x1,y1) + get_cell(x2,y1) + get_cell(x1,y2) + get_cell(x2,y2)
	return neighbors

func read_world():
	for v in range (n_grid):
		for u in range (n_grid):
			world[u][v] = get_cell(u,v)

func update_world():
	for v in range (n_grid):
		for u in range (n_grid):
			if get_cell(u,v) == 1:
				if neighbors(u,v) in [2,3]:
					world[u][v] = 1
				else: world[u][v] = 0
			if get_cell(u,v) == 0:
				if neighbors(u,v) == 3:
					world[u][v] = 1
				else: world[u][v] = 0
	for v in range (n_grid):
		for u in range (n_grid):
			set_cell(u,v,world[u][v])

func new_world(type):
	for v in range (n_grid):
		var cells = []
		for u in range (n_grid):
			if type == "random":
				randomize()
				if randf() <= 0.25: set_cell(u,v,1)
				else: set_cell(u,v,0)
			if type == "empty": set_cell(u,v,0)
			if type == "load": set_cell(u,v,world[u][v])
			cells.append(0)
		world.append(cells)

func toggle_pause():
	if pause: modulate = Color(3,1,1,1)
	else: modulate = Color(1,1,1,1)

func save_world():
	var dir = Directory.new()
	dir.remove("user://save.dat")
	var file = File.new()
	var error = file.open("user://save.dat", File.WRITE)
	if error == OK:
		file.store_var(world)
		file.close()
		
func load_world():
	var file = File.new()
	if file.file_exists("user://save.dat"):
		var error = file.open("user://save.dat", File.READ)
		if error == OK:
			world = file.get_var()
			new_world("load")
			file.close()

Maybe add at the beginning of func new_world(type)? world.clear()

I'm not sure if that would work for the "load" case.