Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Decision making engine.
- # Thinking made easy.
- # 2012 - Tyler Elric
- # Feel free to share / distribute / use / etc
- # Just please don't remove these comments.
- from collections import defaultdict as DefaultDict
- class Action:
- "One potential action to take as the result of a decision."
- def apply(self, context):
- """
- Applies this action to the context.
- Returns feedback to the decision, which
- can be factored into an experience object.
- Must be defined by sub-classes.
- """
- raise NotImplementedError()
- class Feedback(DefaultDict):
- """
- A collection of weights aligned towards different types of guide-lines.
- Adds to experience.
- """
- def __init__(self,*a,**k):DefaultDict.__init__(self,lambda:0, *a, **k)
- class Experience(DefaultDict):
- "A collection of weights aligned towards different types of guide-lines."
- def __init__(self,*a,**k):DefaultDict.__init__(self, lambda:1, *a, **k)
- def handle_feedback(self, feedback):
- for key in self.keys():
- self[key] += feedback[key]
- class Guideline:
- "Scores a possibility based on rules determined by sub-classes."
- def score(self, action, context):
- """
- Returns a float number between 0 and 1 based on the 'goodness'
- of a possible action.
- """
- base = self.judge( action, context)
- b = abs(base)
- if b > 1 and b <= 100:return base / 100.0
- elif b >= 0 and b <= 1: return base
- else:
- raise ArithmeticError(
- "{}.judge() returned an over-saturated score of {}."
- .format(type(self).__name__, base)
- )
- def judge(self, action, context):
- """
- Returns a percentage-score. This must be defined
- by any sub-classes, lest the NotImplementedError should be raised.
- arguments:
- action - a Possible action to take ( class Possibility )
- context- relevant information to take into account when
- judging a possible action.
- """
- raise NotImplementedError()
- class Decision:
- """
- Takes a list of possible actions,
- and finds the best one based on pre-defined guide-lines.
- Sub-classes should define their own class-wide guidelines variable.
- ( eg: )
- class MoveDecision(Decision):
- guidelines = [ GuidelineA(), GuidelineB()]
- or define self.guidelines before calling Decision.__init__()
- """
- guidelines = []
- min_score = .25
- def __init__(self,experience=None):self.experience=experience or Experience()
- def decide(self, context, options):
- def judge(opt):
- score = 0
- for guide in self.guidelines:
- try:
- score+=guide.score(opt,context)*self.experience[type(guide)]
- except ArithmeticError:
- continue
- return score
- choices = sorted([(-1 * judge(o), o) for o in options],key=lambda x:x[0])
- for score, choice in choices:
- if -1 * score >= self.min_score:
- return choice
- return None
- class DecisionMaker:
- """
- Generates decisions,
- and applies actions.
- Also handles any situations
- where an appropriate action
- could not be determined.
- """
- decision_type = None
- actions = []
- def __init__(self,experience=None):
- self.experience=experience or Experience()
- def __call__(self, context):
- assert self.decision_type is not None
- action = self.decision_type(self.experience).decide(context, self.actions)
- if action is not None:
- self.experience.handle_feedback(action.apply(context))
- return context
- if __name__ == "__main__":
- class UrlDirection(Action):
- keywords = []
- correct = "/error"
- def apply(self,context):context[2] = self.correct
- class VideoWatcher(UrlDirection):
- correct = "/video"
- keywords = [ 'watch', 'video', 'movie', 'view']
- class PhotoViewer(UrlDirection):
- correct = "/photo"
- keywords = [ 'view', 'photo', 'image', 'picture']
- class TextReader(UrlDirection):
- correct = "/post"
- keywords = [ 'read', 'text', 'post', 'blog' ]
- class UrlDeterminant(Guideline):
- def judge(self, action, context):
- if not isinstance(action, UrlDirection):
- return 0
- keywords= context[1]
- score = sum(1 for kw in keywords if kw in action.keywords)
- return score / len(keywords)
- class UrlDecision(Decision):
- guidelines = [
- UrlDeterminant()
- ]
- class UrlRouter(DecisionMaker):
- actions = [
- VideoWatcher(),
- PhotoViewer(),
- TextReader()
- ]
- decision_type = UrlDecision
- def url_context(url):return [url,url.split("/"),"/error"]
- test_contexts = [url_context(u) for u in [
- "/lol/video/xyzdqf",
- "/video/xaHUR",
- "durpy/photo/",
- "DUAFPAJFK/HAHA"
- ]]
- server = UrlRouter()
- for context in test_contexts:
- print("Before:",context[2])
- server(context)
- print("After:",context[2],"\n")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement