¿Cómo represento una cuadrícula hextil / hexadecimal en la memoria?

119

Digamos que estoy construyendo un juego de mesa con una cuadrícula hextil, como Settlers of Catan :

Alojado por imgur.com

Tenga en cuenta que cada vértice y borde puede tener un atributo (una carretera y un asentamiento arriba).

¿Cómo haría una estructura de datos que represente este tablero? ¿Cuáles son los patrones para acceder a los vecinos, bordes y vértices de cada mosaico?

un nerd pagado
fuente
También puede utilizar una curva de Hilbert, son curvas de relleno de espaciado de modo que la adyacencia en el plano se conserva en una codificación lineal. ¡Consulte la indexación espacial y cómo se utilizan! muy interesante
pbordeaux

Respuestas:

156

Amit Patel ha publicado una página asombrosa sobre este tema. Es tan completo y maravilloso que debe ser la respuesta definitiva a esta pregunta: Cuadrículas hexagonales

cubez

un nerd pagado
fuente
27
Gracias :) Esa página no cubre bordes ni vértices, pero los cubro en la sección Relaciones entre partes de mi artículo de cuadrículas en www-cs-students.stanford.edu/~amitp/game-programming/grids (los diagramas son para cuadrículas cuadradas, pero la tabla también incluye las fórmulas para cuadrículas hexagonales axiales)
amitp
18

Tal cuadrícula se puede representar en una matriz bidimensional:

Si

   2
7     3
   1   
6     4
   5

es el número uno con sus vecinos en la cuadrícula hexadecimal, entonces puedes poner esto en una matriz 2D así:

2 3
7 1 4
  6 5

Obviamente, la vecindad se determina en esta cuadrícula no solo por ser adyacente horizontal o verticalmente, sino también usando una diagonal.

Sin embargo, también puede usar un gráfico, si lo desea.

Joey
fuente
Frio. ¿Qué pasa con los datos de aristas y vértices?
un nerd pagado
1
Probablemente los almacenaría por separado. Independientemente de si mira principalmente los mosaicos o los bordes / vértices, la otra mitad de los datos es dolorosa o redundante de almacenar.
Joey
Vea el artículo de Amit Patel en la respuesta de "un nerd pagado".
aredridel
11

Este artículo explica cómo configurar un juego de cuadrícula isomérica / hexagonal. Te recomiendo que eches un vistazo a la Forcing Isometric and Hexagonal Maps onto a Rectangular Gridsección y la sección de movimiento. Aunque es diferente de lo que está buscando, puede ayudarlo a formular cómo hacer lo que desea.

zfedoran
fuente
2

He tratado mucho con maleficios. En casos como este, rastreas cada uno de los 6 puntos de los bordes del hex. Esto te permite dibujarlo con bastante facilidad.

Tendría una sola matriz de objetos que representan hexágonos. Cada uno de estos objetos hexadecimales también tiene 6 "punteros" (o un índice a otra matriz) que apuntan a otra matriz de "lados". Lo mismo para "vértices". Por supuesto, los vértices tendrían 3 punteros a los hexágonos contiguos, y los lados tendrían 2.

Entonces, un hex puede ser algo como: X, Y, Punto (6), Vértices (6), Lados (6)

Entonces tienes una matriz Hex, una matriz de vértice y una matriz lateral.

Entonces es bastante simple encontrar los vértices / lados de un hex, o lo que sea.

Cuando digo puntero, podría ser fácilmente un número entero que apunta al elemento en el vértice o matriz lateral o lo que sea. Y, por supuesto, las matrices pueden ser listas o lo que sea.

Nadie en particular
fuente
0
   2
7     3
   1   
6     4
   5

También puede intentar "planar" filas de su mapa. Para este ejemplo sería:

  2
7 1 3
6 5 4

A veces es más útil tener filas en una fila: P

qba
fuente
1
Esto podría tener un código de verificación de vecinos desordenado, porque, por ejemplo, 1 y 6 son vecinos, pero 3 y 5 no lo son, pero tienen las mismas posiciones relativas.
Bernhard Barker
0

Sugeriría algo como lo siguiente (usaré declaraciones de estilo Delphi):

type
  THexEdge = record
    Hexes: array[1..2] of Integer; // Index of adjoining hexes.
    // Other edge stuff goes here.
  end;

  THexVertex = record
    Hexes: array[1..3] of Integer; // Index of adjoining hexes.
    // Other vertex stuff goes here.
  end;

  THex = record
    Edges: array[1..6] of Integer; // Index of edge.
    Vertices: array[1..6] of Integer; // Index of vertex.
    // Other hex stuff goes here.
  end;

var
  Edges: array of THexEdge;
  Vertices: array of THexVertex;
  HexMap: array of THex;

Cada hex tiene seis bordes y seis vértices. Cada borde realiza un seguimiento de sus dos hexágonos contiguos, y cada vértice realiza un seguimiento de sus tres hexágonos contiguos (los hexágonos en los bordes del mapa serán un caso especial).

Por supuesto, hay muchas cosas que podrías hacer de una manera diferente. Podría usar punteros en lugar de matrices, podría usar objetos en lugar de registros, y podría almacenar sus hexes en una matriz bidimensional como han sugerido otros respondedores.

Con suerte, eso podría darte algunas ideas sobre una forma de abordarlo.

Monje incrédulo
fuente
0

Implementamos una IA de Settlers of Catan para un proyecto de clase y modificamos el código de esta respuesta (que tenía errores) para crear un tablero con acceso aleatorio de tiempo constante a vértices y bordes. Fue un problema divertido, pero la placa tomó mucho tiempo, así que en caso de que alguien todavía esté buscando una implementación simple, aquí está nuestro código Python:

class Board:
  # Layout is just a double list of Tiles, some will be None
  def __init__(self, layout=None):
    self.numRows = len(layout)
    self.numCols = len(layout[0])
    self.hexagons = [[None for x in xrange(self.numCols)] for x in xrange(self.numRows)] 
    self.edges = [[None for x in xrange(self.numCols*2+2)] for x in xrange(self.numRows*2+2)] 
    self.vertices = [[None for x in xrange(self.numCols*2+2)] for x in xrange(self.numRows*2+2)] 
    for row in self.hexagons:
      for hexagon in row:
        if hexagon == None: continue
        edgeLocations = self.getEdgeLocations(hexagon)
        vertexLocations = self.getVertexLocations(hexagon)
        for xLoc,yLoc in edgeLocations:
          if self.edges[xLoc][yLoc] == None:
            self.edges[xLoc][yLoc] = Edge(xLoc,yLoc)
        for xLoc,yLoc in vertexLocations:
          if self.vertices[xLoc][yLoc] == None:
            self.vertices[xLoc][yLoc] = Vertex(xLoc,yLoc)

  def getNeighborHexes(self, hex):
    neighbors = []
    x = hex.X
    y = hex.Y
    offset = 1
    if x % 2 != 0:
      offset = -1

    if (y+1) < len(self.hexagons[x]):
      hexOne = self.hexagons[x][y+1]
      if hexOne != None: neighbors.append(hexOne)
    if y > 0:
      hexTwo = self.hexagons[x][y-1]
      if hexTwo != None: neighbors.append(hexTwo)
    if (x+1) < len(self.hexagons):
      hexThree = self.hexagons[x+1][y]
      if hexThree != None: neighbors.append(hexThree)
    if x > 0:
      hexFour = self.hexagons[x-1][y]
      if hexFour != None: neighbors.append(hexFour)
    if (y+offset) >= 0 and (y+offset) < len(self.hexagons[x]):
      if (x+1) < len(self.hexagons):
        hexFive = self.hexagons[x+1][y+offset]
        if hexFive != None: neighbors.append(hexFive)
      if x > 0:
        hexSix = self.hexagons[x-1][y+offset]
        if hexSix != None: neighbors.append(hexSix)
    return neighbors

  def getNeighborVertices(self, vertex):
    neighbors = []
    x = vertex.X
    y = vertex.Y
    offset = -1
    if x % 2 == y % 2: offset = 1
    # Logic from thinking that this is saying getEdgesOfVertex
    # and then for each edge getVertexEnds, taking out the three that are ==vertex
    if (y+1) < len(self.vertices[0]):
      vertexOne = self.vertices[x][y+1]
      if vertexOne != None: neighbors.append(vertexOne)
    if y > 0:
      vertexTwo = self.vertices[x][y-1]
      if vertexTwo != None: neighbors.append(vertexTwo)
    if (x+offset) >= 0 and (x+offset) < len(self.vertices):
      vertexThree = self.vertices[x+offset][y]
      if vertexThree != None: neighbors.append(vertexThree)
    return neighbors

  # used to initially create vertices
  def getVertexLocations(self, hex):
    vertexLocations = []
    x = hex.X
    y = hex.Y
    offset = x % 2
    offset = 0-offset
    vertexLocations.append((x, 2*y+offset))
    vertexLocations.append((x, 2*y+1+offset))
    vertexLocations.append((x, 2*y+2+offset))
    vertexLocations.append((x+1, 2*y+offset))
    vertexLocations.append((x+1, 2*y+1+offset))
    vertexLocations.append((x+1, 2*y+2+offset))
    return vertexLocations

  # used to initially create edges
  def getEdgeLocations(self, hex):
    edgeLocations = []
    x = hex.X
    y = hex.Y
    offset = x % 2
    offset = 0-offset
    edgeLocations.append((2*x,2*y+offset))
    edgeLocations.append((2*x,2*y+1+offset))
    edgeLocations.append((2*x+1,2*y+offset))
    edgeLocations.append((2*x+1,2*y+2+offset))
    edgeLocations.append((2*x+2,2*y+offset))
    edgeLocations.append((2*x+2,2*y+1+offset))
    return edgeLocations

  def getVertices(self, hex):
    hexVertices = []
    x = hex.X
    y = hex.Y
    offset = x % 2
    offset = 0-offset
    hexVertices.append(self.vertices[x][2*y+offset]) # top vertex
    hexVertices.append(self.vertices[x][2*y+1+offset]) # left top vertex
    hexVertices.append(self.vertices[x][2*y+2+offset]) # left bottom vertex
    hexVertices.append(self.vertices[x+1][2*y+offset]) # right top vertex
    hexVertices.append(self.vertices[x+1][2*y+1+offset]) # right bottom vertex
    hexVertices.append(self.vertices[x+1][2*y+2+offset]) # bottom vertex
    return hexVertices

  def getEdges(self, hex):
    hexEdges = []
    x = hex.X
    y = hex.Y
    offset = x % 2
    offset = 0-offset
    hexEdges.append(self.edges[2*x][2*y+offset])
    hexEdges.append(self.edges[2*x][2*y+1+offset])
    hexEdges.append(self.edges[2*x+1][2*y+offset])
    hexEdges.append(self.edges[2*x+1][2*y+2+offset])
    hexEdges.append(self.edges[2*x+2][2*y+offset])
    hexEdges.append(self.edges[2*x+2][2*y+1+offset])
    return hexEdges

  # returns (start, end) tuple
  def getVertexEnds(self, edge):
    x = edge.X
    y = edge.Y
    vertexOne = self.vertices[(x-1)/2][y]
    vertexTwo = self.vertices[(x+1)/2][y]
    if x%2 == 0:
      vertexOne = self.vertices[x/2][y]
      vertexTwo = self.vertices[x/2][y+1]
    return (vertexOne, vertexTwo)

  def getEdgesOfVertex(self, vertex):
    vertexEdges = []
    x = vertex.X
    y = vertex.Y
    offset = -1
    if x % 2 == y % 2: offset = 1
    edgeOne = self.edges[x*2][y-1]
    edgeTwo = self.edges[x*2][y]
    edgeThree = self.edges[x*2+offset][y]
    if edgeOne != None: vertexEdges.append(edgeOne)
    if edgeTwo != None: vertexEdges.append(edgeTwo)
    if edgeThree != None: vertexEdges.append(edgeThree)
    return vertexEdges

  def getHexes(self, vertex):
    vertexHexes = []
    x = vertex.X
    y = vertex.Y
    xOffset = x % 2
    yOffset = y % 2

    if x < len(self.hexagons) and y/2 < len(self.hexagons[x]):
      hexOne = self.hexagons[x][y/2]
      if hexOne != None: vertexHexes.append(hexOne)

    weirdX = x
    if (xOffset+yOffset) == 1: weirdX = x-1
    weirdY = y/2 
    if yOffset == 1: weirdY += 1
    else: weirdY -= 1
    if weirdX >= 0 and weirdX < len(self.hexagons) and weirdY >= 0 and weirdY < len(self.hexagons):
      hexTwo = self.hexagons[weirdX][weirdY]
      if hexTwo != None: vertexHexes.append(hexTwo)

    if x > 0 and x < len(self.hexagons) and y/2 < len(self.hexagons[x]):
      hexThree = self.hexagons[x-1][y/2]
      if hexThree != None: vertexHexes.append(hexThree)

    return vertexHexes
ghopper
fuente
Esta respuesta es espantosa. Acaba de pegar toneladas de código sin explicar nada (excepto quién escribió el código). Incluso si eso estuviera bien aquí, el código en sí es horrible. No hay cadenas de documentación, casi no hay comentarios, y los pocos comentarios que se incluyen son ininteligibles (Lógica de pensar que esto dice getEdgesOfVertex y luego para cada borde getVertexEnds, quitando los tres que son == vértice).
Carl Smith
0

Estoy sentado aquí "en mi tiempo libre codificando por diversión" con maleficios. Y dice así ... te diré cómo se ve en palabras.

  1. Hexágono: tiene seis hexágonos vecinos. Puede entregar la referencia para cada ficha hexagonal vecina. Puede decirte en qué consiste (agua, roca, polvo). Puede conectarse con otros y viceversa. Incluso puede conectar automáticamente a los demás que lo rodean para crear un campo mayor y asegurarse de que todos los campos puedan ser atendidos por sus vecinos.
  2. Un edificio hace referencia a tres caminos y tres baldosas hexagonales. Pueden decirle cuáles son.
  3. Un camino hace referencia a dos hexes y otros caminos cuando están dirigidos por casillas vecinas. Pueden saber qué mosaicos son y con qué carreteras o edificios se conectan.

Esta es solo una idea de cómo lo trabajaría.

Raphael Denken
fuente
0

Puede crear una matriz 2D y luego considerar las posiciones válidas como:

  • En filas pares (0,2,4, ...): las celdas impares.
  • En filas impares (1,3,5, ...): las celdas pares.

Para cada celda, sus vecinos serían:

  • Misma columna, 2 filas arriba
  • Misma columna, 2 filas hacia abajo
  • 1 izquierda + 1 arriba
  • 1 izquierda + 1 abajo
  • 1 derecha + 1 arriba
  • 1 derecha + 1 abajo

Ilustración: Cuadrícula hexagonal

Las marcas x son hexes. x que son diagonales entre sí son vecinos. | conecta vecinos verticales.

yman
fuente