8

I'm making a game with hexagonal tiles, and have decided upon using a triangular/hexagonal grid. I found this question which helped me generate coordinates, and slightly modified the code to store all the coordinates as keys in a dictionary with values of either "." (floor) or "X" (wall,) and included a function that prints out a string representation of the map where each non-blank character represents a hexagonal tile. This is the new code:

deltas = [[1,0,-1],[0,1,-1],[-1,1,0],[-1,0,1],[0,-1,1],[1,-1,0]]
class HexGrid():
    def __init__(self, radius):
        self.radius = radius
        self.tiles = {(0, 0, 0): "X"}
        for r in range(radius):
            a = 0
            b = -r
            c = +r
            for j in range(6):
                num_of_hexas_in_edge = r
                for i in range(num_of_hexas_in_edge):
                    a = a+deltas[j][0]
                    b = b+deltas[j][1]
                    c = c+deltas[j][2]           
                    self.tiles[a,b,c] = "X"

    def show(self):
        l = []
        for y in range(20):
            l.append([])
            for x in range(60):
                l[y].append(".")
        for (a,b,c), tile in self.tiles.iteritems():
            l[self.radius-1-b][a-c+(2*(self.radius-1))] = self.tiles[a,b,c]
        mapString = ""
        for y in range(len(l)):
            for x in range(len(l[y])):
                mapString += l[y][x]
            mapString += "\n"
        print(mapString)

With this code, I can generate all the coordinates within the radius like so:

import hexgrid
hg = hexgrid.HexGrid(radius)

and access a coordinate like this:

hg.tiles[a,b,c]

This seems to work fine for now, but I'm sure there must be some disadvantages to storing the map this way. If there are any disadvantages, could you please point them out, and maybe present a better way to store the map? Thanks a lot for your time.

Community
  • 1
  • 1
RylandAlmanza
  • 1,298
  • 1
  • 8
  • 13

4 Answers4

13

Using an array for storage may save you some CPU time, but the difference is probably neglible.

However, you missed a very simple way of managing such a map. Consider it to be rows and columns, just the cells have slightly different shapes.

+--+--+--+--+--+--+--+
 \/ \/ \/ \/ \/ \/ \/    Even row

  /\ /\ /\ /\ /\ /\ /\   Odd row
 +--+--+--+--+--+--+--+

Or for hexagons:

  __    __    __    __
 /  \__/  \__/  \__/  \__ Even row
 \__/  \__/ A\__/  \__/   Odd  row
 /  \__/ F\__/ B\__/  \__ Even row
 \__/  \__/ X\__/  \__/   Odd  row
 /  \__/ E\__/ C\__/  \__ Even row
 \__/  \__/ D\__/  \__/   Odd  row
 /  \__/  \__/  \__/  \__ Even row
 \__/  \__/  \__/  \__/   Odd  row

Then you can store the data just as a regular 2D array. Odd rows are offset .5 to the right, and you need to figure out the neighborship steps for X: above: A = (0,-2), up right: B = (1,-1), bottom right: C = (1,1), below: D = (0,2), bottom left: E = (0,1), top left: F = (0,-1)

If you are ok with wasting a bit of memory, you can also leave every other column empty, and the neighborship becomes a bit simpler: (0,-2), (1,-1), (1,-1), (0,-2), (-1,-1), (-1,1)

Has QUIT--Anony-Mousse
  • 70,714
  • 12
  • 123
  • 184
  • Thank you for your answer. My favorite method of storing a hexogonal grid in a 2d array is the skewed-axis method described [here](http://roguebasin.roguelikedevelopment.org/index.php/Hexagonal_Tiles#Coordinate_systems_with_a_hex_grid). This is what I was using before. However, I really like the coordinate system I'm using now, and would like to stick with it, so I won't be accepting your answer. Thank you very much for helping, though! I really appreciate it! (It must have been annoying making those ascii diagrams!) – RylandAlmanza Jul 07 '12 at 08:48
  • @RylandAlmanza: The link is now [this](http://roguebasin.roguelikedevelopment.org/index.php?title=Hexagonal_Tiles#Coordinate_systems_with_a_hex_grid). Seems that Wiki has reorganized since! :-) Cheers... – pythonlarry Aug 23 '13 at 03:14
7

I made some research too, and found a far more simple way to do it. You do not have to get as complicated as you do! The table can be an simple array of array, without any special rules.

You want to use an hexagonal root coordonate system. See the theory here : https://en.wikipedia.org/wiki/Root_system . Also https://www.redblobgames.com/grids/hexagons/

The cell (0,0) is located in the centre of the structure, then it have six neighbours : as in the well known orthogonal table (1,0), (0,1), (-1,0), (0,-1) but also (1,1) , (-1-1). Others cell have similarly six neighbours, no need of modulo!

Here some Ascii art for beter understanding:

   _____       _____      ____      __
  / -2,2\_____/ 0,1 \____/2,0 \____/  \__ 
  \_____/-1,1 \_____/ 1,0\____/3,-1\__/   
  /-2,1 \_____/0,0  \____/2,-1\____/  \__     
  \_____/-1,0 \_____/1,-1\____/3,-2\__/   
  /-2,0 \_____/ 0,-1\____/2,-2\____/  \__ 
  \_____/     \_____/    \____/    \__/   

You can calculate the position of the center of each cell in the plane (and so in the screen) because it follows the rules of vectorial geometry. The vector have the coordinates that are in 60° instead of 90° : a=(0,1) but b=(0,87,0.5), simply multiply and add theses coordinates!

You may want to use the python library Hexy.

jarrettyeo
  • 1,857
  • 1
  • 21
  • 32
MUY Belgium
  • 2,055
  • 4
  • 28
  • 40
0

Don't use hg.tiles[a,b,c].

Make tiles a three dimensional list by this way hg.tiles = [[[z for z in range(10)] for y in range(10)] for x in range(10)] now you can access a tile using hg.tiles[a][b][c]

PS:a = a+deltas[j][0] should be a += deltas[j][0] and so on for for the other assignments

Ramchandra Apte
  • 3,835
  • 2
  • 22
  • 39
0

Perhaps the best way to store your hexes is in a python list or tuple; the challenge then becomes how to retrieve a particular hex from your list. In python, a good way to do that is to build a dictionary of hex coordinates as keys with list indices (or hex objects) as values. Assuming your list of hexes doesn't need to change, the list can be converted to a tuple when finished. And if you are saving hex objects as values in the dictionary, you can forgo saving the list entirely; the dictionary maps hex coordinates to the objects directly.

Your code already produces a sequence of hex coordinates, just save them in a list and build your reference dictionary at the same time. Also, if you are coding a class to manage your game board, all the details of storage and retrieval can be hidden in the class.

Some code:

radius = 4
deltas = [[1,0,-1],[0,1,-1],[-1,1,0],[-1,0,1],[0,-1,1],[1,-1,0]]

hexes = []
indices = {}
index = 0

for r in range(radius):
    print("radius %d" % r)
    x = 0
    y = -r
    z = +r

    hexes.append((x,y,z))
    indices[(x,y,z)] = index   # Or store objects here
    index += 1
    print(x,y,z)

    for j in range(6):
        if j==5:
            num_of_hexes_in_edge = r-1
        else:
            num_of_hexes_in_edge = r
        for i in range(num_of_hexes_in_edge):
            x = x+deltas[j][0]
            y = y+deltas[j][1]
            z = z+deltas[j][2]

            hexes.append((x,y,z))
            indices[(x,y,z)] = index   # Or store objects here
            index += 1
            print(x,y,z)

hexes = tuple(hexes)
print(hexes)
print(indices)