New to coding, I don't really understand this

Godot Version

Godot 4.2

Question

Hello, i just started using godot recently after learning some basics about coding.
I was trying to make a minesweeper game just to learn about grid based games and stuff like that, and i got to the part of code where i have choose which tiles have to be mines and which dont

somehow after hours of trial and error i managed to finish it, but i dont understand at all why this works, and why what i did originally didn’t. The problem i had was that, 90% of the time i reached the place_mine() func when turning on the game, the game would crash and say “out of bounds get index … (on base: ‘Array’)”

it still happens like 5-10% of the time now and i want to fix it, but don’t know how, so if someone could explain why this happens to me that would be great

notes:
1 tile_data is an array that has all the tiles in it
2 had to replace the asterisks with × because it made the text italicized
3 the place_mine() func was just made to see if its working properly, thats why i put ‘i’ in there
4 this is in gdscript

this is the code that ended up working:

func set_mines():
# Grab z random numbers from 1 to xy
# Will be using these numbers as placeholders for now: z=40, x=16, y=16
# These will be changed in the future if I decide to add a method to change them in game
var x := 16
var y := 16
var z := 40
tile_bomb.append(tile_data.pick_random())
for i in range(z):
var random_number = randi() % (x×y)
i-=1
if tile_data[random_number] not in tile_bomb:
tile_bomb.append(tile_data[random_number])
place_mine(i, tile_bomb[i-1])

this is the one that didn’t work:

func set_mines():
# Grab z random numbers from 1 to xy
# Will be using these numbers as placeholders for now: z=40, x=16, y=16
# These will be changed in the future if I decide to add a method to change them in game
var x := 16
var y := 16
var z := 40
for a in range(1):
tile_bomb.append(tile_data.pick_random())
for i in range(zzz):
place_mine(i, tile_bomb[i])
var random_number = randi() % (x*y)
if tile_data[random_number] in tile_bomb:
i=i-1
continue;
tile_bomb.append(tile_data[random_number])
if tile_bomb.size() >= z:
break;

It likely happens when random_number is set to a number that is beyond the size of your array.
It doesn’t happen always because the random number might often land in range.
When using random, you want to debug with known values. So until you have everything working use the function seed(x) to set a repeatable set of random numbers.
Start with seed(1) and if it doesn’t error, seed(2), etc until it does error. Then print out what random_number is and you can debug from there.
Remember that arrays begin at index 0 and the last index is array.size() - 1.
An array with 10 elements indexes are 0 - 9

Super hard to debug your code as it is posted here because the formatting is removed.
In order to keep the formatting, select your code and press the </> button on the edit window.

1 Like

When you are doing randi() % (x * y), if randi is cleanly divisible by x*y, your result will be 0. Then, if you check your array and there is already a bomb in array[0], you will attempt to place the bomb at i -=1, which will leave you at array[-1], which is where you’re getting the out of bounds error.

You should be able to solve this by doing randi_range(from: int, to: int) without any modulo and setting your range to be your array size.
You could likely also solve it by clamping i as such: i = clamp (i-1, 0, z), where i-1 is your value, and 0 and z are your lower and upper bounds for said value.

1 Like

thank you 2 so much for helping, it helped a lot

if u dont want to read all this btw, the solution i found is at the end of the reply

im not really sure what the seed(x) func does though, but the thing sancho2 said was one of the things happening, fortunately that one was pretty easy to solve

but i found out eventually after gaining the patience to debug it and go through the loop 30+ times multiple times what the main problem was

this is regarding the first func i sent in the post, the one that worked 90% of the time

basically, every time it went through the loop, it would go through an if statement to check whether the exact tile being selected as a mine is already a mine or not. on the low chance that it is a mine, it would skip that and complete the loop. what i don’t really understand though was for some reason, even though ‘i’ was being subtracted by one, when this happened, i would still be too big to send the array tile_bomb(i-1) to the place_mine() func

im not used to godot so idk why it was like that, so the solution i came up with was to first (courtesy of stickytoe) clamp i to 0 to z (i = clamp (i, 0, z)) and then to always set i to be the same as len(tile_bomb) assuming it wasn’t. this way, i wouldn’t have to worry about i being the wrong number anymore

at this point, it was working properly, but for whatever reason, it would skip the last few tiles, so i just increased the limit of the loop to the total number of tiles, and made it leave the loop once len(tile_bomb) was greater than z, which is the placeholder for the number of mines. and once i did this, i tried removing the clamp thing, and it still worked normally, but honestly, if i couldn’t clamp it before i probably wouldnt have figured out how to fix it

also, i added a few lines to the end to check if it was skipping anything or not, spoiler alert, it wasnt

heres the code if anyones interested:
func set_mines():
# Grab z random numbers from 1 to xy
# Will be using these numbers as placeholders for now: z=40, x=16, y=16
# These will be changed in the future if I decide to add a method to change them in game
var x := 16
var y := 16
var z := 40
var test = 0
tile_bomb.append(tile_data.pick_random())
for i in range(x*y):
. .if len(tile_bomb) > z:
. . .break;
. .if i != len(tile_bomb):
. . .i = len(tile_bomb)
. .var random_number = randi_range(0, len(tile_data)-1)
. .if tile_data[random_number] not in tile_bomb:
. . .tile_bomb.append(tile_data[random_number-1])
. . .place_mine(i, tile_bomb[i-1])
. . .test += i
. . .print(test)

btw, im new to godot and this is quite literally my first post and reply, so i just wanted to ask, should i be explaining the problem and how i got my solution whenever i finally get it? or should i just say what the actual problem was and then show the solution i found?
i rarely every use forums so i dont rly understand things like this much