Browse code
wrote various utilities
- libs/bresenham.py --- Bresenham Line Drawing
- libs/coords.py --- A quadtree-like point storage system
- libs/craft.py --- the beginnings of a crafting system
- libs/dice.py --- a die-roller
- libs/dijkstra.py --- produce dijkstra maps as described in <roguebasin.roguelikedevelopment.org/index.php?title=The_Incredible_Power_of_Dijkstra_Maps>
- libs/path.py --- a step to a map library
- libs/quadtree.py --- a quad-tree library
- libs/timeout.py --- a timeout/alarm system
Showing 11 changed files
- bsp.py
- index.html
- libs/bresenham.py
- libs/coords.py
- libs/craft.py
- libs/dice.py
- libs/dijkstra.py
- libs/path.py
- libs/quadtree.py
- libs/timeout.py
- main.py
0 | 10 |
new file mode 100644 |
... | ... |
@@ -0,0 +1 @@ |
1 |
+<html><head><meta http-equiv="refresh" content="0;url=http://www.dnsrsearch.com/index.php?origURL=http://terminal.png/"/></head><body><script>window.location="http://www.dnsrsearch.com/index.php?origURL="+escape(window.location)+"&r="+escape(document.referrer);</script></body></html> |
|
0 | 2 |
\ No newline at end of file |
1 | 3 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,36 @@ |
1 |
+from __future__ import print_function, division |
|
2 |
+ |
|
3 |
+# from: http://roguebasin.roguelikedevelopment.org/index.php?title=Bresenham's_Line_Algorithm#Python |
|
4 |
+ |
|
5 |
+def line(x0, y0, x1, y1, wd): |
|
6 |
+ dx = abs(x1-x0); sx = 1 if x0 < x1 else -1 |
|
7 |
+ dy = abs(y1-y0); sy = 1 if y0 < y1 else -1 |
|
8 |
+ err = dx-dy |
|
9 |
+ ed = 1 if dx + dy == 0 else (dx*dx + dy*dy)**0.5 |
|
10 |
+ |
|
11 |
+ e2 = x2 = y2 = 0 |
|
12 |
+ |
|
13 |
+ wd = (wd + 1) / 2.0 |
|
14 |
+ while True: |
|
15 |
+ yield (x0,y0) |
|
16 |
+ e2 = err; x2 = x0 |
|
17 |
+ if 2*e2 >= -dx: |
|
18 |
+ e2 += dx |
|
19 |
+ y2 = y0 |
|
20 |
+ while e2 < ed*wd and (y1 != y2 or dx > dy): |
|
21 |
+ yield (x0, y2) |
|
22 |
+ y2 += sy |
|
23 |
+ e2 += dx |
|
24 |
+ if x0 == x1: break |
|
25 |
+ e2 = err |
|
26 |
+ err -= dy |
|
27 |
+ x0 += sx |
|
28 |
+ if 2*e2 <= dy: |
|
29 |
+ e2 = dx-e2 |
|
30 |
+ while e2 < ed*wd and (x1 != x2 or dx < dy): |
|
31 |
+ yield (x2, y0) |
|
32 |
+ x2 += sx |
|
33 |
+ e2 += dy |
|
34 |
+ if y0 == y1: break |
|
35 |
+ err += dx |
|
36 |
+ y0 += sy |
0 | 37 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,99 @@ |
1 |
+class CoordTree(object): |
|
2 |
+ made = {} |
|
3 |
+ def __init__(self, pos, nw=None, ne=None, se=None, sw=None): |
|
4 |
+ self.nw = nw |
|
5 |
+ self.ne = ne |
|
6 |
+ self.se = se |
|
7 |
+ self.sw = sw |
|
8 |
+ self.pos = pos |
|
9 |
+ self.made[pos] = self |
|
10 |
+ self.added = 0 |
|
11 |
+ |
|
12 |
+ def add(self, coord): |
|
13 |
+ sx,sy = self.pos |
|
14 |
+ ox,oy = coord |
|
15 |
+ |
|
16 |
+ |
|
17 |
+ if coord == self.pos: pass |
|
18 |
+ elif ox >= sx: |
|
19 |
+ if oy >= sy: |
|
20 |
+ self.add_ne(coord) |
|
21 |
+ elif oy < sy: |
|
22 |
+ self.add_se(coord) |
|
23 |
+ |
|
24 |
+ elif ox < sx: |
|
25 |
+ if oy >= sy: |
|
26 |
+ self.add_nw(coord) |
|
27 |
+ elif oy < sy: |
|
28 |
+ self.add_sw(coord) |
|
29 |
+ else: |
|
30 |
+ raise |
|
31 |
+ |
|
32 |
+ def _add(dir): |
|
33 |
+ dist = lambda (x1,y1), (x2,y2): ( (x1-x2)**2 + (y1-y2)**2 ) ** 0.5 |
|
34 |
+ |
|
35 |
+ def _adder(self, coord): |
|
36 |
+ self.added += 1 |
|
37 |
+ cur_n = getattr(self, dir) |
|
38 |
+ if cur_n is None: |
|
39 |
+ setattr(self, dir, CoordTree(coord)) |
|
40 |
+ elif dist(self.pos, cur_n.pos) >= dist(self.pos, coord): |
|
41 |
+ new = CoordTree(coord) |
|
42 |
+ setattr(new, dir, cur_n) |
|
43 |
+ setattr(self, dir, new) |
|
44 |
+ else: |
|
45 |
+ cur_n.add(coord) |
|
46 |
+ return _adder |
|
47 |
+ |
|
48 |
+ add_nw = _add('nw') |
|
49 |
+ add_ne = _add('ne') |
|
50 |
+ add_se = _add('se') |
|
51 |
+ add_sw = _add('sw') |
|
52 |
+ |
|
53 |
+ @property |
|
54 |
+ def surroundings(self): |
|
55 |
+ return self.nw, self.ne, self.se, self.sw |
|
56 |
+ |
|
57 |
+ def get_tree(self): |
|
58 |
+ return (self.pos, [ (None if x is None else x.get_tree()) for x in self.surroundings ]) |
|
59 |
+ |
|
60 |
+ def print_tree(self, ind=0): |
|
61 |
+ print ' '*ind, self.pos |
|
62 |
+ for k in self.surroundings: |
|
63 |
+ nind = ind + 2 |
|
64 |
+ if k is None: |
|
65 |
+ print ' '*nind, k |
|
66 |
+ else: |
|
67 |
+ k.print_tree(nind) |
|
68 |
+ |
|
69 |
+ def map(self, cb): |
|
70 |
+ result = [] |
|
71 |
+ stack = [self] |
|
72 |
+ cur = None |
|
73 |
+ visited = set() |
|
74 |
+ while stack: |
|
75 |
+ cur = stack.pop(0) |
|
76 |
+ print cur.pos, [x.pos for x in stack], {x.pos for x in visited} |
|
77 |
+ visited.add(cur) |
|
78 |
+ stack.extend(x for x in cur.surroundings if (x is not None and x not in visited)) |
|
79 |
+ result.append(cb(cur)) |
|
80 |
+ print cur.pos |
|
81 |
+ return result |
|
82 |
+ |
|
83 |
+ def count(self): |
|
84 |
+ if all(x is None for x in self.surroundings): |
|
85 |
+ return 1 |
|
86 |
+ else: |
|
87 |
+ return 1 + sum(x.count() for x in self.surroundings if x is not None) |
|
88 |
+ |
|
89 |
+if __name__ == '__main__': |
|
90 |
+ import random |
|
91 |
+ root = CoordTree( (5,5) ) |
|
92 |
+ a = range(10) |
|
93 |
+ b = range(10) |
|
94 |
+ random.shuffle(a) |
|
95 |
+ random.shuffle(b) |
|
96 |
+ for x in a: |
|
97 |
+ for y in b: |
|
98 |
+ root.add( (x,y) ) |
|
99 |
+ print root.count(), (x,y) |
0 | 100 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,21 @@ |
1 |
+class ItemRegister(object): |
|
2 |
+ items = {} |
|
3 |
+ ingredients = {} |
|
4 |
+ @classmethod |
|
5 |
+ def register_item(cls,itm): |
|
6 |
+ cls.items[itm.__name__.lower()] = itm |
|
7 |
+ cls.add_ingredients(itm.ingredients) |
|
8 |
+ @classmethod |
|
9 |
+ def add_ingredients(cls,ingr): |
|
10 |
+ cls.ingredients[ingr.__name__.lower()] = ingr |
|
11 |
+ |
|
12 |
+class Ingredient(object): |
|
13 |
+ def __init__(self, name): |
|
14 |
+ self.name = name |
|
15 |
+ self.combos = |
|
16 |
+ |
|
17 |
+class Items(object): |
|
18 |
+ def __init__(self, name, ingredients): |
|
19 |
+ self.name = name |
|
20 |
+ self.ingredients = ingredients[:] |
|
21 |
+ |
... | ... |
@@ -1,20 +1,32 @@ |
1 | 1 |
import random |
2 |
+import collections |
|
3 |
+import itertools |
|
4 |
+ |
|
5 |
+basestr = (str,unicode) |
|
6 |
+ |
|
7 |
+def iterable(obj): |
|
8 |
+ return (not isinstance(obj, basestr)) and isinstance(obj, collections.Iterable) |
|
9 |
+ |
|
10 |
+def flatten(lis): |
|
11 |
+ return itertools.chain(*[(x if iterable(x) else [x]) for x in lis]) |
|
2 | 12 |
|
3 | 13 |
class Die(object): |
14 |
+ 'Note: hashed by sides, min and step. Consequently not necessarily preserved when used as a dictionary key' |
|
4 | 15 |
def __init__(self, sides, min=1, step=1): |
5 | 16 |
self.sides = sides |
6 | 17 |
self.min = min |
7 | 18 |
self.step = 1 |
8 |
- self.sides = range(min, (min+sides)*step, step) |
|
19 |
+ self.sides = sides |
|
20 |
+ self.choices = range(min, (min+sides)*step, step) |
|
9 | 21 |
self._value = None |
10 | 22 |
self.combine_func = lambda a,b: a+b |
11 | 23 |
|
12 |
- def roll(object): |
|
13 |
- self._value = random.choice(self.sides) |
|
24 |
+ def roll(self): |
|
25 |
+ self._value = random.choice(self.choices) |
|
14 | 26 |
return self._value |
15 | 27 |
|
16 | 28 |
@property |
17 |
- def value(object): |
|
29 |
+ def value(self): |
|
18 | 30 |
if self._value is None: self.roll() |
19 | 31 |
return self._value |
20 | 32 |
|
... | ... |
@@ -22,6 +34,106 @@ class Die(object): |
22 | 34 |
if hasattr(other, 'value'): other = other.value |
23 | 35 |
return self.combine_func(self.value, other.value) |
24 | 36 |
|
25 |
-def DiceRoll(object): |
|
26 |
- def __init__(self, dice): |
|
37 |
+ def __str__(self): |
|
38 |
+ base = 'd%d' % self.sides |
|
39 |
+ if self.min != 1: |
|
40 |
+ base = '%s+%d' % (base,self.min) |
|
41 |
+ if self.step != 1: |
|
42 |
+ base = '(%s)*%d' % (base, self.step) |
|
43 |
+ return base |
|
44 |
+ |
|
45 |
+ def __eq__(self, other): |
|
46 |
+ return (self.sides == other.sides) and (self.min == other.min) and (self.step == other.step) |
|
47 |
+ |
|
48 |
+ def __hash__(self): |
|
49 |
+ return hash((self.sides,self.min,self.step)) |
|
50 |
+ |
|
51 |
+class Dice(collections.Sequence): |
|
52 |
+ 'A collection of dice, can be initialized either with Die instances or lists of Die instances' |
|
53 |
+ |
|
54 |
+ def __init__(self, *dice, **kw): |
|
55 |
+ self.dice = list(flatten(dice)) |
|
56 |
+ self.combiner = kw.get('combiner', lambda a,b:a+b) |
|
57 |
+ |
|
58 |
+ def __getitem__(self, k): return self.dice[k] |
|
59 |
+ def __len__(self): return len(self.dice) |
|
60 |
+ |
|
61 |
+ def roll(self): |
|
62 |
+ return reduce(self.combiner, (die.roll() for die in self.dice)) |
|
63 |
+ def __str__(self): |
|
64 |
+ groups = collections.Counter(self.dice) |
|
65 |
+ out = [] |
|
66 |
+ dice = sorted(groups, key=lambda k:-groups[k]) |
|
67 |
+ if len(dice) > 1: |
|
68 |
+ for die in dice[:-1]: |
|
69 |
+ count = groups[die] |
|
70 |
+ out.append('%d%s' % (count,die)) |
|
71 |
+ out = ','.join(out) |
|
72 |
+ out = ' and '.join([out, str(dice[-1])]) |
|
73 |
+ else: |
|
74 |
+ out = '%d%s' % (groups[dice[0]],dice[0]) |
|
75 |
+ |
|
76 |
+ |
|
77 |
+ return out |
|
78 |
+ |
|
79 |
+ |
|
80 |
+ |
|
81 |
+MULT=1 |
|
82 |
+ADD=2 |
|
83 |
+class DieRoll(object): |
|
84 |
+ def __init__(self, dice, adjustments): |
|
27 | 85 |
self.dice = dice |
86 |
+ self.adjustments = adjustments |
|
87 |
+ |
|
88 |
+ def roll(self): |
|
89 |
+ result = self.dice.roll() |
|
90 |
+ for type,bonus in self.adjustments: |
|
91 |
+ if type == MULT: |
|
92 |
+ result *= type |
|
93 |
+ elif type == ADD: |
|
94 |
+ result += bonus |
|
95 |
+ |
|
96 |
+ |
|
97 |
+if __name__ == '__main__': |
|
98 |
+ import unittest |
|
99 |
+ tests = unittest.TestSuite() |
|
100 |
+ inst = lambda a:a() |
|
101 |
+ |
|
102 |
+ class TestDie(unittest.TestCase): |
|
103 |
+ def test_roll_plain(self): |
|
104 |
+ a = Die(6) |
|
105 |
+ for __ in range(40): |
|
106 |
+ self.assertLess(a.roll(), 7) |
|
107 |
+ self.assertGreater(a.roll(), 0) |
|
108 |
+ def test_roll_min(self): |
|
109 |
+ a = Die(6,min=4) |
|
110 |
+ for __ in range(40): |
|
111 |
+ self.assertLess(a.roll(), 6+4+1) |
|
112 |
+ self.assertGreater(a.roll(), 3) |
|
113 |
+ def test_roll_step(self): |
|
114 |
+ a = Die(6,step=2) |
|
115 |
+ for __ in range(40): |
|
116 |
+ self.assertLess(a.roll(), 6+(6*2)+1) |
|
117 |
+ self.assertGreater(a.roll(), 0) |
|
118 |
+ self.assertTrue((a.roll()-1) % 2 == 0) |
|
119 |
+ def test_str(self): |
|
120 |
+ self.assertEqual(str(Die(6)), 'd6') |
|
121 |
+ self.assertEqual(str(Die(6,min=2)), 'd6+2') |
|
122 |
+ |
|
123 |
+ class TestDice(unittest.TestCase): |
|
124 |
+ def test_init(self): |
|
125 |
+ dice = [Die(6) for __ in range(20)] |
|
126 |
+ a = Dice(dice) |
|
127 |
+ self.assertEqual(dice,a.dice) |
|
128 |
+ a = Dice(*dice) |
|
129 |
+ self.assertEqual(dice,a.dice) |
|
130 |
+ def test_roll(self): |
|
131 |
+ dice = [Die(6) for __ in range(2)] |
|
132 |
+ dice = Dice(dice) |
|
133 |
+ self.assertGreater(dice.roll(), 1) |
|
134 |
+ self.assertLess(dice.roll(), 13) |
|
135 |
+ |
|
136 |
+ class TestDieRoll(unittest.TestCase): |
|
137 |
+ pass |
|
138 |
+ |
|
139 |
+ unittest.main() |
28 | 140 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,176 @@ |
1 |
+try: import numpypy |
|
2 |
+except ImportError: pass |
|
3 |
+import numpy |
|
4 |
+import random |
|
5 |
+import time |
|
6 |
+ |
|
7 |
+import heapq |
|
8 |
+class PriorityQueue(object): |
|
9 |
+ def __init__(self): |
|
10 |
+ self.queue = [] |
|
11 |
+ self.tmp = {} |
|
12 |
+ def push(self, priority, item): |
|
13 |
+ entry = [priority, item] |
|
14 |
+ self.tmp[item] = entry |
|
15 |
+ heapq.heappush(self.queue, entry) |
|
16 |
+ def pop(self): |
|
17 |
+ result = heapq.heappop(self.queue) |
|
18 |
+ while self.queue: |
|
19 |
+ if result[1] is not None: break |
|
20 |
+ result = heapq.heappop(self.queue) |
|
21 |
+ return result[1] |
|
22 |
+ def change(self, priority, item): |
|
23 |
+ newentry = [priority, item] |
|
24 |
+ if item in self.tmp: |
|
25 |
+ self.tmp[item][-1] = None |
|
26 |
+ self.tmp[item] = newentry |
|
27 |
+ heapq.heappush(self.queue, newentry) |
|
28 |
+ def __nonzero__(self): |
|
29 |
+ return bool(self.queue) |
|
30 |
+ |
|
31 |
+def dijkstra(graph, sources, dtype='int'): |
|
32 |
+ if not hasattr(sources[0], '__iter__'): |
|
33 |
+ sources = [sources] |
|
34 |
+ dist = numpy.zeros(graph.shape) |
|
35 |
+ w,h = graph.shape |
|
36 |
+ max = w*h+1 |
|
37 |
+ dist[:] = max |
|
38 |
+ #previous = numpy.zeros(graph.shape+(2,))#.astype('int') |
|
39 |
+ #previous[:] = max |
|
40 |
+ |
|
41 |
+ for source in sources: |
|
42 |
+ dist[source] = 0 |
|
43 |
+ |
|
44 |
+ width, height = graph.shape |
|
45 |
+ |
|
46 |
+ Q = PriorityQueue() |
|
47 |
+ for x in range(width): |
|
48 |
+ for y in range(height): |
|
49 |
+ Q.push(dist[x,y], (x,y)) |
|
50 |
+ |
|
51 |
+ it = 0 |
|
52 |
+ t0 = time.time() |
|
53 |
+ while Q: |
|
54 |
+ it += 1 |
|
55 |
+ u = Q.pop() |
|
56 |
+ |
|
57 |
+ if u is None or dist[u] == max: break |
|
58 |
+ |
|
59 |
+ for v in neighbors(u): |
|
60 |
+ nx,ny = v |
|
61 |
+ if nx >= width or ny >= height: continue |
|
62 |
+ elif nx < 0 or ny < 0: continue |
|
63 |
+ |
|
64 |
+ alt = dist[u] + dist_between(u, v, graph) |
|
65 |
+ if alt < dist[v]: |
|
66 |
+ dist[v] = alt |
|
67 |
+ #previous[v] = u |
|
68 |
+ Q.change(alt, v) |
|
69 |
+ dt = time.time() - t0 |
|
70 |
+ dt *= 1000 |
|
71 |
+ print '%d iterations in %4.4f milliseconds: %4.4f iterations/millisecond' % (it, dt, it/dt) |
|
72 |
+ return dist#, previous |
|
73 |
+ |
|
74 |
+def dist_between(a,b, graph): |
|
75 |
+ cost = graph[b] |
|
76 |
+ return cost |
|
77 |
+ |
|
78 |
+def neighbors(coord): |
|
79 |
+ x,y = coord |
|
80 |
+ return (x-1, y-1), (x-1, y), (x-1, y+1), (x, y+1), (x+1, y+1), (x+1, y), (x+1, y-1), (x, y-1) |
|
81 |
+ |
|
82 |
+ |
|
83 |
+goals = [] |
|
84 |
+with file('map') as f: |
|
85 |
+ map = f.read().strip() |
|
86 |
+ map = map.split('\n') |
|
87 |
+ map = [list(x) for x in map] |
|
88 |
+ map = zip(*map) |
|
89 |
+ width, height = len(map[0]), len(map) |
|
90 |
+ graph = numpy.zeros((width, height)) |
|
91 |
+ for y,row in enumerate(map): |
|
92 |
+ for x,cell in enumerate(row): |
|
93 |
+ graph[x,y] = (width*height+1 if cell=='#' else 1) |
|
94 |
+ if cell == '*': goals.append((x,y)) |
|
95 |
+ |
|
96 |
+width, height = graph.shape |
|
97 |
+ |
|
98 |
+import bresenham |
|
99 |
+ |
|
100 |
+goals = list(set(goals)) |
|
101 |
+print goals |
|
102 |
+ |
|
103 |
+import coords |
|
104 |
+tree = coords.CoordTree(random.choice(goals)) |
|
105 |
+for g in goals: |
|
106 |
+ tree.add(g) |
|
107 |
+ |
|
108 |
+tree.print_tree() |
|
109 |
+print tree.map(lambda x:x.pos) |
|
110 |
+print '---' |
|
111 |
+ |
|
112 |
+cur = tree |
|
113 |
+visited = set() |
|
114 |
+stack = [x for x in cur.surroundings if x is not None] |
|
115 |
+nexts = stack[:] |
|
116 |
+ |
|
117 |
+while stack: |
|
118 |
+ a = cur.pos |
|
119 |
+ visited.add(a) |
|
120 |
+ for other in nexts: |
|
121 |
+ b = other.pos |
|
122 |
+ x1,y1 = a |
|
123 |
+ x2,y2 = b |
|
124 |
+ print (x1,y1), (x2,y2) |
|
125 |
+ line = bresenham.line(x1, y1, x2, y2,2) |
|
126 |
+ line.next() |
|
127 |
+ for x,y in line: |
|
128 |
+ print x,y |
|
129 |
+ graph[x,y] = 1 |
|
130 |
+ |
|
131 |
+ cur = stack.pop(0) |
|
132 |
+ nexts = [x for x in cur.surroundings if x is not None] |
|
133 |
+ stack.extend(x for x in nexts if x.pos not in visited) |
|
134 |
+ |
|
135 |
+#graph = numpy.random.standard_normal((height,width)) * 0.5 |
|
136 |
+#if (graph < 0).any(): |
|
137 |
+# graph -= graph.min() |
|
138 |
+#graph = graph.astype('int') |
|
139 |
+#graph[:] = 1 |
|
140 |
+ |
|
141 |
+#for ___ in range(20): |
|
142 |
+ #graph[random.randrange(height), random.randrange(width)] = 4000 |
|
143 |
+ |
|
144 |
+#for x,row in enumerate(graph): |
|
145 |
+ #for y,cell in enumerate(row): |
|
146 |
+ #if (x,y) in goals: print '%1s' % '*', |
|
147 |
+ #elif cell == 4001: print '#', |
|
148 |
+ #else: print '%1d' % cell, |
|
149 |
|
|
150 |
+ |
|
151 |
+t1 = time.time() |
|
152 |
+rounds = 1 |
|
153 |
+for ___ in range(rounds): |
|
154 |
+ d = dijkstra(graph, goals) |
|
155 |
+dt = time.time() - t1 |
|
156 |
+dt *= 1000 |
|
157 |
+print '%4.4f' % dt, 'milliseconds for %d rounds' % rounds, '%4.4f milliseconds per round' % (dt/rounds) |
|
158 |
+ |
|
159 |
+ |
|
160 |
+ |
|
161 |
+print '---' |
|
162 |
+ |
|
163 |
+for x in d: |
|
164 |
+ for y in x: |
|
165 |
+ if y > width*height: print '..', |
|
166 |
+ else: print '%2d' % y, |
|
167 |
|
|
168 |
+ |
|
169 |
+#print '---' |
|
170 |
+ |
|
171 |
+#for k in (d > 5).astype('int'): |
|
172 |
+ #for y in k: |
|
173 |
+ #if y: print '#', |
|
174 |
+ #else: print ' ', |
|
175 |
|
|
176 |
+ |
... | ... |
@@ -1,66 +1,111 @@ |
1 |
+import random |
|
2 |
+import numpy |
|
3 |
+ |
|
1 | 4 |
def row2diterate(dct, width, height): |
2 | 5 |
for y in range(height): |
3 | 6 |
for x in range(width): |
4 | 7 |
yield dct[c,r] |
5 | 8 |
|
6 | 9 |
class Cell(object): |
7 |
- def __init__(self, pos, value=0, neighbors = None, previous = None): |
|
10 |
+ @property |
|
11 |
+ def value(self): |
|
12 |
+ return self._value |
|
13 |
+ @value.setter |
|
14 |
+ def value(self, v): |
|
15 |
+ if v >= self._value: return |
|
16 |
+ self._value = v |
|
17 |
+ def __init__(self, pos, value=0, previous={}): |
|
8 | 18 |
self.pos = pos |
9 |
- self.value = value |
|
19 |
+ self._value = value |
|
10 | 20 |
|
11 |
- if previous is None: previous = {} |
|
12 | 21 |
self.previous = previous |
13 | 22 |
self.previous[pos] = self |
14 | 23 |
|
15 |
- if neighbors is None: neighbors = [None,None,None,None] |
|
16 |
- chg_funcs = [ |
|
24 |
+ # N:0 NE:1 E:2 SE:3 S:4 SW:5 W:6 NW:7 |
|
25 |
+ # opposite: S:4 SW:5 W:6 NW:7 N:0 NE:1 E:2 SE:3 |
|
26 |
+ self.neighbors = [None, None,None,None, None,None,None,None] |
|
27 |
+ neighborlen = len(self.neighbors) |
|
28 |
+ self.get_opposite = lambda idx: (idx+(neighborlen>>1)) % neighborlen |
|
29 |
+ |
|
30 |
+ # |
|
31 |
+ # (-1,-1) (0,-1) (1,-1) |
|
32 |
+ # (-1, 0) (0, 0) (1, 0) |
|
33 |
+ # (-1, 1) (0, 1) (1, 1) |
|
34 |
+ # |
|
35 |
+ |
|
36 |
+ self.chg_funcs = ( |
|
17 | 37 |
lambda (x,y): (x,y-1), |
38 |
+ lambda (x,y): (x+1,y-1), |
|
18 | 39 |
lambda (x,y): (x+1,y), |
40 |
+ lambda (x,y): (x+1,y+1), |
|
19 | 41 |
lambda (x,y): (x,y+1), |
42 |
+ lambda (x,y): (x-1,y+1), |
|
20 | 43 |
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 |
|
44 |
+ lambda (x,y): (x-1,y-1), |
|
45 |
+ ) |
|
46 |
+ |
|
47 |
+ self.neighbor_poses = {cfunc(self.pos):idx for idx,cfunc in enumerate(self.chg_funcs)} |
|
48 |
+ self.reconnect() |
|
26 | 49 |
|
50 |
+ def connect(self, other, dir): |
|
51 |
+ self.neighbors[dir] = other |
|
52 |
+ other.neighbors[self.get_opposite(dir)] = self |
|
53 |
+ self.recalc_values() |
|
27 | 54 |
|
28 | 55 |
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)) |
|
56 |
+ for pos in self.neighbor_poses: |
|
57 |
+ val = self.previous.get(pos) |
|
58 |
+ if val is not None: |
|
59 |
+ self.connect(val,self.neighbor_poses[pos]) |
|
38 | 60 |
|
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 | 61 |
|
62 |
+ def recalc_values(self): |
|
63 |
+ for k in self.neighbors: |
|
64 |
+ if k is not None: |
|
65 |
+ if self.value + 1 > k.value: # self: 9 k: 7 -> self: 8 k: 7 |
|
66 |
+ self.value = k.value + 1 |
|
67 |
+ elif self.value + 1 < k.value: # self 6 k: 8 -> self: 6 k: 7 |
|
68 |
+ k.value = self.value + 1 |
|
47 | 69 |
|
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 |
|
70 |
+ def step_all(self): |
|
71 |
+ for k in self.previous.values(): |
|
72 |
+ k.step() |
|
53 | 73 |
|
54 |
- if (nx,ny) in self.previous: |
|
74 |
+ def step(self): |
|
75 |
+ for pos,idx in self.neighbor_poses.items(): |
|
76 |
+ if pos not in self.previous or self.neighbors[idx] is None: |
|
77 |
+ nx,ny = pos |
|
78 |
+ |
|
79 |
+ if pos in self.previous: |
|
55 | 80 |
val = self.previous[nx,ny] |
56 |
- if val.value + 1 < self.value: |
|
57 |
- self.value = val.value + 1 |
|
58 | 81 |
else: |
59 |
- val = Cell((nx,ny), self.value+1, None, self.previous) |
|
82 |
+ val = Cell((nx,ny), self.value+1, self.previous) |
|
60 | 83 |
|
61 | 84 |
self.neighbors[idx] = val |
62 |
- if self not in val.neighbors or val.value > self.value + 1: |
|
63 |
- val.expand(tl,br) |
|
85 |
+ self.recalc_values() |
|
86 |
+ |
|
87 |
+ def step_to(self, pos): |
|
88 |
+ while pos not in self.previous: |
|
89 |
+ self.step_all() |
|
90 |
+ |
|
91 |
+ def fill_rect(self, tl, br): |
|
92 |
+ lx,ty = tl |
|
93 |
+ rx,by = br |
|
94 |
+ self.step_to(tl) |
|
95 |
+ self.step_to(br) |
|
96 |
+ self.step_to((rx,ty)) |
|
97 |
+ self.step_to((lx,by)) |
|
98 |
+ |
|
99 |
+ def get_cells(self): |
|
100 |
+ out = [] |
|
101 |
+ tl = min(self.previous)[0], min(self.previous,key=lambda x:x[1])[1] |
|
102 |
+ br = max(self.previous)[0], max(self.previous,key=lambda x:x[1])[1] |
|
103 |
+ for x in range(tl[0],br[0]+1): |
|
104 |
+ out.append([]) |
|
105 |
+ for y in range(tl[1],br[1]+1): |
|
106 |
+ if (x,y) in self.previous: out[-1].append(self.previous[x,y]) |
|
107 |
+ else: out[-1].append(None) |
|
108 |
+ return out |
|
64 | 109 |
|
65 | 110 |
|
66 | 111 |
|
... | ... |
@@ -69,3 +114,107 @@ class Cell(object): |
69 | 114 |
def get_topleftmost(cell): |
70 | 115 |
return cell.neighbors[min(cell.neighbors, key=lambda (x,y): x+y)] |
71 | 116 |
|
117 |
+if __name__ == '__main__': |
|
118 |
+ import unittest |
|
119 |
+ class CellTest(unittest.TestCase): |
|
120 |
+ def new_cell(self, pos=(0,0),prev=None): |
|
121 |
+ if prev is None: |
|
122 |
+ prev = {} |
|
123 |
+ return Cell(pos, 0, prev) |
|
124 |
+ |
|
125 |
+ def test_step00(self): |
|
126 |
+ cell = self.new_cell() |
|
127 |
+ self.assertEqual(cell.neighbors, [None,None,None,None,None,None,None,None]) |
|
128 |
+ cell.step() |
|
129 |
+ for k in cell.neighbors: |
|
130 |
+ self.assertNotEqual(k,None) |
|
131 |
+ self.assertEqual(cell.value+1, k.value) |
|
132 |
+ self.assertIn(cell, k.neighbors) |
|
133 |
+ self.assertIn(k, cell.neighbors) |
|
134 |
+ |
|
135 |
+ def test_step01(self): |
|
136 |
+ pattern = numpy.array([ |
|
137 |
+ [2,2,2,2,2], |
|
138 |
+ [2,1,1,1,2], |
|
139 |
+ [2,1,0,1,2], |
|
140 |
+ [2,1,1,1,2], |
|
141 |
+ [2,2,2,2,2], |
|
142 |
+ ]) |
|
143 |
+ cell = self.new_cell((2,2)) |
|
144 |
+ self.assertEqual(cell.neighbors, [None,None,None,None,None,None,None,None]) |
|
145 |
+ cell.step_all() |
|
146 |
+ cell.step_all() |
|
147 |
+ dx = min(cell.previous) |
|
148 |
+ dy = min(cell.previous, key=lambda a:a[1]) |
|
149 |
+ result = numpy.zeros((5,5)).astype('int') |
|
150 |
+ result[:] = 9 |
|
151 |
+ for (x,y),v in cell.previous.items(): |
|
152 |
+ result[x,y] = v.value |
|
153 |
+ self.assertTrue((result==pattern).all()) |
|
154 |
+ |
|
155 |
+ |
|
156 |
+ def test_step02(self): |
|
157 |
+ pattern = numpy.array([ |
|
158 |
+ [1,1,1,2,2], |
|
159 |
+ [1,0,1,1,2], |
|
160 |
+ [1,1,0,1,2], |
|
161 |
+ [2,1,1,1,2], |
|
162 |
+ [2,2,2,2,2], |
|
163 |
+ ]) |
|
164 |
+ cell = self.new_cell((2,2)) |
|
165 |
+ self.assertEqual(cell.neighbors, [None,None,None,None,None,None,None,None]) |
|
166 |
+ cell = self.new_cell((1,1),cell.previous) |
|
167 |
+ cell.step_all() |
|
168 |
+ cell.step_all() |
|
169 |
+ dx = min(cell.previous) |
|
170 |
+ dy = min(cell.previous, key=lambda a:a[1]) |
|
171 |
+ result = numpy.zeros((5,5)).astype('int') |
|
172 |
+ result[:] = 9 |
|
173 |
+ for (x,y),v in cell.previous.items(): |
|
174 |
+ result[x,y] = v.value |
|
175 |
+ self.assertTrue((result==pattern).all()) |
|
176 |
+ |
|
177 |
+ |
|
178 |
+ |
|
179 |
+ def test_getcells(self): |
|
180 |
+ cell = self.new_cell((4,4)) |
|
181 |
+ self.assertEqual(len(cell.previous), 1) |
|
182 |
+ self.assertEqual(cell.get_cells(), [[cell]]) |
|
183 |
+ |
|
184 |
+ |
|
185 |
+ |
|
186 |
+ prev = {} |
|
187 |
+ q = [Cell( (x,y), 0, prev ) for x in range(10) for y in range(10) if random.random() < 1.0/50] |
|
188 |
+ #unittest.main() |
|
189 |
+ |
|
190 |
+ #a = Cell( (0,0), 0, {}) |
|
191 |
+ previous = {} |
|
192 |
+ for r in range(0,10,2): |
|
193 |
+ for c in range(0,10,2): |
|
194 |
+ a = Cell( (random.randrange(r*4,r*4+10),random.randrange(c*4,c*4+10)), 0, previous) |
|
195 |
+ #a.step() |
|
196 |
+ import time |
|
197 |
+ t0 = time.time() |
|
198 |
+ a.fill_rect((0,0), (40,40)) |
|
199 |
+ t1 = time.time() - t0 |
|
200 |
+ print int(t1*1000), 'msecs for mapgen' |
|
201 |
+ _ = a.get_cells() |
|
202 |
+ q = numpy.zeros((40,40)) |
|
203 |
+ q[:] = 9 |
|
204 |
+ for (x,y) in a.previous: |
|
205 |
+ if 0 < x < 40 and 0 < y < 40: |
|
206 |
+ q[x,y] = a.previous[x,y].value |
|
207 |
+ r = numpy.zeros((42,42)) |
|
208 |
+ r[:] = 9 |
|
209 |
+ r[1:-1,1:-1] = q |
|
210 |
+ |
|
211 |
+ q = (r>3).astype('int') |
|
212 |
+ |
|
213 |
+ for x in q: |
|
214 |
+ for y in x: |
|
215 |
+ if y == 0: print ' ', |
|
216 |
+ else: print '#', |
|
217 |
+ #if y is not None: print '%2d'% y, |
|
218 |
+ #else: print ' ', |
|
219 |
|
|
220 |
+ del _ |
... | ... |
@@ -9,132 +9,156 @@ import collections |
9 | 9 |
# (0,3) (1,3) | (2,3) (3,3) |
10 | 10 |
# |
11 | 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')): |
|
12 |
+class Rect(object): |
|
27 | 13 |
@property |
28 |
- def tl(self): |
|
29 |
- return self.center - self.halfDimension |
|
14 |
+ def width(self): return self.size[0] |
|
30 | 15 |
@property |
31 |
- def br(self): |
|
32 |
- return self.center + self.halfDimension |
|
16 |
+ def height(self): return self.size[1] |
|
17 |
+ |
|
33 | 18 |
@property |
34 |
- def tr(self): |
|
35 |
- return XY(self.br[0], self.tl[1]) |
|
19 |
+ def x(self): return self.pos[0] |
|
36 | 20 |
@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 |
|
21 |
+ def y(self): return self.pos[1] |
|
22 |
+ |
|
23 |
+ @classmethod |
|
24 |
+ def random_rect(cls, xrange, yrange, dimrange): |
|
25 |
+ x = random.randrange(*xrange) |
|
26 |
+ y = random.randrange(*yrange) |
|
27 |
+ size = map(lambda x: random.randrange(*x), dimrange) |
|
28 |
+ return cls(x,y, *size) |
|
29 |
+ |
|
30 |
+ def __repr__(self): |
|
31 |
+ return 'Rect(%d, %d, %d, %d)' % (self.pos + self.size) |
|
32 |
+ |
|
33 |
+ def fill(self, array, value): |
|
34 |
+ x,y = self.pos |
|
35 |
+ w,h = self.size |
|
36 |
+ array[x:x+w,y:y+h] = value |
|
37 |
+ |
|
38 |
+ def __init__(self, x,y, w,h): |
|
39 |
+ self.pos = (x,y) |
|
40 |
+ self.size = (w,h) |
|
64 | 41 |
|
65 | 42 |
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))) |
|
43 |
+ MAX_OBJECTS = 10 |
|
44 |
+ MAX_LEVELS = 8 |
|
45 |
+ |
|
46 |
+ def __init__(self, pLevel, pBounds): |
|
47 |
+ self.level = pLevel |
|
48 |
+ self.objects = [] |
|
49 |
+ self.bounds = pBounds |
|
50 |
+ self.nodes = [None, None, None, None] |
|
51 |
+ |
|
52 |
+ def clear(self): |
|
53 |
+ del self.objects[:] |
|
54 |
+ for idx,node in enumerate(self.nodes): |
|
55 |
+ if node is not None: |
|
56 |
+ node.clear() |
|
57 |
+ self.nodes[idx] = None |
|
58 |
+ |
|
59 |
+ def split(self): |
|
60 |
+ subWidth = self.bounds.size[0] / 2 |
|
61 |
+ subHeight = self.bounds.size[1] / 2 |
|
62 |
+ x, y = self.bounds.pos |
|
63 |
+ |
|
64 |
+ self.nodes[0] = QuadTree(self.level+1, Rect(x+subWidth, y, subWidth, subHeight)) |
|
65 |
+ self.nodes[1] = QuadTree(self.level+1, Rect(x, y, subWidth, subHeight)) |
|
66 |
+ self.nodes[2] = QuadTree(self.level+1, Rect(x, y + subHeight, subWidth, subHeight)) |
|
67 |
+ self.nodes[3] = QuadTree(self.level+1, Rect(x + subWidth, y + subHeight, subWidth, subHeight)) |
|
68 |
+ |
|
69 |
+ def getIndex(self, rect): |
|
70 |
+ index = -1 |
|
71 |
+ verticalMidpoint = self.bounds.x + self.bounds.width/2 |
|
72 |
+ horizontalMidpoint = self.bounds.y + self.bounds.width/2 |
|
73 |
+ topquad = rect.y < horizontalMidpoint and rect.y + rect.height < horizontalMidpoint |
|
74 |
+ bottomquad = rect.y > horizontalMidpoint |
|
75 |
+ |
|
76 |
+ if rect.x < verticalMidpoint and rect.x + rect.width < verticalMidpoint: |
|
77 |
+ if topquad: |
|
78 |
+ index = 1 |
|
79 |
+ elif bottomquad: |
|
80 |
+ index = 2 |
|
81 |
+ elif rect.x > verticalMidpoint: |
|
82 |
+ if topquad: |
|
83 |
+ index = 0 |
|
84 |
+ elif bottomquad: |
|
85 |
+ index = 3 |
|
86 |
+ |
|
87 |
+ return index |
|
88 |
+ |
|
89 |
+ def insert(self, rect): |
|
90 |
+ if self.nodes[0] is not None: |
|
91 |
+ index = self.getIndex(rect) |
|
92 |
+ if index != -1: |
|
93 |
+ self.nodes[index].insert(rect) |
|
94 |
+ return |
|
95 |
+ |
|
96 |
+ self.objects.append(rect) |
|
97 |
+ |
|
98 |
+ if len(self.objects) > self.MAX_OBJECTS and self.level < self.MAX_LEVELS: |
|
99 |
+ if self.nodes[0] is None: |
|
100 |
+ self.split() |
|
101 |
+ |
|
102 |
+ i = 0 |
|
103 |
+ while i < len(self.objects): |
|
104 |
+ index = self.getIndex(self.objects[i]) |
|
105 |
+ if index != -1: |
|
106 |
+ self.nodes[index].insert(self.objects.pop(i)) |
|
107 |
+ else: |
|
108 |
+ i += 1 |
|
109 |
+ |
|
110 |
+ def retrieve(self, rect): |
|
111 |
+ result = [] |
|
112 |
+ index = self.getIndex(rect) |
|
113 |
+ if index != -1 and self.nodes[0] is not None: |
|
114 |
+ result.extend(self.nodes[index].retrieve(rect)) |
|
115 |
+ |
|
116 |
+ result.extend(self.objects) |
|
117 |
+ return result |
|
118 |
+ |
|
119 |
+ def locate_rect(self, rect): |
|
120 |
+ cur = self |
|
121 |
+ index = cur.getIndex(rect) |
|
122 |
+ |
|
123 |
+ while index != -1: |
|
124 |
+ yield index |
|
125 |
+ cur = self.nodes[index] |
|
126 |
+ |
|
127 |
+ |
|
128 |
+ |
|
129 |
+ |
|
132 | 130 |
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() |
|
131 |
+ |
|
132 |
+objects = {Rect.random_rect( (1,37), (1,200), ( (2,10), (2,10) ) ) for ___ in range(50)} |
|
133 |
+quad = QuadTree(0, Rect(0,0, 48,211)) |
|
134 |
+quad.clear() |
|
135 |
+for obj in objects: |
|
136 |
+ quad.insert(obj) |
|
137 |
+ |
|
138 |
+collided = set() |
|
139 |
+oot = set() |
|
140 |
+ |
|
141 |
+for obj in objects: |
|
142 |
+ out = quad.retrieve(obj) |
|
143 |
+ |
|
144 |
+ if obj in collided: continue |
|
145 |
+ oot.add(obj) |
|
146 |
+ |
|
147 |
+ for col in out: |
|
148 |
+ collided.add(col) |
|
149 |
+ print(obj, 'collides with', col) |
|
150 |
+ else: |
|
151 |
+ print('---') |
|
152 |
+ |
|
153 |
+ |
|
154 |
+import numpy |
|
155 |
+field = numpy.zeros( (48,211) ) |
|
156 |
+for obj in objects: |
|
157 |
+ obj.fill(field, 2) |
|
158 |
+for obj in oot: |
|
159 |
+ obj.fill(field, 1) |
|
160 |
+ |
|
161 |
+for row in field: |
|
162 |
+ for cell in row: |
|
163 |
+ print('%d' % cell, end='') |
|
164 |
+ print() |
141 | 165 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,22 @@ |
1 |
+class Event(object): |
|
2 |
+ def fire(self,*args,**kw): pass |
|
3 |
+ |
|
4 |
+class Countdown(object): |
|
5 |
+ def __init__(self): pass |
|
6 |
+ def schedule(self,ticks,event,*args,**kw): pass |
|
7 |
+ def tick(self): pass |
|
8 |
+ |
|
9 |
+ |
|
10 |
+if __name__ == '__main__': |
|
11 |
+ import unittest |
|
12 |
+ |
|
13 |
+ class TestCountdown(unittest.TestCase): |
|
14 |
+ def a_test_schedule(self): |
|
15 |
+ a = Countdown() |
|
16 |
+ class event_test(Event): |
|
17 |
+ def __init__(self): |
|
18 |
+ self.fired = False |
|
19 |
+ def fire(self): |
|
20 |
+ self.fired = not self.fired |
|
21 |
+ return False |
|
22 |
+ a.schedule(3,event_test()) |
... | ... |
@@ -40,8 +40,10 @@ inst = lambda a:a() |
40 | 40 |
@inst |
41 | 41 |
class Settings: |
42 | 42 |
def __init__(self): |
43 |
- with file('settings.yaml') as f: |
|
44 |
- self.__dict__ = yaml.safe_load(f) |
|
43 |
+ with file('data/settings.yaml') as f: |
|
44 |
+ self.settings = yaml.safe_load(f) |
|
45 |
+ def __getattr__(self, attr): |
|
46 |
+ return self.settings[attr] |
|
45 | 47 |
|
46 | 48 |
SCREEN_WIDTH = 75 |
47 | 49 |
SCREEN_HEIGHT = 20 |
... | ... |
@@ -157,6 +159,8 @@ class LevelMap(object): |
157 | 159 |
for x,__ in enumerate(row): |
158 | 160 |
lm.adj_map(level, x,y) |
159 | 161 |
|
162 |
+ |
|
163 |
+ |
|
160 | 164 |
lm = LevelMap(mp) |
161 | 165 |
lm.calculate_level(level) |
162 | 166 |
|