module Seshbot::Packing::Recipe
Public Class Methods
_calculate_recipe_factor(recipe, items)
click to toggle source
private
# File lib/seshbot/packing/recipe.rb, line 83 def _calculate_recipe_factor(recipe, items) recipe_inputs = recipe['inputs'].nil? ? [recipe] : recipe['inputs'] recipe_input_factors = recipe_inputs.map do |inputs| input_fragment = inputs['input_fragment'] input_quantity = inputs['input_quantity'] item = items.find { |i| i.sku_fragment == input_fragment } raise "recipe contains items not present in order (#{input_fragment})" if item.nil? [inputs, item.quantity / input_quantity] end recipe_input_factors.map { |i,f| f }.min end
_find_recipes(recipes, items, unpacking: false)
click to toggle source
# File lib/seshbot/packing/recipe.rb, line 99 def _find_recipes(recipes, items, unpacking: false) recipes = _reverse_recipes(recipes) if unpacking recipes.select do |recipe_name, recipe_details| recipe_inputs = recipe_details['inputs'].nil? ? [recipe_details] : recipe_details['inputs'] # recipe is a candidate if all inputs may be satisfied by available items recipe_inputs.all? do |inputs| items.any? { |i| inputs["input_fragment"] == i.sku_fragment && i.quantity >= inputs["input_quantity"] } end end end
_reverse_recipes(recipes)
click to toggle source
# File lib/seshbot/packing/recipe.rb, line 111 def _reverse_recipes(recipes) # first exclude recipes that are explicitly configured as not reversible reversible_recipes = recipes.select do |recipe_name, recipe_details| recipe_details['is_reversible'].nil? || recipe_details['is_reversible'] end unique_reversible_output_fragments = reversible_recipes.values.map { |r| r['output_fragment'] }.sort.uniq # further refine by excluding recipes that are technically incapable of being reversed reversible_recipes = reversible_recipes.select do |recipe_name, recipe_details| # currently dont support multipe outputs, so cannot reverse recipes with multiple inputs is_composite = !recipe_details['inputs'].nil? && recipe_details['inputs'].length != 1 # cannot reverse recipes where there are multipe ways of creating the same output is_ambiguous = !unique_reversible_output_fragments.include?(recipe_details['output_fragment']) !is_composite && !is_ambiguous end results = reversible_recipes.map do |recipe_name, recipe_details| # here we know that if 'inputs' is specified it will have exactly 1 input input_fragment = recipe_details["input_fragment"] || recipe_details["inputs"][0]["input_fragment"] input_quantity = recipe_details["input_quantity"] || recipe_details["inputs"][0]["input_quantity"] new_recipe_details = { "input_fragment" => recipe_details["output_fragment"], "input_quantity" => recipe_details["output_quantity"], "output_fragment" => input_fragment, "output_quantity" => input_quantity } [recipe_name, new_recipe_details] end results.to_h end
apply_recipe(recipe, items)
click to toggle source
# File lib/seshbot/packing/recipe.rb, line 51 def apply_recipe(recipe, items) factor = _calculate_recipe_factor(recipe, items) # shortcut - cannot apply recipe, not enough inputs return items if factor == 0 results = items.dup # first remove inputs (add negative quantities) recipe_inputs = recipe['inputs'].nil? ? [recipe] : recipe['inputs'] recipe_inputs.each do |inputs| results << LineItem.new(inputs['input_fragment'], -1 * inputs['input_quantity'] * factor) end # now add input results << LineItem.new(recipe['output_fragment'], recipe['output_quantity'] * factor) # consolidate results = LineItem.merge_line_items(results) end
find_best_recipe(recipes_hash, items, unpacking: false)
click to toggle source
# File lib/seshbot/packing/recipe.rb, line 5 def find_best_recipe(recipes_hash, items, unpacking: false) recipes = _find_recipes(recipes_hash, items, unpacking: unpacking) # we want the recipe that is 'best' (packaging into as few items as possible, or unpacaging into as many as possible) best_recipe = nil best_recipe_output_quantity = nil best_recipe_inputs_quantity = nil recipes.each do |recipe_name, recipe| recipe_inputs = recipe['inputs'].nil? ? [recipe] : recipe['inputs'] recipe_factor = _calculate_recipe_factor(recipe, items) recipe_output_quantity = recipe_factor * recipe['output_quantity'] recipe_inputs_quantity = recipe_factor * recipe_inputs.map { |r| r['input_quantity'] }.sum # is this recipe the 'best'? (if packaging, best is the one that creates fewest quantity) is_best = if best_recipe_output_quantity.nil? true else # # the below code uses output as the main feature, but if they are equal uses the number of inputs # output_equal = recipe_output_quantity == best_recipe_output_quantity # when packing, fewer outputs are better output_better = unpacking ? recipe_output_quantity > best_recipe_output_quantity : recipe_output_quantity < best_recipe_output_quantity # when packing, more inputs are better inputs_better = unpacking ? recipe_inputs_quantity < best_recipe_inputs_quantity : recipe_inputs_quantity > best_recipe_inputs_quantity output_equal ? inputs_better : output_better end if is_best best_recipe_output_quantity = recipe_output_quantity best_recipe_inputs_quantity = recipe_inputs_quantity best_recipe = recipe end end best_recipe end
summarise(recipe)
click to toggle source
# File lib/seshbot/packing/recipe.rb, line 72 def summarise(recipe) recipe_inputs = recipe['inputs'].nil? ? [recipe] : recipe['inputs'] inputs = recipe_inputs.map { |i| "#{i['input_quantity']}x#{i['input_fragment']}" }.join(',') outputs = "#{recipe['output_quantity']}x#{recipe['output_fragment']}" "#{inputs} -> #{outputs}" end