I've been trying to create a Procedurally Generated Dungeon, as seen in this article. But that was a little to hard for me to understand the working this type of algorithm. So instead, I've been using this as a guide to at least understand the room placement.
The program used in the article is made in Java, so I made some adaptations to my "reality", and tried to emulate the same results in Python 3.5.
My code is as follows:
from random import randint
class Room:
"""docstring for Room"""
def __init__(self, x, y, w, h):
"""[summary]
[description]
Arguments:
x {int} -- bottom-left horizontal anchorpoint of the room
y {int} -- bottom-left vertical anchor point of the room
w {int} -- width of the room
h {int} -- height of the room
"""
self.x1 = x
self.x2 = x + w
self.y1 = y
self.y2 = y + h
self.w = w
self.h = h
self.center = ((self.x1 + self.x2)/2, (self.y1 + self.y2)/2)
def intersects(self, room):
"""[summary]
Verifies if the rooms overlap
Arguments:
room {Room} -- a room object
"""
return(self.x1 <= room.x2 and self.x2 >= room.x1 and \
self.y1 <= room.y2 and self.y2 >= room.y1)
def __str__(self):
room_info = ("Coords: (" + str(self.x1) + ", " + str(self.y1) +
") | (" + str(self.x2) + ", " + str(self.y2) + ")\n")
room_info += ("Center: " + str(self.center) + "\n")
return(room_info)
MIN_ROOM_SIZE = 10
MAX_ROOM_SIZE = 20
MAP_WIDTH = 400
MAP_HEIGHT = 200
MAX_NUMBER_ROOMS = 20
dungeon_map = [[None] * MAP_WIDTH for i in range(MAP_HEIGHT)]
# print(dungeon_map)
def crave_room(room):
"""[summary]
"saves" a room in the dungeon map by making everything inside it's limits 1
Arguments:
room {Room} -- the room to crave in the dungeon map
"""
for x in xrange(min(room.x1, room.x2), max(room.x1, room.x2) + 1):
for y in xrange(min(room.y1, room.y2), max(room.y1, room.y2) + 1):
print(x, y) # debug
dungeon_map[x][y] = 1
print("Done") # dungeon
def place_rooms():
rooms = []
for i in xrange(0, MAX_NUMBER_ROOMS):
w = MIN_ROOM_SIZE + randint(0, MAX_ROOM_SIZE - MIN_ROOM_SIZE + 1)
h = MIN_ROOM_SIZE + randint(0, MAX_ROOM_SIZE - MIN_ROOM_SIZE + 1)
x = randint(0, MAP_WIDTH - w) + 1
y = randint(0, MAP_HEIGHT - h) + 1
new_room = Room(x, y, w, h)
fail = False
for other_room in rooms:
if new_room.intersects(other_room):
fail = True
break
if not fail:
print(new_room)
crave_room(new_room) # WIP
new_center = new_room.center
# rooms.append(new_room)
if len(rooms) != 0:
prev_center = rooms[len(rooms) - 1].center
if(randint(0, 1) == 1):
h_corridor(prev_center[0], new_center[0], prev_center[1])
v_corridor(prev_center[1], new_center[1], prev_center[0])
else:
v_corridor(prev_center[1], new_center[1], prev_center[0])
h_corridor(prev_center[0], new_center[0], prev_center[1])
if not fail:
rooms.append(new_room)
for room in rooms:
print(room)
def h_corridor(x1, x2, y):
for x in xrange(min(x1, x2), max(x1, x2) + 1):
dungeon_map[x][y] = 1
def v_corridor(y1, y2, x):
for y in xrange(min(y1, y2), max(y1, y2) + 1):
dungeon_map[x][y] = 1
place_rooms()
but whenever I run it, I get the following error:
Traceback (most recent call last):
File "/home/user/dungeon.py", line 114, in <module>
place_rooms()
File "/home/user/dungeon.py", line 87, in place_rooms
crave_room(new_room)
File "/home/user/dungeon.py", line 65, in crave_room
dungeon_map[x][y] = 1
IndexError: list index out of range
For what I understood from my code, the crave_room function should work correctly, since I'm using the min and max functions. And since the h_corridor and v_corridor functions work in a similar way They present the same kind of problem.
I'm not sure if the problem is happening due the fact that I'm using a matrix as a substitute to the canvas used in the original article. I was suspecting a local/global variable problem, but I don't think that's the problem. I'm afraid I'm making a very stupid mistake and not seeing it.
Any code improvement tips or suggestions about better data structures to use will be welcome, and if anyone has, a more clearer/simpler article in the subject, preferably on Python, I saw a lot of the related posts in here, but I'm still kind of lost.
Thanks for any help. :D
You have your dungeon_map declared incorrectly:
dungeon_map = [[None] * MAP_WIDTH] * MAP_HEIGHT
The correct way should be:
dungeon_map = [[None] * MAP_HEIGHT] * MAP_WIDTH
Now that you done that, let's take a look at the second, more serious problem. Let's have an experiment in smaller scale (smaller map):
MAP_WIDTH = 4
MAP_HEIGHT = 2
dungeon_map = [[None] * MAP_HEIGHT] * MAP_WIDTH
print('\nBefore Assignment:')
print(dungeon_map)
dungeon_map[2][1] = 'y'
print('\nAfter Assignment:')
print(dungeon_map)
In this experiment, we created a 4 column x 2 row matrix and we alter the value of one cell, so let's take a look at the output:
Before Assignment:
[[None, None], [None, None], [None, None], [None, None]]
After Assignment:
[[None, 'y'], [None, 'y'], [None, 'y'], [None, 'y']]
What is going on? Essentially, you declare the same list, MAP_WIDTH times. The declaration line below is convenient and clever, but incorrect:
dungeon_map = [[None] * MAP_HEIGHT] * MAP_WIDTH
The correct way to declare such a matrix is:
dungeon_map = [[None for x in range(MAP_HEIGHT)] for y in range(MAP_WIDTH)]
Related
I am currently trying to make a game of life using tkinter.
One issue though: the rectangles I create won't respond to the itemconfigure method called to change their fill color. Once the tk window opened, there seems to be no way to change their color... Here is the code used: (variable names are in french, my apologies)
from tkinter import *
from numpy import *
Res = (1000, 1000)
dim = (50, 50)
BordABord = False
F = Tk()
C = Canvas(F, height = Res[1], width = Res[0])
L = ndarray(dim, dtype = bool)
Grille = ndarray(dim)
lar = []
for i in range(2):
lar.append(Res[i] // dim[i])
for i in range(dim[0]):
x = i * lar[0]
for j in range(dim[1]):
y = j * lar[1]
Grille[i, j] = C.create_rectangle(x, y, x + lar[0], y + lar[1], fill = 'white')
L[i, j] = False
def rgbTraducteur(rgb):
return "#%02x%02x%02x" % rgb
def process():
global L
global Grille
global dim
global C
for i in range(dim[0]):
for j in range(dim[1]):
if L[i, j]:
print('processssss')
C.itemconfigure(Grille[i, j], fill = 'black')
def clic(event):
print('clic')
global L
global Res
global dim
x = int(dim[0] * event.x / Res[0])
y = int(dim[1] * event.y / Res[1])
print(x, y)
L[x, y] = not(L[x, y])
process()
def pas(event = 0):
global L
global BordABord
global dim
N = L
for i in range(dim[0]):
for j in range(dim[1]):
compte = 0
for k in range(-1, 2):
x = i + k
if BordABord or x // dim[0] == 0:
x %= dim[0]
for l in range(-1, 2):
if l != 0 and k != 0:
y = j + l
if BordABord or y // dim[1] == 0:
y %= dim[1]
compte += L[x, y]
if L[i, j]:
N[i, j] = (compte - 2) // 2 == 0
else:
N[i, j] = compte == 3
L = N
process()
F.bind('<Button-1>', clic)
F.bind('<Return>', pas)
C.pack()
F.mainloop()
it is, of course, not optimised at all, and all rectangles have been set to turn black whenever process() is called as a test.
Canvas items are given an integer identifier. You must use the integer to refer to the item. A floating point number such as 1.0 does not refer to the item with an integer identifier of 1.
The problem in your code is that Grille is an array of floating point numbers. When you do Grille[i, j] = C.create_rectangle(...), create_rectangle will return an integer but it gets stored as floating point. Thus, your call to itemconfigure tries to change something with the floating point number 1.0 rather than the integer id of 1. If you pass something that is not an integer to itemconfigure the canvas assumes it is a tag. Since no elements have the tag 1.0, no elements are configured.
If you change the array to hold integers so that the numbers aren't converted, itemconfigure will work fine.
Grille = ndarray(dim, dtype=int)
I am currently stumped by an artefact in my code. It appears to produce very sharp points in a grid pattern that have a noticeable difference in value to their neighbours.
I am following the blog post at http://www.bluh.org/code-the-diamond-square-algorithm/ and converting from whichever language they are using (assuming either C# or Java), and have double-checked that what I am doing should match.
Is there any chance that someone could have a browse over this, and see what I'm doing wrong? I've stepped through it at smaller levels, and stopped it on specific iterations of the algorithm (by unrolling the top loop, and explicitly calling the algorithm a set number of times) and everything seems to work until we get to the very last set of points/pixels.
I use a class (called Matrix) to access the list, and wrap any out of bounds values.
The code for the algorithm is as follows:
class World :
def genWorld (self, numcells, cellsize, seed):
random.seed(seed)
self.dims = numcells*cellsize
self.seed = seed
self.cells = Matrix(self.dims, self.dims)
# set the cells at cellsize intervals
half = cellsize/2
for y in range(0, self.dims, cellsize):
for x in range(0, self.dims, cellsize):
self.cells[x,y] = random.random()
scale = 1.0
samplesize = cellsize
while samplesize > 1:
self._diamondSquare(samplesize, scale)
scale *= 0.8
samplesize = int(samplesize/2)
# I need to sort out the problem with the diamond-square algo that causes it to make the weird gridding pattern
def _sampleSquare(self, x, y, size, value):
half = size/2
a = self.cells[x-half, y-half]
b = self.cells[x+half, y-half]
c = self.cells[x-half, y+half]
d = self.cells[x+half, y+half]
res = min(((a+b+c+d+value)/5.0), 1.0)
self.cells[x, y] = res
def _sampleDiamond(self, x, y, size, value):
half = size/2
a = self.cells[x+half, y]
b = self.cells[x-half, y]
c = self.cells[x, y+half]
d = self.cells[x, y-half]
res = min(((a+b+c+d+value)/5.0), 1.0)
self.cells[x, y] = res
def _diamondSquare(self, stepsize, scale):
half = int(stepsize/2)
for y in range(half, self.dims+half, stepsize):
for x in range(half, self.dims+half, stepsize):
self._sampleSquare(x, y, stepsize, random.random()*scale)
for y in range(0, self.dims, stepsize):
for x in range(0, self.dims, stepsize):
self._sampleDiamond(x+half, y, stepsize, random.random()*scale)
self._sampleDiamond(x, y+half, stepsize, random.random()*scale)
and is called with:
w = World()
w.genWorld(16, 16, 1) # a 256x256 square world, since the numcells is multiplied by the cellsize to give us the length of ONE side of the resulting grid
then I save to file to check the result:
file = io.open("sample.raw",'wb')
arr = [int(i * 255) for i in w.cells.cells] # w.cells.cells should not have a value >= 1.0, so what's going on?
ind = 0
for a in arr:
if a > 255:
print ("arr["+str(ind)+"] ::= "+str(a))
ind += 1
file.write(bytearray(arr))
file.close()
which gives the result:
EDIT: Okay, so it appears that I managed to get it working. I swapped from using functions for working out the diamond and square steps to doing it all in the _diamondSquare() function, but this wasn't the only thing. I also found out that random.random() provides values in the range [0.0 ->1.0), when I was expecting values in the range [-1.0 -> 1.0). After I corrected this, everything started working properly, which was a relief.
Thanks for the advice everyone, here's the working code in case anyone else is struggling with something similar:
Random Function
# since random.random() gives a value in the range [0.0 -> 1.0), I need to change it to [-1.0 -> 1.0)
def rand():
mag = random.random()
sign = random.random()
if sign >=0.5:
return mag
return mag * -1.0
Matrix class
class Matrix:
def __init__(self, width, height):
self.cells = [0 for i in range(width*height)]
self.width = width
self.height = height
self.max_elems = width*height
def _getsingleindex(self, ind):
if ind < 0:
ind *= -1
while ind >= self.max_elems:
ind -= self.max_elems
return ind
def _getmultiindex(self, xind, yind):
if xind < 0:
xind *= -1
if yind < 0:
yind *= -1
while xind >= self.width:
xind -= self.width
while yind >= self.height:
yind -= self.height
return xind + (yind*self.height)
def __getitem__(self, inds):
# test that index is an integer, or two integers, and throw an indexException if not
if hasattr(inds, "__len__"):
if len(inds) > 1:
return self.cells[self._getmultiindex(int(inds[0]), int(inds[1]))]
return self.cells[self._getsingleindex(int(inds))]
def __setitem__(self, inds, object):
# test that index is an integer, or two integers, and throw an indexException if not
if hasattr(inds, "__len__"):
if len(inds) > 1:
self.cells[self._getmultiindex(int(inds[0]),int(inds[1]))] = object
return self.cells[self._getmultiindex(int(inds[0]),int(inds[1]))]
self.cells[self._getsingleindex(int(inds))] = object
return self.cells[self._getsingleindex(int(inds))]
def __len__(self):
return len(self.cells)
The Actual Diamond-Square Generation
# performs the actual 2D generation
class World:
def genWorld (self, numcells, cellsize, seed, scale = 1.0):
random.seed(seed)
self.dims = numcells*cellsize
self.seed = seed
self.cells = Matrix(self.dims, self.dims)
mountains = Matrix(self.dims, self.dims)
# set the cells at cellsize intervals
for y in range(0, self.dims, cellsize):
for x in range(0, self.dims, cellsize):
# this is the default, sets the heights randomly
self.cells[x,y] = random.random()
while cellsize > 1:
self._diamondSquare(cellsize, scale)
scale *= 0.5
cellsize = int(cellsize/2)
for i in range(len(mountains)):
self.cells[i] = self.cells[i]*0.4 + (mountains[i]*mountains[i])*0.6
def _diamondSquare(self, stepsize, scale):
half = int(stepsize/2)
# diamond part
for y in range(half, self.dims+half, stepsize):
for x in range(half, self.dims+half, stepsize):
self.cells[x, y] = ((self.cells[x-half, y-half] + self.cells[x+half, y-half] + self.cells[x-half, y+half] + self.cells[x+half, y+half])/4.0) + (rand()*scale)
# square part
for y in range(0, self.dims, stepsize):
for x in range(0, self.dims, stepsize):
self.cells[x+half,y] = ((self.cells[x+half+half, y] + self.cells[x+half-half, y] + self.cells[x+half, y+half] + self.cells[x+half, y-half])/4.0)+(rand()*scale)
self.cells[x,y+half] = ((self.cells[x+half, y+half] + self.cells[x-half, y+half] + self.cells[x, y+half+half] + self.cells[x, y+half-half])/4.0)+(rand()*scale)
Main Function (added for completeness)
# a simple main function that uses World to create a 2D array of diamond-square values, then writes it to a file
def main():
w = World()
w.genWorld(20, 16, 1)
mi = min(w.cells.cells)
ma = max(w.cells.cells) - mi
# save the resulting matrix to an image file
file = io.open("sample.raw",'wb')
maxed = [(i-mi)/ma for i in w.cells.cells]
arr = [int(i * 255) for i in maxed]
file.write(bytearray(arr))
file.close()
I want to generate all directions from a point in a 3D grid, but I can't quite get my head around the next bit. For the record it's all stored in a single list, so I need some maths to calculate where the next point will be.
I only really need 3 calculations to calculate any of the 26 or so different directions (up, up left, up left forwards, up left backwards, up right, up right forwards, etc), so I decided to work with X, Y, Z, then split them into up/down left/right etc, to then get the correct number to add or subtract. Generating this list to get the maths working however, seems to be the hard bit.
direction_combinations = 'X Y Z XY XZ YZ XYZ'.split()
direction_group = {}
direction_group['X'] = 'LR'
direction_group['Y'] = 'UD'
direction_group['Z'] = 'FB'
So basically, using the below code, this is the kind of stuff I'd like it to do, but obviously not have it hard coded. I could do it in a hacky way, but I imagine there's something really simple I'm missing here.
#Earlier part of the code to get this bit working
#I've also calculated the edges but it's not needed until after I've got this bit working
grid_size = 4
direction_maths = {}
direction_maths['U'] = pow(grid_size, 2)
direction_maths['R'] = 1
direction_maths['F'] = grid_size
direction_maths['D'] = -direction_maths['U']
direction_maths['L'] = -direction_maths['R']
direction_maths['B'] = -direction_maths['F']
#Bit to get working
starting_point = 25
current_direction = 'Y'
possible_directions = [direction_group[i] for i in list(current_direction)]
for y in list(possible_directions[0]):
print starting_point + direction_maths[y]
# 41 and 9 are adjacent on the Y axis
current_direction = 'XYZ'
possible_directions = [direction_group[i] for i in list(current_direction)]
for x in list(possible_directions[0]):
for y in list(possible_directions[1]):
for z in list(possible_directions[2]):
print starting_point + direction_maths[x] + direction_maths[y] + direction_maths[z]
# 44, 36, 12, 4, 46, 38, 14 and 6 are all adjacent on the corner diagonals
Here's a general idea of how the grid looks with the list indexes (using 4x4x4 as an example):
________________
/ 0 / 1 / 2 / 3 /
/___/___/___/___/
/ 4 / 5 / 6 / 7 /
/___/___/___/___/
/ 8 / 9 /10 /11 /
/___/___/___/___/
/12 /13 /14 /15 /
/___/___/___/___/
________________
/16 /17 /18 /19 /
/___/___/___/___/
/20 /21 /22 /23 /
/___/___/___/___/
/24 /25 /26 /27 /
/___/___/___/___/
/28 /29 /30 /31 /
/___/___/___/___/
________________
/32 /33 /34 /35 /
/___/___/___/___/
/36 /37 /38 /39 /
/___/___/___/___/
/40 /41 /42 /43 /
/___/___/___/___/
/44 /45 /46 /47 /
/___/___/___/___/
________________
/48 /49 /50 /51 /
/___/___/___/___/
/52 /53 /54 /55 /
/___/___/___/___/
/56 /57 /58 /59 /
/___/___/___/___/
/60 /61 /62 /63 /
/___/___/___/___/
Edit: Using the answers mixed with what I posted originally (wanted to avoid converting to and from 3D points if possible), this is what I ended up with to count the number of complete rows :)
def build_directions():
direction_group = {}
direction_group['X'] = 'LR'
direction_group['Y'] = 'UD'
direction_group['Z'] = 'FB'
direction_group[' '] = ' '
#Come up with all possible directions
all_directions = set()
for x in [' ', 'X']:
for y in [' ', 'Y']:
for z in [' ', 'Z']:
x_directions = list(direction_group[x])
y_directions = list(direction_group[y])
z_directions = list(direction_group[z])
for i in x_directions:
for j in y_directions:
for k in z_directions:
all_directions.add((i+j+k).replace(' ', ''))
#Narrow list down to remove any opposite directions
some_directions = all_directions
opposite_direction = all_directions.copy()
for i in all_directions:
if i in opposite_direction:
new_direction = ''
for j in list(i):
for k in direction_group.values():
if j in k:
new_direction += k.replace(j, '')
opposite_direction.remove(new_direction)
return opposite_direction
class CheckGrid(object):
def __init__(self, grid_data):
self.grid_data = grid_data
self.grid_size = calculate_grid_size(self.grid_data)
self.grid_size_squared = pow(grid_size, 2)
self.grid_size_cubed = len(grid_data)
self.direction_edges = {}
self.direction_edges['U'] = range(self.grid_size_squared)
self.direction_edges['D'] = range(self.grid_size_squared*(self.grid_size-1), self.grid_size_squared*self.grid_size)
self.direction_edges['R'] = [i*self.grid_size+self.grid_size-1 for i in range(self.grid_size_squared)]
self.direction_edges['L'] = [i*self.grid_size for i in range(self.grid_size_squared)]
self.direction_edges['F'] = [i*self.grid_size_squared+j+self.grid_size_squared-self.grid_size for i in range(self.grid_size) for j in range(self.grid_size)]
self.direction_edges['B'] = [i*self.grid_size_squared+j for i in range(self.grid_size) for j in range(self.grid_size)]
self.direction_edges[' '] = []
self.direction_maths = {}
self.direction_maths['D'] = pow(self.grid_size, 2)
self.direction_maths['R'] = 1
self.direction_maths['F'] = self.grid_size
self.direction_maths['U'] = -self.direction_maths['D']
self.direction_maths['L'] = -self.direction_maths['R']
self.direction_maths['B'] = -self.direction_maths['F']
self.direction_maths[' '] = 0
def points(self):
total_points = defaultdict(int)
opposite_directions = build_directions()
all_matches = set()
#Loop through each point
for starting_point in range(len(self.grid_data)):
current_player = self.grid_data[starting_point]
if current_player:
for i in opposite_directions:
#Get a list of directions and calculate movement amount
possible_directions = [list(i)]
possible_directions += [[j.replace(i, '') for i in possible_directions[0] for j in direction_group.values() if i in j]]
direction_movement = sum(self.direction_maths[j] for j in possible_directions[0])
#Build list of invalid directions
invalid_directions = [[self.direction_edges[j] for j in possible_directions[k]] for k in (0, 1)]
invalid_directions = [[item for sublist in j for item in sublist] for j in invalid_directions]
num_matches = 1
list_match = [starting_point]
#Use two loops for the opposite directions
for j in (0, 1):
current_point = starting_point
while current_point not in invalid_directions[j]:
current_point += direction_movement*int('-'[:j]+'1')
if self.grid_data[current_point] == current_player:
num_matches += 1
list_match.append(current_point)
else:
break
#Add a point if enough matches
if num_matches == self.grid_size:
list_match = tuple(sorted(list_match))
if list_match not in all_matches:
all_matches.add(list_match)
total_points[current_player] += 1
return total_points
Here's basically the same thing that #AnnoSielder did, but makes use of itertools to reduce the amount of code.
from itertools import product
# Get a list of all 26 possible ways to move from a given coordinate in a 3 coordinate system.
base_deltas = filter(lambda point: not all(axis ==0 for axis in point), list(product([-1, 0, 1], repeat=3)))
# Define your max axis length or your grid size
grid_size = 4
# Simple function that applys the deltas to the given coordinate and returns you the list.
def apply_deltas(deltas, coordinate):
return [
(coordinate[0]+x, coordinate[1]+y, coordinate[2]+z)
for x, y, z in deltas
]
# This will determine whether the point is out of bounds for the given grid
is_out_of_bounds = lambda point: all(0 <= axis < grid_size for axis in point)
# Define your point, in this case it's block #27 in your example
coordinate = [3, 2, 1]
# Apply the deltas, then filter using the is_out_of_bounds lambda
directions = filter(is_out_of_bounds, apply_deltas(base_deltas, coordinate))
# directions is now the list of 17 coordinates that you could move to.
Don't make thinks unnecessary complicated. Do not describe a point in 3 dimensions with 1 number - 3 coordinates means 3 numbers.
Should be something like this:
numb = 37
cube_size = 4
# convert to x - y - z
start = [0, 0, 0]
start[2] = numb / cube_size ** 2
numb = numb % cube_size ** 2
start[1] = numb / cube_size
start[0] = numb % cube_size
for x in [-1, 0, 1]:
current_x = start[0] + x
for y in [-1, 0, 1]:
current_y = start[1] + y
for z in [-1, 0, 1]:
current_z = start[2] + z
#reconvert
convert = current_x + current_y * cube_size + current_z * cube_size ** 2
print("x: " + str(current_x) + " y: " + str(current_y) + " z: " + str(current_z) + " => " + str(convert))
Simply generate your x/y/z-coordinate, then run all possibilities of add -1/0/1 to these coordinates and re-convert to your number in the grid.
I am computing the mandelbrot set recursively and attempting to perform linear interpolation using the smooth coloring algorithm. However, this returns floating point RGB values which I can't put into the ppm image I am using so I am having to round off using int(), creating a smoother but yet still banded image.
Are there any simpler ways that will produce a better non-banded image?
The second function is an extremely bad hack just playing around with ideas as the smooth algorithim seems to be producing rgb values in the range 256**3
Commented out the linear interpolation I was doing.
Here are my three functions:
def linear_interp(self, color_1, color_2, i):
r = (color_1[0] * (1 - i)) + (color_2[0] * i)
g = (color_1[1] * (1 - i)) + (color_2[1] * i)
b = (color_1[2] * (1 - i)) + (color_2[2] * i)
return (int(abs(r)), int(abs(g)), int(abs(b)))
def mandel(self, x, y, z, iteration = 0):
mod_z = sqrt((z.real * z.real) + (z.imag * z.imag))
#If its not in the set or we have reached the maximum depth
if abs(z) >= 2.00 or iteration == DEPTH:
if iteration == DEPTH:
mu = iteration
else:
mu = iteration + 1 - log(log(mod_z)) / log(2)
else:
mu = 0
z = (z * z) + self.c
self.mandel(x, y, z, iteration + 1)
return mu
def create_image(self):
begin = time.time() #For computing how long it took (start time)
self.rgb.palette = []
for y in range(HEIGHT):
self.rgb.palette.append([]) #Need to create the rows of our ppm
for x in range(WIDTH):
self.c = complex(x * ((self.max_a - self.min_a) / WIDTH) + self.min_a,
y * ((self.max_b - self.min_b) / HEIGHT) + self.min_b)
z = self.c
q = (self.c.real - 0.25)**2 + (self.c.imag * self.c.imag)
x = self.c.real
y2 = self.c.imag * self.c.imag
if not (q*(q + (x - 0.25)) < y2 / 4.0 or (x + 1.0)**2 + y2 <0.0625):
mu = self.mandel(x, y, z, iteration = 0)
rgb = self.linear_interp((255, 255, 0), (55, 55, 0), mu)
self.rgb.palette[y].append(rgb)
else:
self.rgb.palette[y].append((55, 55, 0))
if self.progress_bar != None:
self.progress_bar["value"] = y
self.canvas.update()
The image I am getting is below:
I think this is the culprit:
else:
mu = 0
self.mandel(x, y, z, iteration + 1)
return mu
This isn't passing down the value of mu from the recursive call correctly, so you're getting black for everything that doesn't bottom out after 1 call. Try
else:
...
mu = self.mandel(x, y, z, iteration + 1)
return mu
I am trying to generate 1000 robot instances of class ROBOT
class robot:
def __init__(self):
W, H = map.size # Getting dimensions of map or image
valid_pixels = []
for y in xrange(H):
for x in xrange(W):
if ( map.getpixel((x, y)) == 255 ):
valid_pixels.append((x, y))
num_valid_pixels = len(valid_pixels)
p = valid_pixels[ random.randrange(0, num_valid_pixels) ]
self.x = p[0] + random.random();
self.y = p[1] + random.random();
self.orientation = random.uniform(0, 2 * math.pi)
self.forward_noise = 0.0
self.turn_noise = 0.0
self.sense_noise = 0.0
for i in xrange(1000):
r = robot()
It is so slow. It is taking more than 3-4 minutes. I am running Enthought in Pydev. What may be a possible problem?
But when I run the same procedure which is not under any class. It is very fast.
def initialize(map):
n = 1000
W, H = map.size # Getting dimensions of map or image
valid_pixels = []
for y in range(H):
for x in range(W):
if (map.getpixel((x, y)) == 255):
valid_pixels.append((x, y))
num_valid_pixels = len(valid_pixels)
particles = []
for i in range(n):
p = valid_pixels[random.randrange(0, num_valid_pixels)]
particles.append([p[0] + random.random(), p[1] + random.random(), random.uniform(0, 2 * math.pi)])
return particles
You're constructing the valid_pixels list 1000 times. Among other overhead, that involves 160 million map.getpixel calls, so it's pretty time-consuming. The second version of your code constructs the list once and reuses it for every random pixel selection, which is about 1000 times faster.