From e247b377adcc730bc70f96fe15630e7013503c5a Mon Sep 17 00:00:00 2001 From: Nicholas Hayashi Date: Sun, 3 Jan 2021 23:12:42 -0500 Subject: [PATCH] pathfind better --- src/grid.lua | 60 ++++++------------------- src/hexyz.lua | 119 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/main.lua | 8 +++- src/mob.lua | 100 ++++++++++++++++-------------------------- src/table.lua | 14 ------ src/util.lua | 12 +---- 6 files changed, 174 insertions(+), 139 deletions(-) delete mode 100644 src/table.lua diff --git a/src/grid.lua b/src/grid.lua index f3e368a..41cfde6 100644 --- a/src/grid.lua +++ b/src/grid.lua @@ -3,8 +3,8 @@ require "colors" require "gui" HEX_SIZE = 20 -HEX_GRID_WIDTH = 65 -HEX_GRID_HEIGHT = 33 +HEX_GRID_WIDTH = 65 -- 65 +HEX_GRID_HEIGHT = 33 -- 33 HEX_GRID_DIMENSIONS = vec2(HEX_GRID_WIDTH, HEX_GRID_HEIGHT) -- this is in hex coordinates @@ -50,55 +50,27 @@ function color_at(elevation) end end -function generate_flow_field(start) - local frontier = { start } - local came_from = {} - came_from[start.x] = {} - came_from[start.x][start.y] = true - - while not (#frontier == 0) do - local current = table.pop(frontier) - log(current) - - for _,neighbour in pairs(hex_neighbours(current)) do - if get_tile(neighbour.x, neighbour.y) then - if not (came_from[neighbour.x] and came_from[neighbour.x][neighbour.y]) then - log("hi") - if true then return came_from end - table.insert(frontier, neighbour) - came_from[neighbour.x] = {} - came_from[neighbour.x][neighbour.y] = current - end - end - end - end - - return came_from -end - function random_map(seed, do_seed_rng) - local elevation_map = rectangular_map(HEX_GRID_DIMENSIONS.x, HEX_GRID_DIMENSIONS.y, seed) + local map = rectangular_map(HEX_GRID_DIMENSIONS.x, HEX_GRID_DIMENSIONS.y, 105) + --log(map.seed) if do_seed_rng then math.randomseed(elevation_map.seed) end - HEX_MAP = {} local world = am.group():tag"world" - for i,_ in pairs(elevation_map) do - HEX_MAP[i] = {} - for j,elevation in pairs(elevation_map[i]) do - + for i,_ in pairs(map) do + for j,noise in pairs(map[i]) do local off = hex_to_evenq(vec2(i, j)) local mask = vec4(0, 0, 0, math.max(((off.x - HEX_GRID_DIMENSIONS.x/2) / HEX_GRID_DIMENSIONS.x) ^ 2 , ((-off.y - HEX_GRID_DIMENSIONS.y/2) / HEX_GRID_DIMENSIONS.y) ^ 2)) - local color = color_at(elevation) - mask + local color = color_at(noise) - mask local node = am.circle(hex_to_pixel(vec2(i, j)), HEX_SIZE, color, 6) - HEX_MAP[i][j] = { - elevation = elevation, + map.set(i, j, { + elevation = noise, sprite = node, tile = {} - } + }) world:append(node) end @@ -109,16 +81,12 @@ function random_map(seed, do_seed_rng) -- @NOTE no idea why the y-coord doesn't need to be transformed local home = spiral_map(HEX_GRID_CENTER, 3) for _,hex in pairs(home) do - HEX_MAP[hex.x][hex.y].elevation = 0 - HEX_MAP[hex.x][hex.y].sprite.color = color_at(0) + map[hex.x][hex.y].elevation = 0 + map[hex.x][hex.y].sprite.color = color_at(0) world:append(am.circle(hex_to_pixel(vec2(hex.x, hex.y)), HEX_SIZE/2, COLORS.MAGENTA, 4)) end - return am.translate(WORLDSPACE_COORDINATE_OFFSET) - ^ world:tag"world" -end - -function grid_neighbours(hex) - return table.filter(hex_neighbours(hex), function(_hex) return get_tile(_hex.x, _hex.y) end) + return map, am.translate(WORLDSPACE_COORDINATE_OFFSET) + ^ world:tag"world" end diff --git a/src/hexyz.lua b/src/hexyz.lua index ce946fb..be46bc2 100644 --- a/src/hexyz.lua +++ b/src/hexyz.lua @@ -206,6 +206,34 @@ function spiral_map(center, radius) return setmetatable(map, {__index={center=center, radius=radius}}) end +function map_get(t, x, y) + return t[x] and t[x][y] +end + +function map_set(t, x, y, v) + if t[x] then + t[x][y] = v + else + t[x] = {} + t[x][y] = v + end + + return t +end + +function map_partial_set(t, x, y, k, v) + local entry = map_get(t, x, y) + + if not entry then + map_set(t, x, y, { k = v }) + + else + entry.k = v + end + + return t +end + -- Returns Unordered Parallelogram-Shaped Map of |width| and |height| with Simplex Noise function parallelogram_map(width, height, seed) local seed = seed or math.random(width * height) @@ -229,7 +257,14 @@ function parallelogram_map(width, height, seed) map[i][j] = noise end end - return setmetatable(map, {__index={width=width, height=height, seed=seed}}) + return setmetatable(map, { __index = { + width = width, + height = height, + seed = seed, + get = function(x, y) return map_get(map, x, y) end, + set = function(x, y, v) return map_set(map, x, y, v) end, + partial = function(x, y, k, v) return map_partial_set(map, x, y, k, v) end + }}) end -- Returns Unordered Triangular (Equilateral) Map of |size| with Simplex Noise @@ -255,7 +290,13 @@ function triangular_map(size, seed) map[i][j] = noise end end - return setmetatable(map, {__index={size=size, seed=seed}}) + return setmetatable(map, { __index = { + size = size, + seed = seed, + get = function(x, y) return map_get(map, x, y) end, + set = function(x, y, v) return map_set(map, x, y, v) end, + partial = function(x, y, k, v) return map_partial_set(map, x, y, k, v) end + }}) end -- Returns Unordered Hexagonal Map of |radius| with Simplex Noise @@ -286,7 +327,13 @@ function hexagonal_map(radius, seed) map[i][j] = noise end end - return setmetatable(map, {__index={radius=radius, seed=seed}}) + return setmetatable(map, { __index = { + radius = radius, + seed = seed, + get = function(x, y) return map_get(map, x, y) end, + set = function(x, y, v) return map_set(map, x, y, v) end, + partial = function(x, y, k, v) return map_partial_set(map, x, y, k, v) end + }}) end -- Returns Unordered Rectangular Map of |width| and |height| with Simplex Noise @@ -314,6 +361,70 @@ function rectangular_map(width, height, seed) map[i][j] = noise end end - return setmetatable(map, {__index={width=width, height=height, seed=seed}}) + return setmetatable(map, { __index = { + width = width, + height = height, + seed = seed, + get = function(x, y) return map_get(map, x, y) end, + set = function(x, y, v) return map_set(map, x, y, v) end, + partial = function(x, y, k, v) return map_partial_set(map, x, y, k, v) end + }}) +end + +--============================================================================ +-- PATHFINDING + +-- generic A* pathfinding +-- @NOTE is it better to move the functions to be members of the map? +-- +-- returns a map that has map[hex.x][hex.y] = 'the vec2 which is the hex you came from when trying to get to |goal|' +function Astar(map, start, goal, neighbour_f, heuristic_f, cost_f) + local neighbour_f = neighbour_f or hex_neighbours + local heuristic_f = heuristic_f or math.distance + local cost_f = cost_f or function() return 1 end + + local came_from = {} + came_from[start.x] = {} + came_from[start.x][start.y] = false + + local frontier = {} + frontier[1] = { hex = start, priority = 0 } + + local cost_so_far = {} + cost_so_far[start.x] = {} + cost_so_far[start.x][start.y] = 0 + + local made_it = false + while not (#frontier == 0) do + local current = table.remove(frontier, 1) + + if current.hex == goal then + made_it = true + break + end + + for _,next_ in pairs(neighbour_f(current.hex)) do + local entry = map.get(next_.x, next_.y) + + if entry then + local new_cost = map_get(cost_so_far, current.hex.x, current.hex.y) + + cost_f(entry) + + local next_cost = map_get(cost_so_far, next_.x, next_.y) + if not next_cost or new_cost < next_cost then + map_set(cost_so_far, next_.x, next_.y, new_cost) + local priority = new_cost + heuristic_f(goal, next_) + table.insert(frontier, { hex = next_, priority = priority }) + map_set(came_from, next_.x, next_.y, current) + end + end + end + end + + if not made_it then + log(" we didn't make it!") + end + + return came_from end diff --git a/src/main.lua b/src/main.lua index c24deb0..8468b63 100644 --- a/src/main.lua +++ b/src/main.lua @@ -56,7 +56,7 @@ function game_action(scene) -- draw stuff win.scene"hex_cursor".center = hex_to_pixel(hex) + WORLDSPACE_COORDINATE_OFFSET win.scene"score".text = string.format("SCORE: %.2f", time) - win.scene"coords".text = string.format("%d,%d", off.x, off.y) + win.scene"coords".text = string.format("%d,%d", hex.x, hex.y) end function game_scene() @@ -70,8 +70,12 @@ function game_scene() win.scene:remove(curtain) end)) + local world + + HEX_MAP, world = random_map() + local scene = am.group{ - random_map(), + world, curtain, hex_cursor, score, diff --git a/src/mob.lua b/src/mob.lua index dc65229..4eab791 100644 --- a/src/mob.lua +++ b/src/mob.lua @@ -1,59 +1,16 @@ MOBS = {} --- check if a |tile| is passable by |mob| -function can_pass_through(mob, tile) - return tile.elevation < 0.5 and tile.elevation > -0.5 +-- check if a the tile at |hex| is passable by |mob| +function can_pass_through(mob, hex) + local tile = HEX_MAP.get(hex.x, hex.y) + return tile and tile.elevation < 0.5 and tile.elevation > -0.5 end function get_movement_cost(mob, start_hex, goal_hex) return 1 end - - -function Astar(mob, start_hex, goal_hex) - local function heuristic(start_hex, goal_hex) - return math.distance(start_hex, goal_hex) - end - - local came_from = {} - came_from[start_hex.x] = {} - came_from[start_hex.x][start_hex.y] = false - - local frontier = {} - frontier[1] = { position = start_hex, priority = 0 } - - local cost_so_far = {} - cost_so_far[start_hex.x] = {} - cost_so_far[start_hex.x][start_hex.y] = 0 - - while not (#frontier == 0) do - local current = table.remove(frontier, 1) - - - if current.position == goal_hex then log('found it!') break end - - for _,_next in pairs(hex_neighbours(current.position)) do - local tile = get_tile(_next.x, _next.y) - - if tile then - local new_cost = cost_so_far[current.position.x][current.position.y] - + get_movement_cost(mob, current.position, _next) - - if not twoD_get(cost_so_far, _next.x, _next.y) or new_cost < twoD_get(cost_so_far, _next.x, _next.y) then - twoD_set(cost_so_far, _next.x, _next.y, new_cost) - local priority = new_cost + heuristic(goal_hex, _next) - table.insert(frontier, { position = _next, priority = priority }) - twoD_set(came_from, _next.x, _next.y, current) - end - end - end - - end - return came_from -end - function get_spawn_hex(mob) local spawn_hex repeat @@ -78,11 +35,12 @@ function get_spawn_hex(mob) spawn_hex = evenq_to_hex(vec2(x, -y)) local tile = HEX_MAP[spawn_hex.x][spawn_hex.y] - until can_pass_through(mob, tile) + until can_pass_through(mob, spawn_hex) return spawn_hex end +-- @NOTE spawn hex function make_mob() local mob = {} @@ -90,23 +48,28 @@ function make_mob() local spawn_position = hex_to_pixel(spawn_hex) + WORLDSPACE_COORDINATE_OFFSET mob.position = spawn_position - mob.path = Astar(mob, spawn_hex, HEX_GRID_CENTER) - mob.sprite = am.circle(spawn_position, 18, COLORS.WHITE, 4) - - win.scene:action(coroutine.create(function() - local goal = spawn_hex - local current = mob.path[HEX_GRID_CENTER.x][HEX_GRID_CENTER.y].position - log(current) - - while current ~= goal do - if current then - win.scene:append(am.circle(hex_to_pixel(current) + WORLDSPACE_COORDINATE_OFFSET, 4, COLORS.MAGENTA)) - current = mob.path[current.x][current.y].position - end - am.wait(am.delay(0.01)) + mob.hex = spawn_hex + mob.path = Astar(HEX_MAP, HEX_GRID_CENTER, spawn_hex, + + -- neighbour function + function(hex) + return table.filter(hex_neighbours(hex), function(_hex) + return can_pass_through(mob, _hex) + end) + end, + + -- heuristic function + function(source, target) + return math.distance(source, target) + end, + + -- cost function + function(map_entry) + return math.abs(map_entry.elevation) end - end)) + ) + mob.sprite = am.circle(spawn_position, 18, COLORS.WHITE, 4) win.scene:append(mob.sprite) return mob @@ -121,8 +84,19 @@ function do_mob_spawning() end function do_mob_updates() + --if win:key_pressed"a" then for _,mob in pairs(MOBS) do + mob.hex = pixel_to_hex(mob.position - WORLDSPACE_COORDINATE_OFFSET) + + local frame_target = map_get(mob.path, mob.hex.x, mob.hex.y) + if frame_target then + mob.position = lerp(mob.position, hex_to_pixel(frame_target.hex) + WORLDSPACE_COORDINATE_OFFSET, 0.9) + mob.sprite.center = mob.position + else + + end end + --end end diff --git a/src/table.lua b/src/table.lua deleted file mode 100644 index b36c3eb..0000000 --- a/src/table.lua +++ /dev/null @@ -1,14 +0,0 @@ - -function table.shift(t, count) - local e = t[1] - t[1] = nil - - for i,e in pairs(t) do - if e then - t[i - 1] = e - end - end - - return e -end - diff --git a/src/util.lua b/src/util.lua index 638fbff..972a16a 100644 --- a/src/util.lua +++ b/src/util.lua @@ -1,14 +1,6 @@ -function twoD_get(t, x, y) - return t[x] and t[x][y] +function lerp(v1, v2, t) + return v1 * t + v2 * (1 - t) end -function twoD_set(t, x, y, v) - if t[x] then - t[x][y] = v - else - t[x] = {} - t[x][y] = v - end -end