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 |
martiran@30
|
12 class Snake(object): |
me@44
|
13 """Snakes. |
me@44
|
14 |
me@44
|
15 Attributes: |
me@44
|
16 |
me@44
|
17 - `cells` -- list of cells belonging to the snake The first of these cells |
me@44
|
18 becomes head, the last one becomes tail, the rest ar body. If snake has |
me@44
|
19 only one cell, it is tail. |
me@44
|
20 - `color` -- color of snake |
me@44
|
21 - `rules` -- a list of Rule objects |
me@44
|
22 """ |
me@44
|
23 |
martiran@32
|
24 def __init__ (self, cells, color): |
martiran@32
|
25 self.cells = cells |
martiran@32
|
26 self.color = color |
martiran@32
|
27 self.rules = [] |
me@34
|
28 |
me@34
|
29 def load (self, file): |
me@44
|
30 """Load snake description from file. |
me@44
|
31 |
me@44
|
32 See program design docs for file syntax. |
me@44
|
33 """ |
me@34
|
34 magic, name = preprocess(file.readline()).split(' ', 1) |
me@34
|
35 assert magic == "snake", "This is not snake file" |
me@34
|
36 while True: |
me@34
|
37 line = preprocess(file.readline()) |
me@34
|
38 if line == 'end': |
me@34
|
39 break |
me@34
|
40 assert line == '', "Rules must be separated by empty lines" |
me@34
|
41 self.rules.append(Rule().load(file)) |
me@34
|
42 |
martiran@30
|
43 def fill (self): |
me@44
|
44 """Mark every cell in `self.cells` as belonging to self.""" |
martiran@32
|
45 for cell in self.cells: |
martiran@32
|
46 cell.snake = self |
martiran@31
|
47 snake.cells[0].type = 'head' |
me@45
|
48 for cell in self.cells[1:-1]: |
me@45
|
49 cell.type = 'body' |
martiran@31
|
50 snake.cells[-1].type = 'tail' |
martiran@31
|
51 return |
me@34
|
52 |
martiran@30
|
53 class Rule(object): |
me@44
|
54 """Rule defining possible behaviour of snake.""" |
me@34
|
55 |
me@34
|
56 codes = { |
me@34
|
57 'h': 'head', |
me@34
|
58 'b': 'body', |
me@34
|
59 't': 'tail', |
me@34
|
60 '#': 'wall', |
me@34
|
61 ' ': 'any', |
me@34
|
62 '-': 'empty', |
me@34
|
63 } |
me@34
|
64 |
martiran@32
|
65 def __init__ (self, snake): |
martiran@32
|
66 self.snake = snake |
me@34
|
67 self.direction = (1, 0) |
me@34
|
68 self.pattern = {} |
me@34
|
69 |
me@34
|
70 def load (self, file): |
me@44
|
71 """Load rule definition from file. Ignore any leading empty lines.""" |
me@34
|
72 y = 0 |
me@34
|
73 for line in file: |
me@34
|
74 line = preprocess(line) |
me@34
|
75 if y == 0 and line == '': |
me@34
|
76 continue |
me@36
|
77 if y == 7: |
me@36
|
78 break |
me@41
|
79 assert len(line) == 8, "Rule lines must be exactly 7 chars long" |
me@34
|
80 assert line[-1] == ';', "Rule lines must end with semicolon" |
me@34
|
81 for x, char in enumerate(line[:8]): |
me@34
|
82 self.parse_cell(x, y, char) |
me@34
|
83 y += 1 |
me@34
|
84 |
me@34
|
85 def parse_cell(self, x, y, char): |
me@44
|
86 """Parse definition of cell in rule file. |
me@44
|
87 |
me@44
|
88 Cell is defined by one character. |
me@44
|
89 """ |
me@34
|
90 assert char.lower() in self.codes, "Illegal symbol in rule: %s" % char |
me@34
|
91 cell = engine.Cell(x, y, None) |
me@34
|
92 if char in 'htb': |
me@34
|
93 if char.islower(): |
me@34
|
94 cell.snake = 'my' |
me@34
|
95 else: |
me@34
|
96 cell.snake = 'enemy' |
me@37
|
97 if char == 'h': |
me@37
|
98 assert (x, y) == (3, 3), "Own head must in the center of rule" |
me@37
|
99 if (x, y) == (3, 3): |
me@37
|
100 assert char == 'h', "In the center of rule must be own head" |
me@34
|
101 cell.type = self.codes[char.lower()] |
me@34
|
102 self.pattern[x, y] = cell |
me@34
|
103 |
martiran@32
|
104 def applies (self, field, x, y): |
me@44
|
105 """True if the rule applies in the field at position (x,y).""" |
me@38
|
106 for px, fx in zip(range(7), range(x - 3, x + 4)): |
me@38
|
107 for py, fy in zip(range(7), range(y - 3, y + 4)): |
me@49
|
108 if (fx, fy) in field: |
me@43
|
109 if field[fx, fy] != self.pattern[px, py]: |
me@43
|
110 return False |
me@43
|
111 else: |
me@43
|
112 if self.pattern[px, py].type != 'any': |
me@43
|
113 return False |
me@38
|
114 return True |
me@34
|
115 |
martiran@32
|
116 def rotate (self, rot): |
me@44
|
117 """Rotate rule pattern `rot` times counterclockwise.""" |
me@39
|
118 for i in range(((rot % 4) + 4) % 4): |
me@39
|
119 self.rotate_ccw() |
me@39
|
120 |
me@39
|
121 def rotate_ccw(self): |
me@44
|
122 """Rotate rule pattern one time counterclockwise.""" |
me@39
|
123 pattern = {} |
me@39
|
124 for x in range(7): |
me@39
|
125 for y in range(7): |
me@39
|
126 pattern[y, 6 - x] = self.pattern[x, y] |
me@39
|
127 self.pattern = pattern |
me@39
|
128 x, y = self.direction |
me@39
|
129 self.direction = y, -x |
me@34
|
130 |
me@34
|
131 # vim: set ts=4 sts=4 sw=4 et: |