git.fiddlerwoaroof.com
Browse code

quadtree

Ed L authored on 17/07/2013 21:27:47
Showing 8 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1 @@
1
+ASD: 123
0 2
new file mode 100644
... ...
@@ -0,0 +1,27 @@
1
+import random
2
+
3
+class Die(object):
4
+	def __init__(self, sides, min=1, step=1):
5
+		self.sides = sides
6
+		self.min = min
7
+		self.step = 1
8
+		self.sides = range(min, (min+sides)*step, step)
9
+		self._value = None
10
+		self.combine_func = lambda a,b: a+b
11
+
12
+	def roll(object):
13
+		self._value = random.choice(self.sides)
14
+		return self._value
15
+
16
+	@property
17
+	def value(object):
18
+		if self._value is None: self.roll()
19
+		return self._value
20
+
21
+	def combine(self, other):
22
+		if hasattr(other, 'value'): other = other.value
23
+		return self.combine_func(self.value, other.value)
24
+
25
+def DiceRoll(object):
26
+	def __init__(self, dice):
27
+		self.dice = dice
0 28
new file mode 100644
... ...
@@ -0,0 +1,71 @@
1
+def row2diterate(dct, width, height):
2
+	for y in range(height):
3
+		for x in range(width):
4
+			yield dct[c,r]
5
+
6
+class Cell(object):
7
+	def __init__(self, pos, value=0, neighbors = None, previous = None):
8
+		self.pos = pos
9
+		self.value = value
10
+
11
+		if previous is None: previous = {}
12
+		self.previous = previous
13
+		self.previous[pos] = self
14
+
15
+		if neighbors is None: neighbors = [None,None,None,None]
16
+		chg_funcs = [
17
+			lambda (x,y): (x,y-1),
18
+			lambda (x,y): (x+1,y),
19
+			lambda (x,y): (x,y+1),
20
+			lambda (x,y): (x-1,y),
21
+		]
22
+		for idx,val in enumerate(neighbors):
23
+			val = neighbors[idx] = previous.get(chg_funcs[idx](pos))
24
+			if val is not None: val.reconnect()
25
+		self.neighbors = neighbors
26
+
27
+
28
+	def reconnect(self):
29
+		chg_funcs = [
30
+			lambda (x,y): (x,y-1),
31
+			lambda (x,y): (x+1,y),
32
+			lambda (x,y): (x,y+1),
33
+			lambda (x,y): (x-1,y),
34
+		]
35
+		for idx,val in enumerate(self.neighbors):
36
+			if val is None: continue
37
+			self.neighbors[idx] = self.previous.get(chg_funcs[idx](self.pos))
38
+
39
+	def expand(self, tl=(0,0),br=(None,None), recurse=0):
40
+		chg_funcs = [
41
+			lambda (x,y): (x,y-1),
42
+			lambda (x,y): (x+1,y),
43
+			lambda (x,y): (x,y+1),
44
+			lambda (x,y): (x-1,y),
45
+		]
46
+
47
+
48
+		for idx, (cfunc,val) in enumerate(zip(chg_funcs,self.neighbors)):
49
+			if val is None:
50
+				nx,ny = cfunc(self.pos)
51
+				if any(a<b for (a,b) in zip((nx,ny), tl)): continue
52
+				elif br[0] is not None and any(a>b for (a,b) in zip((nx,ny), br)): continue
53
+
54
+				if (nx,ny) in self.previous:
55
+					val = self.previous[nx,ny]
56
+					if val.value + 1 < self.value:
57
+						self.value = val.value + 1
58
+				else:
59
+					val = Cell((nx,ny), self.value+1, None, self.previous)
60
+
61
+				self.neighbors[idx] = val
62
+				if self not in val.neighbors or val.value > self.value + 1:
63
+					val.expand(tl,br)
64
+
65
+
66
+
67
+
68
+	@staticmethod
69
+	def get_topleftmost(cell):
70
+		return cell.neighbors[min(cell.neighbors, key=lambda (x,y): x+y)]
71
+
0 72
new file mode 100644
... ...
@@ -0,0 +1,140 @@
1
+from __future__ import print_function
2
+import collections
3
+
4
+#
5
+#  (0,0)  (1,0) | (2,0) (3,0)
6
+#  (0,1)  (1,1) | (2,1) (3,1)
7
+#  -------------+------------
8
+#  (0,2)  (1,2) | (2,2) (3,2)
9
+#  (0,3)  (1,3) | (2,3) (3,3)
10
+#
11
+
12
+class XY(collections.namedtuple('XY', 'x y')):
13
+	def __add__(self, other):
14
+		dx, dy = other
15
+		return XY(self.x+dx, self.y+dy)
16
+	def __sub__(self, other):
17
+		dx, dy = other
18
+		return XY(self.x-dx, self.y-dy)
19
+	def __div__(self, n):
20
+		return XY(self.x/n, self.y/n)
21
+	def __mul__(self, n):
22
+		return XY(self.x*n, self.y*n)
23
+	def __abs__(self):
24
+		return XY(abs(self.x), abs(self.y))
25
+
26
+class AABB(collections.namedtuple('AABB', 'center halfDimension')):
27
+	@property
28
+	def tl(self):
29
+		return self.center - self.halfDimension
30
+	@property
31
+	def br(self):
32
+		return self.center + self.halfDimension
33
+	@property
34
+	def tr(self):
35
+		return XY(self.br[0], self.tl[1])
36
+	@property
37
+	def bl(self):
38
+		return XY(self.tl[0], self.br[1])
39
+	def containsPoint(self, p):
40
+		tl = self.center - self.halfDimension
41
+		br = (self.center + self.halfDimension) - (1,1)
42
+		return all(x >= 0 for x in p - tl) and all(x > 0 for x in br - p)
43
+
44
+	def intersectsAABB(self, other):
45
+		tl = self.center - self.halfDimension
46
+		br = self.center + self.halfDimension
47
+		tr = XY(br[0], tl[1])
48
+		bl = XY(tl[0], br[1])
49
+		return other.containsPoint(tl) or other.containsPoint(br) or other.containsPoint(tr) or other.containsPoint(bl)
50
+
51
+
52
+	def subdivide(self):
53
+		tl = self.center - self.halfDimension
54
+		br = self.center + self.halfDimension
55
+		tr = XY(br[0], tl[1])
56
+		bl = XY(tl[0], br[1])
57
+
58
+		center = self.center
59
+		ne = AABB( (tl+center)/2, self.halfDimension/2 )
60
+		nw = AABB( (tr+center)/2, self.halfDimension/2 )
61
+		se = AABB( (br+center)/2, self.halfDimension/2 )
62
+		sw = AABB( (bl+center)/2, self.halfDimension/2 )
63
+		return nw, ne, se, sw
64
+
65
+class QuadTree(object):
66
+	QT_NODE_CAPACITY = 4
67
+
68
+	def __init__(self, boundary):
69
+		self.boundary = boundary
70
+		self.nw = None
71
+		self.ne = None
72
+		self.se = None
73
+		self.sw = None
74
+		self.points = []
75
+
76
+	def insert(self, p):
77
+		if not self.boundary.containsPoint(p): return False
78
+
79
+		if len(self.points) < self.QT_NODE_CAPACITY:
80
+			self.points.append(p)
81
+			return True
82
+
83
+		if self.nw is None:
84
+			self.nw, self.ne, self.se, self.sw = map(self.__class__, self.boundary.subdivide())
85
+			while self.points:
86
+				point = self.points.pop()
87
+				if self.nw.insert(point): pass
88
+				elif self.ne.insert(point): pass
89
+				elif self.se.insert(point): pass
90
+				elif self.sw.insert(point): pass
91
+
92
+		elif self.nw.insert(p): return True
93
+		elif self.ne.insert(p): return True
94
+		elif self.se.insert(p): return True
95
+		elif self.sw.insert(p): return True
96
+
97
+		return False
98
+
99
+	def query(self, range):
100
+		pointsInRange = []
101
+
102
+		if not self.boundary.intersectsAABB(range):
103
+			return pointsInRange
104
+
105
+		for p in self.points:
106
+			if range.containsPoint(p):
107
+				pointsInRange.append(p)
108
+
109
+		if self.nw is None: return pointsInRange
110
+
111
+		pointsInRange.extend(self.nw.query(range))
112
+		pointsInRange.extend(self.ne.query(range))
113
+		pointsInRange.extend(self.se.query(range))
114
+		pointsInRange.extend(self.sw.query(range))
115
+
116
+		return pointsInRange
117
+
118
+	def visualize(self):
119
+		points = self.query(self.boundary)
120
+		import numpy
121
+		out = numpy.zeros(self.boundary.halfDimension * 2).astype('int')
122
+		for p in points:
123
+			out[p] = 1
124
+
125
+		for row in out:
126
+			for cell in row:
127
+				if cell == 1: print('#',end='')
128
+				elif cell == 0: print(' ',end='')
129
+			print()
130
+
131
+a = QuadTree(AABB(XY(128,128), XY(128,128)))
132
+import random
133
+for x in range(128):
134
+	for y in range(128):
135
+		if not a.insert(XY(x,y)):
136
+			pass #print (x,y)
137
+#points = {XY(random.randrange(128),random.randrange(128)) for ___ in range(300)}
138
+#for p in points:
139
+	#a.insert(p)
140
+#a.visualize()
... ...
@@ -111,12 +111,13 @@ class LevelMap(object):
111 111
 		self.fovmaps = [None]
112 112
 
113 113
 	def get_tile_type(self, level,x,y):
114
-		tile_val = mapping.get(self.map.data[level][y][x],(None,' '))[1]
114
+		lvl = self.map.get_level(level)
115
+		tile_val = mapping.get(lvl[y][x],(None,' '))[1]
115 116
 		return names.get(tile_val,('',True,True))
116 117
 
117 118
 	def get_fovmap(self,level):
118 119
 		if level >= len(self.fovmaps):
119
-			levelmap = self.map.data[level]
120
+			levelmap = mp.get_level(level)
120 121
 			self.fovmaps.append(libtcod.map_new(len(levelmap), len(levelmap[0])))
121 122
 			libtcod.map_clear(self.fovmaps[-1])
122 123
 		return self.fovmaps[level]
... ...
@@ -151,7 +152,8 @@ class LevelMap(object):
151 152
 	def calculate_level(self, level):
152 153
 		if not self.has_level(level):
153 154
 			print 'calculate_level'
154
-			for y,row in enumerate(mp.data[level]):
155
+			lvl = mp.get_level(level)
156
+			for y,row in enumerate(lvl):
155 157
 				for x,__ in enumerate(row):
156 158
 					lm.adj_map(level, x,y)
157 159
 
... ...
@@ -164,7 +166,8 @@ for c in [con,message_con]:
164 166
 	libtcod.console_clear(c)
165 167
 
166 168
 offset_x, offset_y = 0,0
167
-MAP_Y,MAP_X = len(mp.data[level]), len(mp.data[level][0])
169
+levelmap = mp.get_level(level)
170
+MAP_Y,MAP_X = len(levelmap), len(levelmap[0])
168 171
 player_x, player_y = random.randrange(0,MAP_X),random.randrange(0,MAP_Y)
169 172
 if not lm.get_walkable(level,player_x,player_y):
170 173
 	player_x,player_y = [(x,y) for x in range(player_x-2,player_x+3) for y in range(player_y-2,player_y+3) if lm.get_walkable(level,x,y)][-1]
... ...
@@ -188,7 +191,7 @@ while not libtcod.console_is_window_closed():
188 191
 	t1 = time.time()
189 192
 	lm.comp_fov(level, player_x, player_y,10)
190 193
 	player_screen_pos = player_x-offset_x,player_y-offset_y
191
-	for y,row in enumerate(mp.data[level][offset_y:offset_y+Settings.SCREEN_HEIGHT]):
194
+	for y,row in enumerate(levelmap[offset_y:offset_y+Settings.SCREEN_HEIGHT]):
192 195
 		for x,cell in enumerate(row[offset_x:offset_x+Settings.SCREEN_WIDTH]):
193 196
 			color,char,bgcolor = mapping.get(cell, (libtcod.Color(0,0,0),' ',libtcod.Color(0,0,0)))
194 197
 
... ...
@@ -279,7 +282,8 @@ while not libtcod.console_is_window_closed():
279 282
 	else:
280 283
 		player_prop = player_x/MAP_X, player_y/MAP_Y
281 284
 		offset_prop = offset_x/MAP_X, offset_y/MAP_Y
282
-		MAP_Y,MAP_X = len(mp.data[level]), len(mp.data[level][0])
285
+		levelmap = mp.get_level(level)
286
+		MAP_Y,MAP_X = len(levelmap), len(levelmap[0])
283 287
 		libtcod.console_clear(0)
284 288
 		libtcod.console_clear(con)
285 289
 
... ...
@@ -1,21 +1,22 @@
1
+
1 2
 # Copyright (c) 2013 Edward Langley
2 3
 # All rights reserved.
3
-# 
4
+#
4 5
 # Redistribution and use in source and binary forms, with or without
5 6
 # modification, are permitted provided that the following conditions
6 7
 # are met:
7
-# 
8
+#
8 9
 # Redistributions of source code must retain the above copyright notice,
9 10
 # this list of conditions and the following disclaimer.
10
-# 
11
+#
11 12
 # Redistributions in binary form must reproduce the above copyright
12 13
 # notice, this list of conditions and the following disclaimer in the
13 14
 # documentation and/or other materials provided with the distribution.
14
-# 
15
+#
15 16
 # Neither the name of the project's author nor the names of its
16 17
 # contributors may be used to endorse or promote products derived from
17 18
 # this software without specific prior written permission.
18
-# 
19
+#
19 20
 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 21
 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 22
 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
... ...
@@ -29,6 +30,7 @@
29 30
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 31
 
31 32
 from __future__ import division
33
+import collections
32 34
 import random
33 35
 import numpy
34 36
 
... ...
@@ -62,24 +64,84 @@ P2
62 64
 %(data)s
63 65
 """
64 66
 
67
+class MapData(collections.MutableMapping):
68
+	def __init__(self, data, base=9):
69
+		self.data = data
70
+		self.base = base
71
+		self.below = [[None for __ in range(base)] for ___ in range(base)]
72
+
73
+	@classmethod
74
+	def rand_new(cls, mean, base=9):
75
+		data = numpy.random.normal(mean, max(mean/4,1), (base,base)).astype(int)
76
+		return cls(data,base)
77
+
78
+	def get_cell(self, x,y, depth=1):
79
+		cur = self
80
+		if depth > 1:
81
+			below = self.get_below(x,y)
82
+			depth -= 1
83
+			while depth > 1:
84
+				below = below.get_below(0,0)
85
+				depth -= 1
86
+			cur = below
87
+		return cur.data[y,x]
88
+
89
+	def get_below(self, x,y):
90
+		below = self.below[y][x]
91
+		if below is None:
92
+			mean = self.get_cell(x,y)
93
+			below = self.below[y][x] = self.rand_new(mean,self.base)
94
+		return below
95
+
96
+	def get_all_below(self):
97
+		for y, lis in enumerate(self.below):
98
+			for x, val in enumerate(lis):
99
+				if val is not None: continue
100
+				self.get_below(x,y)
101
+		return numpy.concatenate([numpy.concatenate([x.data for x in l],1) for l in self.below])
102
+
103
+	def get_rect_below(self, x,y, w,h):
104
+		tw = w/self.base
105
+		tw = int(numpy.ceil(tw))
106
+		th = h/self.base
107
+		th = int(numpy.ceil(th))
108
+		out = []
109
+		for ny  in range(y,min(len(self.data),y+th)):
110
+			out.append([])
111
+			for nx in range(x,min(len(self.data[0]),x+tw)):
112
+				out[-1].append(self.get_below(nx,ny))
113
+
114
+		return numpy.concatenate([numpy.concatenate([x.data for x in l],1) for l in out])[:h,:w]
115
+
116
+	def __getitem__(self, key):
117
+		return self.data.__getitem__(key)
118
+	def __delitem__(self, key):
119
+		self.data.__setitem__(key, 0.0)
120
+	def __len__(self):
121
+		return len(self.data)
122
+	def __setitem__(self, key, value):
123
+		self.data.__setitem__(key, value)
124
+	def __iter__(self):
125
+		return iter(self.data)
126
+
127
+
65 128
 class Map(object):
66 129
 	def __init__(self, data, depth, base=9):
67 130
 		self.data = data
68 131
 		self.depth = depth
69 132
 		self.base = base
133
+
70 134
 	@classmethod
71 135
 	def rand_new(cls, depth,base=9):
72
-		data = [numpy.random.random_integers(0,10,(1,1))]
73
-		for x in range(1,depth+1):
74
-			global out
75
-			out = numpy.zeros((base**x, base**x),'int')
76
-			for r, row in enumerate(data[-1]):
77
-				for c, col in enumerate(row):
78
-					new = numpy.random.normal(col,max(col/4,1),(base,base)).astype(int)
79
-					out[r*base:r*base+base,c*base:c*base+base] = new
80
-			data.append(out)
136
+		data = MapData.rand_new(numpy.random.randint(0,10),base)
81 137
 		return cls(data,depth)
82 138
 
139
+	def get_level(self, level):
140
+		cur = self.data
141
+		for idx in range(1, level):
142
+			cur = cur.get_below(0,0)
143
+		return cur.get_rect_below(0,0, self.base**level, self.base**level)
144
+
83 145
 	def to_pgm(self,level):
84 146
 		level = self.data[level].copy()
85 147
 		level -= min(l.min() for l in self.data)
87 149
Binary files a/terminal.png and b/terminal.png differ