From bc161ce8b040edf0d326aaca34ddf8676ffd64d3 Mon Sep 17 00:00:00 2001 From: Nicholas Hayashi Date: Sun, 3 Jan 2021 04:23:22 -0500 Subject: [PATCH] astar --- src/grid.lua | 32 +++++++++++++++++- src/hexyz.lua | 2 +- src/main.lua | 6 ---- src/mob.lua | 94 +++++++++++++++++++++++++++++++-------------------- src/table.lua | 14 ++++++++ src/util.lua | 18 +++++----- 6 files changed, 112 insertions(+), 54 deletions(-) create mode 100644 src/table.lua diff --git a/src/grid.lua b/src/grid.lua index 6c09fc6..f3e368a 100644 --- a/src/grid.lua +++ b/src/grid.lua @@ -7,7 +7,7 @@ HEX_GRID_WIDTH = 65 HEX_GRID_HEIGHT = 33 HEX_GRID_DIMENSIONS = vec2(HEX_GRID_WIDTH, HEX_GRID_HEIGHT) --- @NOTE no idea why the y coordinate doesn't need to be transformed here +-- this is in hex coordinates HEX_GRID_CENTER = vec2(math.floor(HEX_GRID_DIMENSIONS.x/2), 0) -- index is hex coordinates [x][y] @@ -50,6 +50,32 @@ 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) @@ -92,3 +118,7 @@ function random_map(seed, do_seed_rng) ^ world:tag"world" end +function grid_neighbours(hex) + return table.filter(hex_neighbours(hex), function(_hex) return get_tile(_hex.x, _hex.y) end) +end + diff --git a/src/hexyz.lua b/src/hexyz.lua index f13ff96..ce946fb 100644 --- a/src/hexyz.lua +++ b/src/hexyz.lua @@ -97,7 +97,7 @@ function hex_direction(direction) -- Return Hexagon Adjacent to |hex| in Integer Index |direction| function hex_neighbour(hex, direction) - return hex + HEX_DIRECTIONS[(direction % 6) % 6 + 1] end + return hex + HEX_DIRECTIONS[(direction % 6) % 6 + 1] end -- Collect All 6 Neighbours in a Table function hex_neighbours(hex) diff --git a/src/main.lua b/src/main.lua index 93cd056..c24deb0 100644 --- a/src/main.lua +++ b/src/main.lua @@ -37,12 +37,9 @@ function game_action(scene) local _off = hex_to_evenq(hex) local off = _off{ y = -_off.y } - vec2(math.floor(HEX_GRID_WIDTH/2) , math.floor(HEX_GRID_HEIGHT/2)) - local off2 = evenq_to_hex(_off) local tile = get_tile(hex.x, hex.y) if tile and win:mouse_pressed"left" then - log(tile) - --invoke_tile_menu(hex.x, hex.y, tile) end if win:key_pressed"f1" then end @@ -60,13 +57,11 @@ function game_action(scene) 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"rev".text = string.format("%d,%d", off2.x, off2.y) end function game_scene() local score = am.translate(win.left + 10, win.top - 20) ^ am.text("", "left"):tag"score" local coords = am.translate(win.right - 10, win.top - 20) ^ am.text("", "right"):tag"coords" - local coords2 = am.translate(win.right - 10, win.top - 40) ^ am.text("", "right"):tag"rev" local hex_cursor = am.circle(vec2(-6969), HEX_SIZE, COLORS.TRANSPARENT, 6):tag"hex_cursor" local curtain = am.rect(win.left, win.bottom, win.right, win.top, COLORS.TRUEBLACK) @@ -81,7 +76,6 @@ function game_scene() hex_cursor, score, coords, - coords2 } scene:action(game_action) diff --git a/src/mob.lua b/src/mob.lua index c127925..dc65229 100644 --- a/src/mob.lua +++ b/src/mob.lua @@ -3,49 +3,55 @@ MOBS = {} -- check if a |tile| is passable by |mob| function can_pass_through(mob, tile) - return tile and tile.elevation and tile.elevation < 0.5 and tile.elevation > -0.5 + return tile.elevation < 0.5 and tile.elevation > -0.5 end -function get_path(mob, starting_hex, goal_hex) - local moves = {} +function get_movement_cost(mob, start_hex, goal_hex) + return 1 +end - local visited = {} - visited[starting_hex.x] = {} - visited[starting_hex.x][starting_hex.y] = true - repeat - local neighbours = hex_neighbours(pixel_to_hex(mob.position)) - local candidates = {} - - -- get list of candidates: hex positions to consider moving to. - for _,neighbour in pairs(neighbours) do - if can_pass_through(mob, get_tile(neighbour.x, neighbour.y)) then - if not (visited[neighbour.h] and visited[neighbour.x][neighbour.y]) then - table.insert(candidates, neighbour) - else - --table.insert(candidates, neighbour) - end - end - end - -- choose where to move - local move = candidates[1] - for _,hex in pairs(candidates) do - if math.distance(hex, goal_hex) < math.distance(move, goal_hex) then - move = hex - end - 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 move then - table.insert(moves, move) - visited[move.x] = {} - visited[move.x][move.y] = true - end - --if move == goal then log('made it!') return end - until move == goal_hex + if current.position == goal_hex then log('found it!') break end - return moves + 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) @@ -81,12 +87,26 @@ function make_mob() local mob = {} local spawn_hex = get_spawn_hex(mob) - log(spawn_hex) local spawn_position = hex_to_pixel(spawn_hex) + WORLDSPACE_COORDINATE_OFFSET mob.position = spawn_position - --mob.path = get_path(mob, spawn_hex, HEX_GRID_CENTER) + 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)) + end + end)) + win.scene:append(mob.sprite) return mob diff --git a/src/table.lua b/src/table.lua new file mode 100644 index 0000000..b36c3eb --- /dev/null +++ b/src/table.lua @@ -0,0 +1,14 @@ + +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 b36c3eb..638fbff 100644 --- a/src/util.lua +++ b/src/util.lua @@ -1,14 +1,14 @@ -function table.shift(t, count) - local e = t[1] - t[1] = nil +function twoD_get(t, x, y) + return t[x] and t[x][y] +end - for i,e in pairs(t) do - if e then - t[i - 1] = e - end +function twoD_set(t, x, y, v) + if t[x] then + t[x][y] = v + else + t[x] = {} + t[x][y] = v end - - return e end