|
| 1 | +"""Core module of challenges |
| 2 | +
|
| 3 | +This module holds the base class of all challenges. |
| 4 | +""" |
| 5 | + |
1 | 6 | import re |
2 | 7 | import types |
3 | 8 |
|
4 | 9 |
|
5 | 10 | class Challenge: |
| 11 | + """Base class of all challenges |
| 12 | +
|
| 13 | + Idea of this class is the Template Method Design Pattern (GOF). |
| 14 | +
|
| 15 | + Workflow |
| 16 | + ======== |
| 17 | +
|
| 18 | + The main() method controls the overall workflow by calling the worker |
| 19 | + methods read(), build(), calc() and format() in that order. Typically the |
| 20 | + main() method is not changed. |
| 21 | +
|
| 22 | + Workers |
| 23 | + ======= |
| 24 | +
|
| 25 | + The workers need to be extended or implemented by the inheriting challenge |
| 26 | + implementations. |
| 27 | +
|
| 28 | + * read(): This method is typically not changed. |
| 29 | + * build(): Abstract method that needs implementation. |
| 30 | + * calc(): Abstract method. The implementation holds or calls the algorithm |
| 31 | + of the challenge. |
| 32 | + * format(): Needs implementation for advanced challenges. |
| 33 | +
|
| 34 | + Library |
| 35 | + ======= |
| 36 | +
|
| 37 | + The other functions are library methods to support the implementation of |
| 38 | + the workers. Most of them support extraction of data from the input lines |
| 39 | + or formatting the output. |
| 40 | + """ |
| 41 | + |
6 | 42 | sample = 'sample' |
7 | | - line_break = '\n' |
| 43 | + """Holds a minimal example of the input. |
| 44 | +
|
| 45 | + This variable should always be overwritten in the derived class by a |
| 46 | + smallest example of the challenges input. It is expected by the challenge |
| 47 | + runner (--klass option) as well as by unit tests to be present. |
| 48 | + """ |
| 49 | + |
| 50 | + br = line_break = '\n' |
| 51 | + """Line breaks as expected by the most graders.""" |
| 52 | + |
8 | 53 | split_pattern = '\s+|\s?,\s?' |
| 54 | + """Regex pattern to split input lines. |
| 55 | +
|
| 56 | + Used by some of the input parsing functions. By default it splits by |
| 57 | + whitespace and/or comma. If the input is separated differently like colons |
| 58 | + or semicolons it needs adjustment in the inheriting class. |
| 59 | + """ |
| 60 | + |
9 | 61 | edge_pattern = '^(\d+)->(\d+)(:(\d+))?$' |
| 62 | + """Regex to exctract graph edges. |
| 63 | +
|
| 64 | + A default setting used by methods that extract edges from input lines. |
| 65 | + May need adjustment for differnt kind of edge input formats. |
| 66 | + """ |
10 | 67 |
|
11 | 68 | def __init__(self): |
12 | 69 | self.lines = [] |
| 70 | + """A list of lines that will be filled by the method read().""" |
| 71 | + |
13 | 72 | self.model = types.SimpleNamespace() |
| 73 | + """The imported data model. |
| 74 | +
|
| 75 | + A flexible namespace object to take up any kind of data. In simple |
| 76 | + cases this may be completely overwritten, i.e by a list or dict. |
| 77 | + """ |
14 | 78 | self.result = types.SimpleNamespace() |
| 79 | + """The resulting data model. |
| 80 | +
|
| 81 | + A flexible namespace object to take up any kind of data. In simple |
| 82 | + cases this may be completely overwritten, i.e by a list or dict. |
| 83 | + """ |
| 84 | + |
15 | 85 | self.output = '' |
| 86 | + """The output string. |
| 87 | +
|
| 88 | + The string representation of the resulting model as expected by the |
| 89 | + grader. |
| 90 | + """ |
16 | 91 |
|
17 | 92 | def main(self): |
| 93 | + """Control the workflow of the challenge. |
| 94 | +
|
| 95 | + This method usually doesn't need a different implementation. This |
| 96 | + workflow is the common character of all challenges. |
| 97 | +
|
| 98 | + The methods share data via instance variables. |
| 99 | + The overall input is injected into self.sample. |
| 100 | + The overall output is read from self.result. |
| 101 | + """ |
18 | 102 | self.read() |
19 | 103 | self.build() |
20 | 104 | self.calc() |
21 | 105 | self.format() |
22 | 106 |
|
23 | 107 | # -------------------------------------------------- |
24 | | - # Default workflow |
| 108 | + # Default and abstract workers |
25 | 109 | # -------------------------------------------------- |
26 | 110 |
|
27 | 111 | def read(self): |
| 112 | + """Extract the input string self.sample into self.lines. |
| 113 | +
|
| 114 | + Typically this method can be used as is. |
| 115 | + """ |
28 | 116 | lines = self.sample.strip().splitlines() |
29 | 117 | self.lines = [line.strip() for line in lines] |
30 | 118 |
|
31 | 119 | def build(self): |
| 120 | + """Set up the model from the input lines. |
| 121 | +
|
| 122 | + This method must be implemented. |
| 123 | + Reads from self.lines. |
| 124 | + Fills self.model. |
| 125 | + """ |
32 | 126 | pass |
33 | 127 |
|
34 | 128 | def calc(self): |
| 129 | + """Main algorithm of the challenge. |
| 130 | +
|
| 131 | + This method must be implemented. Here the interesting stuff happens. |
| 132 | + Best practice is to delegate to functions, that are named by the |
| 133 | + algorithms used or even to other classes that implement the algorithm. |
| 134 | +
|
| 135 | + Reads from self.model. |
| 136 | + Fills self.result. |
| 137 | + """ |
35 | 138 | pass |
36 | 139 |
|
37 | 140 | def format(self): |
| 141 | + """Foramt the output string. |
| 142 | +
|
| 143 | + In simple cases this method can be used as is. In other cases it |
| 144 | + needs to be reimplemented. |
| 145 | +
|
| 146 | + Reads from self.result. |
| 147 | + Fills self.output. |
| 148 | + """ |
38 | 149 | self.output = str(self.result) |
39 | 150 |
|
40 | 151 | # -------------------------------------------------- |
|
0 commit comments