Add CraftingPlan to compute resource requirements
This commit is contained in:
@@ -71,7 +71,7 @@ module.exports = class FullRecipeController extends BaseController
|
|||||||
inputs.clear()
|
inputs.clear()
|
||||||
|
|
||||||
if @model?
|
if @model?
|
||||||
@model.eachInputStack (stack)->
|
for stack in @model.input
|
||||||
inputs.add stack.itemSlug, stack.quantity
|
inputs.add stack.itemSlug, stack.quantity
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ module.exports = class CraftingNode
|
|||||||
# Public Methods ###############################################################################
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
expand: (queue=[])->
|
expand: (queue=[])->
|
||||||
|
return unless @valid
|
||||||
return queue if @children.length > 0
|
return queue if @children.length > 0
|
||||||
|
|
||||||
for child in @_createChildren()
|
for child in @_createChildren()
|
||||||
child.parent = this
|
child.parent = this
|
||||||
if child.valid
|
|
||||||
@_children.push child
|
@_children.push child
|
||||||
queue.push child
|
queue.push child
|
||||||
return queue
|
return queue
|
||||||
@@ -65,52 +65,46 @@ module.exports = class CraftingNode
|
|||||||
|
|
||||||
# Property Methods #############################################################################
|
# Property Methods #############################################################################
|
||||||
|
|
||||||
getChildren: ->
|
Object.defineProperties @prototype,
|
||||||
return @_children
|
|
||||||
|
|
||||||
isComplete: ->
|
children:
|
||||||
|
get: -> return @_children
|
||||||
|
|
||||||
|
complete:
|
||||||
|
get: ->
|
||||||
if not @_complete?
|
if not @_complete?
|
||||||
@_complete = @_checkCompleteness()
|
@_complete = @_checkCompleteness()
|
||||||
return @_complete
|
return @_complete
|
||||||
|
|
||||||
getCompleteText: ->
|
completeText:
|
||||||
return if @complete then "✓" else "✗"
|
get: -> if @complete then "◼︎" else "◻︎"
|
||||||
|
|
||||||
getDepth: ->
|
depth:
|
||||||
|
get: ->
|
||||||
maxDepth = 1
|
maxDepth = 1
|
||||||
for child in @children
|
for child in @children
|
||||||
maxDepth = Math.max maxDepth, child.getDepth() + 1
|
maxDepth = Math.max maxDepth, child.depth + 1
|
||||||
return maxDepth
|
return maxDepth
|
||||||
|
|
||||||
getId: ->
|
id:
|
||||||
return @_id
|
get: -> return @_id
|
||||||
|
|
||||||
getRotations: ->
|
rotations:
|
||||||
return @_rotations
|
get: -> return @_rotations
|
||||||
|
|
||||||
getSize: ->
|
size:
|
||||||
|
get: ->
|
||||||
size = 1
|
size = 1
|
||||||
for child in @children
|
for child in @children
|
||||||
size += child.size
|
size += child.size
|
||||||
return size
|
return size
|
||||||
|
|
||||||
isValid: ->
|
valid:
|
||||||
return @_valid if @_valid?
|
get: -> @_checkValidity()
|
||||||
|
|
||||||
valid = @_checkValidity()
|
validText:
|
||||||
@_valid = false if not valid
|
get: -> if @valid then "✓" else "✗"
|
||||||
|
|
||||||
return valid
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
children: { get:@prototype.getChildren }
|
|
||||||
complete: { get:@prototype.isComplete }
|
|
||||||
completeText: { get:@prototype.getCompleteText }
|
|
||||||
depth: { get:@prototype.getDepth }
|
|
||||||
id: { get:@prototype.getId }
|
|
||||||
rotations: { get:@prototype.getRotations }
|
|
||||||
size: { get:@prototype.getSize }
|
|
||||||
valid: { get:@prototype.isValid }
|
|
||||||
|
|
||||||
# Virtual Methods ##############################################################################
|
# Virtual Methods ##############################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -39,12 +39,24 @@ module.exports = class CraftingPlan
|
|||||||
# Object Overrides #############################################################################
|
# Object Overrides #############################################################################
|
||||||
|
|
||||||
toString: ->
|
toString: ->
|
||||||
return "#{@constructor.name}{
|
result = ["To Make:"]
|
||||||
wanted:#{@wanted},
|
@_wanted.each (stack)->
|
||||||
required:#{@required},
|
result.push " #{stack}"
|
||||||
steps:[#{(step.toString() for step in @steps).join(',')}],
|
|
||||||
produced:#{@produced}
|
result.push "Start with:"
|
||||||
}"
|
@_required.each (stack)->
|
||||||
|
result.push " #{stack}"
|
||||||
|
|
||||||
|
result.push "Use these recipes:"
|
||||||
|
for step in @_steps
|
||||||
|
result.push " #{step}"
|
||||||
|
|
||||||
|
result.push "To produce:"
|
||||||
|
@_produced.each (stack)->
|
||||||
|
result.push " #{stack}"
|
||||||
|
|
||||||
|
return result.join '\n'
|
||||||
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
# Private Methods ##############################################################################
|
||||||
|
|
||||||
@@ -52,35 +64,32 @@ module.exports = class CraftingPlan
|
|||||||
@_required.addInventory @_wanted
|
@_required.addInventory @_wanted
|
||||||
|
|
||||||
for i in [@_steps.length-1..0] by -1
|
for i in [@_steps.length-1..0] by -1
|
||||||
recipe = @_steps[i]
|
step = @_steps[i]
|
||||||
|
|
||||||
for stack in recipe.output
|
for stack in step.recipe.output
|
||||||
while @_required.quantityOf(stack.itemSlug) > 0
|
while @_required.quantityOf(stack.itemSlug) > 0
|
||||||
@_executeRecipe recipe
|
@_executeStep step
|
||||||
|
|
||||||
@_produced.addInventory @_wanted
|
@_produced.addInventory @_wanted
|
||||||
|
|
||||||
_executeRecipe: (recipe)->
|
_executeStep: (step)->
|
||||||
#console.log "executing: #{recipe}"
|
step.repeat += 1
|
||||||
|
recipe = step.recipe
|
||||||
|
|
||||||
for stack in recipe.input
|
for stack in recipe.input
|
||||||
@_use stack
|
|
||||||
for stack in recipe.output
|
|
||||||
@_produce stack
|
|
||||||
|
|
||||||
_produce: (stack)->
|
|
||||||
deficit = @_required.quantityOf stack.itemSlug
|
|
||||||
replenished = Math.min deficit, stack.quantity
|
|
||||||
surplus = stack.quantity - replenished
|
|
||||||
|
|
||||||
#console.log "producing #{stack.itemSlug}, surplus: #{surplus}, replenished:#{replenished}"
|
|
||||||
@_produced.add stack.itemSlug, surplus
|
|
||||||
@_required.remove stack.itemSlug, replenished
|
|
||||||
|
|
||||||
_use: (stack)->
|
|
||||||
available = @_produced.quantityOf stack.itemSlug
|
available = @_produced.quantityOf stack.itemSlug
|
||||||
consumed = Math.min stack.quantity, available
|
required = recipe.getQuantityRequired stack.itemSlug
|
||||||
deficit = stack.quantity - consumed
|
consumed = Math.min required, available
|
||||||
|
deficit = required - consumed
|
||||||
|
|
||||||
#console.log "using #{stack.itemSlug}, consumed: #{consumed}, deficit:#{deficit}"
|
|
||||||
@_produced.remove stack.itemSlug, consumed
|
@_produced.remove stack.itemSlug, consumed
|
||||||
@_required.add stack.itemSlug, deficit
|
@_required.add stack.itemSlug, deficit
|
||||||
|
|
||||||
|
for stack in recipe.output
|
||||||
|
deficit = @_required.quantityOf stack.itemSlug
|
||||||
|
created = recipe.getQuantityProduced stack.itemSlug
|
||||||
|
replenished = Math.min deficit, created
|
||||||
|
surplus = created - replenished
|
||||||
|
|
||||||
|
@_produced.add stack.itemSlug, surplus
|
||||||
|
@_required.remove stack.itemSlug, replenished
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
###
|
||||||
|
Crafting Guide - crafting_step.coffee
|
||||||
|
|
||||||
|
Copyright (c) 2015 by Redwood Labs
|
||||||
|
All rights reserved.
|
||||||
|
###
|
||||||
|
|
||||||
|
########################################################################################################################
|
||||||
|
|
||||||
|
module.exports = class CraftingStep
|
||||||
|
|
||||||
|
constructor: (recipe, repeat=0)->
|
||||||
|
if not recipe? then throw new Error 'recipe is required'
|
||||||
|
if repeat < 0 then throw new Error 'repeat must be at least 1'
|
||||||
|
|
||||||
|
@_recipe = recipe
|
||||||
|
@repeat = repeat
|
||||||
|
|
||||||
|
# Property Methods #############################################################################
|
||||||
|
|
||||||
|
Object.defineProperties @prototype,
|
||||||
|
|
||||||
|
recipe:
|
||||||
|
get: -> @_recipe
|
||||||
|
|
||||||
|
# Object Overrides #############################################################################
|
||||||
|
|
||||||
|
toString: ->
|
||||||
|
return "#{@repeat}x #{@recipe.slug}"
|
||||||
@@ -44,26 +44,23 @@ module.exports = class GraphBuilder
|
|||||||
|
|
||||||
# Property Methods #############################################################################
|
# Property Methods #############################################################################
|
||||||
|
|
||||||
isComplete: ->
|
Object.defineProperties @prototype,
|
||||||
|
|
||||||
|
complete:
|
||||||
|
get: ->
|
||||||
return false unless @_rootNode?
|
return false unless @_rootNode?
|
||||||
return false unless @_queue?
|
return false unless @_queue?
|
||||||
return false unless @_queue.length is 0
|
return false unless @_queue.length is 0
|
||||||
return true
|
return true
|
||||||
|
|
||||||
getRootNode: ->
|
rootNode:
|
||||||
return @_rootNode
|
get: -> @_rootNode
|
||||||
|
|
||||||
getStepCount: ->
|
stepCount:
|
||||||
return @_stepCount
|
get: -> @_stepCount
|
||||||
|
|
||||||
getWanted: ->
|
wanted:
|
||||||
return @_wanted
|
get: -> @_wanted
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
|
||||||
complete: { get:@prototype.isComplete }
|
|
||||||
rootNode: { get:@prototype.getRootNode }
|
|
||||||
stepCount: { get:@prototype.getStepCount }
|
|
||||||
wanted: { get:@prototype.getWanted }
|
|
||||||
|
|
||||||
# Object Overrides ############################################################################
|
# Object Overrides ############################################################################
|
||||||
|
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ module.exports = class InventoryNode extends CraftingNode
|
|||||||
|
|
||||||
_checkCompleteness: ->
|
_checkCompleteness: ->
|
||||||
for child in @children
|
for child in @children
|
||||||
return false unless child.isComplete
|
return false unless child.complete
|
||||||
return true
|
return true
|
||||||
|
|
||||||
_checkValidity: ->
|
_checkValidity: ->
|
||||||
for child in @children
|
for child in @children
|
||||||
return false unless child.isValid
|
return false unless child.valid
|
||||||
return true
|
return true
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
# Object Overrides #############################################################################
|
||||||
@@ -47,8 +47,7 @@ module.exports = class InventoryNode extends CraftingNode
|
|||||||
options.indent ?= ''
|
options.indent ?= ''
|
||||||
options.recursive ?= true
|
options.recursive ?= true
|
||||||
|
|
||||||
completeText = if @complete then 'complete' else 'incomplete'
|
parts = ["#{options.indent}#{@completeText} #{@validText} InventoryNode for #{@inventory}"]
|
||||||
parts = ["#{options.indent}#{@completeText} InventoryNode for #{@inventory}"]
|
|
||||||
nextIndent = options.indent + ' '
|
nextIndent = options.indent + ' '
|
||||||
if options.recursive
|
if options.recursive
|
||||||
for child in @children
|
for child in @children
|
||||||
|
|||||||
@@ -46,7 +46,10 @@ module.exports = class ItemNode extends CraftingNode
|
|||||||
return [] unless recipes.length > 0
|
return [] unless recipes.length > 0
|
||||||
|
|
||||||
for recipe in recipes
|
for recipe in recipes
|
||||||
result.push new RecipeNode modPack:@modPack, recipe:recipe
|
child = new RecipeNode modPack:@modPack, recipe:recipe
|
||||||
|
child.parent = this
|
||||||
|
if child.valid
|
||||||
|
result.push child
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -55,13 +58,13 @@ module.exports = class ItemNode extends CraftingNode
|
|||||||
return false unless @children?
|
return false unless @children?
|
||||||
|
|
||||||
for child in @children
|
for child in @children
|
||||||
return true if child.isComplete
|
return true if child.complete
|
||||||
return false
|
return false
|
||||||
|
|
||||||
_checkValidity: ->
|
_checkValidity: ->
|
||||||
return true unless @children.length > 0
|
return true unless @children.length > 0
|
||||||
for child in @children
|
for child in @children
|
||||||
return true if child.isValid
|
return true if child.valid
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
# Object Overrides #############################################################################
|
||||||
@@ -70,8 +73,7 @@ module.exports = class ItemNode extends CraftingNode
|
|||||||
options.indent ?= ''
|
options.indent ?= ''
|
||||||
options.recursive ?= true
|
options.recursive ?= true
|
||||||
|
|
||||||
completeText = if @complete then 'complete' else 'incomplete'
|
parts = ["#{options.indent}#{@completeText} #{@validText} ItemNode for #{@item.name}"]
|
||||||
parts = ["#{options.indent}#{@completeText} ItemNode for #{@item.name}"]
|
|
||||||
nextIndent = options.indent + ' '
|
nextIndent = options.indent + ' '
|
||||||
if options.recursive
|
if options.recursive
|
||||||
for child in @children
|
for child in @children
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ All rights reserved.
|
|||||||
|
|
||||||
CraftingNode = require './crafting_node'
|
CraftingNode = require './crafting_node'
|
||||||
CraftingPlan = require './crafting_plan'
|
CraftingPlan = require './crafting_plan'
|
||||||
|
CraftingStep = require './crafting_step'
|
||||||
Inventory = require '../inventory'
|
Inventory = require '../inventory'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
@@ -30,37 +31,34 @@ module.exports = class PlanBuilder
|
|||||||
producePlans: (maxPlans=null)->
|
producePlans: (maxPlans=null)->
|
||||||
maxPlans = if maxPlans then @plans.length + maxPlans else Number.MAX_VALUE
|
maxPlans = if maxPlans then @plans.length + maxPlans else Number.MAX_VALUE
|
||||||
|
|
||||||
while @plans.length is 0 or (@plans.length < maxPlans and not @complete)
|
while not @complete and (@plans.length < maxPlans)
|
||||||
plan = @_captureCurrentPlan()
|
plan = @_captureCurrentPlan()
|
||||||
|
if plan?
|
||||||
@plans.push plan
|
@plans.push plan
|
||||||
|
|
||||||
@_incrementChoiceNodes()
|
@_incrementChoiceNodes()
|
||||||
|
|
||||||
return @_plans
|
return @_plans
|
||||||
|
|
||||||
# Property Methods #############################################################################
|
# Property Methods #############################################################################
|
||||||
|
|
||||||
isComplete: ->
|
|
||||||
return @_complete
|
|
||||||
|
|
||||||
getPlans: ->
|
|
||||||
return @_plans
|
|
||||||
|
|
||||||
getWanted: ->
|
|
||||||
return @_wanted
|
|
||||||
|
|
||||||
setWanted: (wanted)->
|
|
||||||
@_wanted = wanted or new Inventory
|
|
||||||
|
|
||||||
Object.defineProperties @prototype,
|
Object.defineProperties @prototype,
|
||||||
complete: { get:@prototype.isComplete }
|
|
||||||
plans: { get:@prototype.getPlans }
|
complete:
|
||||||
wanted: { get:@prototype.getWanted, set:@prototype.setWanted }
|
get: -> @_complete
|
||||||
|
|
||||||
|
plans:
|
||||||
|
get: -> @_plans
|
||||||
|
|
||||||
|
wanted:
|
||||||
|
get: -> @_wanted
|
||||||
|
set: (wanted)-> @_wanted = wanted or new Inventory
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
# Private Methods ##############################################################################
|
||||||
|
|
||||||
_captureCurrentPlan: ->
|
_captureCurrentPlan: ->
|
||||||
toVisit = [@_rootNode]
|
toVisit = [@_rootNode]
|
||||||
steps = []
|
stepNodes = []
|
||||||
|
|
||||||
while toVisit.length > 0
|
while toVisit.length > 0
|
||||||
node = toVisit.shift()
|
node = toVisit.shift()
|
||||||
@@ -70,14 +68,31 @@ module.exports = class PlanBuilder
|
|||||||
else if node.TYPE is CraftingNode::TYPES.ITEM
|
else if node.TYPE is CraftingNode::TYPES.ITEM
|
||||||
toVisit.push node.children[0] if node.children.length > 0
|
toVisit.push node.children[0] if node.children.length > 0
|
||||||
else if node.TYPE is CraftingNode::TYPES.RECIPE
|
else if node.TYPE is CraftingNode::TYPES.RECIPE
|
||||||
steps.push node.recipe
|
stepNodes.push node
|
||||||
toVisit.push(c) for c in node.children
|
toVisit.push(c) for c in node.children
|
||||||
|
|
||||||
steps.reverse()
|
steps = []
|
||||||
|
seenRecipes = {}
|
||||||
|
index = stepNodes.length - 1
|
||||||
|
while index >= 0
|
||||||
|
node = stepNodes[index]
|
||||||
|
index -= 1
|
||||||
|
return null unless node.valid and node.complete
|
||||||
|
|
||||||
|
recipeSlug = node.recipe.slug
|
||||||
|
continue if seenRecipes[recipeSlug]?
|
||||||
|
|
||||||
|
seenRecipes[recipeSlug] = true
|
||||||
|
steps.push new CraftingStep node.recipe
|
||||||
|
|
||||||
plan = new CraftingPlan steps, @_wanted
|
plan = new CraftingPlan steps, @_wanted
|
||||||
return plan
|
return plan
|
||||||
|
|
||||||
_incrementChoiceNodes: ->
|
_incrementChoiceNodes: ->
|
||||||
|
if @_choiceNodes.length is 0
|
||||||
|
@_complete = true
|
||||||
|
return
|
||||||
|
|
||||||
index = @_choiceNodes.length - 1
|
index = @_choiceNodes.length - 1
|
||||||
while true
|
while true
|
||||||
if index is -1
|
if index is -1
|
||||||
|
|||||||
@@ -34,14 +34,15 @@ module.exports = class RecipeNode extends CraftingNode
|
|||||||
|
|
||||||
_checkCompleteness: ->
|
_checkCompleteness: ->
|
||||||
for child in @children
|
for child in @children
|
||||||
return false unless child.isComplete
|
return false unless child.complete
|
||||||
return true
|
return true
|
||||||
|
|
||||||
_checkValidity: ->
|
_checkValidity: ->
|
||||||
for child in @children
|
|
||||||
return false unless child.isValid
|
|
||||||
|
|
||||||
return false if @_isRepeatedRecipe()
|
return false if @_isRepeatedRecipe()
|
||||||
|
|
||||||
|
for child in @children
|
||||||
|
return false unless child.valid
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
# Private Methods ##############################################################################
|
||||||
@@ -60,8 +61,7 @@ module.exports = class RecipeNode extends CraftingNode
|
|||||||
options.indent ?= ''
|
options.indent ?= ''
|
||||||
options.recursive ?= true
|
options.recursive ?= true
|
||||||
|
|
||||||
completeText = if @complete then 'complete' else 'incomplete'
|
parts = ["#{options.indent}#{@completeText} #{@validText} RecipeNode for #{@recipe.slug}"]
|
||||||
parts = ["#{options.indent}#{@completeText} RecipeNode for #{@recipe.slug}"]
|
|
||||||
nextIndent = options.indent + ' '
|
nextIndent = options.indent + ' '
|
||||||
if options.recursive
|
if options.recursive
|
||||||
for child in @children
|
for child in @children
|
||||||
|
|||||||
@@ -1,233 +0,0 @@
|
|||||||
###
|
|
||||||
Crafting Guide - crafting_plan.coffee
|
|
||||||
|
|
||||||
Copyright (c) 2014-2015 by Redwood Labs
|
|
||||||
All rights reserved.
|
|
||||||
###
|
|
||||||
|
|
||||||
BaseModel = require './base_model'
|
|
||||||
Inventory = require './inventory'
|
|
||||||
ItemSlug = require './item_slug'
|
|
||||||
Step = require './step'
|
|
||||||
_ = require 'underscore'
|
|
||||||
{Event} = require '../constants'
|
|
||||||
|
|
||||||
########################################################################################################################
|
|
||||||
|
|
||||||
module.exports = class CraftingPlan extends BaseModel
|
|
||||||
|
|
||||||
constructor: (attributes={}, options={})->
|
|
||||||
if not attributes.modPack then throw new Error 'modPack is required'
|
|
||||||
attributes.includingTools ?= false
|
|
||||||
super attributes, options
|
|
||||||
|
|
||||||
@have = new Inventory modPack:@modPack
|
|
||||||
@want = new Inventory modPack:@modPack
|
|
||||||
@need = new Inventory modPack:@modPack
|
|
||||||
@result = new Inventory modPack:@modPack
|
|
||||||
|
|
||||||
recraft = _.debounce (=> @craft()), 100
|
|
||||||
for inventory in [@have, @want]
|
|
||||||
inventory.on Event.change, recraft
|
|
||||||
|
|
||||||
@on Event.change + ':includingTools', recraft
|
|
||||||
|
|
||||||
@clear()
|
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
|
||||||
|
|
||||||
clear: (options={})->
|
|
||||||
@steps = []
|
|
||||||
@need.clear()
|
|
||||||
@result.clear()
|
|
||||||
|
|
||||||
@trigger 'change', this
|
|
||||||
return this
|
|
||||||
|
|
||||||
craft: ->
|
|
||||||
toolsMessage = if @includingTools then ' (including tools)' else ''
|
|
||||||
haveMessage = if @have.isEmpty then '' else " starting with #{@have.unparse()}"
|
|
||||||
logger.info => "crafting #{@want.unparse()}#{toolsMessage}#{haveMessage}"
|
|
||||||
|
|
||||||
@clear()
|
|
||||||
@have.localize()
|
|
||||||
@want.localize()
|
|
||||||
|
|
||||||
@result.addInventory @have
|
|
||||||
|
|
||||||
@steps = {}
|
|
||||||
@want.each (stack)=>
|
|
||||||
@_findSteps stack.itemSlug, {}, ignoreGatherable:true
|
|
||||||
item = @modPack.findItem stack.itemSlug
|
|
||||||
@need.add item.slug, stack.quantity
|
|
||||||
|
|
||||||
@steps = (step for recipeSlug, step of @steps)
|
|
||||||
@_resolveNeeds()
|
|
||||||
@_removeExtraSteps()
|
|
||||||
|
|
||||||
@result.addInventory @want
|
|
||||||
|
|
||||||
@need.trigger 'change', @need
|
|
||||||
@result.trigger 'change', @result
|
|
||||||
@trigger 'change', this
|
|
||||||
|
|
||||||
removeUncraftableItems: ->
|
|
||||||
toRemove = []
|
|
||||||
@want.each (stack)=>
|
|
||||||
item = @modPack.findItem stack.itemSlug
|
|
||||||
if not item? then toRemove.push stack.itemSlug
|
|
||||||
|
|
||||||
for itemSlug in toRemove
|
|
||||||
@want.remove itemSlug
|
|
||||||
|
|
||||||
# Event Methods ################################################################################
|
|
||||||
|
|
||||||
onIncludingToolsChanged: ->
|
|
||||||
@storage.setItem 'includingTools', "#{@includingTools}"
|
|
||||||
@craft()
|
|
||||||
|
|
||||||
# Object Overrides #############################################################################
|
|
||||||
|
|
||||||
toString: ->
|
|
||||||
return "#{@constructor.name} {
|
|
||||||
have:#{@have},
|
|
||||||
want:#{@want},
|
|
||||||
need:#{@need},
|
|
||||||
result:#{@result},
|
|
||||||
steps:#{@steps}
|
|
||||||
}"
|
|
||||||
|
|
||||||
# Private Methods ##############################################################################
|
|
||||||
|
|
||||||
_findRecipes: (item)->
|
|
||||||
recipes = @modPack.findRecipes item.slug
|
|
||||||
return null unless recipes? and recipes.length > 0
|
|
||||||
|
|
||||||
recipeOptions = []
|
|
||||||
for recipe in recipes
|
|
||||||
if recipe.tools.length is 0
|
|
||||||
recipeOptions.push recipe:recipe, missingTools:0
|
|
||||||
else
|
|
||||||
missingTools = []
|
|
||||||
for tool in recipe.tools
|
|
||||||
if not @have.hasAtLeast tool.itemSlug
|
|
||||||
missingTools.push tool
|
|
||||||
recipeOptions.push recipe:recipe, missingTools:missingTools.length
|
|
||||||
|
|
||||||
recipeOptions.sort (a, b)->
|
|
||||||
return 0 if a.missingTools is b.missingTools
|
|
||||||
return if a.missingTools < b.missingTools then -1 else +1
|
|
||||||
|
|
||||||
return (option.recipe for option in recipeOptions)
|
|
||||||
|
|
||||||
_findSteps: (itemSlug, parentSteps={})->
|
|
||||||
item = @modPack.findItem itemSlug
|
|
||||||
return unless item?
|
|
||||||
return unless item.isCraftable
|
|
||||||
|
|
||||||
ignoreGatherable = @want.hasAtLeast itemSlug, 1
|
|
||||||
if (not item.isGatherable) or ignoreGatherable
|
|
||||||
recipes = @_findRecipes item
|
|
||||||
recipes ?= []
|
|
||||||
|
|
||||||
if parentSteps[item.slug]?
|
|
||||||
logger.verbose -> "found cycle at #{item.slug}"
|
|
||||||
throw new Error 'invalid recipe path'
|
|
||||||
parentSteps[item.slug] = item
|
|
||||||
|
|
||||||
logger.verbose -> "exploring: #{item.slug}"
|
|
||||||
logger.indent()
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
logger.verbose -> "adding step for: #{recipe.slug}"
|
|
||||||
@steps[recipe.slug] = new Step outputItemSlug:item.slug, recipe: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)->
|
|
||||||
for recipeSlug, step of @steps
|
|
||||||
return true if step.recipe.produces itemSlug
|
|
||||||
return false
|
|
||||||
|
|
||||||
_qualifyItemSlug: (itemSlug)->
|
|
||||||
item = @modPack.findItem itemSlug
|
|
||||||
return item.slug if item?
|
|
||||||
return itemSlug
|
|
||||||
|
|
||||||
_removeExtraSteps: ->
|
|
||||||
result = []
|
|
||||||
|
|
||||||
number = 1
|
|
||||||
for step in @steps
|
|
||||||
if step.multiplier > 0
|
|
||||||
result.push step
|
|
||||||
step.number = number
|
|
||||||
number += 1
|
|
||||||
|
|
||||||
@steps = result
|
|
||||||
|
|
||||||
_resolveNeeds: ->
|
|
||||||
for i in [@steps.length-1..0] by -1
|
|
||||||
step = @steps[i]
|
|
||||||
step.number = i + 1
|
|
||||||
|
|
||||||
recipe = step.recipe
|
|
||||||
outputQuantity = recipe.getQuantityProducedOf step.outputItemSlug
|
|
||||||
|
|
||||||
step.multiplier = Math.ceil(@need.quantityOf(step.outputItemSlug) / outputQuantity)
|
|
||||||
|
|
||||||
if @includingTools
|
|
||||||
recipe.eachToolStack (stack)=>
|
|
||||||
itemSlug = @_qualifyItemSlug stack.itemSlug
|
|
||||||
available = @result.quantityOf(itemSlug) + @need.quantityOf(itemSlug)
|
|
||||||
needed = Math.max 0, stack.quantity - available
|
|
||||||
|
|
||||||
@need.add itemSlug, needed
|
|
||||||
@result.add itemSlug, needed
|
|
||||||
|
|
||||||
recipe.eachInputStack (stack)=>
|
|
||||||
itemSlug = @_qualifyItemSlug stack.itemSlug
|
|
||||||
needed = step.multiplier * stack.quantity
|
|
||||||
consumed = Math.min needed, @result.quantityOf itemSlug
|
|
||||||
remaining = needed - consumed
|
|
||||||
|
|
||||||
@result.remove itemSlug, consumed
|
|
||||||
@need.add itemSlug, remaining
|
|
||||||
|
|
||||||
recipe.eachOutputStack (stack)=>
|
|
||||||
itemSlug = @_qualifyItemSlug stack.itemSlug
|
|
||||||
created = stack.quantity * step.multiplier
|
|
||||||
consumed = Math.min created, @need.quantityOf itemSlug
|
|
||||||
remaining = created - consumed
|
|
||||||
|
|
||||||
@result.add itemSlug, remaining
|
|
||||||
@need.remove itemSlug, consumed
|
|
||||||
@@ -6,6 +6,7 @@ All rights reserved.
|
|||||||
###
|
###
|
||||||
|
|
||||||
BaseModel = require './base_model'
|
BaseModel = require './base_model'
|
||||||
|
ItemSlug = require './item_slug'
|
||||||
Stack = require './stack'
|
Stack = require './stack'
|
||||||
{Event} = require '../constants'
|
{Event} = require '../constants'
|
||||||
{StringBuilder} = require 'crafting-guide-common'
|
{StringBuilder} = require 'crafting-guide-common'
|
||||||
@@ -46,50 +47,15 @@ module.exports = class Recipe extends BaseModel
|
|||||||
return -1 if aValue
|
return -1 if aValue
|
||||||
return +1 if bValue
|
return +1 if bValue
|
||||||
|
|
||||||
aValue = a.getQuantityProducedOf itemSlug
|
aValue = a.getQuantityProduced itemSlug
|
||||||
bValue = b.getQuantityProducedOf itemSlug
|
bValue = b.getQuantityProduced 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.getOutputCount()
|
|
||||||
bValue = b.getOutputCount()
|
|
||||||
if aValue isnt bValue
|
|
||||||
return if aValue > bValue then -1 else +1
|
|
||||||
|
|
||||||
aValue = a.tools.length
|
|
||||||
bValue = b.tools.length
|
|
||||||
if aValue isnt bValue
|
|
||||||
return if aValue < bValue then -1 else +1
|
|
||||||
|
|
||||||
aValue = a.getInputCount()
|
|
||||||
bValue = b.getInputCount()
|
|
||||||
if aValue isnt bValue
|
|
||||||
return if aValue < bValue then -1 else +1
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Public Methods ###############################################################################
|
# Public Methods ###############################################################################
|
||||||
|
|
||||||
eachInputStack: (callback)->
|
|
||||||
for i in [0...@pattern.length]
|
|
||||||
stack = @getStackAtSlot(i)
|
|
||||||
continue unless stack?
|
|
||||||
callback stack
|
|
||||||
|
|
||||||
eachOutputStack: (callback)->
|
|
||||||
for stack in @output
|
|
||||||
callback stack
|
|
||||||
|
|
||||||
eachToolStack: (callback)->
|
|
||||||
for stack in @tools
|
|
||||||
callback stack
|
|
||||||
|
|
||||||
getInputCount: ->
|
|
||||||
result = 0
|
|
||||||
for stack in @input
|
|
||||||
result += stack.quantity
|
|
||||||
return result
|
|
||||||
|
|
||||||
getStackAtSlot: (patternSlot)->
|
getStackAtSlot: (patternSlot)->
|
||||||
trueIndex = 0:0, 1:1, 2:2, 3:4, 4:5, 5:6, 6:8, 7:9, 8:10
|
trueIndex = 0:0, 1:1, 2:2, 3:4, 4:5, 5:6, 6:8, 7:9, 8:10
|
||||||
patternDigit = @pattern[trueIndex[patternSlot]]
|
patternDigit = @pattern[trueIndex[patternSlot]]
|
||||||
@@ -101,13 +67,7 @@ module.exports = class Recipe extends BaseModel
|
|||||||
|
|
||||||
return stack
|
return stack
|
||||||
|
|
||||||
getOutputCount: ->
|
getQuantityProduced: (itemSlug)->
|
||||||
result = 0
|
|
||||||
for stack in @output
|
|
||||||
result += stack.quantity
|
|
||||||
return result
|
|
||||||
|
|
||||||
getQuantityProducedOf: (itemSlug)->
|
|
||||||
total = 0
|
total = 0
|
||||||
for stack in @output
|
for stack in @output
|
||||||
if stack.itemSlug.matches itemSlug
|
if stack.itemSlug.matches itemSlug
|
||||||
@@ -115,6 +75,16 @@ module.exports = class Recipe extends BaseModel
|
|||||||
|
|
||||||
return total
|
return total
|
||||||
|
|
||||||
|
getQuantityRequired: (itemSlug)->
|
||||||
|
total = 0
|
||||||
|
for i in [0...@pattern.length]
|
||||||
|
stack = @getStackAtSlot i
|
||||||
|
continue unless stack?
|
||||||
|
|
||||||
|
if ItemSlug.equal stack.itemSlug, itemSlug
|
||||||
|
total += stack.quantity
|
||||||
|
return total
|
||||||
|
|
||||||
hasAllTools: (modPack)->
|
hasAllTools: (modPack)->
|
||||||
modPack ?= @modVersion?.mod?.modPack
|
modPack ?= @modVersion?.mod?.modPack
|
||||||
return true unless modPack
|
return true unless modPack
|
||||||
@@ -179,7 +149,7 @@ module.exports = class Recipe extends BaseModel
|
|||||||
if not @_slug?
|
if not @_slug?
|
||||||
builder = new StringBuilder
|
builder = new StringBuilder
|
||||||
delimiterNeeded = false
|
delimiterNeeded = false
|
||||||
@eachInputStack (stack)->
|
for stack in @input
|
||||||
if delimiterNeeded then builder.push ','
|
if delimiterNeeded then builder.push ','
|
||||||
delimiterNeeded = true
|
delimiterNeeded = true
|
||||||
|
|
||||||
@@ -187,12 +157,12 @@ module.exports = class Recipe extends BaseModel
|
|||||||
builder.push stack.itemSlug.qualified
|
builder.push stack.itemSlug.qualified
|
||||||
|
|
||||||
builder.push '>'
|
builder.push '>'
|
||||||
@eachToolStack (stack)->
|
for stack in @tools
|
||||||
builder.push stack.itemSlug.qualified
|
builder.push stack.itemSlug.qualified
|
||||||
builder.push '>'
|
builder.push '>'
|
||||||
|
|
||||||
delimiterNeeded = false
|
delimiterNeeded = false
|
||||||
@eachOutputStack (stack)->
|
for stack in @output
|
||||||
if delimiterNeeded then builder.push ','
|
if delimiterNeeded then builder.push ','
|
||||||
delimiterNeeded = true
|
delimiterNeeded = true
|
||||||
|
|
||||||
@@ -215,7 +185,9 @@ module.exports = class Recipe extends BaseModel
|
|||||||
needsDelimiter = false
|
needsDelimiter = false
|
||||||
for stack in @input
|
for stack in @input
|
||||||
if needsDelimiter then result.push ', '
|
if needsDelimiter then result.push ', '
|
||||||
result.push stack.toString()
|
result.push @getQuantityRequired stack.itemSlug
|
||||||
|
result.push ' '
|
||||||
|
result.push stack.itemSlug
|
||||||
needsDelimiter = true
|
needsDelimiter = true
|
||||||
result.push ']'
|
result.push ']'
|
||||||
|
|
||||||
@@ -223,7 +195,9 @@ module.exports = class Recipe extends BaseModel
|
|||||||
needsDelimiter = false
|
needsDelimiter = false
|
||||||
for stack in @output
|
for stack in @output
|
||||||
if needsDelimiter then result.push ', '
|
if needsDelimiter then result.push ', '
|
||||||
result.push stack.toString()
|
result.push @getQuantityProduced stack.itemSlug
|
||||||
|
result.push ' '
|
||||||
|
result.push stack.itemSlug
|
||||||
needsDelimiter = true
|
needsDelimiter = true
|
||||||
result.push ']'
|
result.push ']'
|
||||||
|
|
||||||
|
|||||||
@@ -31,5 +31,5 @@ module.exports = class Step extends BaseModel
|
|||||||
|
|
||||||
_computeInventory: ->
|
_computeInventory: ->
|
||||||
@inventory.clear()
|
@inventory.clear()
|
||||||
@recipe.eachInputStack (stack)=>
|
for stack in @recipe.input
|
||||||
@inventory.add stack.itemSlug, stack.quantity * @multiplier
|
@inventory.add stack.itemSlug, stack.quantity * @multiplier
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ All rights reserved.
|
|||||||
|
|
||||||
CraftingPlan = require '../../src/coffee/models/crafting/crafting_plan'
|
CraftingPlan = require '../../src/coffee/models/crafting/crafting_plan'
|
||||||
fixtures = require './fixtures'
|
fixtures = require './fixtures'
|
||||||
|
ItemSlug = require '../../src/coffee/models/item_slug'
|
||||||
|
|
||||||
########################################################################################################################
|
########################################################################################################################
|
||||||
|
|
||||||
@@ -20,30 +21,75 @@ describe 'crafting_plan.coffee', ->
|
|||||||
plan.required.unparse().should.equal 'coal'
|
plan.required.unparse().should.equal 'coal'
|
||||||
plan.produced.unparse().should.equal 'coal'
|
plan.produced.unparse().should.equal 'coal'
|
||||||
|
|
||||||
it 'can compute a single item with a single step', ->
|
it 'can compute a single item with one single step plan', ->
|
||||||
plans = fixtures.makePlans [1, 'test__charcoal']
|
plans = fixtures.makePlans [1, 'test__charcoal']
|
||||||
plans.length.should.equal 1
|
plans.length.should.equal 1
|
||||||
|
|
||||||
plan = plans[0]
|
plan = plans[0]
|
||||||
plan.required.unparse().should.equal 'coal:8.oak_wood'
|
plan.required.unparse().should.equal 'coal:8.oak_wood'
|
||||||
plan.produced.unparse().should.equal '8.charcoal'
|
plan.produced.unparse().should.equal '8.charcoal'
|
||||||
|
(s.toString() for s in plan.steps).should.eql ['1x 8 test__oak_wood,test__coal>>8 test__charcoal']
|
||||||
|
|
||||||
it 'can compute a large quantity of a single item with a single step', ->
|
it 'can compute a large quantity of a single item with one single step plan', ->
|
||||||
plans = fixtures.makePlans [15, 'test__charcoal']
|
plans = fixtures.makePlans [15, 'test__charcoal']
|
||||||
plans.length.should.equal 1
|
plans.length.should.equal 1
|
||||||
|
|
||||||
plan = plans[0]
|
plan = plans[0]
|
||||||
plan.required.unparse().should.equal '2.coal:16.oak_wood'
|
plan.required.unparse().should.equal '2.coal:16.oak_wood'
|
||||||
plan.produced.unparse().should.equal '16.charcoal'
|
plan.produced.unparse().should.equal '16.charcoal'
|
||||||
|
(s.toString() for s in plan.steps).should.eql ['2x 8 test__oak_wood,test__coal>>8 test__charcoal']
|
||||||
|
|
||||||
it 'can compute a single item with multiple steps', ->
|
it 'can compute a single item with multiple plans', ->
|
||||||
plans = fixtures.makePlans [1, 'test__iron_ingot']
|
plans = fixtures.makePlans [1, 'test__iron_ingot']
|
||||||
plans.length.should.equal 2
|
plans.length.should.equal 2
|
||||||
|
|
||||||
plan = plans[0]
|
plan = plans[0]
|
||||||
plan.required.unparse().should.equal 'coal:8.iron_ore:8.oak_wood'
|
plan.required.unparse().should.equal 'coal:8.iron_ore:8.oak_wood'
|
||||||
plan.produced.unparse().should.equal '7.charcoal:iron_ingot'
|
plan.produced.unparse().should.equal '7.charcoal:8.iron_ingot'
|
||||||
|
(s.toString() for s in plan.steps).should.eql [
|
||||||
|
'1x 8 test__oak_wood,test__coal>>8 test__charcoal'
|
||||||
|
'1x 8 test__iron_ore,test__charcoal>test__furnace>8 test__iron_ingot'
|
||||||
|
]
|
||||||
|
|
||||||
plan = plans[1]
|
plan = plans[1]
|
||||||
plan.required.unparse().should.equal 'coal:8.iron_ore'
|
plan.required.unparse().should.equal 'coal:8.iron_ore'
|
||||||
plan.produced.unparse().should.equal 'iron_ingot'
|
plan.produced.unparse().should.equal '8.iron_ingot'
|
||||||
|
(s.toString() for s in plan.steps).should.eql [
|
||||||
|
'1x 8 test__iron_ore,test__coal>test__furnace>8 test__iron_ingot'
|
||||||
|
]
|
||||||
|
|
||||||
|
it 'can compute multiple items with multiple plans', ->
|
||||||
|
plans = fixtures.makePlans [1, 'test__copper_block'], [1, 'test__iron_sword']
|
||||||
|
plans.length.should.equal 4
|
||||||
|
|
||||||
|
for plan in plans
|
||||||
|
plan.required.unparse().should.match /16.copper_ore.*8.iron_ore/
|
||||||
|
plan.produced.unparse().should.match /copper_block.*:iron_sword/
|
||||||
|
|
||||||
|
plan = plans[0]
|
||||||
|
plan.required.unparse().should.match /3.coal.*9.oak_wood/
|
||||||
|
plan.produced.unparse().should.match /7.charcoal/
|
||||||
|
"#{plan.steps[3]}".should.equal '1x 8 test__iron_ore,test__charcoal>test__furnace>8 test__iron_ingot'
|
||||||
|
"#{plan.steps[4]}".should.equal '2x 8 test__copper_ore,test__coal>test__furnace>8 test__copper_ingot'
|
||||||
|
plan.steps.length.should.equal 7
|
||||||
|
|
||||||
|
plan = plans[1]
|
||||||
|
plan.required.unparse().should.match /3.coal.*:oak_wood/
|
||||||
|
plan.produced.unparse().should.not.match /charcoal/
|
||||||
|
"#{plan.steps[2]}".should.equal '1x 8 test__iron_ore,test__coal>test__furnace>8 test__iron_ingot'
|
||||||
|
"#{plan.steps[3]}".should.equal '2x 8 test__copper_ore,test__coal>test__furnace>8 test__copper_ingot'
|
||||||
|
plan.steps.length.should.equal 6
|
||||||
|
|
||||||
|
plan = plans[2]
|
||||||
|
plan.required.unparse().should.match /^coal.*9.oak_wood/
|
||||||
|
plan.produced.unparse().should.match /5.charcoal/
|
||||||
|
"#{plan.steps[3]}".should.equal '1x 8 test__iron_ore,test__charcoal>test__furnace>8 test__iron_ingot'
|
||||||
|
"#{plan.steps[4]}".should.equal '2x 8 test__copper_ore,test__charcoal>test__furnace>8 test__copper_ingot'
|
||||||
|
plan.steps.length.should.equal 7
|
||||||
|
|
||||||
|
plan = plans[3]
|
||||||
|
plan.required.unparse().should.match /2.coal.*9.oak_wood/
|
||||||
|
plan.produced.unparse().should.match /6.charcoal/
|
||||||
|
"#{plan.steps[3]}".should.equal '1x 8 test__iron_ore,test__coal>test__furnace>8 test__iron_ingot'
|
||||||
|
"#{plan.steps[4]}".should.equal '2x 8 test__copper_ore,test__charcoal>test__furnace>8 test__copper_ingot'
|
||||||
|
plan.steps.length.should.equal 7
|
||||||
|
|||||||
@@ -41,16 +41,19 @@ MOD_VERSION_FILE =
|
|||||||
|
|
||||||
item: Copper Ingot
|
item: Copper Ingot
|
||||||
recipe:
|
recipe:
|
||||||
input: Copper Ore, Coal
|
input: 8 Copper Ore, Coal
|
||||||
pattern: .0. ... .1.
|
pattern: .0. ... .1.
|
||||||
|
quantity: 8
|
||||||
tools: Furnace
|
tools: Furnace
|
||||||
recipe:
|
recipe:
|
||||||
input: Copper Ore, Charcoal
|
input: 8 Copper Ore, Charcoal
|
||||||
pattern: .0. ... .1.
|
pattern: .0. ... .1.
|
||||||
|
quantity: 8
|
||||||
tools: Furnace
|
tools: Furnace
|
||||||
recipe:
|
recipe:
|
||||||
input: Copper Block
|
input: Copper Block
|
||||||
pattern: ... .0. ...
|
pattern: ... .0. ...
|
||||||
|
quantity: 9
|
||||||
|
|
||||||
item: Copper Ore
|
item: Copper Ore
|
||||||
|
|
||||||
@@ -66,10 +69,12 @@ MOD_VERSION_FILE =
|
|||||||
recipe:
|
recipe:
|
||||||
input: 8 Iron Ore, Charcoal
|
input: 8 Iron Ore, Charcoal
|
||||||
pattern: .0. ... .1.
|
pattern: .0. ... .1.
|
||||||
|
quantity: 8
|
||||||
tools: Furnace
|
tools: Furnace
|
||||||
recipe:
|
recipe:
|
||||||
input: 8 Iron Ore, Coal
|
input: 8 Iron Ore, Coal
|
||||||
pattern: .0. ... .1.
|
pattern: .0. ... .1.
|
||||||
|
quantity: 8
|
||||||
tools: Furnace
|
tools: Furnace
|
||||||
|
|
||||||
item: Iron Sword
|
item: Iron Sword
|
||||||
|
|||||||
@@ -13,27 +13,31 @@ PlanBuilder = require '../../src/coffee/models/crafting/plan_builder'
|
|||||||
describe 'plan_builder.coffee', ->
|
describe 'plan_builder.coffee', ->
|
||||||
|
|
||||||
printPlan = (plan)->
|
printPlan = (plan)->
|
||||||
return ((s.slug.replace(/^.*>.*>/, '') for s in plan.steps;;)).join ' > '
|
return '' unless plan?
|
||||||
|
return ((s.recipe.slug.replace(/^.*>.*>/, '') for s in plan.steps;;)).join ' > '
|
||||||
|
|
||||||
it 'generates an empty plan for a gatherable item', ->
|
it 'generates an empty plan for a gatherable item', ->
|
||||||
builder = new PlanBuilder fixtures.makeTree 'test__oak_wood'
|
plans = fixtures.makePlans [1, 'test__oak_wood']
|
||||||
plans = builder.producePlans 100
|
|
||||||
|
|
||||||
plans.length.should.equal 1
|
plans.length.should.equal 1
|
||||||
plans[0].length.should.equal 0
|
plans[0].length.should.equal 0
|
||||||
builder.complete.should.be.true
|
|
||||||
|
|
||||||
it 'can find a multi-step plan', ->
|
it 'can find a multi-step plan', ->
|
||||||
builder = new PlanBuilder fixtures.makeTree 'test__lever'
|
plans = fixtures.makePlans [1, 'test__lever']
|
||||||
plans = builder.producePlans 100
|
|
||||||
|
|
||||||
printPlan(plans[0]).should.equal '4 test__oak_planks > 4 test__stick > test__lever'
|
printPlan(plans[0]).should.equal '4 test__oak_planks > 4 test__stick > test__lever'
|
||||||
plans.length.should.equal 1
|
plans.length.should.equal 1
|
||||||
builder.complete.should.be.true
|
|
||||||
|
|
||||||
it 'can find multiple plans', ->
|
it 'can find multiple plans', ->
|
||||||
builder = new PlanBuilder fixtures.makeTree 'test__iron_ingot'
|
plans = fixtures.makePlans [1, 'test__iron_ingot']
|
||||||
plans = builder.producePlans 100
|
|
||||||
|
|
||||||
printPlan(plans[0]).should.equal '8 test__charcoal > test__iron_ingot'
|
printPlan(plans[0]).should.equal '8 test__charcoal > 8 test__iron_ingot'
|
||||||
printPlan(plans[1]).should.equal 'test__iron_ingot'
|
printPlan(plans[1]).should.equal '8 test__iron_ingot'
|
||||||
|
plans.length.should.equal 2
|
||||||
|
|
||||||
|
it 'ignores invalid plans', ->
|
||||||
|
plans = fixtures.makePlans [1, 'test__copper_block']
|
||||||
|
|
||||||
|
printPlan(plans[0]).should.equal '8 test__copper_ingot > test__copper_block'
|
||||||
|
printPlan(plans[1]).should.equal '8 test__charcoal > 8 test__copper_ingot > test__copper_block'
|
||||||
|
plans.length.should.equal 2
|
||||||
|
|||||||
@@ -91,6 +91,11 @@ describe 'mod_version_parser_v1.coffee', ->
|
|||||||
modVersion = parser.parse baseText + 'recipe:; input:Delta, Echo, Foxtrot; pattern:...012...'
|
modVersion = parser.parse baseText + 'recipe:; input:Delta, Echo, Foxtrot; pattern:...012...'
|
||||||
(s.item for s in modVersion._slugs).should.eql ['charlie', 'delta', 'echo', 'foxtrot']
|
(s.item for s in modVersion._slugs).should.eql ['charlie', 'delta', 'echo', 'foxtrot']
|
||||||
|
|
||||||
|
it 'correctly handles recipes which use the same input multiple times', ->
|
||||||
|
modVersion = parser.parse baseText + 'recipe:; input:Alpha; pattern:.0..0....'
|
||||||
|
recipe = _.values(modVersion._recipes)[0]
|
||||||
|
recipe.getQuantityRequired(ItemSlug.slugify('alpha')).should.equal 2
|
||||||
|
|
||||||
it 'allows a quantity for each input', ->
|
it 'allows a quantity for each input', ->
|
||||||
modVersion = parser.parse baseText + 'recipe:; input: 12 Delta, 3 Echo; pattern:... 0.1 ...'
|
modVersion = parser.parse baseText + 'recipe:; input: 12 Delta, 3 Echo; pattern:... 0.1 ...'
|
||||||
recipe = _.values(modVersion._recipes)[0]
|
recipe = _.values(modVersion._recipes)[0]
|
||||||
|
|||||||
Reference in New Issue
Block a user