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}, } }