Browse code
switched to libtcod\'s dijkstra implementation
Ed L authored on 22/09/2012 22:05:22
Showing 8 changed files
Showing 8 changed files
... | ... |
@@ -2,9 +2,10 @@ Homepage: http://fiddlerwoaroof.github.com/yinjar/ |
2 | 2 |
|
3 | 3 |
If you want to modify the code, the files contained under data/ might be a good place to start |
4 | 4 |
|
5 |
-### Prereqs: |
|
5 |
+### Requirements: |
|
6 | 6 |
|
7 | 7 |
- Python (included on OS X and usually on Linux, install from http://python.org for Windows) |
8 | 8 |
- PyYAML |
9 |
+- Numpy |
|
9 | 10 |
- libTCOD (if you are running 64bit Linux, run linux64.sh before starting the game) |
10 | 11 |
- SDL (included with libTCOD) |
... | ... |
@@ -36,8 +36,7 @@ class DjikstraMap(object): |
36 | 36 |
def load_map(self, mp): |
37 | 37 |
self.width = len(mp) |
38 | 38 |
self.height = len(mp[0]) |
39 |
- self.max = dist( (0,0), (self.width, self.height) ) ** 2 |
|
40 |
- self.max = int(self.max) |
|
39 |
+ self.max = self.width*self.height |
|
41 | 40 |
self.wall = self.width*self.height + 1 |
42 | 41 |
self.map = numpy.array([ |
43 | 42 |
[ [self.max,self.wall][cell] for (y,cell) in enumerate(row) ] |
... | ... |
@@ -63,7 +62,7 @@ class DjikstraMap(object): |
63 | 62 |
if cell == self.wall: continue |
64 | 63 |
h = self.height |
65 | 64 |
x,y = idx // h, idx % h |
66 |
- assert cell == self.map[x,y], "%s %s %s != %s" % (x,y, cell, self[x,y]) |
|
65 |
+ #assert cell == self.map[x,y], "%s %s %s != %s" % (x,y, cell, self[x,y]) |
|
67 | 66 |
yield (x,y), cell |
68 | 67 |
|
69 | 68 |
def iter_map(self): |
... | ... |
@@ -94,10 +93,10 @@ class DjikstraMap(object): |
94 | 93 |
|
95 | 94 |
def get_rect(self, pos, rad): |
96 | 95 |
x,y = pos |
97 |
- lx,ty = x-rad, y-rad |
|
98 |
- if x-rad < 0: lx = 0 |
|
99 |
- if y-rad < 0: ty = 0 |
|
96 |
+ lx = max(x-rad,0) |
|
97 |
+ ty = max(y-rad,0) |
|
100 | 98 |
end = rad+1 |
99 |
+ #print (x,y), end, (lx, ty) ,(x+end,y+end) |
|
101 | 100 |
result = self.map[lx:x+end,ty:y+end] |
102 | 101 |
return result |
103 | 102 |
|
... | ... |
@@ -121,14 +120,42 @@ class DjikstraMap(object): |
121 | 120 |
return min( (g for g in self.goals), key=lambda g: dist(g,pos) ) |
122 | 121 |
|
123 | 122 |
def cycle(self): |
123 |
+ goals = self.goals[:] |
|
124 |
+ for i in range(1,max(self.width,self.height)): |
|
125 |
+ for (x,y) in goals: |
|
126 |
+ neighbors = self.get_rect( (x,y), i ) |
|
127 |
+ neighbors[(neighbors >= i) & (neighbors != self.wall)] = i |
|
128 |
+ return False |
|
129 |
+ |
|
130 |
+ def cycle1(self): |
|
131 |
+ t0 = time.time() |
|
124 | 132 |
changed = False |
125 | 133 |
out = self.map |
126 | 134 |
for pos, cell in self.iter_map(): |
127 | 135 |
x,y = pos |
128 |
- neighbors = numpy.min(self.get_rect(pos, 1)) |
|
129 |
- if cell > neighbors + 1: |
|
136 |
+ |
|
137 |
|
|
138 |
+ neighbors = self.get_rect(pos,1) |
|
139 |
+ |
|
140 |
+ #print pos, cell == self.max, cell==self.wall, cell == 0 |
|
141 |
+ #print neighbors |
|
142 |
+ |
|
143 |
+ if len(neighbors[0]) == 0: |
|
144 |
+ print 'ack' |
|
145 |
+ continue |
|
146 |
+ #for l in neighbors: |
|
147 |
+ # print (x,y), l |
|
148 |
|
|
149 |
+ |
|
150 |
+ min_neighbor = numpy.min(neighbors) |
|
151 |
+ |
|
152 |
+ if cell > min_neighbor + 1: |
|
153 |
+ self[x,y] = min_neighbor + 1 |
|
130 | 154 |
changed = True |
131 |
- self[x,y] = neighbors + 1 |
|
155 |
+ |
|
156 |
+ #if cell > min_neighbor + 1: |
|
157 |
+ # changed = True |
|
158 |
+ # self[x,y] = min_neighbor + 1 |
|
132 | 159 |
|
133 | 160 |
if changed: |
134 | 161 |
self.iters += 1 |
... | ... |
@@ -186,19 +213,32 @@ def dist( p1, p2 ): |
186 | 213 |
if __name__ == '__main__': |
187 | 214 |
import random |
188 | 215 |
width, height = 199,50 |
189 |
- map = [ [ random.choice([0,0,0,0,0]) for _ in range(height) ] for __ in range(width) ] |
|
216 |
+ #map = [ [ random.choice([1,0,0,0,0]) for _ in range(height) ] for __ in range(width) ] |
|
217 |
+ import sys |
|
218 |
+ |
|
219 |
+ out = [] |
|
220 |
+ for line in sys.stdin: |
|
221 |
+ out.append( [ int(x) for x in line.strip() ] ) |
|
222 |
+ map = out |
|
223 |
+ width, height = len(map), len(map[0]) |
|
224 |
+ |
|
225 |
+ |
|
190 | 226 |
|
191 | 227 |
import time |
192 |
- goals = [ (random.randrange(width), random.randrange(height)) for _ in range(1) ] |
|
228 |
+ goals = [ (random.randrange(width), random.randrange(height)) for _ in range(5) ] |
|
193 | 229 |
ot = time.time() |
194 |
- for _ in range(5): |
|
230 |
+ for _ in range(3): |
|
195 | 231 |
print '\tinit' |
196 | 232 |
t0 = time.time() |
197 | 233 |
dj = DjikstraMap() |
198 | 234 |
dj.set_goals(*goals) |
199 | 235 |
dj.load_map(map) |
200 |
- dj.iter(10) |
|
201 |
- #while dj.cycle(): pass |
|
236 |
+ #dj.iter(10) |
|
237 |
+ while dj.cycle(): |
|
238 |
+ pass |
|
239 |
+ #print 'c' |
|
240 |
+ #dj.visualize() |
|
241 |
|
|
202 | 242 |
t = time.time() - t0 |
203 | 243 |
print '\tdone', t, 'iters', dj.iters |
204 | 244 |
print time.time() - ot |
... | ... |
@@ -17,14 +17,13 @@ class Level(object): |
17 | 17 |
self.number = num |
18 | 18 |
return self |
19 | 19 |
|
20 |
+ dijkstra_cache = {} |
|
20 | 21 |
def get_djikstra(self, x,y): |
21 | 22 |
if (x,y) not in self.djikstra_cache: |
22 | 23 |
print 'new (%s, %s)' % (x,y) |
23 |
- dj = self.djikstra_cache[x,y] = djikstra.DjikstraMap() |
|
24 |
- dj.set_goals( (x,y), weight=0) |
|
25 |
- dj.load_map(self.map.map.data) |
|
26 |
- dj = self.djikstra_cache[x,y] |
|
27 |
- dj.iter(5) |
|
24 |
+ dj = libtcod.dijkstra_new(self.fov_map) |
|
25 |
+ libtcod.dijkstra_compute(dj, x, y) |
|
26 |
+ self.dijkstra_cache[x,y] = dj |
|
28 | 27 |
return dj |
29 | 28 |
|
30 | 29 |
def __init__(self, width, height, con, item_types=None, monster_types=None): |
... | ... |
@@ -99,7 +98,6 @@ class Level(object): |
99 | 98 |
cell.explored = True |
100 | 99 |
|
101 | 100 |
color = libtcod.black |
102 |
- #if True: |
|
103 | 101 |
if cell.explored: |
104 | 102 |
wall = cell.block_sight |
105 | 103 |
walkable = not cell.blocked |
... | ... |
@@ -113,11 +111,14 @@ class Level(object): |
113 | 111 |
color = libtcod.Color(100,100,200) |
114 | 112 |
|
115 | 113 |
if cell.explored or clear_all: |
116 |
- libtcod.console_set_char_background(self.con, x, y, color, libtcod.BKGND_SET) |
|
114 |
+ if cell.block_sight and cell.explored: |
|
115 |
+ libtcod.console_put_char_ex(self.con, x, y, '#', libtcod.white, color) |
|
116 |
+ else: |
|
117 |
+ libtcod.console_set_char_background(self.con, x, y, color, libtcod.BKGND_SET) |
|
117 | 118 |
|
118 | 119 |
def init_fov(self): |
119 | 120 |
libtcod.map_clear(self.fov_map) |
120 |
- #self.fov_map = libtcod.map_new(self.map.width, self.map.height) |
|
121 |
+ |
|
121 | 122 |
for x,y,cell in self.map.iter_cells_with_coords(): |
122 | 123 |
libtcod.map_set_properties(self.fov_map, x,y, |
123 | 124 |
not cell.block_sight, |
... | ... |
@@ -1,4 +1,5 @@ |
1 | 1 |
from __future__ import division |
2 |
+import time |
|
2 | 3 |
import copy |
3 | 4 |
import libtcodpy as libtcod |
4 | 5 |
import random |
... | ... |
@@ -28,7 +29,7 @@ class AutomataEngine(object): |
28 | 29 |
self.data = [ [ random.choice([0]+[1]*3) for y in range(height)] for x in range(width) ] |
29 | 30 |
else: |
30 | 31 |
self.data = [ [1 for y in range(height)] for x in range(width) ] |
31 |
- self.data = np.array(self.data) |
|
32 |
+ #self.data = np.array(self.data) |
|
32 | 33 |
self.width = width |
33 | 34 |
self.height = height |
34 | 35 |
|
... | ... |
@@ -37,7 +38,8 @@ class AutomataEngine(object): |
37 | 38 |
x2,y2 = p2 |
38 | 39 |
x1,x2 = min([x1,x2]), max([x1,x2]) |
39 | 40 |
y1,y2 = min([y1,y2]), max([y1,y2]) |
40 |
- result = self.data[x1:x2,y1:y2] |
|
41 |
+ #result = self.data[x1:x2,y1:y2] |
|
42 |
+ result = [ row[y1:y2] for row in self.data[x1:x2] ] |
|
41 | 43 |
return result |
42 | 44 |
|
43 | 45 |
def sum_area(self, p1, p2=None, summator=sum): |
... | ... |
@@ -274,14 +276,13 @@ class NewSmoother(AutomataEngine): |
274 | 276 |
return 1 |
275 | 277 |
|
276 | 278 |
import collections |
277 |
-from algorithms import djikstra |
|
278 | 279 |
class Map(collections.MutableSequence): |
279 | 280 |
def __init__(self, width, height, con, level): |
280 | 281 |
print 'hello again' |
281 | 282 |
self.gen = MazeGen(width, height) |
282 | 283 |
self.map = self.data = self.gen.munge() |
283 |
- self.data = Automata1(data=self.data.data).iter(2) |
|
284 |
- self.map = Smoother(data=self.data.data).munge() |
|
284 |
+ #self.data = Automata1(data=self.data.data).iter(2) |
|
285 |
+ #self.map = Smoother(data=self.data.data).munge() |
|
285 | 286 |
self.data = self.map.to_map() |
286 | 287 |
|
287 | 288 |
self.width = width |
... | ... |
@@ -332,6 +333,8 @@ class Map(collections.MutableSequence): |
332 | 333 |
rooms = [] |
333 | 334 |
num_rooms = 0 |
334 | 335 |
|
336 |
+ print '\n'.join(''.join(map(str, row)) for row in self.map.data) |
|
337 |
+ |
|
335 | 338 |
for x in range(self.width): |
336 | 339 |
for y in range(self.height): |
337 | 340 |
if x in {0,self.width-1} or y in {0,self.height-1}: |
... | ... |
@@ -346,55 +349,11 @@ class Map(collections.MutableSequence): |
346 | 349 |
|
347 | 350 |
self.place_items(self.gen.rooms[0], item_types, max_num_items) |
348 | 351 |
for r in self.gen.rooms[1:]: |
349 |
- # pass |
|
350 | 352 |
self.place_objects(r, |
351 | 353 |
monster_types, max_num_monsters, |
352 | 354 |
item_types, max_num_items |
353 | 355 |
) |
354 | 356 |
|
355 |
-# for r in range(max_rooms): |
|
356 |
-# w = random.randrange(min_size, max_size) |
|
357 |
-# h = random.randrange(min_size, max_size) |
|
358 |
- |
|
359 |
-# x = random.randrange(0, self.width-w-1) |
|
360 |
-# y = random.randrange(0, self.height-h-1) |
|
361 |
- |
|
362 |
-# new_room = Rect(x,y, w,h) |
|
363 |
- |
|
364 |
-# failed = any( |
|
365 |
-# new_room ^ other_room for other_room in rooms |
|
366 |
-# ) |
|
367 |
- |
|
368 |
-# if not failed: |
|
369 |
-# #self.create_room(new_room) |
|
370 |
-# (new_x, new_y) = new_room.random_point |
|
371 |
-# |
|
372 |
-# if num_rooms == 0: |
|
373 |
-# x,y = new_room.random_point |
|
374 |
-# while self.is_blocked(x,y): |
|
375 |
-# x,y = new_room.random_point |
|
376 |
-# |
|
377 |
-# self.map_entrance = x,y |
|
378 |
-# |
|
379 |
-# else: |
|
380 |
-# self.place_objects(new_room, |
|
381 |
-# monster_types, max_num_monsters, |
|
382 |
-# item_types, max_num_items |
|
383 |
-# ) |
|
384 |
-# |
|
385 |
-# prev_x, prev_y = rooms[-1].center |
|
386 |
-# #while self.is_blocked(prev_x, prev_y): |
|
387 |
-# # prev_x, prev_y = rooms[-1].random_point |
|
388 |
-# |
|
389 |
-# if random.randrange(0,1) == 1: |
|
390 |
-# self.create_h_tunnel(prev_x, new_x, prev_y) |
|
391 |
-# self.create_v_tunnel(new_x, prev_y, new_y) |
|
392 |
-# else: |
|
393 |
-# self.create_v_tunnel(new_x, prev_y, new_y) |
|
394 |
-# self.create_h_tunnel(prev_x, new_x, prev_y) |
|
395 |
-# |
|
396 |
-# rooms.append(new_room) |
|
397 |
-# num_rooms += 1 |
|
398 | 357 |
return self |
399 | 358 |
|
400 | 359 |
|
... | ... |
@@ -428,7 +387,6 @@ class Map(collections.MutableSequence): |
428 | 387 |
result = None,None |
429 | 388 |
if empty_points: |
430 | 389 |
result = random.choice(empty_points) |
431 |
- self.level.get_djikstra(*result) |
|
432 | 390 |
|
433 | 391 |
return result |
434 | 392 |
|
... | ... |
@@ -6,7 +6,6 @@ import libtcodpy as libtcod |
6 | 6 |
import items |
7 | 7 |
|
8 | 8 |
from main import game_instance |
9 |
-from algorithms import djikstra |
|
10 | 9 |
|
11 | 10 |
class Monster(object): |
12 | 11 |
def init(self,*a): pass |
... | ... |
@@ -70,13 +69,6 @@ class Thief(BasicMonster): |
70 | 69 |
class DjikstraMonster(Monster): |
71 | 70 |
maps = {} |
72 | 71 |
|
73 |
- @property |
|
74 |
- def dj(self): |
|
75 |
- result = self.maps.get(id(self.level)) |
|
76 |
- if result is None: |
|
77 |
- result = self.maps[id(self.level)] = djikstra.DjikstraMap() |
|
78 |
- return result |
|
79 |
- |
|
80 | 72 |
def init(self, level): |
81 | 73 |
self.level = level |
82 | 74 |
self.owner.always_visible = True |
... | ... |
@@ -85,7 +77,6 @@ class DjikstraMonster(Monster): |
85 | 77 |
self.ppos = None |
86 | 78 |
|
87 | 79 |
map = level.map |
88 |
- #self.dj.visualize() |
|
89 | 80 |
|
90 | 81 |
def take_turn(self): |
91 | 82 |
pos = self.owner.x, self.owner.y |
... | ... |
@@ -99,14 +90,17 @@ class DjikstraMonster(Monster): |
99 | 90 |
else: |
100 | 91 |
dx, dy = self.owner.get_step_towards(*self.level.player.pos) |
101 | 92 |
|
102 |
- #elif random.random() < .4: |
|
103 |
- # dx,dy = self.dj.nav(*pos) |
|
104 | 93 |
|
105 |
- elif player_room is not None: |
|
106 |
- dj = self.level.get_djikstra(*player_room.center) |
|
107 |
- #print pos, '<---', self.level.player.distance(*pos) |
|
108 |
- x,y = pos |
|
109 |
- dx,dy = dj.nav(x,y) |
|
94 |
+ else: |
|
95 |
+ dj = self.level.get_djikstra(*self.owner.pos) |
|
96 |
+ path = libtcod.dijkstra_path_set(dj, *self.level.player.pos) |
|
97 |
+ x,y = libtcod.dijkstra_path_walk(dj) |
|
98 |
+ |
|
99 |
+ if x is not None: |
|
100 |
+ dx = x - self.owner.x |
|
101 |
+ dy = y - self.owner.y |
|
102 |
+ else: |
|
103 |
+ print '!' |
|
110 | 104 |
|
111 | 105 |
self.owner.move(dx,dy) |
112 | 106 |
|