1
0

ensure bundles don't contains cycles

This commit is contained in:
Rokas Puzonas 2022-09-03 18:16:37 +00:00
parent 7aa0ab57ac
commit ff661daa7d
2 changed files with 84 additions and 22 deletions

View File

@ -1,5 +1,4 @@
local ui = require("ui")
local term_stack = require("term-stack")
require("dbg")("logs.txt")

View File

@ -19,6 +19,10 @@ local function copy_table(t)
return t_copy
end
local function is_integer(num)
return num % 1 == 0
end
local function list_items(inventories)
local item_registry = {}
for _, name in ipairs(inventories) do
@ -96,6 +100,39 @@ local function is_bundle_name(name)
return name:find("#") == 1
end
local function do_bundles_contain_cycle(bundles)
local visited = {}
local stack = {}
local function is_cyclic_util(bundle_name)
if not visited[bundle_name] then
visited[bundle_name] = true
stack[bundle_name] = true
for dep_name in pairs(bundles[bundle_name]) do
if is_bundle_name(dep_name) then
dep_name = dep_name:sub(2)
if not visited[dep_name] and is_cyclic_util(dep_name) then
return true
elseif stack[dep_name] then
return true
end
end
end
end
stack[bundle_name] = false
return false
end
for bundle_name in pairs(bundles) do
if not visited[bundle_name] and is_cyclic_util(bundle_name) then
return true
end
end
return false
end
-- TODO: improve this function
local function resolve_bundle_items(bundles, bundle_name, seen_bundles)
seen_bundles = seen_bundles or {}
@ -218,6 +255,8 @@ function main_view:prepare(inventories, bundles, result_inventory)
end
function main_view:list_filtered_names(items, bundles, name_filter)
name_filter = name_filter:lower()
@ -293,17 +332,21 @@ function main_view:process_movement_keys(store, area)
local bundle = self.bundle_details[bundle_name]
if self.right_pressed and lstore.bundles[bundle_name] > 0 then
for item, count in pairs(bundle) do
lstore.items[item] = lstore.items[item] - count
end
local transferred = math.min(lstore.bundles[bundle_name], 1)
rstore.bundles[bundle_name] = (rstore.bundles[bundle_name] or 0) + transferred
elseif self.left_pressed and rstore.bundles[bundle_name] > 0 then
for item, count in pairs(bundle) do
lstore.items[item] = lstore.items[item] + count
lstore.items[item] = lstore.items[item] - math.floor(count * transferred)
end
rstore.bundles[bundle_name] = (rstore.bundles[bundle_name] or 0) + transferred
elseif self.left_pressed and rstore.bundles[bundle_name] > 0 then
local transferred
if is_integer(rstore.bundles[bundle_name]) then
transferred = math.min(rstore.bundles[bundle_name], 1)
else
transferred = rstore.bundles[bundle_name] % 1
end
for item, count in pairs(bundle) do
lstore.items[item] = lstore.items[item] + math.floor(count * transferred)
end
local transferred = math.min(rstore.bundles[bundle_name], 1)
rstore.bundles[bundle_name] = rstore.bundles[bundle_name] - transferred
end
else
@ -337,9 +380,6 @@ function main_view:display_list(store, area, selected_idx)
local function get_number_width(num)
return math.floor(math.log(num, 10) + 1)
end
local function is_integer(num)
return num % 1 == 0
end
local count_collumn_width
do -- Figure out how wide does the count column need to be
@ -427,9 +467,6 @@ function main_view:display_list_with_search(store, area, active)
term_stack.pop_cursor()
end
function main_view:move_item(item_name, amount, destination)
local item_collection = self.item_registry[item_name]
@ -488,8 +525,29 @@ function main_view:deposit_items()
self:refresh_items()
end
function main_view:save_bundle(name, items)
self.bundles[name] = items
function main_view:save_bundle(name, items, bundles)
local bundle = {}
for item, count in pairs(items) do
if count > 0 then
bundle[item] = count
end
end
for bundle_name, count in pairs(bundles) do
if count > 0 then
bundle[bundle_name] = count
end
end
self.bundles[name] = bundle
if do_bundles_contain_cycle(self.bundles) then
self.bundles[name] = nil
return false
end
populate_bundle_details(self.bundle_details, self.bundles)
self.left_store.bundles = derive_available_bundles(self.left_store.items, self.bundle_details)
self:refresh_filtered_names(self.left_store, self.left_area)
return true
end
function main_view:display_bundle_popup(area)
@ -518,7 +576,10 @@ function main_view:display_bundle_popup(area)
self.bundle_popup = false
elseif self.submit_pressed and #self.bundle_name > 0 then
self.bundle_popup = false
self:save_bundle(self.bundle_name, self.right_store.items)
local success = self:save_bundle(self.bundle_name, self.right_store.items, self.right_store.bundles)
if success then
self.bundle_name = ""
end
end
end
@ -534,8 +595,9 @@ function main_view:request_items()
for bundle_name, bundle_count in pairs(rstore.bundles) do
bundle_count = math.max(bundle_count, 1)
for item, count in pairs(self.bundle_details[bundle_name]) do
local transferred = self:move_item(item, count*bundle_count, self.result_inventory)
lstore.items[item] = lstore.items[item] + (count*bundle_count - transferred)
local amount = math.floor(count*bundle_count)
local transferred = self:move_item(item, amount, self.result_inventory)
lstore.items[item] = lstore.items[item] + (amount - transferred)
end
end
@ -548,9 +610,10 @@ function main_view:request_items()
local selected_option = lstore.filtered_names[lstore.selected_idx]
if is_bundle_name(selected_option) then
local bundle_name = selected_option
local bundle_count = math.min(lstore.bundles[bundle_name], 1)
for item, count in pairs(self.bundle_details[bundle_name]) do
local transferred = self:move_item(item, count, self.result_inventory)
lstore.items[item] = lstore.items[item] - transferred
lstore.items[item] = lstore.items[item] - math.floor(transferred * bundle_count)
end
else
local item = selected_option
@ -626,7 +689,7 @@ function main_view:on_key(key)
end
if not self.bundle_popup then
if key == keys.n and self:has_selected_items() then
if self.ctrl_down and key == keys.n and self:has_selected_items() then
self.bundle_popup = true
elseif key == keys.tab then