Best way to index out-of-bounds?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
milon
Citizen
Posts: 60
Joined: Thu Jan 18, 2018 9:14 pm

Best way to index out-of-bounds?

Post by milon » Wed Feb 28, 2018 12:24 am

I'm still pretty new to Lua, and I'm trying to wrap my head around best practices. I have a chunk of code that will occasionally index a 2D table with out-of-bounds values. If the 1st index is invalid, it crashes (fair enough since nil can't do anything with the 2nd index). I want the code to find the proper values for proper indices, and return nil for everything else. I'll add a bunch of error-checking if I need to, but that doesn't seem terribly elegant. I'm thinking metatables might be the answer here, but I'm not sure if this is considered best practice or not.

Here's my (functional!) reduced code:

Code: Select all

-- The goal is to be able to lookup ANY 2D index without tripping over an error

map = {w=10, h=10}
for x = 1, map.w do
	map[x] = {}
	for y = 1, map.h do
		map[x][y] = love.math.random()
	end
end

setmetatable(map, { __call = function(t, x, y) return x>0 and x<=t.w and y>0 and y<=t.h and t[x][y] or nil end })

print(map[2][5])
print(map[7][21])
--~ print(map[21][7])	-- THIS CRASHES
print(map(2,5))
print(map(7,21))
print(map(21,7))
Is this best practice? Why or why not? Is there any way to make it work with out-of-bounds [] references, or is __call() the only way to go? I don't really have a handle on metatables, by the way. I've been reading about them for days now, and I've just spent the last 2 hours getting this chunk of code to work. ;)
Last edited by milon on Thu Mar 08, 2018 8:05 am, edited 1 time in total.

User avatar
pgimeno
Party member
Posts: 1943
Joined: Sun Oct 18, 2015 2:58 pm
Location: Valencia, ES

Re: Best way to index out-of-bounds?

Post by pgimeno » Wed Feb 28, 2018 1:28 am

Yes, you can make it work with the [] syntax:

Code: Select all

map = {w=10, h=10}
for x = 1, map.w do
  map[x] = {}
  for y = 1, map.h do
    map[x][y] = love.math.random()
  end
end

local dummy_table = {}

setmetatable(map, {__index = function(t, x)
  if type(x) == "number" and x >= 1 and x <= rawget(t, "w") then
    return rawget(t, x)
  end
  return dummy_table
end})

print(map[2][3])
print(map[7][21]) -- nil
print(map[21]) -- table xxx
print(map[21][7]) -- nil
The simplest approach is probably to use an accessor function which does the bounds checking, rather than a metatable.

Code: Select all

local function getmap(x, y)
  if x < 1 or x > map.w or y < 1 or y > map.h then
    return nil
  end
  return map[x][y]
end
I don't discuss best practices, since I see that as a "religious" topic.

User avatar
raidho36
Party member
Posts: 1994
Joined: Mon Jun 17, 2013 12:00 pm

Re: Best way to index out-of-bounds?

Post by raidho36 » Wed Feb 28, 2018 2:21 am

The best practice to go about out of bounds memory access is to make sure you never try to access out of bounds memory to start with. Fastest, too. It will take a bit of effort from you, but it's a best practice for a reason.

milon
Citizen
Posts: 60
Joined: Thu Jan 18, 2018 9:14 pm

Re: Best way to index out-of-bounds?

Post by milon » Wed Feb 28, 2018 3:13 am

Good answers, thank you both! I'll probably use an accessor function then, but thank you for the metatable info too. They're still strange and magical to me and this helps to demystify them a bit.

User avatar
ivan
Party member
Posts: 1546
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: Best way to index out-of-bounds?

Post by ivan » Wed Feb 28, 2018 12:21 pm

I'm with raidho on this one.
A common technique is to use 0-based indexing with modulus:

Code: Select all

local function getmap(x, y)
  return map[x%map.w][y%map.h]
end

milon
Citizen
Posts: 60
Joined: Thu Jan 18, 2018 9:14 pm

Re: Best way to index out-of-bounds?

Post by milon » Wed Feb 28, 2018 12:59 pm

Thanks, ivan. That's a pretty nice solution for wrapping. It's not what I want in this case, but I'll log it away for the future. :)

User avatar
raidho36
Party member
Posts: 1994
Joined: Mon Jun 17, 2013 12:00 pm

Re: Best way to index out-of-bounds?

Post by raidho36 » Thu Mar 01, 2018 5:31 am

Another common technique is to assert that index is within existing bounds. If your program is feeding invalid data to your functions, something is terribly wrong with it. That'd help debugging.

milon
Citizen
Posts: 60
Joined: Thu Jan 18, 2018 9:14 pm

Re: Best way to index out-of-bounds?

Post by milon » Wed Mar 07, 2018 7:02 pm

It's not a bug, it's a feature! :) (Really!)
I'm working on a 2D RPG as a side hobby. Currently, I'm always drawing the player in the center of the screen and that means when I'm near the edge of a map, the graphics routine will try to look up an out-of-bounds region. Each map has a defined "default" tile to draw for the out-of-bounds areas. I made a helper function to handle the lookup and return the correct tile to draw. I may change this approach in the future, however, since the default-tile look isn't quite what I'm going for.

And yes, as soon as I have something worth showing I'll put up a .love file for everyone to play with / mock / etc.

User avatar
tentus
Inner party member
Posts: 1060
Joined: Sun Oct 31, 2010 7:56 pm
Location: Appalachia
Contact:

Re: Best way to index out-of-bounds?

Post by tentus » Thu Mar 08, 2018 11:07 pm

In my last game I used the first tile of the map as a default, and drew a screen-sized spritebatch of it underneath the map, modulo'd to the same partial tile offset as the player. What this mean was that even we were looking over the edge of the map (or even if the map had partially transparent tiles in it), as long as the default tile was right I didn''t need to worry about seeing the background through. This made laying out compound tiles like dirt paths easier, and meant I could be a little looser with the player approaching the edges of the map.
Kurosuke needs beta testers

Post Reply

Who is online

Users browsing this forum: Google [Bot], steVeRoll and 4 guests