Files
game-of-life/grid/hex.go
Natercio Moniz ea5b5c4e75 Initial commit
implemented the game of life variation with hexagonal grid
2025-12-16 11:33:00 +00:00

184 lines
2.9 KiB
Go

package grid
import (
"fmt"
"github.com/nmoniz/game-of-life/math"
)
type HexLayout struct {
Orientation Orientation
Origin math.Pair[int]
Radius math.Pair[int]
}
func (l HexLayout) HexToPix(h Hex) math.Pair[int] {
m := l.Orientation.forward
x := float64(l.Radius.X) * (m[0]*float64(h.q) + m[1]*float64(h.r))
y := float64(l.Radius.Y) * (m[2]*float64(h.q) + m[3]*float64(h.r))
return math.Pair[int]{
X: int(x) + l.Origin.X,
Y: int(y) + l.Origin.Y,
}
}
func (l HexLayout) PixToHex(c math.Pair[int]) Hex {
relC := math.Pair[float64]{
X: float64(c.X-l.Origin.X) / float64(l.Radius.X),
Y: float64(c.Y-l.Origin.Y) / float64(l.Radius.Y),
}
m := l.Orientation.inverse
return NewHexRounded(
m[0]*relC.X+m[1]*relC.Y,
m[2]*relC.X+m[3]*relC.Y,
)
}
type Hex struct {
q, r, s int
}
func NewHex(q, r int) Hex {
return Hex{
q: q,
r: r,
s: -q - r,
}
}
func NewHexRounded(q, r float64) Hex {
var (
s = -q - r
qi = math.RoundInt(q)
ri = math.RoundInt(r)
si = math.RoundInt(s)
)
if qi+ri+si == 0 {
return Hex{
q: qi,
r: ri,
s: si,
}
}
var (
qdiff = math.Abs(float64(qi) - q)
rdiff = math.Abs(float64(ri) - r)
sdiff = math.Abs(float64(si) - s)
)
switch {
case qdiff > rdiff && qdiff > sdiff:
qi = -ri - si
case rdiff > sdiff:
ri = -qi - si
default:
si = -qi - ri
}
return Hex{
q: qi,
r: ri,
s: si,
}
}
func (h Hex) Q() int {
return h.q
}
func (h Hex) R() int {
return h.r
}
func (h Hex) S() int {
return h.s
}
func (h Hex) Add(o Hex) Hex {
return Hex{
q: h.q + o.q,
r: h.r + o.r,
s: h.s + o.s,
}
}
func (h Hex) Sub(o Hex) Hex {
return Hex{
q: h.q - o.q,
r: h.r - o.r,
s: h.s - o.s,
}
}
func (h Hex) Mul(o Hex) Hex {
return Hex{
q: h.q * o.q,
r: h.r * o.r,
s: h.s * o.s,
}
}
func (h Hex) Equal(other Hex) bool {
return h.q == other.q && h.r == other.r && h.s == other.s
}
func (h Hex) Length() int {
return (math.Abs(h.q) + math.Abs(h.r) + math.Abs(h.s)) / 2
}
func (h Hex) Distance(o Hex) int {
return h.Sub(o).Length()
}
func (h Hex) AllNeighbors() []Hex {
return []Hex{
NewHex(h.q+1, h.r),
NewHex(h.q, h.r+1),
NewHex(h.q-1, h.r+1),
NewHex(h.q-1, h.r),
NewHex(h.q, h.r-1),
NewHex(h.q+1, h.r-1),
}
}
func (h Hex) NeighborAt(direction int) Hex {
neighbors := h.AllNeighbors()
direction = direction % 6
if direction < 0 {
direction += 6
}
return neighbors[direction]
}
func (h Hex) String() string {
return fmt.Sprintf("(%d,%d,%d)", h.Q(), h.R(), h.S())
}
type Orientation struct {
forward []float64
inverse []float64
}
func PointyTopLayout() Orientation {
return Orientation{
forward: []float64{math.Sqrt(3.0), math.Sqrt(3.0) / 2.0, 0.0, 3.0 / 2.0},
inverse: []float64{math.Sqrt(3.0) / 3.0, -1.0 / 3.0, 0.0, 2.0 / 3.0},
}
}
func FlatTopLayout() Orientation {
return Orientation{
forward: []float64{3.0 / 2.0, 0.0, math.Sqrt(3.0) / 2.0, math.Sqrt(3.0)},
inverse: []float64{2.0 / 3.0, 0.0, -1.0 / 3.0, math.Sqrt(3.0) / 3.0},
}
}