web-dev-qa-db-fra.com

Comment représenter une grille hextile/hex en mémoire?

Disons que je construis un jeu de plateau avec une grille de type hextile, comme Settlers of Catan :

Hosted by imgur.com

Notez que chaque sommet et chaque bord peuvent avoir un attribut (une route et un règlement ci-dessus).

Comment pourrais-je créer une structure de données qui représente ce tableau? Quels sont les modèles pour accéder aux voisins, arêtes et sommets de chaque tuile?

110
a paid nerd

Amit Patel a posté une page amazing sur ce sujet. C'est tellement complet et merveilleux qu'il doit être la réponse définitive à cette question: Grilles hexagonales

 cubez

137
a paid nerd

Une telle grille peut être représentée dans un tableau à deux dimensions:

Si

   2
7     3
   1   
6     4
   5

est le numéro un avec ses voisins dans la grille hexadécimale, alors vous pouvez le mettre dans un tableau 2D comme ceci:

2 3
7 1 4
  6 5

Il est évident que le voisinage est déterminé dans cette grille non seulement en étant adjacent horizontalement ou verticalement, mais également en utilisant une diagonale.

Vous pouvez aussi utiliser un graphique, si vous le souhaitez.

16
Joey

Cet article explique comment configurer un jeu de grille isomérique/hexagonal. Je vous recommande de consulter la section Forcing Isometric and Hexagonal Maps onto a Rectangular Grid et la section des mouvements. Bien que ce soit différent de ce que vous recherchez, il peut vous aider à formuler comment faire ce que vous voulez.

10
zfedoran

Nous avons implémenté Settlers of Catan AI pour un projet de classe et modifié le code de this answer (qui posait problème) pour créer une carte avec un accès aléatoire de temps en temps aux sommets et aux arêtes. C'était un problème amusant, mais le forum a pris beaucoup de temps, donc si quelqu'un cherche encore une implémentation simple, voici notre code 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
2
ghopper

J'ai beaucoup traité avec des hexagones. Dans de tels cas, vous suivez chacun des 6 points pour les limites de l'hex. Cela vous permet de le dessiner assez facilement.

Vous auriez un seul tableau d'objets représentant des hexs. Chacun de ces objets hexagonaux a également 6 "pointeurs" (ou un index vers un autre tableau) pointant vers un autre tableau de "côtés". Même chose pour les "sommets". Bien entendu, les sommets auraient 3 pointeurs sur les hexs adjacents et les côtés 2.

Ainsi, un hex peut être quelque chose comme: X, Y, Point (6), Vertices (6), Côtés (6)

Ensuite, vous avez un tableau Hex, un tableau vertice et un tableau latéral.

Ensuite, il est assez simple de trouver les sommets/côtés d’un hexagone ou autre.

Lorsque je parle de pointeur, il peut tout aussi bien s'agir d'un entier désignant l'élément du tableau des sommets, des côtés ou autre. Et bien sûr, les tableaux peuvent être des listes ou autre chose.

2

Je suis assis ici "pendant mon temps libre à coder pour m'amuser" avec des hexagones. Et ça se passe comme ça ... Je vais vous dire à quoi ça ressemble en mots. 

  1. Hexagone: il a six hexagones voisins. Il peut fournir la référence pour chaque tuile hexagonale voisine. Il peut vous dire en quoi il consiste (eau, roches, poussière). Il peut se connecter aux autres et vice versa. Il peut même connecter automatiquement les personnes qui l'entourent pour créer un champ plus grand et/ou s'assurer que tous les champs peuvent être adressés par ses voisins.
  2. Un bâtiment fait référence à trois routes et trois tuiles Hex. Ils peuvent vous dire ce qu'ils sont.
  3. Une route fait référence à deux hexs et à d'autres routes quand ils sont adressés par des tuiles voisines. Ils peuvent dire quelles tuiles sont et à quelles routes ou bâtiments ils se connectent. 

Ceci est juste une idée de comment je pourrais y travailler. 

0
Raphael Denken
   2
7     3
   1   
6     4
   5

Vous pouvez également essayer d’aplatir les lignes de votre carte. Pour cet exemple ce serait:

  2
7 1 3
6 5 4

Son parfois plus utile d'avoir des lignes dans une ligne: P

0
qba

Je suggérerais quelque chose comme ceci (je vais utiliser des déclarations de style 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;

Chaque hexagone a six arêtes et six sommets. Chaque bord garde une trace de ses deux hexs adjacents et chaque sommet suit ses trois hexs adjacents (les hexs situés sur les bords de la carte constitueront un cas particulier).

Il y a beaucoup de choses que vous pourriez faire d'une manière différente bien sûr. Vous pouvez utiliser des pointeurs plutôt que des tableaux, des objets plutôt que des enregistrements et stocker vos hexs dans un tableau à deux dimensions, comme l'ont suggéré d'autres répondeurs.

J'espère que cela vous donnera quelques idées sur une façon de l'aborder.

0
Incredulous Monk