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
This commit is contained in:
Andrew Miner
2015-02-18 21:29:21 -08:00
parent aab3baec1a
commit b1a6637005
6 changed files with 148 additions and 111 deletions
@@ -566,9 +566,9 @@ group: Energy: Transmission
item: Copper Cable item: Copper Cable
recipe: recipe:
extras: Cutter tools: Cutter
input: Copper Plate, Cutter input: Copper Plate
pattern: ... 01. ... pattern: ... .0. ...
quantity: 2 quantity: 2
recipe: recipe:
input: Copper Ingot input: Copper Ingot
@@ -607,9 +607,9 @@ group: Energy: Transmission
item: Gold Cable item: Gold Cable
recipe: recipe:
extras: Cutter tools: Cutter
input: Cutter, Gold Plate input: Gold Plate
pattern: ... 10. ... pattern: ... .0. ...
quantity: 4 quantity: 4
recipe: recipe:
input: Gold Ingot input: Gold Ingot
@@ -674,9 +674,9 @@ group: Energy: Transmission
item: Tin Cable item: Tin Cable
recipe: recipe:
extras: Cutter tools: Cutter
input: Cutter, Tin Plate input: Tin Plate
pattern: ... 10. ... pattern: ... .0. ...
quantity: 3 quantity: 3
recipe: recipe:
input: Tin Ingot input: Tin Ingot
@@ -735,9 +735,9 @@ group: Machine Parts
item: Bronze Item Casing item: Bronze Item Casing
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Bronze Plate, Forge Hammer input: Bronze Plate
pattern: ... 01. ... pattern: ... .0. ...
quantity: 2 quantity: 2
recipe: recipe:
input: Bronze Plate input: Bronze Plate
@@ -747,9 +747,9 @@ group: Machine Parts
item: Bronze Plate item: Bronze Plate
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Bronze Ingot, Forge Hammer input: Bronze Ingot
pattern: ... 01. ... pattern: ... .0. ...
recipe: recipe:
input: Bronze Ingot input: Bronze Ingot
pattern: ... .0. ... pattern: ... .0. ...
@@ -786,9 +786,9 @@ group: Machine Parts
item: Copper Item Casing item: Copper Item Casing
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Copper Plate, Forge Hammer input: Copper Plate
pattern: ... 01. ... pattern: ... .0. ...
quantity: 2 quantity: 2
recipe: recipe:
input: Copper Plate input: Copper Plate
@@ -798,9 +798,9 @@ group: Machine Parts
item: Copper Plate item: Copper Plate
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Copper Ingot, Forge Hammer input: Copper Ingot
pattern: ... 01. ... pattern: ... .0. ...
recipe: recipe:
input: Copper Ingot input: Copper Ingot
pattern: ... .0. ... pattern: ... .0. ...
@@ -909,9 +909,9 @@ group: Machine Parts
item: Gold Item Casing item: Gold Item Casing
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Forge Hammer, Gold Plate input: Gold Plate
pattern: ... 10. ... pattern: ... .0. ...
quantity: 2 quantity: 2
recipe: recipe:
input: Gold Plate input: Gold Plate
@@ -921,9 +921,9 @@ group: Machine Parts
item: Gold Plate item: Gold Plate
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Forge Hammer, Gold Ingot input: Gold Ingot
pattern: ... 10. ... pattern: ... .0. ...
recipe: recipe:
input: Gold Ingot input: Gold Ingot
pattern: ... .0. ... pattern: ... .0. ...
@@ -941,9 +941,9 @@ group: Machine Parts
item: Iron Item Casing item: Iron Item Casing
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Forge Hammer, Iron Plate input: Iron Plate
pattern: ... 10. ... pattern: ... .0. ...
quantity: 2 quantity: 2
recipe: recipe:
input: Iron Plate input: Iron Plate
@@ -953,9 +953,9 @@ group: Machine Parts
item: Iron Plate item: Iron Plate
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Forge Hammer, Iron Ingot input: Iron Ingot
pattern: ... 10. ... pattern: ... .0. ...
recipe: recipe:
input: Iron Ingot input: Iron Ingot
pattern: ... .0. ... pattern: ... .0. ...
@@ -1026,9 +1026,9 @@ group: Machine Parts
item: Lead Item Casing item: Lead Item Casing
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Forge Hammer, Lead Plate input: Lead Plate
pattern: ... 10. ... pattern: ... .0. ...
quantity: 2 quantity: 2
recipe: recipe:
input: Lead Plate input: Lead Plate
@@ -1038,9 +1038,9 @@ group: Machine Parts
item: Lead Plate item: Lead Plate
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Forge Hammer, Lead Ingot input: Lead Ingot
pattern: ... 10. ... pattern: ... .0. ...
recipe: recipe:
input: Lead Ingot input: Lead Ingot
pattern: ... .0. ... pattern: ... .0. ...
@@ -1230,9 +1230,9 @@ group: Machine Parts
item: Tin Item Casing item: Tin Item Casing
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Forge Hammer, Tin Plate input: Tin Plate
pattern: ... 10. ... pattern: ... .0. ...
quantity: 2 quantity: 2
recipe: recipe:
input: Tin Plate input: Tin Plate
@@ -1242,9 +1242,9 @@ group: Machine Parts
item: Tin Plate item: Tin Plate
recipe: recipe:
extras: Forge Hammer tools: Forge Hammer
input: Forge Hammer, Tin Ingot input: Tin Ingot
pattern: ... 10. ... pattern: ... .0. ...
recipe: recipe:
input: Tin Ingot input: Tin Ingot
pattern: ... .0. ... pattern: ... .0. ...
@@ -1859,31 +1859,6 @@ group: Minerals
tools: Furnace tools: Furnace
item: Slag 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 item: Steam
@@ -3135,3 +3110,9 @@ update: Iron Ingot
input: Iron Dust, furnace fuel input: Iron Dust, furnace fuel
pattern: .0. ... .1. pattern: .0. ... .1.
tools: Furnace tools: Furnace
update: Diamond
recipe:
input: Coal Chunk
pattern: ... .0. ...
tools: Compressor
+14 -3
View File
@@ -21,16 +21,23 @@ module.exports = class Logger
constructor: (options={})-> constructor: (options={})->
options.level ?= Logger.FATAL 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 @level = @_parseLevel options
@_format = _.template @formatText @_format = _.template @formatText
@_indent = ''
# Public Methods ###############################################################################
indent: ->
@_indent += ' '
log: (level, message)-> log: (level, message)->
return unless level.value >= @level.value return unless level.value >= @level.value
message = message() if _.isFunction message 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 entry.level ?= @level
lines = @_formatEntry entry lines = @_formatEntry entry
@@ -39,6 +46,9 @@ module.exports = class Logger
else else
console.error(line) for line in lines console.error(line) for line in lines
outdent: ->
@_indent = @_indent[0...@_indent.length - 4]
# Log Methods ################################################################################## # Log Methods ##################################################################################
trace: (message)-> @log Logger.TRACE, message trace: (message)-> @log Logger.TRACE, message
@@ -67,6 +77,7 @@ module.exports = class Logger
timestamp: "#{entry.timestamp}" timestamp: "#{entry.timestamp}"
level: entry.level.name level: entry.level.name
message: line message: line
indent: entry.indent
lines.push result.join '' lines.push result.join ''
return lines return lines
+50 -20
View File
@@ -8,6 +8,7 @@ All rights reserved.
BaseModel = require './base_model' BaseModel = require './base_model'
{Event} = require '../constants' {Event} = require '../constants'
Inventory = require './inventory' Inventory = require './inventory'
ItemSlug = require './item_slug'
######################################################################################################################## ########################################################################################################################
@@ -53,14 +54,12 @@ module.exports = class CraftingPlan extends BaseModel
@result.addInventory @have @result.addInventory @have
@steps = {} @steps = {}
@_reservedSteps = {}
@want.each (stack)=> @want.each (stack)=>
@_findSteps stack.itemSlug @_findSteps stack.itemSlug, {}, ignoreGatherable:true
item = @modPack.findItem stack.itemSlug item = @modPack.findItem stack.itemSlug
@need.add item.slug, stack.quantity @need.add item.slug, stack.quantity
@_reservedSteps = null
@steps = _.values @steps @steps = (recipe:recipe for recipeSlug, recipe of @steps)
@_resolveNeeds() @_resolveNeeds()
@_removeExtraSteps() @_removeExtraSteps()
@@ -99,38 +98,69 @@ module.exports = class CraftingPlan extends BaseModel
# Private Methods ############################################################################## # Private Methods ##############################################################################
_addStep: (recipe)-> _addStep: (recipe)->
logger.verbose -> "adding step: #{recipe.itemSlug}" logger.verbose -> "adding step for: #{recipe.slug}"
@steps[recipe.itemSlug] = recipe:recipe @steps[recipe.slug] = recipe
_chooseRecipe: (item)-> _chooseRecipe: (item)->
recipes = @modPack.findRecipes item.slug recipes = @modPack.findRecipes item.slug
return null unless recipes? and recipes.length > 0 return null unless recipes? and recipes.length > 0
return recipes[0] return recipes[0]
_findSteps: (itemSlug)-> _findSteps: (itemSlug, parentSteps={})->
item = @modPack.findItem itemSlug item = @modPack.findItem itemSlug
return unless item? return unless item?
return unless item.isCraftable 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 if parentSteps[item.slug]?
for toolStack in recipe.tools logger.verbose -> "found cycle at #{item.slug}"
if not @_hasStep toolStack.itemSlug throw new Error 'invalid recipe path'
@_findSteps toolStack.itemSlug parentSteps[item.slug] = item
return if @_hasStep item.slug logger.verbose -> "exploring: #{item.slug}"
@_reservedSteps[item.slug] = recipe logger.indent()
for inputStack in recipe.input currentSteps = _.clone @steps
@_findSteps inputStack.itemSlug 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)-> _hasStep: (itemSlug)->
return true if @steps[itemSlug]? for recipeSlug, recipe of @steps
return true if @_reservedSteps[itemSlug]? return true if recipe.produces itemSlug
return false return false
_qualifyItemSlug: (itemSlug)-> _qualifyItemSlug: (itemSlug)->
+4 -8
View File
@@ -84,14 +84,10 @@ module.exports = class ModPack extends BaseModel
findRecipes: (itemSlug, result=[])-> findRecipes: (itemSlug, result=[])->
return null unless itemSlug? return null unless itemSlug?
if itemSlug.isQualified for mod in @_mods
mod = @getMod itemSlug.mod continue unless mod.enabled
if mod? then mod.findRecipes itemSlug, result continue if itemSlug.isQualified and itemSlug.mod isnt mod.slug
mod.findRecipes itemSlug, result
if result.length is 0
for mod in @_mods
continue unless mod.enabled
mod.findRecipes itemSlug, result
result.sort (a, b)-> Recipe.compareFor a, b, itemSlug result.sort (a, b)-> Recipe.compareFor a, b, itemSlug
+22 -3
View File
@@ -5,8 +5,9 @@ Copyright (c) 2014-2015 by Redwood Labs
All rights reserved. All rights reserved.
### ###
BaseModel = require './base_model' BaseModel = require './base_model'
Stack = require './stack' Stack = require './stack'
StringBuilder = require './string_builder'
######################################################################################################################## ########################################################################################################################
@@ -43,7 +44,7 @@ module.exports = class Recipe extends BaseModel
aValue = a.getQuantityProducedOf itemSlug aValue = a.getQuantityProducedOf itemSlug
bValue = b.getQuantityProducedOf itemSlug bValue = b.getQuantityProducedOf itemSlug
if aValue isnt bValue if aValue isnt bValue
return if aValue < bValue then -1 else +1 return if aValue > bValue then -1 else +1
aValue = a.getInputCount() aValue = a.getInputCount()
bValue = b.getInputCount() bValue = b.getInputCount()
@@ -102,6 +103,24 @@ module.exports = class Recipe extends BaseModel
return true return true
return false 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 ############################################################################# # Object Overrides #############################################################################
toString: -> toString: ->
+7 -7
View File
@@ -45,25 +45,25 @@ describe 'crafting_plan.coffee', ->
plan.want.add ItemSlug.slugify 'oak_plank' plan.want.add ItemSlug.slugify 'oak_plank'
plan.craft() plan.craft()
plan.need.unparse().should.equal 'oak_log' 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', -> it 'can craft a multi-step recipe', ->
plan.want.add ItemSlug.slugify 'crafting_table' plan.want.add ItemSlug.slugify 'crafting_table'
plan.craft() plan.craft()
plan.need.unparse().should.equal 'oak_log' 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', -> it 'can craft a multi-step recipe using tools', ->
plan.want.add ItemSlug.slugify 'furnace' plan.want.add ItemSlug.slugify 'furnace'
plan.craft() plan.craft()
plan.need.unparse().should.equal '8.cobblestone' 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', -> it 'can craft a multi-step recipe re-using tools', ->
plan.want.add ItemSlug.slugify 'iron_sword' plan.want.add ItemSlug.slugify 'iron_sword'
plan.craft() plan.craft()
plan.need.unparse().should.equal '2.furnace_fuel:2.iron_ore:oak_log' 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', -> describe 'with building tools', ->
@@ -72,7 +72,7 @@ describe 'crafting_plan.coffee', ->
plan.want.add ItemSlug.slugify 'furnace' plan.want.add ItemSlug.slugify 'furnace'
plan.craft() plan.craft()
plan.need.unparse().should.equal '8.cobblestone:oak_log' 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', -> it 'can craft a multi-step recipe re-using tools', ->
plan.includingTools = true plan.includingTools = true
@@ -80,5 +80,5 @@ describe 'crafting_plan.coffee', ->
plan.craft() plan.craft()
plan.need.unparse().should.eql '8.cobblestone:2.furnace_fuel:2.iron_ore:2.oak_log' 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:' + plan.result.unparse().should.equal 'crafting_table:furnace:' +
'minecraft__iron_sword:2.minecraft__oak_plank:3.minecraft__stick' 'iron_sword:2.oak_plank:3.stick'