git.fiddlerwoaroof.com
Browse code

switched to libtcod\'s dijkstra implementation

Ed L authored on 22/09/2012 22:05:22
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
+			#print
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
+			#print
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
+			#print
202 242
 		t = time.time() - t0
203 243
 		print '\tdone', t, 'iters', dj.iters
204 244
 	print time.time() - ot
... ...
@@ -1,3 +1,3 @@
1 1
 ---
2
-#screen_width: 155 # widescreen (1280x800)
3
-#screen_height: 90
2
+screen_width: 155 # widescreen (1280x800)
3
+screen_height: 90
... ...
@@ -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
 
... ...
@@ -17,7 +17,6 @@ class Object(object):
17 17
 
18 18
 		if level is not None:
19 19
 			level.add_object(self)
20
-			#level.get_djikstra(x,y)
21 20
 
22 21
 		self.level = level
23 22
 
... ...
@@ -209,7 +209,6 @@ class Player(Object):
209 209
 	@triggers_recompute
210 210
 	def move(self, dx, dy):
211 211
 		result = Object.move(self, dx,dy)
212
-		#self.level.get_djikstra(*self.pos)
213 212
 		return result
214 213
 
215 214
 	def move_or_attack(self, dx, dy):