Add plan builder and tests
This commit is contained in:
@@ -5,18 +5,29 @@ Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
_ = require 'underscore'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class CraftingNode
|
||||
|
||||
@::TYPES =
|
||||
INVENTORY: 0
|
||||
ITEM: 1
|
||||
RECIPE: 2
|
||||
|
||||
@::TYPE = null
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.modPack? then throw new Error 'options.modPack is required'
|
||||
|
||||
@modPack = options.modPack
|
||||
|
||||
@_children = []
|
||||
@_complete = null
|
||||
@_valid = null
|
||||
@_children = []
|
||||
@_complete = null
|
||||
@_id = _.uniqueId 'node_'
|
||||
@_rotations = 0
|
||||
@_valid = null
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
@@ -25,8 +36,9 @@ module.exports = class CraftingNode
|
||||
|
||||
for child in @_createChildren()
|
||||
child.parent = this
|
||||
@_children.push child
|
||||
queue.push child
|
||||
if child.valid
|
||||
@_children.push child
|
||||
queue.push child
|
||||
return queue
|
||||
|
||||
acceptVisitor: (visitor)->
|
||||
@@ -47,6 +59,10 @@ module.exports = class CraftingNode
|
||||
leave = visitor['onLeaveOtherNode']
|
||||
if leave? then leave.call visitor, this
|
||||
|
||||
rotateChildren: ->
|
||||
@_children.push @_children.shift()
|
||||
@_rotations += 1
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
getChildren: ->
|
||||
@@ -66,6 +82,12 @@ module.exports = class CraftingNode
|
||||
maxDepth = Math.max maxDepth, child.getDepth() + 1
|
||||
return maxDepth
|
||||
|
||||
getId: ->
|
||||
return @_id
|
||||
|
||||
getRotations: ->
|
||||
return @_rotations
|
||||
|
||||
getSize: ->
|
||||
size = 1
|
||||
for child in @children
|
||||
@@ -85,7 +107,10 @@ module.exports = class CraftingNode
|
||||
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 ##############################################################################
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
###
|
||||
Crafting Guide - crafting_plan.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class CraftingPlan
|
||||
|
||||
constructor: ->
|
||||
@@ -14,6 +14,7 @@ module.exports = class InventoryNode extends CraftingNode
|
||||
|
||||
@::ENTER_METHOD = 'onEnterInventoryNode'
|
||||
@::LEAVE_METHOD = 'onLeaveInventoryNode'
|
||||
@::TYPE = CraftingNode::TYPES.INVENTORY
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.inventory? then throw new Error 'options.inventory is required'
|
||||
@@ -38,6 +39,7 @@ module.exports = class InventoryNode extends CraftingNode
|
||||
_checkValidity: ->
|
||||
for child in @children
|
||||
return false unless child.isValid
|
||||
return true
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ module.exports = class ItemNode extends CraftingNode
|
||||
|
||||
@::ENTER_METHOD = 'onEnterItemNode'
|
||||
@::LEAVE_METHOD = 'onLeaveItemNode'
|
||||
@::TYPE = CraftingNode::TYPES.ITEM
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.item? then throw new Error 'options.item is required'
|
||||
@@ -58,8 +59,10 @@ module.exports = class ItemNode extends CraftingNode
|
||||
return false
|
||||
|
||||
_checkValidity: ->
|
||||
return true unless @children.length > 0
|
||||
for child in @children
|
||||
return true if child.isValid
|
||||
return false
|
||||
|
||||
# Object Overrides #############################################################################
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
###
|
||||
Crafting Plan - plan_builder.coffee
|
||||
|
||||
Copyright (c) 2015 by Redwood Labs
|
||||
All rights reserved.
|
||||
###
|
||||
|
||||
CraftingNode = require './crafting_node'
|
||||
|
||||
########################################################################################################################
|
||||
|
||||
module.exports = class PlanBuilder
|
||||
|
||||
constructor: (rootNode)->
|
||||
if not rootNode? then throw new Error 'rootNode is required'
|
||||
|
||||
@_choiceNodes = []
|
||||
@_complete = false
|
||||
@_plans = []
|
||||
@_rootNode = rootNode
|
||||
|
||||
@_isolateChoiceNodes()
|
||||
|
||||
# Public Methods ###############################################################################
|
||||
|
||||
producePlans: (maxPlans=null)->
|
||||
maxPlans = if maxPlans then @plans.length + maxPlans else Number.MAX_VALUE
|
||||
|
||||
while @plans.length < maxPlans and not @complete
|
||||
plan = @_captureCurrentPlan()
|
||||
@plans.push plan if plan.length > 0
|
||||
@_incrementChoiceNodes()
|
||||
|
||||
return @_plans
|
||||
|
||||
# Property Methods #############################################################################
|
||||
|
||||
isComplete: ->
|
||||
return @_complete
|
||||
|
||||
getPlans: ->
|
||||
return @_plans
|
||||
|
||||
Object.defineProperties @prototype,
|
||||
complete: { get:@prototype.isComplete }
|
||||
plans: { get:@prototype.getPlans }
|
||||
|
||||
# Private Methods ##############################################################################
|
||||
|
||||
_captureCurrentPlan: ->
|
||||
toVisit = [@_rootNode]
|
||||
plan = []
|
||||
|
||||
while toVisit.length > 0
|
||||
node = toVisit.shift()
|
||||
|
||||
if node.TYPE is CraftingNode::TYPES.INVENTORY
|
||||
toVisit.push(c) for c in node.children
|
||||
else if node.TYPE is CraftingNode::TYPES.ITEM
|
||||
toVisit.push node.children[0] if node.children.length > 0
|
||||
else if node.TYPE is CraftingNode::TYPES.RECIPE
|
||||
plan.push node.recipe
|
||||
toVisit.push(c) for c in node.children
|
||||
|
||||
return plan.reverse()
|
||||
|
||||
_incrementChoiceNodes: ->
|
||||
index = @_choiceNodes.length - 1
|
||||
while true
|
||||
if index is -1
|
||||
@_complete = true
|
||||
return
|
||||
|
||||
node = @_choiceNodes[index]
|
||||
node.rotateChildren()
|
||||
|
||||
return unless node.rotations % node.children.length is 0
|
||||
index -= 1
|
||||
|
||||
_isolateChoiceNodes: ->
|
||||
@_rootNode.acceptVisitor
|
||||
onEnterItemNode: (node)=>
|
||||
if node.children.length > 1
|
||||
@_choiceNodes.push node
|
||||
@@ -14,6 +14,7 @@ module.exports = class RecipeNode extends CraftingNode
|
||||
|
||||
@::ENTER_METHOD = 'onEnterRecipeNode'
|
||||
@::LEAVE_METHOD = 'onLeaveRecipeNode'
|
||||
@::TYPE = CraftingNode::TYPES.RECIPE
|
||||
|
||||
constructor: (options={})->
|
||||
if not options.recipe? then throw new Error 'options.recipe is required'
|
||||
|
||||
Reference in New Issue
Block a user