tins2021/tiles.go
Sander Schobers 13b6a50a45 Added bitmap font.
Added monsters.
Added animations.
Added rendering of geometries.
2021-08-09 01:25:51 +02:00

198 lines
4.2 KiB
Go

package tins2021
import (
"math"
"math/rand"
"opslag.de/schobers/geom"
)
var AllDirections = []Direction{DirectionDownRight, DirectionDownLeft, DirectionUpLeft, DirectionUpRight}
func RandomDirection() Direction {
return AllDirections[rand.Intn(len(AllDirections))]
}
func AdjacentPosition(pos geom.Point, dir Direction) geom.Point {
if pos.Y%2 == 0 {
switch dir {
case DirectionDownRight:
return geom.Pt(pos.X+1, pos.Y+1)
case DirectionDownLeft:
return geom.Pt(pos.X, pos.Y+1)
case DirectionUpLeft:
return geom.Pt(pos.X, pos.Y-1)
case DirectionUpRight:
return geom.Pt(pos.X+1, pos.Y-1)
default:
panic("invalid direction")
}
}
switch dir {
case DirectionDownRight:
return geom.Pt(pos.X, pos.Y+1)
case DirectionDownLeft:
return geom.Pt(pos.X-1, pos.Y+1)
case DirectionUpLeft:
return geom.Pt(pos.X-1, pos.Y-1)
case DirectionUpRight:
return geom.Pt(pos.X, pos.Y-1)
default:
panic("invalid direction")
}
}
type Direction int
const (
DirectionDownRight Direction = iota
DirectionDownLeft
DirectionUpLeft
DirectionUpRight
)
func (d Direction) Reverse() Direction {
switch d {
case DirectionDownRight:
return DirectionUpLeft
case DirectionDownLeft:
return DirectionUpRight
case DirectionUpLeft:
return DirectionDownRight
case DirectionUpRight:
return DirectionDownLeft
default:
panic("direction not supported")
}
}
type Tile struct {
Inversed bool
Star bool
Heart bool
}
func (t *Tile) Occupied() bool {
return t.Star || t.Heart
}
func (t *Tile) Invert() { t.Inversed = !t.Inversed }
type Tiles map[geom.Point]*Tile
func (t Tiles) AllReachable(from geom.Point) bool {
visited := map[geom.Point]bool{}
visit := []geom.Point{from}
for len(visit) > 0 {
next := visit[0]
visit = visit[1:]
for _, dir := range AllDirections {
neighbour, ok := t.CanMove(next, dir)
if !ok || visited[neighbour] {
continue
}
visited[neighbour] = true
visit = append(visit, neighbour)
}
}
return len(visited) == len(t)
}
func (t Tiles) CanMove(p geom.Point, dir Direction) (geom.Point, bool) {
towards := AdjacentPosition(p, dir)
from := t[p]
to := t[towards]
if to == nil {
return geom.Point{}, false
}
if dir == DirectionDownRight || dir == DirectionDownLeft {
if !from.Inversed && to.Inversed {
return geom.Point{}, false
}
} else {
if from.Inversed && !to.Inversed {
return geom.Point{}, false
}
}
return towards, true
}
func (t Tiles) CanMoveTile(p geom.Point, dir Direction) (geom.Point, *Tile, bool) {
to, ok := t.CanMove(p, dir)
if !ok {
return geom.Point{}, nil, false
}
return to, t[to], true
}
func (t Tiles) Distances(from geom.Point) map[geom.Point]int {
distance := map[geom.Point]int{
from: 0,
}
visit := []geom.Point{from}
for len(visit) > 0 {
next := visit[0]
visit = visit[1:]
for _, dir := range AllDirections {
neighbour, ok := t.CanMove(next, dir)
if !ok {
continue
}
d := distance[next] + 1
if neighbourD, ok := distance[neighbour]; ok && neighbourD <= d {
continue
}
distance[neighbour] = d
visit = append(visit, neighbour)
}
}
return distance
}
func (t Tiles) ShortestPath(from, to geom.Point, canMoveTo func(geom.Point, *Tile) bool) []geom.Point {
distances := map[geom.Point]int{
from: 0,
}
origins := map[geom.Point]geom.Point{
from: from,
}
estimated := map[geom.Point]int{
from: from.DistInt(to),
}
visit := map[geom.Point]bool{from: true}
for len(visit) > 0 {
var next geom.Point
best := math.MaxInt32
for candidate := range visit {
e := estimated[candidate]
if e < best {
next = candidate
best = e
}
}
if next == to {
path := []geom.Point{to}
for path[0] != from {
path = append([]geom.Point{origins[path[0]]}, path...)
}
return path
}
delete(visit, next)
for _, dir := range AllDirections {
neighbour, ok := t.CanMove(next, dir)
if !ok || (canMoveTo != nil && !canMoveTo(neighbour, t[neighbour])) {
continue
}
d := distances[next] + 1
if neighbourD, ok := distances[neighbour]; ok && neighbourD <= d {
continue
}
distances[neighbour] = d
origins[neighbour] = next
estimated[neighbour] = d + neighbour.DistInt(to)
visit[neighbour] = true
}
}
return []geom.Point{}
}