Skip to content

Commit

Permalink
Merge pull request #39 from bengardner/remember_failure
Browse files Browse the repository at this point in the history
Remember failure
  • Loading branch information
sfence authored Sep 1, 2023
2 parents 05115ed + 474ae0a commit 658b78f
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 34 deletions.
40 changes: 40 additions & 0 deletions working_villagers/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,46 @@ working_villages.registered_jobs = {}

working_villages.registered_eggs = {}

-- records failed node place attempts to prevent repeating mistakes
-- key=minetest.pos_to_string(pos) val=(os.clock()+180)
local failed_pos_data = {}
local failed_pos_time = 0

-- remove old positions
local function failed_pos_cleanup()
-- build a list of all items to discard
local discard_tab = {}
local now = os.clock()
for key, val in pairs(failed_pos_data) do
if now >= val then
discard_tab[key] = true
end
end
-- discard the old entries
for key, _ in pairs(discard_tab) do
failed_pos_data[key] = nil
end
end

-- add a failed place position
function working_villages.failed_pos_record(pos)
local key = minetest.hash_node_position(pos)
failed_pos_data[key] = os.clock() + 180 -- mark for 3 minutes

-- cleanup if more than 1 minute has passed since the last cleanup
if os.clock() > failed_pos_time then
failed_pos_time = os.clock() + 60
failed_pos_cleanup()
end
end

-- check if a position is marked as failed and hasn't expired
function working_villages.failed_pos_test(pos)
local key = minetest.hash_node_position(pos)
local exp = failed_pos_data[key]
return exp ~= nil and exp >= os.clock()
end

-- working_villages.is_job reports whether a item is a job item by the name.
function working_villages.is_job(item_name)
if working_villages.registered_jobs[item_name] then
Expand Down
7 changes: 7 additions & 0 deletions working_villagers/async_actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ function working_villages.villager:collect_nearest_item_by_condition(cond, searc
end
end

-- delay the async action by @step_count steps
function working_villages.villager:delay(step_count)
for _=0,step_count do
coroutine.yield()
end
end

local drop_range = {x = 2, y = 10, z = 2}

function working_villages.villager:dig(pos,collect_drops)
Expand Down
66 changes: 37 additions & 29 deletions working_villagers/jobs/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ end

local find_adjacent_clear = func.find_adjacent_clear

-- search in an expanding box around pos in the XZ plane
-- first hit would be closest
local function search_surrounding(pos, pred, searching_range)
pos = vector.round(pos)
local max_xz = math.max(searching_range.x, searching_range.z)
Expand All @@ -87,47 +89,53 @@ local function search_surrounding(pos, pred, searching_range)
mod_y = searching_range.h
end

for j = mod_y - searching_range.y, searching_range.y do
local p = vector.add({x = 0, y = j, z = 0}, pos)
if pred(p) and find_adjacent_clear(p)~=false then
return p
local ret = {}

local function check_column(dx, dz)
if ret.pos ~= nil then return end
for j = mod_y - searching_range.y, searching_range.y do
local p = vector.add({x = dx, y = j, z = dz}, pos)
if pred(p) and find_adjacent_clear(p)~=false then
ret.pos = p
return
end
end
end

for i = 0, max_xz do
for j = mod_y - searching_range.y, searching_range.y do
for k = -i, i do
if searching_range.x >= k and searching_range.z >= i then
local p = vector.add({x = k, y = j, z = i}, pos)
if pred(p) and find_adjacent_clear(p)~=false then
return p
end

p = vector.add({x = k, y = j, z = -i}, pos)
if pred(p) and find_adjacent_clear(p)~=false then
return p
end
for k = 0, i do
-- hit the 8 points of symmetry, bound check and skip duplicates
if k <= searching_range.x and i <= searching_range.z then
check_column(k, i)
if i > 0 then
check_column(k, -i)
end

if searching_range.z >= i and searching_range.z >= k then
if i ~= k then
local p = vector.add({x = i, y = j, z = k}, pos)
if pred(p) and find_adjacent_clear(p)~=false then
return p
end
if k > 0 then
check_column(-k, i)
if k ~= i then
check_column(-k, -i)
end
end
end

if -i ~= k then
local p = vector.add({x = -i, y = j, z = k}, pos)
if pred(p) and find_adjacent_clear(p)~=false then
return p
end
if i <= searching_range.x and k <= searching_range.z then
if i > 0 then
check_column(-i, k)
end
if k ~= i then
check_column(i, k)
if k > 0 then
check_column(-i, -k)
check_column(i, -k)
end
end
end
if ret.pos ~= nil then
break
end
end
end
return nil
return ret.pos
end

func.search_surrounding = search_surrounding
Expand Down
41 changes: 36 additions & 5 deletions working_villagers/jobs/woodcutter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ local func = working_villages.require("jobs/util")
local function find_tree(p)
local adj_node = minetest.get_node(p)
if minetest.get_item_group(adj_node.name, "tree") > 0 then
-- FIXME: need a player name if villagers can own a protected area
if minetest.is_protected(p, "") then return false end
if working_villages.failed_pos_test(p) then return false end
return true
end
return false
Expand All @@ -22,13 +25,24 @@ local function is_sapling(n)
end

local function is_sapling_spot(pos)
local node = minetest.get_node(pos)
if node.name ~= "air" then return false end
-- FIXME: need a player name if villagers can own a protected area
if minetest.is_protected(pos, "") then return false end
if working_villages.failed_pos_test(pos) then return false end
local lpos = vector.add(pos, {x = 0, y = -1, z = 0})
local lnode = minetest.get_node(lpos)
if minetest.get_item_group(lnode.name, "soil") == 0 then return false end
local light_level = minetest.get_node_light(pos)
if light_level <= 12 then return false end
-- A sapling needs room to grow. Require a volume of air around the spot.
for x = -1,1 do
for z = -1,1 do
for y = 0,2 do
lpos = vector.add(pos, {x=x, y=y, z=z})
lnode = minetest.get_node(lpos)
if lnode.name ~= "air" then return false end
end
end
end
return true
end

Expand Down Expand Up @@ -73,7 +87,12 @@ When I find a sappling I'll plant it on some soil near a bright place so a new t
end
self:set_displayed_action("planting a tree")
self:go_to(destination)
self:place(is_sapling, target)
local success, ret = self:place(is_sapling, target)
if not success then
working_villages.failed_pos_record(target)
self:set_displayed_action("confused as to why planting failed")
self:delay(100)
end
end
end
local target = func.search_surrounding(self.object:get_pos(), find_tree, searching_range)
Expand All @@ -85,8 +104,20 @@ When I find a sappling I'll plant it on some soil near a bright place so a new t
destination = target
end
self:set_displayed_action("cutting a tree")
self:go_to(destination)
self:dig(target,true)
-- We may not be able to reach the log
local success, ret = self:go_to(destination)
if not success then
working_villages.failed_pos_record(target)
self:set_displayed_action("looking at the unreachable log")
self:delay(100)
else
success, ret = self:dig(target,true)
if not success then
working_villages.failed_pos_record(target)
self:set_displayed_action("confused as to why cutting failed")
self:delay(100)
end
end
end
self:set_displayed_action("looking for work")
elseif self:timer_exceeded("woodcutter:change_dir",50) then
Expand Down

0 comments on commit 658b78f

Please sign in to comment.