Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions modfiles/backend/calculation/matrix_engine.lua
Original file line number Diff line number Diff line change
Expand Up @@ -226,22 +226,33 @@ function matrix_engine.get_linear_dependence_data(factory_data, matrix_metadata)
local num_cols = matrix_metadata.num_cols

local linearly_dependent_recipes = {}
local linearly_dependent_items = {}
local linearly_dependent_free_items = {}
local allowed_free_items = {}

local linearly_dependent_cols = matrix_engine.run_matrix_solver(factory_data, true)
if next(linearly_dependent_cols) ~= nil then
local free_items = matrix_metadata.free_items
local free_keys = {}
for _, free_item in ipairs(free_items) do
local key = matrix_engine.get_item_key(free_item.type, free_item.name)
free_keys[key] = free_item
end

for col_name, _ in pairs(linearly_dependent_cols) do
local col_split_str = util.split_string(col_name, "_")
if col_split_str[1] == "recipe" then
local recipe_key = col_split_str[2]
linearly_dependent_recipes[recipe_key] = true
else -- "item"
local item_key = col_split_str[2].."_"..col_split_str[3]
linearly_dependent_items[item_key] = true
if free_keys[item_key] then
linearly_dependent_free_items[item_key] = true
end
end
end
end
-- check which eliminated items could be made free while still retaining linear independence
if #linearly_dependent_cols == 0 and num_cols < num_rows then
if next(linearly_dependent_cols) == nil and num_cols < num_rows then
local matrix_data = matrix_engine.get_matrix_data(factory_data)
local items = matrix_data.rows
local col_to_item = {}
Expand Down Expand Up @@ -271,8 +282,8 @@ function matrix_engine.get_linear_dependence_data(factory_data, matrix_metadata)
local result = {
linearly_dependent_recipes = matrix_engine.get_recipe_protos(
matrix_engine.set_to_ordered_list(linearly_dependent_recipes)),
linearly_dependent_items = matrix_engine.get_item_protos(
matrix_engine.set_to_ordered_list(linearly_dependent_items)),
linearly_dependent_free_items = matrix_engine.get_item_protos(
matrix_engine.set_to_ordered_list(linearly_dependent_free_items)),
allowed_free_items = matrix_engine.get_item_protos(
matrix_engine.set_to_ordered_list(allowed_free_items))
}
Expand Down
24 changes: 20 additions & 4 deletions modfiles/backend/calculation/solver.lua
Original file line number Diff line number Diff line change
Expand Up @@ -282,14 +282,30 @@ function solver.update(player, factory, blank)
elseif factory.matrix_free_items ~= nil then -- meaning the matrix solver is active
local matrix_metadata = matrix_engine.get_matrix_solver_metadata(factory_data)

if matrix_metadata.num_cols > matrix_metadata.num_rows and #factory.matrix_free_items > 0 then
factory.matrix_free_items = {}
if matrix_metadata.num_rows ~= 0 then -- don't run calculations if the factory has no lines
local linear_dependence_data = matrix_engine.get_linear_dependence_data(factory_data, matrix_metadata)

-- in the case of linearly dependent free items, we remove it automatically if there's only one option.
-- otherwise we present the user with a choice to remove problematic free items in the production box.
local num_ld_free_items = 0
local last_ld_free_item = nil
for _, ld_free_item in pairs(linear_dependence_data.linearly_dependent_free_items) do
num_ld_free_items = num_ld_free_items + 1
last_ld_free_item = ld_free_item
end
if num_ld_free_items == 1 then
for index, item in pairs(factory.matrix_free_items) do
if item.type == last_ld_free_item.type and item.name == last_ld_free_item.name then
table.remove(factory.matrix_free_items, index)
break
end
end
-- redo all these since we've changed the factory
factory_data = solver.generate_factory_data(player, factory)
matrix_metadata = matrix_engine.get_matrix_solver_metadata(factory_data)
linear_dependence_data = matrix_engine.get_linear_dependence_data(factory_data, matrix_metadata)
end

if matrix_metadata.num_rows ~= 0 then -- don't run calculations if the factory has no lines
local linear_dependence_data = matrix_engine.get_linear_dependence_data(factory_data, matrix_metadata)
if matrix_metadata.num_rows == matrix_metadata.num_cols
and #linear_dependence_data.linearly_dependent_recipes == 0 then
matrix_engine.run_matrix_solver(factory_data, false)
Expand Down
2 changes: 2 additions & 0 deletions modfiles/locale/en/config.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ linearly_dependent_recipes=Linearly dependent recipes
linearly_dependent_recipes_tt=The matrix solver detected linearly dependent recipes. Make sure the following items are not produced by more than one recipe.
choose_unrestricted_items=Choose unrestricted items
choose_unrestricted_items_tt=Choose [font=default-bold]__1__[/font] unrestricted __2__ in total, so the matrix solver can determine a unique solution.\n\nThe left side lists the constrained items, while the right side contains the unrestricted ones. Click on any item to move it to the other side.\n\nUnrestricted items may become byproducts or ingredients, depending on the solver’s solution.
remove_unrestricted_items=Remove unrestricted items
remove_unrestricted_items_tt=Some unrestricted items were found to be linearly dependent. These must be removed until there are no more linearly dependent items.
unrestricted_items_balanced=Unrestricted items balanced
unrestricted_items_balanced_tt=The current selection of unrestricted items is balanced.\n\nTo choose different unrestricted items, constrain one of the current ones first.
turn_unrestricted=Switch [font=default-bold]__1__[/font] to be constrained
Expand Down
42 changes: 31 additions & 11 deletions modfiles/ui/main/production_box.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,37 @@ local function refresh_solver_frame(player)
local linear_dependence_data = matrix_engine.get_linear_dependence_data(factory_data, matrix_metadata)
local num_needed_free_items = matrix_metadata.num_rows - matrix_metadata.num_cols + #matrix_metadata.free_items

if next(linear_dependence_data.linearly_dependent_recipes) then
local function build_item_flow(flow, status, items)
for _, proto in pairs(items) do
local tooltip = {"fp.turn_" .. status, proto.localised_name}
local color = (status == "unrestricted") and "green" or "default"
flow.add{type="sprite-button", sprite=proto.sprite, tooltip=tooltip,
tags={mod="fp", on_gui_click="switch_matrix_item", status=status, type=proto.type, name=proto.name},
style="flib_slot_button_" .. color .. "_small", mouse_button_filter={"left"}}
end
end

if next(linear_dependence_data.linearly_dependent_free_items) then
main_elements.solver_frame.visible = true

local item_count = 0
local num_needed_restricted_items = #linear_dependence_data.linearly_dependent_free_items

local caption = {"fp.error_message", {"fp.info_label", {"fp.remove_unrestricted_items"}}}
local tooltip = {"fp.remove_unrestricted_items_tt"}
solver_flow.add{type="label", caption=caption, tooltip=tooltip, style="bold_label"}

local flow_unrestricted = solver_flow.add{type="flow", direction="horizontal"}
build_item_flow(flow_unrestricted, "unrestricted", linear_dependence_data.linearly_dependent_free_items)
item_count = item_count + #matrix_metadata.free_items

-- This is some total bullshit because extra_bottom_padding_when_activated doesn't work
local total_width = 180 + (4 * 12) + (item_count * 40)
local interface_width = util.globals.ui_state(player).main_dialog_dimensions.width
local box_width = interface_width - MAGIC_NUMBERS.list_width
solver_flow.style.bottom_padding = (total_width > box_width) and 16 or 4

elseif next(linear_dependence_data.linearly_dependent_recipes) then
main_elements.solver_frame.visible = true

local caption = {"fp.error_message", {"fp.info_label", {"fp.linearly_dependent_recipes"}}}
Expand All @@ -40,16 +70,6 @@ local function refresh_solver_frame(player)
elseif num_needed_free_items ~= 0 then
main_elements.solver_frame.visible = true

local function build_item_flow(flow, status, items)
for _, proto in pairs(items) do
local tooltip = {"fp.turn_" .. status, proto.localised_name}
local color = (status == "unrestricted") and "green" or "default"
flow.add{type="sprite-button", sprite=proto.sprite, tooltip=tooltip,
tags={mod="fp", on_gui_click="switch_matrix_item", status=status, type=proto.type, name=proto.name},
style="flib_slot_button_" .. color .. "_small", mouse_button_filter={"left"}}
end
end

local needs_choice = (#linear_dependence_data.allowed_free_items > 0)
local item_count = 0

Expand Down