This is an appendix for the neighbors section of the page.
Derive offset-grid neighbor formula
First, be sure you understand the cube neighbor algorithm. There are six neighbors, and each increments one axis by 1 and decrements a different axis by 1, thus preserving the
q + r + s = 0 constraint.
For each of the six directions, we have
Δs. The key idea here is that we’ll start with
col, row coordinates from the offset grid, convert them into cube coordinates
q, r, s, then add
Δr, Δs, then convert back into offset coordinates
col, row. This general principle works for all the algorithms on the page.
I’ll show how this works for a single offset grid type, “even-q”: These are the coordinate conversions for “even-q”, from the conversions section:
# convert cube to even-q offset col = q row = r + (q + (q&1)) / 2 # convert even-q offset to cube q = col r = row - (col + (col&1)) / 2 s = -q-r
I’m only going to show this for a single direction, northeast, with
Δq = +1, Δr = -1, Δs = 0:
# (1) convert even-q offset to cube coords: q = col r = row - (col + (col&1)) / 2 s = -q-r # (2) apply Δq, Δr, Δs: q' = q + 1 r' = r - 1 s' = s # (3) substitute eq (1) into eq (2): q' = col + 1 r' = row - (col + (col&1)) / 2 - 1 s' = -q'-r' # (4) convert back into even-q coords: col' = q' row' = r' + (q' + (q'&1)) / 2 # (5) substitute eq (3) into eq (4) col' = col + 1 row' = row - ( col + (col&1)) / 2 - 1 + (col + 1 + ((col + 1)&1)) / 2 = row + (-col - (col&1) - 2) / 2 + (col + 1 + (1 - (col&1))) / 2 = row + (-col - (col&1) - 2 + col + 1 + 1 - (col&1)) / 2 = row + ( -2 * (col&1)) / 2 = row - (col&1)
So that’s it! We’ve worked through some math and it simplifies down to
col' = col + 1 and
row' = row -
(col&1). (For this case,
s doesn’t play a role, but it does in other situations.) That’s just for one direction, and only for even-q offset grids, but that’s how it works. For those of you who are compiler/language enthusiasts, this is defining
cube_neighbors(evenq_to_cube(H))), then inlining all that code and greatly simplifying. It’s the type of work an compiler optimizer would do for you.
You can either work through all the cases, or you can perform the conversions at runtime (which likely will be optimized out by your compiler), or you can precalculate everything and put the results into lookup tables. I used lookup tables for my last game.