Add code to build crafting trees

This commit is contained in:
Andrew Miner
2015-08-25 21:15:08 -07:00
parent 4ff2915d85
commit ea82518b0d
8 changed files with 492 additions and 2 deletions
@@ -0,0 +1,99 @@
###
Crafting Guide - crafting_node.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
########################################################################################################################
module.exports = class CraftingNode
constructor: (options={})->
if not options.modPack? then throw new Error 'options.modPack is required'
@modPack = options.modPack
@_children = []
@_complete = null
@_valid = null
# Public Methods ###############################################################################
expand: (queue=[])->
return queue if @children.length > 0
for child in @_createChildren()
child.parent = this
@_children.push child
queue.push child
return queue
getNode: (path)->
return null unless _.isArray(path) and path.length > 0
child = @children[path[0]]
return null unless child?
return child.getPath path[1..]
# Property Methods #############################################################################
getChildren: ->
return @_children
isComplete: ->
if not @_complete?
@_complete = @_checkCompleteness()
return @_complete
getCompleteText: ->
return if @complete then "" else ""
getDepth: ->
maxDepth = 1
for child in @children
maxDepth = Math.max maxDepth, child.getDepth() + 1
return maxDepth
getSize: ->
size = 1
for child in @children
size += child.size
return size
getNodeType: ->
return @_nodeType
isValid: ->
return @_valid if @_valid?
valid = @_checkValidity()
@_valid = false if not valid
return valid
Object.defineProperties @prototype,
children: { get:@prototype.getChildren }
complete: { get:@prototype.isComplete }
completeText: { get:@prototype.getCompleteText }
depth: { get:@prototype.getDepth }
size: { get:@prototype.getSize }
# Virtual Methods ##############################################################################
# Subclasses must override this method to create such children as are appropriate for that kind of node. The new
# nodes should be appended to the given array.
_createChildren: (result)->
throw new Error "#{@constructor.name} must override the _createChildren method"
# Subclasses must override this method to indicate whether the node has at least one "complete" subtree rooted with
# itself. Completeness may be defined differently by different subclasses.
_checkCompleteness: ->
throw new Error "#{@constructor.name} must override the _checkCompletness method"
# Subclasses must override this method to determine whether the subtree represented by a node should still be
# explored for having a useful crafting plan.
_checkValidity: ->
throw new Error "#{@constructor.name} must override the _checkValidity method"
@@ -0,0 +1,72 @@
###
Crafting Guide - graph_builder.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
Inventory = require '../inventory'
InventoryNode = require './inventory_node'
########################################################################################################################
module.exports = class GraphBuilder
constructor: (options={})->
if not options.modPack? then throw new Error 'options.modPack is required'
@modPack = options.modPack
@_wanted = options.wanted ?= new Inventory
@_wanted.on 'change', => @reset()
@reset()
# Public Methods ###############################################################################
expandGraph: (steps=null)->
maxSteps = if steps? then @_stepCount + steps else Number.MAX_VALUE
@_queue ?= []
while true
break if @_queue.length is 0
break if @_stepCount >= maxSteps
node = @_queue.shift()
break unless node?
node.expand @_queue
@_stepCount += 1
reset: ->
@_rootNode = new InventoryNode modPack:@modPack, inventory:@wanted
@_queue = [@_rootNode]
@_stepCount = 0
# Property Methods #############################################################################
isComplete: ->
return false unless @_rootNode?
return false unless @_queue?
return false unless @_queue.length is 0
return true
getRootNode: ->
return @_rootNode
getStepCount: ->
return @_stepCount
getWanted: ->
return @_wanted
Object.defineProperties @prototype,
complete: { get:@prototype.isComplete }
rootNode: { get:@prototype.getRootNode }
stepCount: { get:@prototype.getStepCount }
wanted: { get:@prototype.getWanted }
# Object Overrides ############################################################################
toString: (indent='')->
"Build Tree\n#{@_rootNode.toString(indent + ' ')}"
@@ -0,0 +1,47 @@
###
Crafting Guide - crafting_plan_node.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
CraftingNode = require './crafting_node'
ItemNode = require './item_node'
########################################################################################################################
module.exports = class InventoryNode extends CraftingNode
constructor: (options={})->
if not options.inventory? then throw new Error 'options.inventory is required'
super options
@inventory = options.inventory
# CraftingNode Overrides #######################################################################
_createChildren: (result=[])->
@inventory.each (stack)=>
item = @modPack.findItem stack.itemSlug
if not item? then throw new Error "Could not find an item for slug: #{stack.itemSlug}"
result.push new ItemNode modPack:@modPack, item:item
return result
_checkCompleteness: ->
for child in @children
return false unless child.isComplete
return true
_checkValidity: ->
for child in @children
return false unless child.isValid
# Object Overrides #############################################################################
toString: (indent='')->
completeText = if @complete then 'complete' else 'incomplete'
parts = ["#{indent}#{@completeText} InventoryNode for #{@inventory}"]
nextIndent = indent + ' '
for child in @children
parts.push child.toString nextIndent
return parts.join '\n'
@@ -0,0 +1,69 @@
###
Crafting Guide - item_node.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
CraftingNode = require './crafting_node'
RecipeNode = require './recipe_node'
########################################################################################################################
module.exports = class ItemNode extends CraftingNode
constructor: (options={})->
if not options.item? then throw new Error 'options.item is required'
super options
@item = options.item
@_recipes = null
# Property Methods #############################################################################
getRecipes: ->
if not @_recipes?
@_recipes = @modPack.findRecipes @item.slug
return @_recipes or []
isGatherable: ->
return true if @item.isGatherable
return true unless @getRecipes().length > 0
return false
Object.defineProperties @prototype,
gatherable: { get:@prototype.isGatherable }
recipes: { get:@prototype.getRecipes }
# CraftingNode Overrides #######################################################################
_createChildren: (result=[])->
recipes = @getRecipes()
return [] unless recipes.length > 0
for recipe in recipes
result.push new RecipeNode modPack:@modPack, recipe:recipe
return result
_checkCompleteness: ->
return true if @gatherable
return false unless @children?
for child in @children
return true if child.isComplete
return false
_checkValidity: ->
for child in @children
return true if child.isValid
# Object Overrides #############################################################################
toString: (indent)->
completeText = if @complete then 'complete' else 'incomplete'
parts = ["#{indent}#{@completeText} ItemNode for #{@item.name}"]
nextIndent = indent + ' '
for child in @children
parts.push child.toString nextIndent
return parts.join '\n'
@@ -0,0 +1,61 @@
###
Crafting Guide - recipe_node.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
CraftingNode = require './crafting_node'
# ItemNode = require './item_node' # don't include here, causes a cycle
########################################################################################################################
module.exports = class RecipeNode extends CraftingNode
constructor: (options={})->
if not options.recipe? then throw new Error 'options.recipe is required'
super options
@recipe = options.recipe
# CraftingNode Overrides #######################################################################
_createChildren: (result=[])->
ItemNode = require './item_node' # include here to avoid a cycle
for stack in @recipe.input
item = @modPack.findItem stack.itemSlug
result.push new ItemNode modPack:@modPack, item:item
return result
_checkCompleteness: ->
for child in @children
return false unless child.isComplete
return true
_checkValidity: ->
for child in @children
return false unless child.isValid
return false if @_isRepeatedRecipe()
return true
# Private Methods ##############################################################################
_isRepeatedRecipe: ->
nextParent = @parent
while nextParent?
return true if nextParent.recipe is @recipe
nextParent = nextParent.parent
return false
# Object Overrides ############################################################################
toString: (indent)->
completeText = if @complete then 'complete' else 'incomplete'
parts = ["#{indent}#{@completeText} RecipeNode for #{@recipe.slug}"]
nextIndent = indent + ' '
for child in @children
parts.push child.toString nextIndent
return parts.join '\n'
+1 -1
View File
@@ -170,7 +170,7 @@ module.exports = class Inventory extends BaseModel
# Object Overrides #############################################################################
toString: ->
result = [@constructor.name, " (", @cid, ") { items: ["]
result = [@constructor.name, " (", @cid, ") {items: ["]
needsDelimiter = false
@each (stack)->