big ol refactor
This commit is contained in:
@ -1,91 +1,157 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"fmt"
|
||||
|
||||
"gitea.boner.be/bdnugget/goonscape/types"
|
||||
)
|
||||
|
||||
// Node represents a node in the A* pathfinding algorithm
|
||||
type Node struct {
|
||||
Tile types.Tile
|
||||
Parent *Node
|
||||
G, H, F float32
|
||||
G, H, F float32 // G = cost from start, H = heuristic to goal, F = G + H
|
||||
}
|
||||
|
||||
// PriorityQueue implements a min-heap for nodes ordered by F value
|
||||
type PriorityQueue []*Node
|
||||
|
||||
// Implement the heap.Interface for PriorityQueue
|
||||
func (pq PriorityQueue) Len() int { return len(pq) }
|
||||
|
||||
func (pq PriorityQueue) Less(i, j int) bool {
|
||||
return pq[i].F < pq[j].F
|
||||
}
|
||||
|
||||
func (pq PriorityQueue) Swap(i, j int) {
|
||||
pq[i], pq[j] = pq[j], pq[i]
|
||||
}
|
||||
|
||||
func (pq *PriorityQueue) Push(x interface{}) {
|
||||
item := x.(*Node)
|
||||
*pq = append(*pq, item)
|
||||
}
|
||||
|
||||
func (pq *PriorityQueue) Pop() interface{} {
|
||||
old := *pq
|
||||
n := len(old)
|
||||
item := old[n-1]
|
||||
*pq = old[0 : n-1]
|
||||
return item
|
||||
}
|
||||
|
||||
// Helper to check if tile is in priority queue
|
||||
func isInQueue(queue *PriorityQueue, tile types.Tile) (bool, *Node) {
|
||||
for _, node := range *queue {
|
||||
if node.Tile.X == tile.X && node.Tile.Y == tile.Y {
|
||||
return true, node
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// FindPath implements A* pathfinding algorithm with a priority queue
|
||||
func FindPath(start, end types.Tile) []types.Tile {
|
||||
openList := []*Node{}
|
||||
closedList := make(map[[2]int]bool)
|
||||
// Initialize open and closed sets
|
||||
openSet := &PriorityQueue{}
|
||||
heap.Init(openSet)
|
||||
|
||||
startNode := &Node{Tile: start, G: 0, H: heuristic(start, end)}
|
||||
closedSet := make(map[[2]int]bool)
|
||||
|
||||
// Create start node and add to open set
|
||||
startNode := &Node{
|
||||
Tile: start,
|
||||
Parent: nil,
|
||||
G: 0,
|
||||
H: heuristic(start, end),
|
||||
}
|
||||
startNode.F = startNode.G + startNode.H
|
||||
openList = append(openList, startNode)
|
||||
heap.Push(openSet, startNode)
|
||||
|
||||
for len(openList) > 0 {
|
||||
current := openList[0]
|
||||
currentIndex := 0
|
||||
for i, node := range openList {
|
||||
if node.F < current.F {
|
||||
current = node
|
||||
currentIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
openList = append(openList[:currentIndex], openList[currentIndex+1:]...)
|
||||
closedList[[2]int{current.Tile.X, current.Tile.Y}] = true
|
||||
// Main search loop
|
||||
for openSet.Len() > 0 {
|
||||
// Get node with lowest F score
|
||||
current := heap.Pop(openSet).(*Node)
|
||||
|
||||
// If we reached the goal, reconstruct and return the path
|
||||
if current.Tile.X == end.X && current.Tile.Y == end.Y {
|
||||
path := []types.Tile{}
|
||||
node := current
|
||||
for node != nil {
|
||||
path = append([]types.Tile{node.Tile}, path...)
|
||||
node = node.Parent
|
||||
}
|
||||
fmt.Printf("Path found: %v\n", path)
|
||||
return path
|
||||
return reconstructPath(current)
|
||||
}
|
||||
|
||||
neighbors := GetNeighbors(current.Tile)
|
||||
for _, neighbor := range neighbors {
|
||||
if !neighbor.Walkable || closedList[[2]int{neighbor.X, neighbor.Y}] {
|
||||
// Add current to closed set
|
||||
closedSet[[2]int{current.Tile.X, current.Tile.Y}] = true
|
||||
|
||||
// Check all neighbors
|
||||
for _, neighbor := range GetNeighbors(current.Tile) {
|
||||
// Skip if in closed set or not walkable
|
||||
if !neighbor.Walkable || closedSet[[2]int{neighbor.X, neighbor.Y}] {
|
||||
continue
|
||||
}
|
||||
|
||||
// Calculate tentative G score
|
||||
tentativeG := current.G + distance(current.Tile, neighbor)
|
||||
inOpen := false
|
||||
var existingNode *Node
|
||||
for _, node := range openList {
|
||||
if node.Tile.X == neighbor.X && node.Tile.Y == neighbor.Y {
|
||||
existingNode = node
|
||||
inOpen = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check if in open set
|
||||
inOpen, existingNode := isInQueue(openSet, neighbor)
|
||||
|
||||
// If not in open set or better path found
|
||||
if !inOpen || tentativeG < existingNode.G {
|
||||
newNode := &Node{
|
||||
Tile: neighbor,
|
||||
Parent: current,
|
||||
G: tentativeG,
|
||||
H: heuristic(neighbor, end),
|
||||
// Create or update the node
|
||||
var neighborNode *Node
|
||||
if inOpen {
|
||||
neighborNode = existingNode
|
||||
} else {
|
||||
neighborNode = &Node{
|
||||
Tile: neighbor,
|
||||
Parent: current,
|
||||
}
|
||||
}
|
||||
newNode.F = newNode.G + newNode.H
|
||||
|
||||
// Update scores
|
||||
neighborNode.G = tentativeG
|
||||
neighborNode.H = heuristic(neighbor, end)
|
||||
neighborNode.F = neighborNode.G + neighborNode.H
|
||||
neighborNode.Parent = current
|
||||
|
||||
// Add to open set if not already there
|
||||
if !inOpen {
|
||||
openList = append(openList, newNode)
|
||||
heap.Push(openSet, neighborNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No path found
|
||||
return nil
|
||||
}
|
||||
|
||||
// reconstructPath builds the path from goal node to start
|
||||
func reconstructPath(node *Node) []types.Tile {
|
||||
path := []types.Tile{}
|
||||
current := node
|
||||
|
||||
// Follow parent pointers back to start
|
||||
for current != nil {
|
||||
path = append([]types.Tile{current.Tile}, path...)
|
||||
current = current.Parent
|
||||
}
|
||||
|
||||
fmt.Printf("Path found: %v\n", path)
|
||||
return path
|
||||
}
|
||||
|
||||
// heuristic estimates cost from current to goal (Manhattan distance)
|
||||
func heuristic(a, b types.Tile) float32 {
|
||||
return float32(abs(a.X-b.X) + abs(a.Y-b.Y))
|
||||
}
|
||||
|
||||
// distance calculates cost between adjacent tiles
|
||||
func distance(a, b types.Tile) float32 {
|
||||
return 1.0 // uniform cost for now
|
||||
}
|
||||
|
||||
// GetNeighbors returns walkable tiles adjacent to the given tile
|
||||
func GetNeighbors(tile types.Tile) []types.Tile {
|
||||
directions := [][2]int{
|
||||
{1, 0}, {-1, 0}, {0, 1}, {0, -1},
|
||||
@ -104,6 +170,7 @@ func GetNeighbors(tile types.Tile) []types.Tile {
|
||||
return neighbors
|
||||
}
|
||||
|
||||
// abs returns the absolute value of x
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
|
||||
Reference in New Issue
Block a user