Advertisement
Tyler_Elric

logic.py

Feb 20th, 2013
118
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.38 KB | None | 0 0
  1.  
  2. # Decision making engine.
  3. # Thinking made easy.
  4. # 2012 - Tyler Elric
  5. # Feel free to share / distribute / use / etc
  6. # Just please don't remove these comments.
  7.  
  8. from collections import defaultdict as DefaultDict
  9.  
  10. class Action:
  11.     "One potential action to take as the result of a decision."
  12.     def apply(self, context):
  13.         """
  14.         Applies this action to the context.
  15.         Returns feedback to the decision, which
  16.         can be factored into an experience object.
  17.         Must be defined by sub-classes.
  18.         """
  19.         raise NotImplementedError()
  20.  
  21. class Feedback(DefaultDict):
  22.     """
  23.     A collection of weights aligned towards different types of guide-lines.
  24.     Adds to experience.
  25.     """
  26.     def __init__(self,*a,**k):DefaultDict.__init__(self,lambda:0, *a, **k)
  27.  
  28. class Experience(DefaultDict):
  29.     "A collection of weights aligned towards different types of guide-lines."
  30.     def __init__(self,*a,**k):DefaultDict.__init__(self, lambda:1, *a, **k)
  31.     def handle_feedback(self, feedback):
  32.         for key in self.keys():
  33.             self[key] += feedback[key]
  34.  
  35. class Guideline:
  36.     "Scores a possibility based on rules determined by sub-classes."
  37.     def score(self, action, context):
  38.         """
  39.         Returns a float number between 0 and 1 based on the 'goodness'
  40.         of a possible action.
  41.         """
  42.         base = self.judge( action, context)
  43.         b = abs(base)
  44.         if b > 1 and b <= 100:return base / 100.0
  45.         elif b >= 0 and b <= 1: return base
  46.         else:
  47.             raise ArithmeticError(
  48.                 "{}.judge() returned an over-saturated score of {}."
  49.                 .format(type(self).__name__, base)
  50.             )
  51.  
  52.     def judge(self, action, context):
  53.         """
  54.         Returns a percentage-score. This must be defined
  55.         by any sub-classes, lest the NotImplementedError should be raised.
  56.         arguments:
  57.          action - a Possible action to take ( class Possibility )
  58.          context- relevant information to take into account when
  59.                  judging a possible action.
  60.         """
  61.         raise NotImplementedError()
  62.  
  63. class Decision:
  64.     """
  65.     Takes a list of possible actions,
  66.     and finds the best one based on pre-defined guide-lines.
  67.     Sub-classes should define their own class-wide guidelines variable.
  68.     ( eg: )
  69.     class MoveDecision(Decision):
  70.         guidelines = [ GuidelineA(), GuidelineB()]
  71.     or define self.guidelines before calling Decision.__init__()
  72.     """
  73.  
  74.     guidelines = []
  75.     min_score = .25
  76.  
  77.     def __init__(self,experience=None):self.experience=experience or Experience()
  78.  
  79.     def decide(self, context, options):
  80.         def judge(opt):
  81.             score = 0
  82.             for guide in self.guidelines:
  83.                 try:
  84.                     score+=guide.score(opt,context)*self.experience[type(guide)]
  85.                 except ArithmeticError:
  86.                     continue
  87.             return score
  88.         choices = sorted([(-1 * judge(o), o) for o in options],key=lambda x:x[0])
  89.         for score, choice in choices:
  90.             if -1 * score >= self.min_score:
  91.                 return choice
  92.         return None
  93.  
  94. class DecisionMaker:
  95.     """
  96.     Generates decisions,
  97.     and applies actions.
  98.     Also handles any situations
  99.     where an appropriate action
  100.     could not be determined.
  101.     """
  102.     decision_type = None
  103.     actions = []
  104.     def __init__(self,experience=None):
  105.         self.experience=experience or Experience()
  106.  
  107.     def __call__(self, context):
  108.         assert self.decision_type is not None
  109.         action = self.decision_type(self.experience).decide(context, self.actions)
  110.         if action is not None:
  111.             self.experience.handle_feedback(action.apply(context))
  112.         return context
  113.  
  114. if __name__ == "__main__":
  115.  
  116.     class UrlDirection(Action):
  117.         keywords = []
  118.         correct = "/error"
  119.         def apply(self,context):context[2] = self.correct
  120.  
  121.     class VideoWatcher(UrlDirection):
  122.         correct = "/video"
  123.         keywords = [ 'watch', 'video', 'movie', 'view']
  124.    
  125.     class PhotoViewer(UrlDirection):
  126.         correct = "/photo"
  127.         keywords = [ 'view', 'photo', 'image', 'picture']
  128.  
  129.     class TextReader(UrlDirection):
  130.         correct = "/post"
  131.         keywords = [ 'read', 'text', 'post', 'blog' ]
  132.  
  133.     class UrlDeterminant(Guideline):
  134.         def judge(self, action, context):
  135.             if not isinstance(action, UrlDirection):
  136.                 return 0
  137.             keywords= context[1]
  138.             score = sum(1 for kw in keywords if kw in action.keywords)
  139.             return score / len(keywords)
  140.  
  141.     class UrlDecision(Decision):
  142.         guidelines = [
  143.             UrlDeterminant()
  144.         ]
  145.    
  146.     class UrlRouter(DecisionMaker):
  147.         actions = [
  148.             VideoWatcher(),
  149.             PhotoViewer(),
  150.             TextReader()
  151.         ]
  152.         decision_type = UrlDecision
  153.  
  154.     def url_context(url):return [url,url.split("/"),"/error"]
  155.  
  156.     test_contexts = [url_context(u) for u in [
  157.         "/lol/video/xyzdqf",
  158.         "/video/xaHUR",
  159.         "durpy/photo/",
  160.         "DUAFPAJFK/HAHA"
  161.     ]]
  162.    
  163.     server = UrlRouter()
  164.  
  165.     for context in test_contexts:
  166.         print("Before:",context[2])
  167.         server(context)
  168.         print("After:",context[2],"\n")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement