Come si rappresenta una griglia esagonale / esadecimale in memoria?


118

Supponiamo che stia costruendo un gioco da tavolo con una griglia esile, come Settlers of Catan :

Ospitato da imgur.com

Nota che ogni vertice e bordo può avere un attributo (una strada e un insediamento sopra).

Come creerei una struttura dati che rappresenti questo tabellone? Quali sono i modelli per accedere ai vicini, ai bordi e ai vertici di ogni piastrella?


puoi anche usare una curva di Hilbert, sono curve di archiviazione di spaziatura tali che l'adiacenza nel piano è preservata in una codifica lineare. controlla l'indicizzazione spaziale e come vengono utilizzati! v interessante
pbordeaux

Risposte:


155

Amit Patel ha pubblicato una pagina straordinaria su questo argomento. È così completo e meraviglioso che deve essere la risposta definitiva a questa domanda: griglie esagonali

Cubez


27
Grazie :) Quella pagina non copre bordi e vertici, ma li copro nella sezione Relazioni tra le parti del mio articolo sulle griglie su www-cs-students.stanford.edu/~amitp/game-programming/grids (i diagrammi sono per griglie quadrate ma la tabella include anche le formule per griglie esagonali assiali)
amitp

18

Tale griglia può essere rappresentata in una matrice bidimensionale:

Se

   2
7     3
   1   
6     4
   5

è il numero uno con i suoi vicini nella griglia esadecimale, quindi puoi inserirlo in un array 2D in questo modo:

2 3
7 1 4
  6 5

Ovviamente la vicinanza è determinata in questa griglia non solo dall'essere adiacenti orizzontalmente o verticalmente, ma anche utilizzando una diagonale.

Puoi usare anche un grafico, se lo desideri, però.


Freddo. E i dati per bordi e vertici?
un nerd pagato il

1
Probabilmente li conserverei separatamente. Indipendentemente dal fatto che si guardino principalmente le tessere o i bordi / vertici, l'altra metà dei dati è dolorosa o ridondante da memorizzare.
Joey

Vedi l'articolo di Amit Patel nella risposta "a paid nerd".
aredridel

11

Questo articolo spiega come impostare un gioco a griglia isomerica / esagonale. Ti consiglio di dare un'occhiata alla Forcing Isometric and Hexagonal Maps onto a Rectangular Gridsezione e alla sezione movimento. Sebbene sia diverso da quello che stai cercando, può aiutarti a formulare come fare ciò che desideri.


2

Ho avuto a che fare molto con le fatture. In casi come questo, tieni traccia di ciascuno dei 6 punti per i bordi dell'esagono. Questo ti consente di disegnarlo abbastanza facilmente.

Avresti un unico array di oggetti che rappresentano gli esagoni. Ciascuno di questi oggetti esadecimali ha anche 6 "puntatori" (o un indice a un altro array) che puntano a un altro array di "lati". Stessa cosa per i "vertici". Ovviamente i vertici avrebbero 3 puntatori agli esagoni adiacenti e i lati ne avrebbero 2.

Quindi, un esagono può essere qualcosa come: X, Y, Punto (6), Vertici (6), Lati (6)

Quindi hai un array Hex, un array vertice e un array laterale.

Quindi è abbastanza semplice trovare i vertici / lati di un esadecimale o qualsiasi altra cosa.

Quando dico puntatore, potrebbe essere altrettanto facilmente un numero intero che punta all'elemento nell'array verticale o laterale o qualsiasi altra cosa. E ovviamente gli array potrebbero essere elenchi o altro.


0
   2
7     3
   1   
6     4
   5

Puoi anche provare a creare righe "piatte" della mappa. Per questo esempio sarebbe:

  2
7 1 3
6 5 4

A volte è più utile avere righe in una riga: P


1
Questo potrebbe avere un codice disordinato per il controllo dei vicini, perché, ad esempio, 1 e 6 sono vicini, ma 3 e 5 non lo sono, ma hanno le stesse posizioni relative.
Bernhard Barker

0

Suggerirei qualcosa di simile al seguente (userò dichiarazioni in stile 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;

Ogni esagono ha sei bordi e sei vertici. Ciascun bordo tiene traccia dei suoi due esagoni adiacenti, e ogni vertice tiene traccia dei suoi tre esagoni adiacenti (gli esagoni sui bordi della mappa saranno un caso speciale).

Ci sono molte cose che potresti fare in modo diverso, naturalmente. Potresti usare puntatori piuttosto che array, potresti usare oggetti piuttosto che record e potresti memorizzare i tuoi esagoni in una matrice bidimensionale come hanno suggerito altri risponditori.

Si spera che questo possa darti alcune idee su un modo per affrontarlo.


0

Abbiamo implementato un'IA di Settlers of Catan per un progetto di classe e abbiamo modificato il codice da questa risposta (che era buggata) per creare una scheda con accesso casuale a tempo costante a vertici e bordi. È stato un problema divertente, ma il consiglio ha richiesto molto tempo, quindi nel caso in cui qualcuno stia ancora cercando una semplice implementazione, ecco il nostro codice 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

Questa risposta è orribile. Hai appena incollato tonnellate di codice senza spiegare nulla (tranne chi ha scritto il codice). Anche se qui andava bene, il codice stesso è orribile. Non ci sono stringhe di documentazione, quasi nessun commento, ei pochi commenti inclusi sono incomprensibili (Logica dal pensare che questo sta dicendo getEdgesOfVertex e poi per ogni bordo getVertexEnds, eliminando i tre che sono == vertice).
Carl Smith

0

Sono seduto qui "nel mio tempo libero a programmare per divertimento" con gli esagoni. E va così ... ti dirò come appare a parole.

  1. Esagono: ha sei esagoni vicini. Può fornire il riferimento per ogni tessera esagonale adiacente. Può dirti in cosa consiste (acqua, roccia, polvere). Può connettersi ad altri e viceversa. Può anche connettere automaticamente gli altri che lo circondano per creare un campo più grande e / o assicurandosi che tutti i campi possano essere indirizzati dai suoi vicini.
  2. Un edificio fa riferimento a tre strade e tre tessere esagonali. Possono dirti quali sono.
  3. Una strada fa riferimento a due esagoni e ad altre strade quando sono raggiunte da tessere vicine. Possono dire quali tessere sono ea quali strade o edifici si collegano.

Questa è solo un'idea di come lavorerei su di essa.


0

È possibile creare un array 2D e quindi considerare le posizioni valide come:

  • Sulle righe pari (0,2,4, ...): le celle dispari.
  • Sulle righe dispari (1,3,5, ...): le celle con numero pari.

Per ogni cella, i suoi vicini sarebbero:

  • Stessa colonna, 2 righe in alto
  • Stessa colonna, 2 righe in basso
  • 1 sinistra + 1 su
  • 1 sinistra + 1 giù
  • 1 a destra + 1 in alto
  • 1 a destra + 1 in basso

Illustrazione: griglia esadecimale

I segni x sono esagoni. x che sono diagonali tra loro sono vicini. | collega i vicini verticali.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.