forked from faif/python-patterns
-
Notifications
You must be signed in to change notification settings - Fork 0
/
blackboard.py
126 lines (97 loc) · 3.6 KB
/
blackboard.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""
@author: Eugene Duboviy <eugene.dubovoy@gmail.com> | 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:
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:
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(metaclass=abc.ABCMeta):
def __init__(self, blackboard):
self.blackboard = blackboard
@property
@abc.abstractmethod
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)
def 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)
['Student',
'Student',
'Student',
'Student',
'Scientist',
'Student',
'Student',
'Student',
'Scientist',
'Student',
'Scientist',
'Student',
'Student',
'Scientist',
'Professor']
"""
if __name__ == "__main__":
random.seed(1234) # for deterministic doctest outputs
import doctest
doctest.testmod()