From b1a66370057a080ab1ee6154c1ea68bfca59440d Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Wed, 18 Feb 2015 21:21:48 -0800 Subject: [PATCH] Major updates to the crafting algo. * Update crafting algo. to favor highest output recipes for making items, and to be more flexible about trying a variety of recipes if some one recipe doesn't work. * Modify crafting algo. to allow crafting of "gatherable" items. * Update logger to support indent/outdent for clearer formatting * Convert IC2 Forge Hammer / Cutter recipes to treat them as tools --- .../experimental-2.2.663/mod-version.cg | 121 ++++++++---------- src/scripts/logger.coffee | 17 ++- src/scripts/models/crafting_plan.coffee | 70 +++++++--- src/scripts/models/mod_pack.coffee | 12 +- src/scripts/models/recipe.coffee | 25 +++- test/crafting_plan.test.coffee | 14 +- 6 files changed, 148 insertions(+), 111 deletions(-) diff --git a/src/data/industrial_craft_2/experimental-2.2.663/mod-version.cg b/src/data/industrial_craft_2/experimental-2.2.663/mod-version.cg index 3e148f403..2c1ec16a5 100644 --- a/src/data/industrial_craft_2/experimental-2.2.663/mod-version.cg +++ b/src/data/industrial_craft_2/experimental-2.2.663/mod-version.cg @@ -566,9 +566,9 @@ group: Energy: Transmission item: Copper Cable recipe: - extras: Cutter - input: Copper Plate, Cutter - pattern: ... 01. ... + tools: Cutter + input: Copper Plate + pattern: ... .0. ... quantity: 2 recipe: input: Copper Ingot @@ -607,9 +607,9 @@ group: Energy: Transmission item: Gold Cable recipe: - extras: Cutter - input: Cutter, Gold Plate - pattern: ... 10. ... + tools: Cutter + input: Gold Plate + pattern: ... .0. ... quantity: 4 recipe: input: Gold Ingot @@ -674,9 +674,9 @@ group: Energy: Transmission item: Tin Cable recipe: - extras: Cutter - input: Cutter, Tin Plate - pattern: ... 10. ... + tools: Cutter + input: Tin Plate + pattern: ... .0. ... quantity: 3 recipe: input: Tin Ingot @@ -735,9 +735,9 @@ group: Machine Parts item: Bronze Item Casing recipe: - extras: Forge Hammer - input: Bronze Plate, Forge Hammer - pattern: ... 01. ... + tools: Forge Hammer + input: Bronze Plate + pattern: ... .0. ... quantity: 2 recipe: input: Bronze Plate @@ -747,9 +747,9 @@ group: Machine Parts item: Bronze Plate recipe: - extras: Forge Hammer - input: Bronze Ingot, Forge Hammer - pattern: ... 01. ... + tools: Forge Hammer + input: Bronze Ingot + pattern: ... .0. ... recipe: input: Bronze Ingot pattern: ... .0. ... @@ -786,9 +786,9 @@ group: Machine Parts item: Copper Item Casing recipe: - extras: Forge Hammer - input: Copper Plate, Forge Hammer - pattern: ... 01. ... + tools: Forge Hammer + input: Copper Plate + pattern: ... .0. ... quantity: 2 recipe: input: Copper Plate @@ -798,9 +798,9 @@ group: Machine Parts item: Copper Plate recipe: - extras: Forge Hammer - input: Copper Ingot, Forge Hammer - pattern: ... 01. ... + tools: Forge Hammer + input: Copper Ingot + pattern: ... .0. ... recipe: input: Copper Ingot pattern: ... .0. ... @@ -909,9 +909,9 @@ group: Machine Parts item: Gold Item Casing recipe: - extras: Forge Hammer - input: Forge Hammer, Gold Plate - pattern: ... 10. ... + tools: Forge Hammer + input: Gold Plate + pattern: ... .0. ... quantity: 2 recipe: input: Gold Plate @@ -921,9 +921,9 @@ group: Machine Parts item: Gold Plate recipe: - extras: Forge Hammer - input: Forge Hammer, Gold Ingot - pattern: ... 10. ... + tools: Forge Hammer + input: Gold Ingot + pattern: ... .0. ... recipe: input: Gold Ingot pattern: ... .0. ... @@ -941,9 +941,9 @@ group: Machine Parts item: Iron Item Casing recipe: - extras: Forge Hammer - input: Forge Hammer, Iron Plate - pattern: ... 10. ... + tools: Forge Hammer + input: Iron Plate + pattern: ... .0. ... quantity: 2 recipe: input: Iron Plate @@ -953,9 +953,9 @@ group: Machine Parts item: Iron Plate recipe: - extras: Forge Hammer - input: Forge Hammer, Iron Ingot - pattern: ... 10. ... + tools: Forge Hammer + input: Iron Ingot + pattern: ... .0. ... recipe: input: Iron Ingot pattern: ... .0. ... @@ -1026,9 +1026,9 @@ group: Machine Parts item: Lead Item Casing recipe: - extras: Forge Hammer - input: Forge Hammer, Lead Plate - pattern: ... 10. ... + tools: Forge Hammer + input: Lead Plate + pattern: ... .0. ... quantity: 2 recipe: input: Lead Plate @@ -1038,9 +1038,9 @@ group: Machine Parts item: Lead Plate recipe: - extras: Forge Hammer - input: Forge Hammer, Lead Ingot - pattern: ... 10. ... + tools: Forge Hammer + input: Lead Ingot + pattern: ... .0. ... recipe: input: Lead Ingot pattern: ... .0. ... @@ -1230,9 +1230,9 @@ group: Machine Parts item: Tin Item Casing recipe: - extras: Forge Hammer - input: Forge Hammer, Tin Plate - pattern: ... 10. ... + tools: Forge Hammer + input: Tin Plate + pattern: ... .0. ... quantity: 2 recipe: input: Tin Plate @@ -1242,9 +1242,9 @@ group: Machine Parts item: Tin Plate recipe: - extras: Forge Hammer - input: Forge Hammer, Tin Ingot - pattern: ... 10. ... + tools: Forge Hammer + input: Tin Ingot + pattern: ... .0. ... recipe: input: Tin Ingot pattern: ... .0. ... @@ -1859,31 +1859,6 @@ group: Minerals tools: Furnace item: Slag - recipe: - extras: Reinforced Iron Ingot, Empty Cell - input: Compressed Air Cell, Iron Ore - pattern: ... .1. .0. - tools: Blast Furnace - recipe: - extras: Reinforced Iron Ingot, Empty Cell - input: Compressed Air Cell, Iron Dust - pattern: ... .1. .0. - tools: Blast Furnace - recipe: - extras: Reinforced Iron Ingot, Empty Cell - input: Compressed Air Cell, Iron Ingot - pattern: ... .1. .0. - tools: Blast Furnace - recipe: - extras: Reinforced Iron Ingot, Empty Cell - input: Compressed Air Cell, Crushed Iron Ore - pattern: ... .1. .0. - tools: Blast Furnace - recipe: - extras: Reinforced Iron Ingot, Empty Cell - input: Compressed Air Cell, Purified Crushed Iron Ore - pattern: ... .1. .0. - tools: Blast Furnace item: Steam @@ -3135,3 +3110,9 @@ update: Iron Ingot input: Iron Dust, furnace fuel pattern: .0. ... .1. tools: Furnace + +update: Diamond + recipe: + input: Coal Chunk + pattern: ... .0. ... + tools: Compressor diff --git a/src/scripts/logger.coffee b/src/scripts/logger.coffee index 67f6e149d..f54762d43 100644 --- a/src/scripts/logger.coffee +++ b/src/scripts/logger.coffee @@ -21,16 +21,23 @@ module.exports = class Logger constructor: (options={})-> options.level ?= Logger.FATAL - @formatText = if options.format? then options.format else "<%= timestamp %> | <%= level %> | <%= message %>" + @formatText = options.format + @formatText ?= "<%= timestamp %> | <%= level %> | <%= indent %><%= message %>" @level = @_parseLevel options - @_format = _.template @formatText + @_format = _.template @formatText + @_indent = '' + + # Public Methods ############################################################################### + + indent: -> + @_indent += ' ' log: (level, message)-> return unless level.value >= @level.value message = message() if _.isFunction message - entry = {timestamp:new Date(), level:level, message:message} + entry = {timestamp:new Date(), level:level, message:message, indent:@_indent} entry.level ?= @level lines = @_formatEntry entry @@ -39,6 +46,9 @@ module.exports = class Logger else console.error(line) for line in lines + outdent: -> + @_indent = @_indent[0...@_indent.length - 4] + # Log Methods ################################################################################## trace: (message)-> @log Logger.TRACE, message @@ -67,6 +77,7 @@ module.exports = class Logger timestamp: "#{entry.timestamp}" level: entry.level.name message: line + indent: entry.indent lines.push result.join '' return lines diff --git a/src/scripts/models/crafting_plan.coffee b/src/scripts/models/crafting_plan.coffee index 6241a593d..8a985cd3c 100644 --- a/src/scripts/models/crafting_plan.coffee +++ b/src/scripts/models/crafting_plan.coffee @@ -8,6 +8,7 @@ All rights reserved. BaseModel = require './base_model' {Event} = require '../constants' Inventory = require './inventory' +ItemSlug = require './item_slug' ######################################################################################################################## @@ -53,14 +54,12 @@ module.exports = class CraftingPlan extends BaseModel @result.addInventory @have @steps = {} - @_reservedSteps = {} @want.each (stack)=> - @_findSteps stack.itemSlug + @_findSteps stack.itemSlug, {}, ignoreGatherable:true item = @modPack.findItem stack.itemSlug @need.add item.slug, stack.quantity - @_reservedSteps = null - @steps = _.values @steps + @steps = (recipe:recipe for recipeSlug, recipe of @steps) @_resolveNeeds() @_removeExtraSteps() @@ -99,38 +98,69 @@ module.exports = class CraftingPlan extends BaseModel # Private Methods ############################################################################## _addStep: (recipe)-> - logger.verbose -> "adding step: #{recipe.itemSlug}" - @steps[recipe.itemSlug] = recipe:recipe + logger.verbose -> "adding step for: #{recipe.slug}" + @steps[recipe.slug] = recipe _chooseRecipe: (item)-> recipes = @modPack.findRecipes item.slug return null unless recipes? and recipes.length > 0 return recipes[0] - _findSteps: (itemSlug)-> + _findSteps: (itemSlug, parentSteps={})-> item = @modPack.findItem itemSlug return unless item? return unless item.isCraftable - return if item.isGatherable - recipe = @_chooseRecipe item + ignoreGatherable = @want.hasAtLeast itemSlug, 1 + if (not item.isGatherable) or ignoreGatherable + recipes = @modPack.findRecipes item.slug + recipes ?= [] - if @includingTools - for toolStack in recipe.tools - if not @_hasStep toolStack.itemSlug - @_findSteps toolStack.itemSlug + if parentSteps[item.slug]? + logger.verbose -> "found cycle at #{item.slug}" + throw new Error 'invalid recipe path' + parentSteps[item.slug] = item - return if @_hasStep item.slug - @_reservedSteps[item.slug] = recipe + logger.verbose -> "exploring: #{item.slug}" + logger.indent() - for inputStack in recipe.input - @_findSteps inputStack.itemSlug + currentSteps = _.clone @steps + foundValidRecipe = false + for i in [0...recipes.length] by 1 + recipe = recipes[i] + logger.verbose -> "trying recipe #{i+1} of #{recipes.length}: #{recipe.slug}" + if @steps[recipe.slug]? + logger.verbose -> "already accepted this recipe" + foundValidRecipe = true + break - @_addStep recipe + try + if @includingTools + for toolStack in recipe.tools + if not @_hasStep toolStack.itemSlug + @_findSteps toolStack.itemSlug, parentSteps + + for inputStack in recipe.input + @_findSteps inputStack.itemSlug, parentSteps + + @_addStep recipe + foundValidRecipe = true + break + catch error + logger.verbose -> "recipe didn't work out: #{recipe.slug}" + if error.message isnt 'invalid recipe path' then throw error + @steps = _.clone currentSteps + + delete parentSteps[item.slug] + logger.outdent() + + if not (foundValidRecipe or item.isGatherable) + logger.verbose -> "could not find a valid recipe for #{item.slug}" + throw new Error 'invalid recipe path' _hasStep: (itemSlug)-> - return true if @steps[itemSlug]? - return true if @_reservedSteps[itemSlug]? + for recipeSlug, recipe of @steps + return true if recipe.produces itemSlug return false _qualifyItemSlug: (itemSlug)-> diff --git a/src/scripts/models/mod_pack.coffee b/src/scripts/models/mod_pack.coffee index 4d312bafc..0094cf2da 100644 --- a/src/scripts/models/mod_pack.coffee +++ b/src/scripts/models/mod_pack.coffee @@ -84,14 +84,10 @@ module.exports = class ModPack extends BaseModel findRecipes: (itemSlug, result=[])-> return null unless itemSlug? - if itemSlug.isQualified - mod = @getMod itemSlug.mod - if mod? then mod.findRecipes itemSlug, result - - if result.length is 0 - for mod in @_mods - continue unless mod.enabled - mod.findRecipes itemSlug, result + for mod in @_mods + continue unless mod.enabled + continue if itemSlug.isQualified and itemSlug.mod isnt mod.slug + mod.findRecipes itemSlug, result result.sort (a, b)-> Recipe.compareFor a, b, itemSlug diff --git a/src/scripts/models/recipe.coffee b/src/scripts/models/recipe.coffee index 35179d52f..376db9882 100644 --- a/src/scripts/models/recipe.coffee +++ b/src/scripts/models/recipe.coffee @@ -5,8 +5,9 @@ Copyright (c) 2014-2015 by Redwood Labs All rights reserved. ### -BaseModel = require './base_model' -Stack = require './stack' +BaseModel = require './base_model' +Stack = require './stack' +StringBuilder = require './string_builder' ######################################################################################################################## @@ -43,7 +44,7 @@ module.exports = class Recipe extends BaseModel aValue = a.getQuantityProducedOf itemSlug bValue = b.getQuantityProducedOf itemSlug if aValue isnt bValue - return if aValue < bValue then -1 else +1 + return if aValue > bValue then -1 else +1 aValue = a.getInputCount() bValue = b.getInputCount() @@ -102,6 +103,24 @@ module.exports = class Recipe extends BaseModel return true return false + # Property Methods ############################################################################# + + getSlug: -> + if not @_slug? + builder = new StringBuilder + builder + .loop(@input, delimiter:',', onEach:(b, stack)-> b.push stack.itemSlug.qualified) + .onlyIf @tools.length > 0, (b)=> + b.push(' + ').loop(@tools, delimiter:',', onEach:(b, stack)-> b.push stack.itemSlug.qualified) + .push(' => ') + .loop(@output, delimiter:',', onEach:(b, stack)-> b.push stack.itemSlug.qualified) + @_slug = builder.toString() + + return @_slug + + Object.defineProperties @prototype, + slug: {get:@prototype.getSlug} + # Object Overrides ############################################################################# toString: -> diff --git a/test/crafting_plan.test.coffee b/test/crafting_plan.test.coffee index d71bdf502..64a737430 100644 --- a/test/crafting_plan.test.coffee +++ b/test/crafting_plan.test.coffee @@ -45,25 +45,25 @@ describe 'crafting_plan.coffee', -> plan.want.add ItemSlug.slugify 'oak_plank' plan.craft() plan.need.unparse().should.equal 'oak_log' - plan.result.unparse().should.equal '4.minecraft__oak_plank' + plan.result.unparse().should.equal '4.oak_plank' it 'can craft a multi-step recipe', -> plan.want.add ItemSlug.slugify 'crafting_table' plan.craft() plan.need.unparse().should.equal 'oak_log' - plan.result.unparse().should.equal 'minecraft__crafting_table' + plan.result.unparse().should.equal 'crafting_table' it 'can craft a multi-step recipe using tools', -> plan.want.add ItemSlug.slugify 'furnace' plan.craft() plan.need.unparse().should.equal '8.cobblestone' - plan.result.unparse().should.equal 'minecraft__furnace' + plan.result.unparse().should.equal 'furnace' it 'can craft a multi-step recipe re-using tools', -> plan.want.add ItemSlug.slugify 'iron_sword' plan.craft() plan.need.unparse().should.equal '2.furnace_fuel:2.iron_ore:oak_log' - plan.result.unparse().should.equal 'minecraft__iron_sword:2.minecraft__oak_plank:3.minecraft__stick' + plan.result.unparse().should.equal 'iron_sword:2.oak_plank:3.stick' describe 'with building tools', -> @@ -72,7 +72,7 @@ describe 'crafting_plan.coffee', -> plan.want.add ItemSlug.slugify 'furnace' plan.craft() plan.need.unparse().should.equal '8.cobblestone:oak_log' - plan.result.unparse().should.equal 'minecraft__crafting_table:minecraft__furnace' + plan.result.unparse().should.equal 'crafting_table:furnace' it 'can craft a multi-step recipe re-using tools', -> plan.includingTools = true @@ -80,5 +80,5 @@ describe 'crafting_plan.coffee', -> plan.craft() plan.need.unparse().should.eql '8.cobblestone:2.furnace_fuel:2.iron_ore:2.oak_log' - plan.result.unparse().should.equal 'minecraft__crafting_table:minecraft__furnace:' + - 'minecraft__iron_sword:2.minecraft__oak_plank:3.minecraft__stick' + plan.result.unparse().should.equal 'crafting_table:furnace:' + + 'iron_sword:2.oak_plank:3.stick'