Browse code
init
Ed L authored on 27/02/2012 18:56:32
Showing 3 changed files
Showing 3 changed files
1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,31 @@ |
1 |
+Copyright (c) 2012 Edward Langley |
|
2 |
+All rights reserved. |
|
3 |
+ |
|
4 |
+Redistribution and use in source and binary forms, with or without |
|
5 |
+modification, are permitted provided that the following conditions |
|
6 |
+are met: |
|
7 |
+ |
|
8 |
+Redistributions of source code must retain the above copyright notice, |
|
9 |
+this list of conditions and the following disclaimer. |
|
10 |
+ |
|
11 |
+Redistributions in binary form must reproduce the above copyright |
|
12 |
+notice, this list of conditions and the following disclaimer in the |
|
13 |
+documentation and/or other materials provided with the distribution. |
|
14 |
+ |
|
15 |
+Neither the name of the project's author nor the names of its |
|
16 |
+contributors may be used to endorse or promote products derived from |
|
17 |
+this software without specific prior written permission. |
|
18 |
+ |
|
19 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
20 |
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
21 |
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
22 |
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
23 |
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
24 |
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
|
25 |
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
26 |
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
27 |
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
28 |
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
29 |
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 |
+ |
|
31 |
+ |
0 | 32 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,147 @@ |
1 |
+from __future__ import print_function |
|
2 |
+# |
|
3 |
+# Copyright (c) 2012 Edward Langley |
|
4 |
+# All rights reserved. |
|
5 |
+# |
|
6 |
+# Redistribution and use in source and binary forms, with or without |
|
7 |
+# modification, are permitted provided that the following conditions |
|
8 |
+# are met: |
|
9 |
+# |
|
10 |
+# Redistributions of source code must retain the above copyright notice, |
|
11 |
+# this list of conditions and the following disclaimer. |
|
12 |
+# |
|
13 |
+# Redistributions in binary form must reproduce the above copyright |
|
14 |
+# notice, this list of conditions and the following disclaimer in the |
|
15 |
+# documentation and/or other materials provided with the distribution. |
|
16 |
+# |
|
17 |
+# Neither the name of the project's author nor the names of its |
|
18 |
+# contributors may be used to endorse or promote products derived from |
|
19 |
+# this software without specific prior written permission. |
|
20 |
+# |
|
21 |
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
22 |
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
23 |
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
24 |
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
25 |
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
26 |
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
|
27 |
+# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
28 |
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
29 |
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
30 |
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
31 |
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
32 |
+# |
|
33 |
+# |
|
34 |
+ |
|
35 |
+import time |
|
36 |
+import random |
|
37 |
+import pygame |
|
38 |
+import sys |
|
39 |
+import collections |
|
40 |
+import game |
|
41 |
+ |
|
42 |
+import collections |
|
43 |
+ |
|
44 |
+############### Cell ################ |
|
45 |
+class Cell(game.Cell): |
|
46 |
+ pass |
|
47 |
+ |
|
48 |
+Cell.add_rule(3)(0) |
|
49 |
+Cell.add_rule(2)(3) |
|
50 |
+ |
|
51 |
+@Cell.add_rule(1) |
|
52 |
+def hook_1(cell): |
|
53 |
+ neighbors = [c and c.value for c in cell.neighbors] |
|
54 |
+ neighbor_value = cell.neighbor_value() |
|
55 |
+ rnd = random.random() |
|
56 |
+ if 2 in neighbors or 3 in neighbors: |
|
57 |
+ if rnd < .62 * neighbor_value/2: return 2 |
|
58 |
+ |
|
59 |
+ else: |
|
60 |
+ if rnd < .01 * neighbor_value: |
|
61 |
+ return 0 |
|
62 |
+ |
|
63 |
+@Cell.add_rule(0) |
|
64 |
+def hook_0(cell): |
|
65 |
+ neighbor_value = cell.neighbor_value() |
|
66 |
+ if neighbor_value != 0: |
|
67 |
+ nearby_fire = set([2,3]) & set(c and c.value for c in cell.neighbors) |
|
68 |
+ if nearby_fire == set() and random.random() < .02 * neighbor_value: |
|
69 |
+ return 1 |
|
70 |
+ return cell.value |
|
71 |
+ |
|
72 |
+########## End Cell ################# |
|
73 |
+ |
|
74 |
+class Presence(game.Presence): |
|
75 |
+ def act(self): |
|
76 |
+ value = int(round( (self.locale.neighbor_value() + self.locale.value) )) |
|
77 |
+ self.locale.set_value(0) |
|
78 |
+ for x in self.locale.neighbors: |
|
79 |
+ if not x: continue |
|
80 |
+ if x.value == 1: |
|
81 |
+ x.set_value(2) |
|
82 |
+ |
|
83 |
+import time |
|
84 |
+ |
|
85 |
+if __name__ == '__main__': |
|
86 |
+ pygame.init() |
|
87 |
+ pygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN]) |
|
88 |
+ pygame.display.set_caption('Simulator') |
|
89 |
+ |
|
90 |
+ board = game.Board(120, 120) |
|
91 |
+ window = pygame.display.set_mode(board.rect.size, pygame.DOUBLEBUF) |
|
92 |
+ window.set_alpha(None) |
|
93 |
+ for sprite in board.board_group: |
|
94 |
+ sprite.image.convert() |
|
95 |
+ |
|
96 |
+ a = game.Controller(board) |
|
97 |
+ |
|
98 |
+ p = a.add_observer(Presence, a.board.init.south) |
|
99 |
+ np = a.board.init.southeast |
|
100 |
+ for x in range(20): |
|
101 |
+ np = np.east |
|
102 |
+ np = a.add_observer(Presence, np) |
|
103 |
+ |
|
104 |
+ window.fill( (64,64,64) ) |
|
105 |
+ |
|
106 |
+ running = True |
|
107 |
+ |
|
108 |
+ board.board_group.draw(window) |
|
109 |
+ pygame.display.flip() |
|
110 |
+ |
|
111 |
+ clck = pygame.time.Clock() |
|
112 |
+ |
|
113 |
+ paused = True |
|
114 |
+ while running: |
|
115 |
+ clck.tick(80) |
|
116 |
+ |
|
117 |
+ for event in pygame.event.get(): |
|
118 |
+ if event.type == pygame.QUIT: |
|
119 |
+ running=False |
|
120 |
+ break |
|
121 |
+ elif event.type == pygame.KEYDOWN and event.unicode == 'e': |
|
122 |
+ paused = not paused |
|
123 |
+ elif event.type == pygame.KEYDOWN and event.unicode == 'w': |
|
124 |
+ new = p.locale.north |
|
125 |
+ if new: |
|
126 |
+ p.locale = new |
|
127 |
+ elif event.type == pygame.KEYDOWN and event.unicode == 'a': |
|
128 |
+ new = p.locale.west |
|
129 |
+ if new: |
|
130 |
+ p.locale = new |
|
131 |
+ elif event.type == pygame.KEYDOWN and event.unicode == 's': |
|
132 |
+ new = p.locale.south |
|
133 |
+ if new: |
|
134 |
+ p.locale = new |
|
135 |
+ elif event.type == pygame.KEYDOWN and event.unicode == 'd': |
|
136 |
+ new = p.locale.east |
|
137 |
+ if new: |
|
138 |
+ p.locale = new |
|
139 |
+ |
|
140 |
+ if not paused: |
|
141 |
+ a.step() |
|
142 |
+ updates = a.board.board_group.draw(window) |
|
143 |
+ pygame.display.update(updates) |
|
144 |
+ |
|
145 |
+ print( clck.get_fps(), '\r', end='' ) |
|
146 |
+ sys.stdout.flush() |
|
147 |
+ |
0 | 148 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,279 @@ |
1 |
+from __future__ import print_function |
|
2 |
+# |
|
3 |
+# Copyright (c) 2012 Edward Langley |
|
4 |
+# All rights reserved. |
|
5 |
+# |
|
6 |
+# Redistribution and use in source and binary forms, with or without |
|
7 |
+# modification, are permitted provided that the following conditions |
|
8 |
+# are met: |
|
9 |
+# |
|
10 |
+# Redistributions of source code must retain the above copyright notice, |
|
11 |
+# this list of conditions and the following disclaimer. |
|
12 |
+# |
|
13 |
+# Redistributions in binary form must reproduce the above copyright |
|
14 |
+# notice, this list of conditions and the following disclaimer in the |
|
15 |
+# documentation and/or other materials provided with the distribution. |
|
16 |
+# |
|
17 |
+# Neither the name of the project's author nor the names of its |
|
18 |
+# contributors may be used to endorse or promote products derived from |
|
19 |
+# this software without specific prior written permission. |
|
20 |
+# |
|
21 |
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
22 |
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
23 |
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
24 |
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
25 |
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
26 |
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
|
27 |
+# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
28 |
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
29 |
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
30 |
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
31 |
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
32 |
+# |
|
33 |
+# |
|
34 |
+ |
|
35 |
+import sys |
|
36 |
+import collections |
|
37 |
+import pygame |
|
38 |
+ |
|
39 |
+class Cell(object): |
|
40 |
+ north = None |
|
41 |
+ south = None |
|
42 |
+ east = None |
|
43 |
+ west = None |
|
44 |
+ rules = {} |
|
45 |
+ |
|
46 |
+ |
|
47 |
+ def rule(self, *a): |
|
48 |
+ '''The rule used to determine the new value for the current cell, add_rule reassigns this based on the current value''' |
|
49 |
+ return self.value |
|
50 |
+ |
|
51 |
+ @classmethod |
|
52 |
+ def add_rule(self, value): |
|
53 |
+ '''Add a rule to correspond to the current value of the cell''' |
|
54 |
+ def _inner(rule): |
|
55 |
+ self.rules[value] = rule |
|
56 |
+ return rule |
|
57 |
+ return _inner |
|
58 |
+ |
|
59 |
+ def __init__(self): |
|
60 |
+ self.value = None |
|
61 |
+ self.update_neighbors() |
|
62 |
+ |
|
63 |
+ def set_value(self, v): |
|
64 |
+ '''Change the cells value and select the appropriate rule''' |
|
65 |
+ if v != self.value: |
|
66 |
+ self.value = v |
|
67 |
+ if v is not None: |
|
68 |
+ self.rule = self.rules[v] |
|
69 |
+ |
|
70 |
+ def neighbor_value(self): |
|
71 |
+ '''Get the total of the values of neighbouring cells''' |
|
72 |
+ result = 0 |
|
73 |
+ for c in self.neighbors: |
|
74 |
+ if c is None: continue |
|
75 |
+ elif c.value is None: continue |
|
76 |
+ result += c.value |
|
77 |
+ return result |
|
78 |
+ |
|
79 |
+ def update_neighbors(self): |
|
80 |
+ '''Change the neighbors attribute to reflect the current situation''' |
|
81 |
+ self.neighbors = (self.north, self.south, self.east, self.west, self.northwest, self.northeast, self.southwest, self.southeast) |
|
82 |
+ |
|
83 |
+ def autoconnect(self): |
|
84 |
+ '''Set up the reverse links appropriate to the current situation''' |
|
85 |
+ updated = [] |
|
86 |
+ |
|
87 |
+ if self.south and self.south.north is not self: |
|
88 |
+ self.south.north = self |
|
89 |
+ updated.append(self.south) |
|
90 |
+ if self.north and self.north.south is not self: |
|
91 |
+ self.north.south = self |
|
92 |
+ updated.append(self.north) |
|
93 |
+ if self.east and self.east.west is not self: |
|
94 |
+ self.east.west = self |
|
95 |
+ updated.append(self.east) |
|
96 |
+ if self.west and self.west.east is not self: |
|
97 |
+ self.west.east = self |
|
98 |
+ updated.append(self.west) |
|
99 |
+ |
|
100 |
+ for up in updated: |
|
101 |
+ up.autoconnect() |
|
102 |
+ |
|
103 |
+ self.update_neighbors() |
|
104 |
+ |
|
105 |
+ @property |
|
106 |
+ def northeast(self): |
|
107 |
+ r = self.north |
|
108 |
+ if r is not None: |
|
109 |
+ return r.east |
|
110 |
+ |
|
111 |
+ @property |
|
112 |
+ def northwest(self): |
|
113 |
+ r = self.north |
|
114 |
+ if r is not None: |
|
115 |
+ return r.west |
|
116 |
+ |
|
117 |
+ @property |
|
118 |
+ def southeast(self): |
|
119 |
+ r = self.south |
|
120 |
+ if r is not None: |
|
121 |
+ return r.east |
|
122 |
+ |
|
123 |
+ @property |
|
124 |
+ def southwest(self): |
|
125 |
+ r = self.south |
|
126 |
+ if r is not None: |
|
127 |
+ return r.west |
|
128 |
+ |
|
129 |
+ |
|
130 |
+ def __repr__(self): |
|
131 |
+ return '%s().set_value(%s)' % (self.__name__, self.value) |
|
132 |
+ |
|
133 |
+ def __str__(self): |
|
134 |
+ return str(self.value) |
|
135 |
+ |
|
136 |
+class CellSprite(pygame.sprite.DirtySprite): |
|
137 |
+ '''Display code for :py:class:`Cell`''' |
|
138 |
+ |
|
139 |
+ def __init__(self, cell, row, col, *a): |
|
140 |
+ self.image = pygame.surface.Surface( (6,6) ) |
|
141 |
+ self.cell = cell |
|
142 |
+ self.row = row |
|
143 |
+ self.col = col |
|
144 |
+ cell.sprite = self |
|
145 |
+ self.spacing = 0 |
|
146 |
+ |
|
147 |
+ row, col = self.row, self.col |
|
148 |
+ spacing = self.spacing |
|
149 |
+ size = width, height = self.image.get_size() |
|
150 |
+ self.rect = pygame.rect.Rect( (width*(col)+spacing*(col), (height*(row)+spacing*(row))), size ) |
|
151 |
+ |
|
152 |
+ pygame.sprite.DirtySprite.__init__(self, *a) |
|
153 |
+ self.start_update(self.cell.value) |
|
154 |
+ |
|
155 |
+ |
|
156 |
+ colors = { |
|
157 |
+ None: (128,128,128), |
|
158 |
+ 0: (0,0,0), |
|
159 |
+ 1: (0,255,0), |
|
160 |
+ 2: (128,64,0), |
|
161 |
+ 3: (255,0,0) |
|
162 |
+ } |
|
163 |
+ |
|
164 |
+ def start_update(self, value): |
|
165 |
+ '''Begin to update the cell: store the new value and mark the sprite "dirty"''' |
|
166 |
+ self.new_value = value |
|
167 |
+ self.dirty = 1 |
|
168 |
+ self.ud = True |
|
169 |
+ |
|
170 |
+ def update(self): |
|
171 |
+ '''Update the cell: set the cell's value and paint the new image''' |
|
172 |
+ if self.ud: |
|
173 |
+ self.cell.set_value(self.new_value) |
|
174 |
+ self.image.fill( self.colors[self.cell.value] ) |
|
175 |
+ |
|
176 |
+ |
|
177 |
+class Board(object): |
|
178 |
+ '''Set up and store the cells''' |
|
179 |
+ |
|
180 |
+ def __init__(self, width, height): |
|
181 |
+ self.board_group = pygame.sprite.LayeredDirty(_use_update=True) |
|
182 |
+ self.rect = pygame.Rect(0,0,0,0) |
|
183 |
+ self.dct = {} |
|
184 |
+ |
|
185 |
+ for col in xrange(width): |
|
186 |
+ for row in xrange(0,height): |
|
187 |
+ cell = self.dct[row, col] = Cell() |
|
188 |
+ sprite = CellSprite(cell, row, col, self.board_group) |
|
189 |
+ |
|
190 |
+ for col in xrange(width): |
|
191 |
+ for row in xrange(height): |
|
192 |
+ cell = self.dct[row,col] |
|
193 |
+ cell.east = self.dct.get((row,col+1)) |
|
194 |
+ cell.south = self.dct.get((row+1,col)) |
|
195 |
+ cell.autoconnect() |
|
196 |
+ |
|
197 |
+ self.init = self.dct[0,0] |
|
198 |
+ self.rect.unionall_ip(list(self.board_group)) |
|
199 |
+ |
|
200 |
+ |
|
201 |
+ def __str__(self): |
|
202 |
+ current = self.init |
|
203 |
+ result = [] |
|
204 |
+ while current.east != None: |
|
205 |
+ top = current |
|
206 |
+ result.append([]) |
|
207 |
+ while top.south != None: |
|
208 |
+ result[-1].append(str(top)) |
|
209 |
+ top = top.south |
|
210 |
+ current = current.east |
|
211 |
+ result = zip(*result) |
|
212 |
+ return '\n'.join( |
|
213 |
+ ' '.join(r) for r in result |
|
214 |
+ ) |
|
215 |
+ |
|
216 |
+import random |
|
217 |
+ |
|
218 |
+ |
|
219 |
+ |
|
220 |
+class Controller(object): |
|
221 |
+ '''Initialize cell values and run the simulation''' |
|
222 |
+ def __init__(self, board): |
|
223 |
+ self.board = board |
|
224 |
+ self.observers = [] |
|
225 |
+ |
|
226 |
+ column = self.board.init |
|
227 |
+ for sprite in self.board.board_group: |
|
228 |
+ sprite.start_update( random.choice([1]*7+[0]*5 +[2]) ) |
|
229 |
+ self.board.board_group.update() |
|
230 |
+ |
|
231 |
+ |
|
232 |
+ def step(self): |
|
233 |
+ '''Run one iteration of the simulation''' |
|
234 |
+ for observer in self.observers: |
|
235 |
+ observer.act() |
|
236 |
+ |
|
237 |
+ c_sprites = pygame.sprite.Group() |
|
238 |
+ for sprite in self.board.board_group: |
|
239 |
+ cell = sprite.cell |
|
240 |
+ nvalue = cell.rule |
|
241 |
+ if callable(nvalue): |
|
242 |
+ nvalue = nvalue(cell) |
|
243 |
+ if nvalue is None: |
|
244 |
+ nvalue = cell.value |
|
245 |
+ if nvalue != cell.value: |
|
246 |
+ sprite.start_update(nvalue) |
|
247 |
+ c_sprites.add(sprite) |
|
248 |
+ c_sprites.update() |
|
249 |
+ return self |
|
250 |
+ |
|
251 |
+ def add_observer(self, cls, *args, **kwargs): |
|
252 |
+ '''add an observer to a cell''' |
|
253 |
+ observer = cls(*args, **kwargs) |
|
254 |
+ self.observers.append(observer) |
|
255 |
+ return observer |
|
256 |
+ |
|
257 |
+ def init(self, cb): |
|
258 |
+ self.init = cb |
|
259 |
+ return self |
|
260 |
+ |
|
261 |
+ def add_event(self, cb): |
|
262 |
+ self.events.append(cb) |
|
263 |
+ return self |
|
264 |
+ |
|
265 |
+ def loop(self): |
|
266 |
+ variables = {} |
|
267 |
+ variables.update(self.init()) |
|
268 |
+ clck = pygame.time.Clock() |
|
269 |
+ variables['running'] = True |
|
270 |
+ |
|
271 |
+class Presence(object): |
|
272 |
+ '''Observer interface''' |
|
273 |
+ def __init__(self, locale): |
|
274 |
+ self.locale = locale |
|
275 |
+ |
|
276 |
+ def act(self): |
|
277 |
+ '''Override to implement behavior''' |
|
278 |
+ pass |
|
279 |
+ |