rev |
line source |
me@44
|
1 """Guts of snakes.""" |
me@44
|
2 |
me@34
|
3 import engine |
me@34
|
4 |
me@34
|
5 def preprocess(line): |
me@44
|
6 """Remove comments and junk spaces from line of snake definition file.""" |
me@34
|
7 if '//' in line: |
me@34
|
8 line = line[:line.index('//')] |
me@34
|
9 line = line.rstrip() |
me@34
|
10 return line |
martiran@30
|
11 |
me@109
|
12 class File(object): |
me@109
|
13 """Wrapper around file that saves the current line number.""" |
me@109
|
14 def __init__(self, file): |
me@109
|
15 self.file = file |
me@109
|
16 self.name = file.name |
me@109
|
17 self.line_no = 0 |
me@120
|
18 self.iterator = self.enumerate_lines() |
me@109
|
19 def __iter__(self): |
me@120
|
20 return self.iterator |
me@120
|
21 def enumerate_lines(self): |
me@109
|
22 for line_no, line in enumerate(self.file, self.line_no): |
me@109
|
23 self.line_no = line_no |
me@109
|
24 yield line |
me@109
|
25 |
martiran@30
|
26 class Snake(object): |
me@44
|
27 """Snakes. |
me@44
|
28 |
me@44
|
29 Attributes: |
me@44
|
30 |
me@44
|
31 - `cells` -- list of cells belonging to the snake The first of these cells |
me@44
|
32 becomes head, the last one becomes tail, the rest ar body. If snake has |
me@44
|
33 only one cell, it is tail. |
me@44
|
34 - `color` -- color of snake |
me@44
|
35 - `rules` -- a list of Rule objects |
me@44
|
36 """ |
me@44
|
37 |
martiran@32
|
38 def __init__ (self, cells, color): |
martiran@32
|
39 self.cells = cells |
martiran@32
|
40 self.color = color |
martiran@32
|
41 self.rules = [] |
me@34
|
42 |
me@34
|
43 def load (self, file): |
me@44
|
44 """Load snake description from file. |
me@44
|
45 |
me@44
|
46 See program design docs for file syntax. |
me@44
|
47 """ |
me@109
|
48 file = File(file) |
me@109
|
49 try: |
me@109
|
50 self._load(file) |
me@109
|
51 except Exception, e: |
me@109
|
52 raise Exception("%s:%s: %s" % (file.name, file.line_no, e)) |
me@109
|
53 |
me@109
|
54 def _load (self, file): |
me@109
|
55 """Actually do the loading.""" |
me@65
|
56 for line in file: |
me@88
|
57 magic, self.name = preprocess(line).split(' ', 1) |
me@65
|
58 break |
me@34
|
59 assert magic == "snake", "This is not snake file" |
me@65
|
60 for line in file: |
me@65
|
61 line = preprocess(line) |
me@34
|
62 if line == 'end': |
me@34
|
63 break |
me@34
|
64 assert line == '', "Rules must be separated by empty lines" |
me@51
|
65 self.rules.append(Rule(self).load(file)) |
me@34
|
66 |
martiran@30
|
67 def fill (self): |
me@44
|
68 """Mark every cell in `self.cells` as belonging to self.""" |
martiran@32
|
69 for cell in self.cells: |
martiran@32
|
70 cell.snake = self |
me@129
|
71 if self.cells == []: |
me@129
|
72 return |
martiran@111
|
73 for cell in self.cells: |
martiran@111
|
74 cell.type = 'body' |
Alex@75
|
75 self.cells[0].type = 'head' |
Alex@75
|
76 self.cells[-1].type = 'tail' |
me@34
|
77 |
martiran@30
|
78 class Rule(object): |
me@44
|
79 """Rule defining possible behaviour of snake.""" |
me@34
|
80 |
me@34
|
81 codes = { |
me@34
|
82 'h': 'head', |
me@34
|
83 'b': 'body', |
me@34
|
84 't': 'tail', |
me@34
|
85 '#': 'wall', |
me@34
|
86 ' ': 'any', |
me@34
|
87 '-': 'empty', |
me@34
|
88 } |
me@34
|
89 |
martiran@32
|
90 def __init__ (self, snake): |
martiran@32
|
91 self.snake = snake |
me@80
|
92 self.direction = (0, -1) |
me@34
|
93 self.pattern = {} |
me@34
|
94 |
me@34
|
95 def load (self, file): |
me@70
|
96 """Load rule definition from file. |
me@70
|
97 |
me@70
|
98 Ignore any leading empty lines. |
me@70
|
99 Return self. |
me@70
|
100 """ |
me@34
|
101 y = 0 |
me@34
|
102 for line in file: |
me@34
|
103 line = preprocess(line) |
me@34
|
104 if y == 0 and line == '': |
me@34
|
105 continue |
me@41
|
106 assert len(line) == 8, "Rule lines must be exactly 7 chars long" |
me@34
|
107 assert line[-1] == ';', "Rule lines must end with semicolon" |
me@63
|
108 for x, char in enumerate(line[:7]): |
me@34
|
109 self.parse_cell(x, y, char) |
me@34
|
110 y += 1 |
me@68
|
111 if y == 7: |
me@68
|
112 break |
me@70
|
113 return self |
me@34
|
114 |
me@34
|
115 def parse_cell(self, x, y, char): |
me@44
|
116 """Parse definition of cell in rule file. |
me@44
|
117 |
me@44
|
118 Cell is defined by one character. |
me@44
|
119 """ |
me@73
|
120 is_my = char.islower() |
me@73
|
121 char = char.lower() |
me@73
|
122 assert char in self.codes, "Illegal symbol in rule: %s" % char |
me@115
|
123 cell = engine.Cell(x, y) |
me@115
|
124 cell.snake = self.snake |
me@124
|
125 cell.snake_type = None |
me@34
|
126 if char in 'htb': |
me@73
|
127 if is_my: |
me@51
|
128 cell.snake_type = 'my' |
me@34
|
129 else: |
me@51
|
130 cell.snake_type = 'enemy' |
me@37
|
131 if char == 'h': |
me@37
|
132 assert (x, y) == (3, 3), "Own head must in the center of rule" |
me@37
|
133 if (x, y) == (3, 3): |
me@37
|
134 assert char == 'h', "In the center of rule must be own head" |
me@73
|
135 cell.type = self.codes[char] |
me@34
|
136 self.pattern[x, y] = cell |
me@34
|
137 |
martiran@32
|
138 def applies (self, field, x, y): |
me@44
|
139 """True if the rule applies in the field at position (x,y).""" |
me@115
|
140 wall = engine.Cell(-1, -1) |
me@115
|
141 wall.type = 'void' |
me@115
|
142 |
me@38
|
143 for px, fx in zip(range(7), range(x - 3, x + 4)): |
me@38
|
144 for py, fy in zip(range(7), range(y - 3, y + 4)): |
me@115
|
145 if field.get((fx, fy), wall) != self.pattern[px, py]: |
me@115
|
146 return False |
me@38
|
147 return True |
me@34
|
148 |
me@104
|
149 def rotate (self, direction): |
me@104
|
150 """Rotate rule pattern to head in `direction`.""" |
me@104
|
151 for i in range(4): |
me@104
|
152 if self.direction == direction: |
me@104
|
153 return |
me@39
|
154 self.rotate_ccw() |
me@104
|
155 raise AssertionError("Illegal direction: %s" % direction) |
me@39
|
156 |
me@39
|
157 def rotate_ccw(self): |
me@44
|
158 """Rotate rule pattern one time counterclockwise.""" |
me@39
|
159 pattern = {} |
me@39
|
160 for x in range(7): |
me@39
|
161 for y in range(7): |
me@39
|
162 pattern[y, 6 - x] = self.pattern[x, y] |
me@39
|
163 self.pattern = pattern |
me@39
|
164 x, y = self.direction |
me@39
|
165 self.direction = y, -x |
me@34
|
166 |
me@34
|
167 # vim: set ts=4 sts=4 sw=4 et: |