Disons que je construis un jeu de plateau avec une grille de type hextile, comme Settlers of Catan :
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?
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
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.
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.
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
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.
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.
Ceci est juste une idée de comment je pourrais y travailler.
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
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.