Como eu represento uma grade hexadecimal / hexadecimal na memória?


118

Digamos que eu esteja construindo um jogo de tabuleiro com uma grade hextila, como Settlers of Catan :

Hospedado por imgur.com

Observe que cada vértice e aresta podem ter um atributo (uma estrada e assentamento acima).

Como eu faria uma estrutura de dados que representasse esta placa? Quais são os padrões para acessar os vizinhos, bordas e vértices de cada bloco?


você também pode usar uma curva de Hilbert, elas são curvas de preenchimento de espaçamento de modo que a adjacência no plano é preservada em uma codificação linear. verifique a indexação espacial e como eles são usados! v interessante
pbordeaux de

Respostas:


155

Amit Patel postou uma página incrível sobre esse assunto. É tão abrangente e maravilhoso que precisa ser a resposta definitiva para esta pergunta: Grades hexagonais

cubez


27
Obrigado :) Essa página não cobre arestas e vértices, mas eu os abordo na seção Relações entre as partes do meu artigo de grades em www-cs-students.stanford.edu/~amitp/game-programming/grids (os diagramas são para grades quadradas, mas a tabela inclui as fórmulas para grades hexadecimais axiais também)
amitp

18

Essa grade pode ser representada em uma matriz bidimensional:

E se

   2
7     3
   1   
6     4
   5

é o número um com seus vizinhos na grade hexadecimal, então você pode colocá-lo em uma matriz 2D assim:

2 3
7 1 4
  6 5

Obviamente, a vizinhança é determinada nesta grade não apenas por ser horizontal ou verticalmente adjacente, mas também usando uma diagonal.

Você também pode usar um gráfico, se quiser.


Legal. E os dados para arestas e vértices?
um nerd pago

1
Eu provavelmente os armazenaria separadamente. Independentemente de você olhar principalmente para os blocos ou para as bordas / vértices, a outra metade dos dados é dolorosa ou redundante para armazenar.
Joey

Veja o artigo de Amit Patel na resposta de "um nerd pago".
aredridel

11

Este artigo explica como configurar um jogo de grade isomérica / hexagonal. Eu recomendo que você dê uma olhada na Forcing Isometric and Hexagonal Maps onto a Rectangular Gridseção e na seção de movimento. Embora seja diferente do que você está procurando, pode ajudá-lo a formular como fazer o que deseja.


2

Eu lidei muito com feitiços. Em casos como esse, você rastreia cada um dos 6 pontos das bordas do hexágono. Isso permite que você desenhe com bastante facilidade.

Você teria uma única matriz de objetos que representam hexágonos. Cada um desses objetos hexadecimais também tem 6 "ponteiros" (ou um índice para outro array) apontando para outro array de "lados". Mesma coisa para "vértices". É claro que os vértices teriam 3 ponteiros para os hexágonos adjacentes e os lados teriam 2.

Portanto, um hexágono pode ser algo como: X, Y, Ponto (6), Vértices (6), Lados (6)

Então você tem uma matriz Hex, uma matriz de vértices e uma matriz lateral.

Então é muito simples encontrar os vértices / lados de um hex, ou qualquer outra coisa.

Quando digo ponteiro, poderia facilmente ser um inteiro apontando para o elemento no vértice ou na matriz lateral ou qualquer outra coisa. E, claro, os arrays podem ser listas ou o que for.


0
   2
7     3
   1   
6     4
   5

Você também pode tentar 'achatar' as linhas do mapa. Para este exemplo, seria:

  2
7 1 3
6 5 4

Às vezes é mais útil ter linhas em uma linha: P


1
Isso pode ter algum código de verificação de vizinho confuso, porque, por exemplo, 1 e 6 são vizinhos, mas 3 e 5 não são, mas eles têm as mesmas posições relativas.
Bernhard Barker

0

Eu sugeriria algo como o seguinte (usarei declarações no 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ágono possui seis arestas e seis vértices. Cada aresta acompanha seus dois hexágonos adjacentes e cada vértice monitora seus três hexágonos adjacentes (hexágonos nas arestas do mapa serão um caso especial).

Existem muitas coisas que você poderia fazer de maneira diferente, é claro. Você poderia usar ponteiros em vez de matrizes, poderia usar objetos em vez de registros e poderia armazenar seus hexágonos em uma matriz bidimensional, como outros respondentes sugeriram.

Esperançosamente, isso pode lhe dar algumas idéias sobre uma maneira de abordá-lo.


0

Implementamos um Settlers of Catan AI para um projeto de classe e modificamos o código desta resposta (que estava cheio de erros) para criar uma placa com acesso aleatório em tempo constante a vértices e arestas. Foi um problema divertido, mas a placa demorou muito, então caso alguém ainda esteja procurando por uma implementação simples aqui está nosso 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

Essa resposta é terrível. Você acabou de colar toneladas de código sem explicar nada (exceto quem escreveu o código). Mesmo que estivesse tudo bem aqui, o código em si é horrível. Não há docstrings, quase não há comentários, e os poucos comentários incluídos são ininteligíveis (lógica de pensar que isso está dizendo getEdgesOfVertex e, em seguida, para cada borda getVertexEnds, retirando os três que são == vertex).
Carl Smith

0

Estou sentado aqui "no meu tempo livre, codificando para me divertir" com feitiços. E é assim ... Eu vou te dizer como fica em palavras.

  1. Hexágono: possui seis hexágonos vizinhos. Ele pode fornecer a referência para cada hexágono vizinho. Ele pode dizer em que consiste (água, pedra, poeira). Ele pode se conectar a outros e vice-versa. Ele pode até mesmo conectar automaticamente os outros ao seu redor para criar um campo maior e / ou garantir que todos os campos possam ser endereçados por seus vizinhos.
  2. Um edifício faz referência a até três estradas e três Hex Tiles. Eles podem dizer quais são.
  3. Uma estrada faz referência a dois hexágonos e outras estradas quando eles são direcionados por ladrilhos vizinhos. Eles sabem quais são os blocos e a quais estradas ou edifícios eles se conectam.

Esta é apenas uma ideia de como eu trabalharia nisso.


0

Você pode criar uma matriz 2D e, em seguida, considerar as posições válidas como:

  • Em linhas pares (0,2,4, ...): as células ímpares.
  • Em linhas ímpares (1,3,5, ...): as células pares.

Para cada célula, seus vizinhos seriam:

  • Mesma coluna, 2 linhas acima
  • Mesma coluna, 2 linhas para baixo
  • 1 esquerda + 1 acima
  • 1 esquerda + 1 abaixo
  • 1 direita + 1 acima
  • 1 direita + 1 abaixo

Ilustração: Hex Grid

As marcas x são hexágonos. x que são diagonais entre si são vizinhos. | conecta vizinhos verticais.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.