diff --git a/blackboard.py b/blackboard.py new file mode 100644 index 0000000..1c9fa1d --- /dev/null +++ b/blackboard.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +@author: Eugene Duboviy | github.com/duboviy + +In Blackboard pattern several specialised sub-systems (knowledge sources) +assemble their knowledge to build a possibly partial or approximate solution. +In this way, the sub-systems work together to solve the problem, +where the solution is the sum of its parts. + +https://en.wikipedia.org/wiki/Blackboard_system +""" + +import abc +import random + + +class Blackboard(object): + + def __init__(self): + self.experts = [] + self.common_state = { + 'problems': 0, + 'suggestions': 0, + 'contributions': [], + 'progress': 0 # percentage, if 100 -> task is finished + } + + def add_expert(self, expert): + self.experts.append(expert) + + +class Controller(object): + + def __init__(self, blackboard): + self.blackboard = blackboard + + def run_loop(self): + while self.blackboard.common_state['progress'] < 100: + for expert in self.blackboard.experts: + if expert.is_eager_to_contribute: + expert.contribute() + return self.blackboard.common_state['contributions'] + + +class AbstractExpert(object): + + __metaclass__ = abc.ABCMeta + + def __init__(self, blackboard): + self.blackboard = blackboard + + @abc.abstractproperty + def is_eager_to_contribute(self): + raise NotImplementedError('Must provide implementation in subclass.') + + @abc.abstractmethod + def contribute(self): + raise NotImplementedError('Must provide implementation in subclass.') + + +class Student(AbstractExpert): + + @property + def is_eager_to_contribute(self): + return True + + def contribute(self): + self.blackboard.common_state['problems'] += random.randint(1, 10) + self.blackboard.common_state['suggestions'] += random.randint(1, 10) + self.blackboard.common_state['contributions'] += [self.__class__.__name__] + self.blackboard.common_state['progress'] += random.randint(1, 2) + + +class Scientist(AbstractExpert): + + @property + def is_eager_to_contribute(self): + return random.randint(0, 1) + + def contribute(self): + self.blackboard.common_state['problems'] += random.randint(10, 20) + self.blackboard.common_state['suggestions'] += random.randint(10, 20) + self.blackboard.common_state['contributions'] += [self.__class__.__name__] + self.blackboard.common_state['progress'] += random.randint(10, 30) + + +class Professor(AbstractExpert): + + @property + def is_eager_to_contribute(self): + return True if self.blackboard.common_state['problems'] > 100 else False + + def contribute(self): + self.blackboard.common_state['problems'] += random.randint(1, 2) + self.blackboard.common_state['suggestions'] += random.randint(10, 20) + self.blackboard.common_state['contributions'] += [self.__class__.__name__] + self.blackboard.common_state['progress'] += random.randint(10, 100) + + +if __name__ == '__main__': + blackboard = Blackboard() + + blackboard.add_expert(Student(blackboard)) + blackboard.add_expert(Scientist(blackboard)) + blackboard.add_expert(Professor(blackboard)) + + c = Controller(blackboard) + contributions = c.run_loop() + + from pprint import pprint + pprint(contributions) + +### OUTPUT ### +# ['Student', +# 'Student', +# 'Scientist', +# 'Student', +# 'Scientist', +# 'Student', +# 'Scientist', +# 'Student', +# 'Scientist', +# 'Student', +# 'Scientist', +# 'Professor'] diff --git a/command.py b/command.py index 6abbc63..947aaaa 100644 --- a/command.py +++ b/command.py @@ -91,7 +91,7 @@ def name(self): # exit command class ExitCommand(Command): def execute(self): - raise SessionClosed("Good bay!") + raise SessionClosed("Good day!") def name(self): return "exit" diff --git a/factory.py b/factory.py index 4226d34..717497e 100644 --- a/factory.py +++ b/factory.py @@ -29,42 +29,5 @@ def create_pizza(pizza_type): if __name__ == '__main__': for pizza_type in ('HamMushroom', 'Deluxe', 'Hawaiian'): - print 'Price of {0} is {1}'.format(pizza_type, PizzaFactory.create_pizza(pizza_type).get_price()) + print('Price of {0} is {1}'.format(pizza_type, PizzaFactory.create_pizza(pizza_type).get_price())) - -# ------------------- Second example ----------------- - - -class JapaneseGetter: - """A simple localizer a la gettext""" - - def __init__(self): - self.trans = dict(dog="犬", cat="猫") - - def get(self, msgid): - """We'll punt if we don't have a translation""" - - try: - return unicode(self.trans[msgid], "utf-8") - except KeyError: - return unicode(msgid) - -class EnglishGetter: - """Simply echoes the msg ids""" - def get(self, msgid): - return unicode(msgid) - -def get_localizer(language="English"): - """The factory method""" - - languages = dict(English=EnglishGetter, - Japanese=JapaneseGetter) - - return languages[language]() - -# Create our localizers -e, j = get_localizer("English"), get_localizer("Japanese") - -# Localize some text -for msgid in "dog parrot cat".split(): - print e.get(msgid), j.get(msgid)