Skip to content

Instantly share code, notes, and snippets.

@ftsf
Created May 5, 2021 01:23

Revisions

  1. ftsf created this gist May 5, 2021.
    169 changes: 169 additions & 0 deletions grids.nim
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,169 @@
    import nico
    import nico/vec

    type GridLayout* = enum
    gridSquare
    gridHexPointyTop

    type Grid*[T] = ref object
    layout*: GridLayout
    wrapX*: bool
    wrapY*: bool
    width*: int
    height*: int
    data*: seq[T]
    default*: T

    proc set*[T](g: Grid[T], x,y: int, t: T) =
    if x < 0 or y < 0 or x >= g.width or y >= g.height:
    return
    g.data[y * g.width + x] = t

    proc get*[T](g: Grid[T], x,y: int): T =
    if x < 0 or y < 0 or x >= g.width or y >= g.height:
    return default(T)
    return g.data[y * g.width + x]

    proc get*[T](g: Grid[T], v: Vec2i): T =
    return g.get(v.x, v.y)

    proc set*[T](g: Grid[T], v: Vec2i, t: T) =
    g.set(v.x, v.y, t)

    proc `[]`*[T](g: Grid[T], v: Vec2i): T =
    g.get(v)

    proc `[]`*[T](g: Grid[T], x,y: int): T =
    g.data[y * g.width + x]

    proc `[]`*[T](g: var Grid[T], x,y: int): var T =
    g.data[y * g.width + x]

    proc `[]`*[T](g: var Grid[T], v: Vec2i): var T =
    g.data[v.y * g.width + v.x]

    proc `[]=`*[T](g: Grid[T], v: Vec2i, t: T) =
    g.set(v,t)

    proc `[]=`*[T](g: Grid[T], x,y: int, t: T) =
    g.set(vec2i(x,y),t)

    const hexNeighbours = [
    [ 0,+1,-1],
    [+1, 0,-1],
    [+1,-1, 0],

    [ 0,-1,+1],
    [-1, 0,+1],
    [-1,+1, 0],
    ]

    func toHexCoord*(hex: Vec2i): Vec3i =
    let x = hex.x - (hex.y - (hex.y and 1)) div 2
    let z = hex.y
    let y = -x-z
    return vec3i(x,y,z)

    func toOffset*(cube: Vec3i): Vec2i =
    let col = cube.x + (cube.z - (cube.z and 1)) div 2
    let row = cube.z
    return vec2i(col, row)

    proc isAdjacent*(g: Grid, a,b: Vec2i): bool =
    if g.layout == gridSquare:
    if a.x == b.x:
    if abs(a.y - b.y) == 1:
    return true
    elif g.wrapY and abs(a.y - b.y) == g.height - 1:
    return true
    elif a.y == b.y:
    if abs(a.x - b.x) == 1:
    return true
    if g.wrapX and abs(a.x - b.x) == g.width - 1:
    return true
    else:
    let aCube = toHexCoord(a)
    let bCube = toHexCoord(b)
    let d = abs(aCube.x - bCube.x) + abs(aCube.y - bCube.y) + abs(aCube.z - bCube.z)
    if d == 1:
    return true
    # TODO handle wrapping
    return false

    iterator adjacent*[T](g: Grid[T], v: Vec2i, diagonal = false): Vec2i =
    case g.layout:
    of gridSquare:
    if g.wrapX:
    yield vec2i(wrap(v.x-1,g.width),v.y)
    yield vec2i(wrap(v.x+1,g.width),v.y)
    else:
    if v.x > 0:
    yield vec2i(v.x-1,v.y)
    if v.x < g.width - 1:
    yield vec2i(v.x+1,v.y)

    if g.wrapY:
    yield vec2i(v.x, wrap(v.y-1,g.height))
    yield vec2i(v.x, wrap(v.y+1,g.height))
    else:
    if v.y > 0:
    yield vec2i(v.x,v.y-1)
    if v.y < g.height - 1:
    yield vec2i(v.x,v.y+1)

    if diagonal:
    if v.x > 0:
    if v.y > 0:
    yield vec2i(v.x-1,v.y-1)
    if v.y < g.height - 1:
    yield vec2i(v.x-1,v.y+1)
    if v.x < g.width - 1:
    if v.y > 0:
    yield vec2i(v.x+1,v.y-1)
    if v.y < g.height - 1:
    yield vec2i(v.x+1,v.y+1)

    of gridHexPointyTop:
    let cube = v.toHexCoord()
    for offset in hexNeighbours:
    let n = cube + offset
    let pos = n.toOffset()
    if pos.x >= 0 and pos.x < g.width and pos.y >= 0 and pos.y < g.height:
    yield pos

    proc getNeighbor*(g: Grid, start: Vec2i, dir: Vec3i): Vec2i =
    if g.layout == gridSquare:
    return start + dir.xyi
    else:
    let startCube = start.toHexCoord()
    let offset = (startCube + dir).toOffset()
    return offset

    iterator items*[T](g: Grid[T]): T =
    for v in g.data:
    yield v

    iterator mitems*[T](g: var Grid[T]): var T =
    for v in g.data.mitems:
    yield v

    iterator pairs*[T](g: Grid[T]): (Vec2i,T) =
    for y in 0..<g.height:
    for x in 0..<g.width:
    yield (vec2i(x,y), g[x,y])

    iterator mpairs*[T](g: var Grid[T]): (Vec2i, var T) =
    for y in 0..<g.height:
    for x in 0..<g.width:
    yield (vec2i(x,y), g[x,y])

    proc newGrid*[T](w,h: int, layout: GridLayout = gridSquare, default: T = default(T)): Grid[T] =
    result = new(Grid[T])
    result.layout = layout
    result.width = w
    result.height = h
    result.default = default
    result.data = newSeq[T](w*h)
    for y in 0..<h:
    for x in 0..<w:
    result.set(x,y,default)