class CraftingTable::RecipeManager

A class which contains recipes, and allows to search through them.

@author Michael Senn <morrolan@morrolan.ch> @since 0.2

Attributes

item_manager[R]
recipes[R]

Public Class Methods

new(item_manager) click to toggle source

Create a new RecipeManager

@param [ItemManager] item_manager ItemManager which contains the items

on which this manager's recipes are based.
# File lib/crafting_table/recipe_manager.rb, line 17
def initialize(item_manager)
  @item_manager = item_manager
  @recipes = []
end

Public Instance Methods

add(recipe) click to toggle source

Add a new recipe to the internal collection.

@param [Recipe] recipe Recipe which to add to the collection. @return [void]

# File lib/crafting_table/recipe_manager.rb, line 26
def add(recipe)
  @recipes << recipe
end
add_from_file(path) click to toggle source

Add new recipes by reading them from a YAML file.

@param [String] path The path to the file from which to read

recipes from.

@return [void]

# File lib/crafting_table/recipe_manager.rb, line 35
def add_from_file(path)
  YAML.load_file(path).each do |recipe_hash|
    raw_input = recipe_hash.fetch('input', {})
    raw_output = recipe_hash.fetch('output', {})
    name = recipe_hash.fetch('name', 'UNKNOWN')

    input = {}
    output = {}
    raw_input.each do |identifier_string, amount|
      identifier = identifier_string.split(':').map(&:to_i)
      item = item_manager.find_by_identifier(identifier).first
      input[item] = amount
    end

    raw_output.each do |identifier_string, amount|
      identifier = identifier_string.split(':').map(&:to_i)
      item = item_manager.find_by_identifier(identifier).first
      output[item] = amount
    end

    @recipes << Recipe.new(name, input, output)
  end
  
end
clear() click to toggle source

Clear the internal collection of recipes. @return [void]

# File lib/crafting_table/recipe_manager.rb, line 62
def clear
  @recipes.clear
end
find() { |builder| ... } click to toggle source

Find recipes.

@since 0.3

@example Searching for name.

results = manager.find do |search|
  search.name = 'slab'
  search.exact = false
  search.case_sensitive = false
end
results.map(&:name)
#=> ['Cobblestone slab', 'Wooden slab', 'Stone slab', '...']

@example Searching for recipes which result in torch(es).

manager.find do |search|
  manager.output = Item.new('Torch', 50)
end

@example Searching for recipes which require oak planks.

results = manager.find do |search|
  manager.input = Item.new('Oak Wood Planks', 5)
end
results.map(&:name)
#=> ['Sticks', 'Crafting Table', 'Chest', 'Bed', '...']

@yieldparam builder [SearchBuilder]

An instance of the SearchBuilder class which allows to easily specify
multiple search conditions.

@return [Array<Recipe>] Recipes which matched the search conditions.

# File lib/crafting_table/recipe_manager.rb, line 96
def find(&block)
  builder = Search::SearchBuilder.new
  yield builder

  builder.searches.inject(recipes) { |recipes, search| search.apply_to(recipes) }
end
find_by_input(item) click to toggle source

Find recipes by their input.

@deprecated Use {#find} instead.

@example Searching for recipes which require planks.

manager.find_by_input(Item.new('Oak Wood Planks', 5, 0)).map(&:name)
#=> ['Sticks', 'Crafting Table', 'Chest', 'Bed', '...']

@param [Item] item Item for which to search. @return [Array<Recipe>] Collection of recipes which matched the search condition.

# File lib/crafting_table/recipe_manager.rb, line 147
def find_by_input(item)
  recipes.select { |recipe| recipe.input.key? item }
end
find_by_name(name, options = {}) click to toggle source

Find recipes by their name.

@deprecated Use {#find} instead.

@example Search using default parameters.

manager.find_by_name('Torch').first.input.map(&:name) #=> ['Wooden Planks', 'Coal']

@example Case-insensitive search, non-exact matching.

manager.find_by_name('slab').map(&:name) #=> ['Cobblestone Slab', 'Wooden slab', 'Stone slab', '...']

@param [String] name The name for which to search. @param [Hash] options Options which influence the search. @option options [Boolean] :exact (true) Whether to match names exactly. @option options [Boolean] :case_sensitive (true) Whether to search case-sensitively. @return [Array<Recipe>] Collection of recipes which matched the search condition.

# File lib/crafting_table/recipe_manager.rb, line 118
def find_by_name(name, options = {})
  default_options = { exact: true, case_sensitive: true }
  default_options.update(options)

  if default_options[:case_sensitive]
    if default_options[:exact]
      recipes.select { |recipe| recipe.name == name }
    else
      recipes.select { |recipe| recipe.name.include? name }
    end
  else
    if default_options[:exact]
      recipes.select { |recipe| recipe.name.downcase == name.downcase }
    else
      recipes.select { |recipe| recipe.name.downcase.include? name.downcase }
    end
  end
end
find_by_output(item) click to toggle source

Find recipes by their output.

@deprecated Use {#find} instead.

@example Searching for recipes which result in Coal.

manager.find_by_output(Item.new('Coal', 263, 0)).map(&:name)
#=> ['Coal']

@return [Array<Recipe>]

Collection of recipes which matched the search condition.
# File lib/crafting_table/recipe_manager.rb, line 160
def find_by_output(item)
  recipes.select { |recipe| recipe.output.key? item }
end
resolve_recipe(recipe, amount) click to toggle source

Resolve a recipe into its base components.

@since 0.3

@example Components required to craft 50 torches.

torch = recipe_manager.find_by_name('Torch').first
components = recipe_manager.resolve_recipe(recipe_torch, 50)
components.map { |item, amount| item.name => amount }
#=> { "Wood" => 2, "Coal" => 13 }

@param [Recipe] recipe

The recipe which to resolve to its components.

@param [Integer] amount Desired amount of the recipe. @return [Hash{Item => Integer}]

A hash mapping the base components
to the required amount of each.
# File lib/crafting_table/recipe_manager.rb, line 180
def resolve_recipe(recipe, amount)
  # Todo: Allow to specify arbitrary outputs.
  # Todo: Fail if the specified output is not part of the recipe.
  desired_output = recipe.output.keys.first
  amount_per_iteration = recipe.output[desired_output]
  # If we get four items per iteration, and want 21 items in
  # total, we'll need 6 iterations.
  iterations = (amount.to_f / amount_per_iteration).ceil

  requirements = Hash.new(0)
  
  recipe.input.each do |input, amount|
    # Finding potential recipes for the input.
    recipes_for_input = find_by_output(input)

    # Todo: Allow for other criteria where the recipe should be ignored.
    if recipes_for_input.empty?
      requirements[input] += amount * iterations
    else
      recipe_for_input = recipes_for_input.first
      requirements_for_input = resolve_recipe(recipe_for_input,
                                              amount * iterations)

      requirements.merge!(requirements_for_input) do |key, old_amount, new_amount|
        old_amount + new_amount
      end

    end
  end

  requirements
  
end