Как мне представить гексилированную / шестигранную сетку в памяти? - PullRequest
111 голосов
/ 03 декабря 2009

Скажем, я строю настольную игру с гексильной сеткой, например Поселенцы Катана :

Hosted by imgur.com

Обратите внимание, что каждая вершина и ребро могут иметь атрибут (дорога и населенный пункт выше).

Как бы я создал структуру данных, которая представляет эту доску? Каковы шаблоны для доступа к соседям, ребрам и вершинам каждой плитки?

Ответы [ 8 ]

140 голосов
/ 01 июня 2013

Амит Патель разместил удивительную страницу на эту тему. Он настолько всеобъемлющий и замечательный, что на него необходимо дать окончательный ответ: Шестиугольные сетки

cubez

16 голосов
/ 03 декабря 2009

Такая сетка может быть представлена ​​в двумерном массиве:

Если

   2
7     3
   1   
6     4
   5

- это номер один со своими соседями в шестнадцатеричной сетке, затем вы можете поместить это в 2D-массив следующим образом:

2 3
7 1 4
  6 5

Очевидно, что соседство определяется в этой сетке не только по горизонтали или вертикали, но также по одной диагонали.

Вы также можете использовать график, если хотите.

10 голосов
/ 03 декабря 2009

В этой статье рассказывается, как настроить игру Изомерная / Шестиугольная сетка. Я рекомендую вам взглянуть на раздел Forcing Isometric and Hexagonal Maps onto a Rectangular Grid и раздел движения. Хотя это отличается от того, что вы ищете, оно может помочь вам сформулировать, как делать то, что вы хотите.

2 голосов
/ 10 сентября 2015

Мы реализовали Settlers of Catan AI для проекта класса и изменили код из этого ответа (который был ошибочным), чтобы создать доску с произвольным доступом к вершинам и ребрам с постоянным временем. Это была забавная проблема, но плата заняла много времени, поэтому, если кто-то все еще ищет простую реализацию, вот наш код 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 голосов
/ 24 мая 2012

Я много имел дело с гексами. В таких случаях вы отслеживаете каждую из 6 точек для границ гекса. Это позволяет вам нарисовать его довольно легко.

У вас будет один массив объектов, представляющих гексы. Каждый из этих шестнадцатеричных объектов также имеет 6 «указателей» (или указатель на другой массив), указывающих на другой массив «сторон». То же самое для "вершин". Конечно, у вершин будет 3 указателя на смежные гексы, а у сторон будет 2.

Итак, гекс может быть что-то вроде: X, Y, точка (6), вершины (6), стороны (6)

Тогда у вас есть массив Hex, массив вершин и боковой массив.

Тогда довольно просто найти вершины / стороны для гекса или что-то еще.

Когда я говорю «указатель», это может быть просто целое число, указывающее на элемент в массиве вершин, боковых сторон или что-то еще И, конечно, массивы могут быть списками или чем-то еще.

0 голосов
/ 28 августа 2018

Я сижу здесь "в свободное время для кодирования ради забавы" с гексами. И это выглядит так ... Я скажу вам, как это выглядит на словах.

  1. Шестиугольник: у него шесть соседних шестиугольников. Он может доставить ссылку для каждой соседней шестнадцатеричной плитки. Он может сказать вам, из чего он состоит (вода, камень, пыль). Он может соединиться с другими и наоборот. Он может даже автоматически соединять окружающих его, чтобы создать большее поле и / или убедиться, что все поля могут быть адресованы его соседями.
  2. В здании имеется до трех дорог и трех шестнадцатеричных плиток. Они могут сказать вам, кто они.
  3. Дорога относится к двум гексам и другим дорогам, когда они обозначены соседними плитками. Они могут сказать, какие плитки и с какими дорогами или зданиями они соединяются.

Это просто идея, как бы я работал над этим.

0 голосов
/ 04 декабря 2009

Я бы предложил что-то вроде следующего (я буду использовать объявления в стиле 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;

Каждый гекс имеет шесть ребер и шесть вершин. Каждый край отслеживает два смежных гекса, а каждая вершина отслеживает три смежных гекса (особый случай - гексы по краям карты).

Есть много вещей, которые вы могли бы сделать по-другому, конечно. Вы можете использовать указатели, а не массивы, вы можете использовать объекты, а не записи, и вы можете хранить свои гексы в двумерном массиве, как предлагали другие авторы.

Надеюсь, это может дать вам некоторые идеи об одном способе приближения к нему.

0 голосов
/ 03 декабря 2009
   2
7     3
   1   
6     4
   5

Вы также можете попытаться «выровнять» ряды своей карты. Для этого примера это будет:

  2
7 1 3
6 5 4

Иногда полезно иметь строки в одном ряду: P

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...