ฉันจะแสดงตาราง hextile / hex ในหน่วยความจำได้อย่างไร


119

สมมติว่าฉันกำลังสร้างเกมกระดานที่มีตาราง hextile เช่นSettlers of Catan :

เป็นเจ้าภาพโดย imgur.com

โปรดทราบว่าแต่ละจุดยอดและขอบอาจมีแอตทริบิวต์ (ถนนและการทรุดตัวด้านบน)

ฉันจะสร้างโครงสร้างข้อมูลที่แสดงถึงบอร์ดนี้ได้อย่างไร? รูปแบบในการเข้าถึงเพื่อนบ้านขอบและจุดยอดของกระเบื้องแต่ละแผ่นมีอะไรบ้าง


นอกจากนี้คุณยังสามารถใช้เส้นโค้งฮิลเบิร์ตซึ่งเป็นเส้นโค้งการเว้นระยะห่างเพื่อให้การปรับระยะในระนาบถูกเก็บรักษาไว้ในการเข้ารหัสเชิงเส้น ตรวจสอบการจัดทำดัชนีเชิงพื้นที่และวิธีใช้! v ที่น่าสนใจ
pbordeaux

คำตอบ:


156

Amit Patel ได้โพสต์หน้าเว็บที่น่าทึ่งในหัวข้อนี้ มันครอบคลุมและยอดเยี่ยมมากจนต้องเป็นคำตอบที่ชัดเจนสำหรับคำถามนี้: กริดหกเหลี่ยม

Cubez


27
ขอบคุณ :) หน้านั้นไม่ครอบคลุมขอบและจุดยอด แต่ฉันครอบคลุมในส่วนความสัมพันธ์ระหว่างส่วนของบทความกริดของฉันที่www-cs-students.stanford.edu/~amitp/game-programming/grids (ไดอะแกรมคือ สำหรับกริดสี่เหลี่ยม แต่ตารางรวมสูตรสำหรับกริดฐานสิบหกแกนด้วย)
amitp

18

ตารางดังกล่าวสามารถแสดงในอาร์เรย์สองมิติ:

ถ้า

   2
7     3
   1   
6     4
   5

เป็นหมายเลขหนึ่งที่มีเพื่อนบ้านในตารางฐานสิบหกจากนั้นคุณสามารถใส่สิ่งนี้ลงในอาร์เรย์ 2 มิติดังนี้:

2 3
7 1 4
  6 5

เห็นได้ชัดว่าเพื่อนบ้านถูกกำหนดในตารางนี้ไม่เพียง แต่อยู่ติดกันในแนวนอนหรือแนวตั้งเท่านั้น แต่ยังใช้เส้นทแยงมุมหนึ่งเส้นด้วย

คุณสามารถใช้กราฟได้เช่นกันหากต้องการ


เย็น. แล้วข้อมูลสำหรับขอบและจุดยอดล่ะ?
เนิร์ดจ่าย

1
ฉันอาจจะจัดเก็บแยกต่างหาก ไม่ว่าคุณจะมองไปที่กระเบื้องหรือขอบ / จุดยอดเป็นหลักก็ตามข้อมูลอีกครึ่งหนึ่งนั้นเจ็บปวดหรือซ้ำซ้อนในการจัดเก็บ
Joey

ดูบทความของ Amit Patel ในคำตอบ "a paid nerd"
aredridel

11

บทความนี้อธิบายถึงวิธีการตั้งค่าเกมกริด Isomeric / Hexagonal ฉันขอแนะนำให้คุณดูที่Forcing Isometric and Hexagonal Maps onto a Rectangular Gridส่วนและส่วนการเคลื่อนไหว แม้ว่าจะแตกต่างจากสิ่งที่คุณกำลังมองหา แต่อาจช่วยให้คุณกำหนดวิธีการทำสิ่งที่คุณต้องการได้


2

ฉันจัดการกับ hexes มามากแล้ว ในกรณีเช่นนี้คุณจะติดตามจุดทั้ง 6 จุดสำหรับเส้นขอบของฐานสิบหก ซึ่งจะช่วยให้คุณวาดมันได้อย่างง่ายดาย

คุณจะมีอาร์เรย์ของอ็อบเจ็กต์เดียวที่แสดงถึงฐานสิบหก แต่ละออบเจ็กต์ฐานสิบหกเหล่านี้ยังมี "พอยน์เตอร์" 6 ตัว (หรือดัชนีไปยังอาร์เรย์อื่น) ที่ชี้ไปยังอาร์เรย์ของ "ด้าน" อื่น สิ่งเดียวกันสำหรับ "จุดยอด" แน่นอนว่าจุดยอดจะมีตัวชี้ 3 ตัวสำหรับเลขฐานสิบหกที่อยู่ติดกันและด้านข้างจะมี 2

ดังนั้นเลขฐานสิบหกอาจเป็นเช่น: X, Y, Point (6), Vertices (6), Sides (6)

จากนั้นคุณจะมีอาร์เรย์ Hex อาร์เรย์แนวตั้งและอาร์เรย์ด้านข้าง

จากนั้นก็ค่อนข้างง่ายที่จะหาจุดยอด / ด้านของฐานสิบหกหรืออะไรก็ตาม

เมื่อฉันพูดตัวชี้มันอาจเป็นจำนวนเต็มที่ชี้ไปยังองค์ประกอบในจุดยอดหรืออาร์เรย์ด้านข้างหรืออะไรก็ได้ และแน่นอนว่าอาร์เรย์อาจเป็นรายการหรืออะไรก็ได้


0
   2
7     3
   1   
6     4
   5

คุณยังสามารถลอง "แบน" แถวของแผนที่ได้ สำหรับตัวอย่างนี้จะเป็น:

  2
7 1 3
6 5 4

บางครั้งก็มีประโยชน์มากกว่าที่จะมีแถวในหนึ่งแถว: P


1
สิ่งนี้อาจมีรหัสการตรวจสอบเพื่อนบ้านที่ยุ่งเหยิงเนื่องจากตัวอย่างเช่น 1 และ 6 เป็นเพื่อนบ้าน แต่ 3 และ 5 ไม่ใช่ แต่ก็มีตำแหน่งสัมพัทธ์เหมือนกัน
Bernhard Barker

0

ฉันขอแนะนำสิ่งต่อไปนี้ (ฉันจะใช้การประกาศแบบเดลฟี):

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

เราใช้ 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

คำตอบนี้แย่มาก คุณเพิ่งวางโค้ดจำนวนมากโดยไม่ได้อธิบายอะไรเลย (ยกเว้นใครเป็นคนเขียนโค้ด) แม้ว่ามันจะโอเคที่นี่ แต่รหัสก็น่ากลัว ไม่มี docstrings แทบไม่มีความคิดเห็นและความคิดเห็นบางส่วนที่รวมอยู่นั้นไม่สามารถเข้าใจได้ (ตรรกะจากการคิดว่าสิ่งนี้กำลังพูดว่า getEdgesOfVertex จากนั้นสำหรับแต่ละขอบ getVertexEnds โดยนำทั้งสามที่เป็น == จุดยอด)
Carl Smith

0

ฉันนั่งอยู่ที่นี่ "ในเวลาว่างเขียนโค้ดเพื่อความสนุก" ด้วยเลขฐานสิบหก และมันก็เป็นเช่นนี้ ... ฉันจะบอกคุณว่ามันเป็นอย่างไรในคำพูด

  1. หกเหลี่ยม: มีหกเหลี่ยมเพื่อนบ้านหกเหลี่ยม สามารถส่งข้อมูลอ้างอิงสำหรับกระเบื้องฐานสิบหกที่อยู่ใกล้เคียงกันได้ สามารถบอกได้ว่าประกอบด้วยอะไรบ้าง (น้ำหินฝุ่น) มันสามารถเชื่อมต่อตัวเองกับผู้อื่นและในทางกลับกัน มันยังสามารถเชื่อมต่อกับคนอื่น ๆ ที่อยู่รอบ ๆ ตัวเขาโดยอัตโนมัติเพื่อสร้างสนามที่ใหญ่ขึ้นและหรือเพื่อให้แน่ใจว่าทุกเขตสามารถอยู่ได้โดยเพื่อนบ้านของมัน
  2. อาคารอ้างอิงถึงสามถนนและสามกระเบื้อง Hex พวกเขาสามารถบอกคุณได้ว่าพวกเขาคืออะไร
  3. ถนนอ้างถึงสองฐานสิบหกและถนนอื่น ๆ เมื่อยึดติดกับกระเบื้องใกล้เคียง พวกเขาสามารถบอกได้ว่ากระเบื้องคืออะไรและเชื่อมต่อกับถนนหรืออาคารใด

นี่เป็นเพียงแนวคิดว่าฉันจะทำอย่างไร


0

คุณสามารถสร้างอาร์เรย์ 2 มิติจากนั้นพิจารณาตำแหน่งที่ถูกต้องเป็น:

  • ในแถวเลขคู่ (0,2,4, ... ): เซลล์เลขคี่
  • ในแถวเลขคี่ (1,3,5, ... ): เซลล์ที่มีเลขคู่

สำหรับแต่ละเซลล์เพื่อนบ้านจะเป็น:

  • คอลัมน์เดียวกัน 2 แถวขึ้นไป
  • คอลัมน์เดียวกันลง 2 แถว
  • 1 ซ้าย +1 อัพ
  • 1 ซ้าย + 1 ลง
  • 1 สิทธิ์ + 1 ขึ้น
  • 1 ขวา + 1 ลง

ภาพประกอบ: Hex Grid

เครื่องหมาย x คือฐานสิบหก x ที่อยู่ในแนวทแยงซึ่งกันและกันคือเพื่อนบ้าน | เชื่อมต่อเพื่อนบ้านแนวตั้ง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.