git.fiddlerwoaroof.com
Ed L authored on 27/02/2012 18:56:32
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
+