210 lines
3.8 KiB
Odin
210 lines
3.8 KiB
Odin
package main
|
|
|
|
Path :: struct {
|
|
tiles: [dynamic]Tile_Coord,
|
|
count: int,
|
|
}
|
|
|
|
max_path_int :: 1_000_000_000
|
|
|
|
path_clear :: proc(path: ^Path) {
|
|
resize(&path.tiles, 0)
|
|
path.count = 0
|
|
}
|
|
|
|
pathfind_bfs :: proc(tilemap: ^Tilemap, start, goal: Tile_Coord, path: ^Path) -> bool {
|
|
path_clear(path)
|
|
|
|
if start == goal {
|
|
resize(&path.tiles, 1)
|
|
path.tiles[0] = start
|
|
path.count = 1
|
|
return true
|
|
}
|
|
|
|
w, h := tilemap.width, tilemap.height
|
|
|
|
parents: [MAP_HEIGHT][MAP_WIDTH]Tile_Coord
|
|
visited: [MAP_HEIGHT][MAP_WIDTH]bool
|
|
|
|
found := false
|
|
|
|
for y in 0 ..< h {
|
|
for x in 0 ..< w {
|
|
parents[y][x] = {-1, -1}
|
|
}
|
|
}
|
|
|
|
queue: [MAP_WIDTH * MAP_HEIGHT]Tile_Coord
|
|
head, tail: int = 0, 0
|
|
|
|
queue[tail] = start; tail += 1
|
|
visited[start.y][start.x] = true
|
|
parents[start.y][start.x] = start
|
|
|
|
for head < tail {
|
|
current := queue[head]; head += 1
|
|
|
|
if current == goal {
|
|
found = true
|
|
break
|
|
}
|
|
|
|
for offset in NEIGHBOR_OFFSET {
|
|
nx := current.x + offset.x
|
|
ny := current.y + offset.y
|
|
if !tile_in_bounds(tilemap, nx, ny) ||
|
|
visited[ny][nx] ||
|
|
!tile_walkable(tilemap, nx, ny) {
|
|
continue
|
|
}
|
|
|
|
visited[ny][nx] = true
|
|
parents[ny][nx] = current
|
|
queue[tail] = {nx, ny}
|
|
tail += 1
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return false
|
|
}
|
|
|
|
return path_reconstruct(&parents, start, goal, path)
|
|
}
|
|
|
|
manhattan :: proc(a, b: Tile_Coord) -> int {
|
|
return abs(a.x - b.x) + abs(a.y - b.y)
|
|
}
|
|
|
|
pathfind_astar :: proc(tm: ^Tilemap, start, goal: Tile_Coord, path: ^Path) -> bool {
|
|
path_clear(path)
|
|
|
|
if start == goal {
|
|
resize(&path.tiles, 1)
|
|
path.tiles[0] = start
|
|
path.count = 1
|
|
return true
|
|
}
|
|
|
|
if !tile_walkable(tm, goal.x, goal.y) {
|
|
return false
|
|
}
|
|
|
|
w, h := tm.width, tm.height
|
|
|
|
g_score: [MAP_HEIGHT][MAP_WIDTH]int
|
|
f_score: [MAP_HEIGHT][MAP_WIDTH]int
|
|
parents: [MAP_HEIGHT][MAP_WIDTH]Tile_Coord
|
|
open: [MAP_HEIGHT][MAP_WIDTH]bool
|
|
closed: [MAP_HEIGHT][MAP_WIDTH]bool
|
|
|
|
for y in 0 ..< h {
|
|
for x in 0 ..< w {
|
|
g_score[y][x] = max_path_int
|
|
f_score[y][x] = max_path_int
|
|
parents[y][x] = {-1, -1}
|
|
}
|
|
}
|
|
|
|
g_score[start.y][start.x] = 0
|
|
f_score[start.y][start.x] = manhattan(start, goal)
|
|
parents[start.y][start.x] = start
|
|
open[start.y][start.x] = true
|
|
|
|
open_list: [MAP_WIDTH * MAP_HEIGHT]Tile_Coord
|
|
open_count := 1
|
|
open_list[0] = start
|
|
|
|
found := false
|
|
|
|
for open_count > 0 {
|
|
best_i := 0
|
|
best_f := max_path_int
|
|
for i in 0 ..< open_count {
|
|
c := open_list[i]
|
|
if f_score[c.y][c.x] < best_f {
|
|
best_f = f_score[c.y][c.x]
|
|
best_i = i
|
|
}
|
|
}
|
|
|
|
current := open_list[best_i]
|
|
open_list[best_i] = open_list[open_count - 1]
|
|
open_count -= 1
|
|
open[current.y][current.x] = false
|
|
closed[current.y][current.x] = true
|
|
|
|
if current == goal {
|
|
found = true
|
|
break
|
|
}
|
|
|
|
for offset in NEIGHBOR_OFFSET {
|
|
nx := current.x + offset.x
|
|
ny := current.y + offset.y
|
|
if !tile_in_bounds(tm, nx, ny) || closed[ny][nx] {
|
|
continue
|
|
}
|
|
|
|
step_cost := tile_cost(tm, nx, ny)
|
|
if step_cost >= max_path_int {
|
|
continue
|
|
}
|
|
|
|
tentative := g_score[current.y][current.x] + step_cost
|
|
if tentative < g_score[ny][nx] {
|
|
parents[ny][nx] = current
|
|
g_score[ny][nx] = tentative
|
|
f_score[ny][nx] = tentative + manhattan({nx, ny}, goal)
|
|
|
|
if !open[ny][nx] {
|
|
open[ny][nx] = true
|
|
open_list[open_count] = {nx, ny}
|
|
open_count += 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
return false
|
|
}
|
|
|
|
return path_reconstruct(&parents, start, goal, path)
|
|
}
|
|
|
|
path_reconstruct :: proc(
|
|
parents: ^[MAP_HEIGHT][MAP_WIDTH]Tile_Coord,
|
|
start, goal: Tile_Coord,
|
|
path: ^Path,
|
|
) -> bool {
|
|
rev: [MAX_PATH]Tile_Coord
|
|
rev_count := 0
|
|
cur := goal
|
|
|
|
for {
|
|
if rev_count >= MAX_PATH {
|
|
return false
|
|
}
|
|
|
|
rev[rev_count] = cur
|
|
rev_count += 1
|
|
|
|
if cur == start {
|
|
break
|
|
}
|
|
|
|
cur = parents[cur.y][cur.x]
|
|
}
|
|
|
|
resize(&path.tiles, rev_count)
|
|
path.count = rev_count
|
|
|
|
for i in 0 ..< rev_count {
|
|
path.tiles[i] = rev[rev_count - 1 - i]
|
|
}
|
|
|
|
return true
|
|
}
|
|
|