def path(given_map, x, y):
x = given_map[0][0]
y = given_map[0][0]
cnt = 0
if x == len(given_map) and y == len(given_map):
cnt += 1
return cnt
else:
if x < len(given_map) and y < len(given_map):
return path(given_map, x, y)
elif x < len(given_map) and y == len(given_map):
return path(given_map, x, y)
elif x == len(given_map) and y < len(given_map):
return path(given_map, x, y)
else:
cnt = 0
return cnt
if __name__ == '__main__':
input_map = [[1,2,9,4,9],
[1,5,8,7,9],
[9,3,9,9,2],
[2,3,7,5,9],
[1,9,9,1,0]]
print(path(input_map, 0, 0))
input_map = [[1,1,2],
[1,2,2],
[1,2,0]]
print(path(input_map, 0, 0))
The n*n list must be input to create a function that returns the number of paths that can reach from the starting point [0][0] to the ending point [n-1][n-1].
Movement can only be done downward and right, and can only be moved in one direction by the number shown in the corresponding x and y coordinates.
It does not include cases outside the map after movement.
The above code is implemented as much as possible within my ability. How can I modify it to function normally?
Unless you are required to implement this using recursion, I would suggest a BFS (Breadth First Search) approach using a dictionary to track the positions/count that you have reached at each step of the progression:
def path(M):
result = 0 # final result
bounds = range(len(M)) # inside map
paths = {(0,0):1} # one path at start
while paths: # spread counts
nextPos = dict() # track next positions
for (x,y),count in paths.items(): # extend positon/count
if x == y == len(M)-1: result += count # capture final count
dist = M[x][y] # travel distance
if not dist: continue # no movement ...
for x2,y2 in [(x,y+dist),(x+dist,y)]: # travel down & right
if x2 in bounds and y2 in bounds: # propagate count
nextPos[x2,y2] = nextPos.get((x2,y2),0)+count
paths = nextPos # next step on paths
return result
input_map = [[1,2,9,4,9],
[1,5,8,7,9],
[9,3,9,9,2],
[2,3,7,5,9],
[1,9,9,1,0]]
print(path(input_map)) # 2
input_map = [[1,1,2],
[1,2,2],
[1,2,0]]
print(path(input_map)) # 1
If you are required to implement a recursive approach, the function can be adapted like this (returning the sum of recursive path counts) but then it won't be a BFS anymore:
def path(M,x=0,y=0):
if x == y == len(M)-1: return 1 # goal reached
dist = M[x][y] # travel distance
if not dist: return 0 # no movement
bounds = range(len(M)) # inside map
return sum(path(M,x2,y2) for x2,y2 in [(x,y+dist),(x+dist,y)]
if x2 in bounds and y2 in bounds) # sum of paths
Related
A video by Sebastion Lague explained the Bridson's algorithm really well.
To oversimplify,
Create cell grid that has sides of radius/sqrt(2).
Place initial point and list as spawnpoint.
Place point into cell in grid.
For any spawnpoint, spawn a point between radius and 2*radius.
Look at the cells 2 units away from cell of new point.
If contains other points, compare distance.
If any point is closer to new point than the radius, new point is invalid.
If new point is valid, new point is listed as spawnpoint and placed into cell in grid.
If spawnpoint spawns too many invalid points, spawnpoint is removed and turns into point.
Repeat until no more spawnpoints exists.
Return points.
I basically written the same thing down in Python 3.7.2 and pygame 1.7~, but as said in the title, I'm stuck in recursive purgatory.
I used one Point() class for this algorithm, which might seem redundant given that pygame.Vector2() exists, but I needed some elements for a separate algorithm (Delaunay's with infinite vertices) that required this class to work.
For the sake of simplicity I'm going to cut away all the Delaunay-specific elements and show the bare-bones of this class that is needed for this algorithm:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def DistanceToSquared(self,other):
return (self.x-other.x)**2 + (self.y-other.y)**2
The code that is related to the Bridson's algorithm is:
def PoissonDiskSampling(width, height, radius, startPos = None, spawnAttempts = 10):
if startPos == None:
startPos = [width//2,height//2]
cellSize = radius / math.sqrt(2)
cellNumberX = int(width // cellSize + 1) # Initialise a cells grid for optimisation
cellNumberY = int(height // cellSize + 1)
cellGrid = [[None for x in range(cellNumberX)] for y in range(cellNumberY)]
startingPoint = Point(startPos[0],startPos[1]) # Add an iniial point for spawning purposes
cellGrid[startingPoint.x//radius][startingPoint.y//radius] = startingPoint
points = [startingPoint] # Initialise 2 lists tracking all points and active points
spawnpoints = [startingPoint]
while len(spawnpoints) > 0:
spawnIndex = random.randint(0,len(spawnpoints)-1)
spawnpoint = spawnpoints[spawnIndex]
spawned = False
for i in range(spawnAttempts):
r = random.uniform(radius,2*radius)
radian = random.uniform(0,2*math.pi)
newPoint = Point(spawnpoint.x + r*math.cos(radian),
spawnpoint.y + r*math.sin(radian))
if 0 <= newPoint.x <= width and 0 <= newPoint.y <= height:
isValid = True
else:
continue
newPointIndex = [int(newPoint.x//cellSize), int(newPoint.y//cellSize)]
neighbours = FindNeighbours(cellNumberX,cellNumberY,newPointIndex,cellGrid)
for neighbour in neighbours:
if newPoint.DistanceToSquared(neighbour) < radius**2:
isValid = False
break
if isValid:
points.append(newPoint)
spawnpoints.append(newPoint)
spawned = True
break
else:
continue
if spawned == False:
spawnpoints.remove(spawnpoint)
return points
def FindNeighbours(cellNumberX, cellNumberY, index, cellGrid):
neighbours = []
for cellX in range(max(0,(index[0]-2)), min(cellNumberX,(index[1]+2))):
for cellY in range(max(0,(index[0]-2)), min(cellNumberY,(index[1]+2))):
if cellGrid[cellX][cellY] != None:
neighbours.append(cellGrid[cellX][cellY])
return neighbours
Please help.
The probably most important step is missing in your code:
If new point is valid, new point is listed as spawnpoint and placed into cell in grid.
I suggest to add the point to the cellGrid if it is valid:
if isValid:
cellGrid[newPointIndex[0]][newPointIndex[1]] = newPoint
points.append(newPoint)
spawnpoints.append(newPoint)
spawned = True
break
Further, you have to verify if the cell with the index newPointIndex is not already occupied before a point can be add:
newPointIndex = [int(newPoint.x/cellSize), int(newPoint.y/cellSize)]
if cellGrid[newPointIndex[0]][newPointIndex[1]] != None:
continue
neighbours = FindNeighbours(cellNumberX,cellNumberY,newPointIndex,cellGrid)
Finally there is an issue in the function FindNeighbours. range(start, stop) creates a range for x in start <= x < stop.
So the stop has to be index[0]+3 rather than index[0]+2.
Further the ranges which control the 2 nested for loops, run both from x-2 to y+2 rather than from x-2 to x+2 respectively from y-2 to y+2:
for cellX in range(max(0,(index[0]-2)), min(cellNumberX,(index[1]+2))):
for cellY in range(max(0,(index[0]-2)), min(cellNumberY,(index[1]+2)))
The fixed function has to be:
def FindNeighbours(cellNumberX, cellNumberY, index, cellGrid):
neighbours = []
for cellX in range(max(0, index[0]-2), min(cellNumberX, index[0]+3)):
for cellY in range(max(0, index[1]-2), min(cellNumberY, index[1]+3)):
if cellGrid[cellX][cellY] != None:
neighbours.append(cellGrid[cellX][cellY])
return neighbours
See the result, for a size of 300 x 300 and a radius of 15:
An even better result can be achieve, if always the 1st point of spawnpoints is used to rather than a random point:
# spawnIndex = random.randint(0,len(spawnpoints)-1)
spawnIndex = 0 # 0 rather than random
spawnpoint = spawnpoints[spawnIndex]
I've written an algorithm that given a set of 2-D co-ordinates,generates the shortest path to traverse a building given that there are certain prohibited points, i.e., the points where the building is present.
I create a list of these points by using a nested while loops and incrementing between the boundaries by 0.01.
I also then remove these points from a set of potential neighbors that is generated while trying to figure out the next best point to go to.
The heuristic I'm using is to minimize the sum of the distances of the potential point to the start and to the goal.
I have 4 different goals (nodes) and iterate through them and changing the starting points for that iteration when needed.
In spite of this, there are some points being included in the end result that are located where the building is supposed to be, essentially making this a non-viable path.
Any help would be appreciated!
import math
import copy
def distance(point1, point2):
'''
Computers distance between two points, two dimensional for now
'''
dist = math.sqrt(math.pow(point1[0] - point2[0], 2) + math.pow(point1[1] - point2[1], 2))
return dist
def check(point1, point2):
'''
Checks if they are the same point or not
'''
for i in range(len(point1)):
if round(point1[i], 1) != round(point2[i], 1):
return False
return True
def round_point(point):
'''
Rounds all the 2 co-ordinates of a point
'''
for i in range(len(point)):
for j in range(len(point[i])):
point[i][j] = round(point[i][j], 2)
return point
def check_in(points, prohibited, visited):
'''
Removes all prohibited points from points and returns
'''
points = round_point(points)
temp = copy.deepcopy(points)
for i in range(len(points)):
if points[i] in prohibited:
#print('b')
temp.remove(points[i])
elif len(visited) != 0:
if points[i] in visited:
#print('c')
temp.remove(points[i])
return temp
def heuristic(points, start, destination):
'''
Calculates heuristic for each point
'''
l = []
for i in range(len(points)):
dist = distance(start, points[i]) + distance(destination, points[i])
l.append(dist)
return l
def neighbours(point):
'''
Finds all the points at a distance of 0.1m from the given point
'''
points = []
for i in range(360):
y = point[1] + 0.1 * math.sin((i * 2 * math.pi) / 180)
x = point[0] + 0.1 * math.cos((i * 2 * math.pi) / 180)
points.append([x, y])
return points
def best(points, prohibited, start, end, visited):
'''
Chooses the point which is least distance that is not prohibited
'''
points = check_in(points, prohibited, visited)
distances = heuristic(points, start, end)
loc = distances.index(min(distances))
return points[loc]
def search():
'''
Main function that searches
'''
nodes = [[2.5, 1.5], [2.5, 8.5], [7.5, 1.5], [7.5, 8.5]]
# Creates a list of prohibited points
prohibited = []
x = 2.5
y = 1.5
visited = []
while x <= 7.5:
while y <= 8.5:
prohibited.append([x, y])
y += 0.01
x += 0.01
# Path to be traversed
path = []
for i in range(len(nodes)):
if i == 0:
# Start has to be origin for first iteration
start = [0, 0]
else:
start = nodes[i - 1]
destination = nodes[i]
# Calculates points for first iteration
points = neighbours(start)
loc = best(points, prohibited, start, destination, visited)
path.append(loc)
visited.append(loc)
while True:
# If the current location is the destination, loop is done
if check(loc, destination): break
else:
points = neighbours(loc)
loc = best(points, prohibited, start, destination, visited)
path.append(loc)
visited.append(loc)
continue
return path
path = search()
print(path)
print(len(path))
So I'm trying to create a maze game with levels for a project at school. The code is a bit repetitive sorry I've only just started coding using pygame. When run the program should output a maze that once the user completes moves onto the next level - each level is randomly generated. However, only the first level is showing properly the rest of the levels appear to be a grid- which is making me think that the game is creating a new maze over the old.
I've pasted the code below - feel free to leave any advice on how to improve what I have :)
class Maze:
def __init__(self, rows=30, cols=40):
self.rows = rows
self.cols = cols
self.keep_going = 1
self.maze = {}
for y in range(rows):
for x in range(cols):
cell = {'south' : 1, 'east' : 1, 'visited': 0}
self.maze[(x,y)] = cell
def generate(self, start_cell=None, stack=[])
if start_cell is None:
start_cell = self.maze[(self.cols-1, self.rows-1)]
if not self.keep_going:
return
self.check_finished()
neighbors = []
# if the stack is empty, add the start cell
if len(stack) == 0:
stack.append(start_cell)
# set current cell to last cell
curr_cell = stack[-1]
# get neighbors and shuffle 'em up a bit
neighbors = self.get_neighbors(curr_cell)
shuffle(neighbors)
for neighbor in neighbors:
if neighbor['visited'] == 0:
neighbor['visited'] = 1
stack.append(neighbor)
self.knock_wall(curr_cell, neighbor)
self.generate(start_cell, stack)
def get_coords(self, cell):
# grabs coords of a given cell
coords = (-1, -1)
for k in self.maze:
if self.maze[k] is cell:
coords = (k[0], k[1])
break
return coords
def get_neighbors(self, cell):
# obvious
neighbors = []
(x, y) = self.get_coords(cell)
if (x, y) == (-1, -1):
return neighbors
north = (x, y-1)
south = (x, y+1)
east = (x+1, y)
west = (x-1, y)
if north in self.maze:
neighbors.append(self.maze[north])
if south in self.maze:
neighbors.append(self.maze[south])
if east in self.maze:
neighbors.append(self.maze[east])
if west in self.maze:
neighbors.append(self.maze[west])
return neighbors
def knock_wall(self, cell, neighbor):
# knocks down wall between cell and neighbor.
xc, yc = self.get_coords(cell)
xn, yn = self.get_coords(neighbor)
# Which neighbor?
if xc == xn and yc == yn + 1:
# neighbor's above, knock out south wall of neighbor
neighbor['south'] = 0
elif xc == xn and yc == yn - 1:
# neighbor's below, knock out south wall of cell
cell['south'] = 0
elif xc == xn + 1 and yc == yn:
# neighbor's left, knock out east wall of neighbor
neighbor['east'] = 0
elif xc == xn - 1 and yc == yn:
# neighbor's right, knock down east wall of cell
cell['east'] = 0
def check_finished(self):
# Checks if we're done generating
done = 1
for k in self.maze:
if self.maze[k]['visited'] == 0:
done = 0
break
if done:
self.keep_going = 0
[...] the rest of the levels appear to be a grid- which is making me think that the game is creating a new maze over the old.
The issue is caused by a common mistake in Python.
See Default Argument Values
Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes
In your case the arguments to the method generate of class Maze has default arguments:
class Maze:
# [...]
def generate(self, start_cell=None, stack=[]):
# [...]
In the method generate elements are append to stack. The maze is generated trusting in the default argument:
self.maze_obj.generate(self.maze_obj.maze[(0,0)])
That causes that the 1st generation of the mace succeeds, but the following generation fails, because stack contains all the elements of the former generation process.
Pass an empty list to generate to solve the issue:
self.maze_obj.generate(self.maze_obj.maze[(0,0)])
self.maze_obj.generate(self.maze_obj.maze[(0,0)], [])
Or change the default argument to None:
class Maze:
# [...]
def generate(self, start_cell=None, stack=None):
if stack == None:
stack = []
A video by Sebastion Lague explained the Bridson's algorithm really well.
To oversimplify,
Create cell grid that has sides of radius/sqrt(2).
Place initial point and list as spawnpoint.
Place point into cell in grid.
For any spawnpoint, spawn a point between radius and 2*radius.
Look at the cells 2 units away from cell of new point.
If contains other points, compare distance.
If any point is closer to new point than the radius, new point is invalid.
If new point is valid, new point is listed as spawnpoint and placed into cell in grid.
If spawnpoint spawns too many invalid points, spawnpoint is removed and turns into point.
Repeat until no more spawnpoints exists.
Return points.
I basically written the same thing down in Python 3.7.2 and pygame 1.7~, but as said in the title, I'm stuck in recursive purgatory.
I used one Point() class for this algorithm, which might seem redundant given that pygame.Vector2() exists, but I needed some elements for a separate algorithm (Delaunay's with infinite vertices) that required this class to work.
For the sake of simplicity I'm going to cut away all the Delaunay-specific elements and show the bare-bones of this class that is needed for this algorithm:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def DistanceToSquared(self,other):
return (self.x-other.x)**2 + (self.y-other.y)**2
The code that is related to the Bridson's algorithm is:
def PoissonDiskSampling(width, height, radius, startPos = None, spawnAttempts = 10):
if startPos == None:
startPos = [width//2,height//2]
cellSize = radius / math.sqrt(2)
cellNumberX = int(width // cellSize + 1) # Initialise a cells grid for optimisation
cellNumberY = int(height // cellSize + 1)
cellGrid = [[None for x in range(cellNumberX)] for y in range(cellNumberY)]
startingPoint = Point(startPos[0],startPos[1]) # Add an iniial point for spawning purposes
cellGrid[startingPoint.x//radius][startingPoint.y//radius] = startingPoint
points = [startingPoint] # Initialise 2 lists tracking all points and active points
spawnpoints = [startingPoint]
while len(spawnpoints) > 0:
spawnIndex = random.randint(0,len(spawnpoints)-1)
spawnpoint = spawnpoints[spawnIndex]
spawned = False
for i in range(spawnAttempts):
r = random.uniform(radius,2*radius)
radian = random.uniform(0,2*math.pi)
newPoint = Point(spawnpoint.x + r*math.cos(radian),
spawnpoint.y + r*math.sin(radian))
if 0 <= newPoint.x <= width and 0 <= newPoint.y <= height:
isValid = True
else:
continue
newPointIndex = [int(newPoint.x//cellSize), int(newPoint.y//cellSize)]
neighbours = FindNeighbours(cellNumberX,cellNumberY,newPointIndex,cellGrid)
for neighbour in neighbours:
if newPoint.DistanceToSquared(neighbour) < radius**2:
isValid = False
break
if isValid:
points.append(newPoint)
spawnpoints.append(newPoint)
spawned = True
break
else:
continue
if spawned == False:
spawnpoints.remove(spawnpoint)
return points
def FindNeighbours(cellNumberX, cellNumberY, index, cellGrid):
neighbours = []
for cellX in range(max(0,(index[0]-2)), min(cellNumberX,(index[1]+2))):
for cellY in range(max(0,(index[0]-2)), min(cellNumberY,(index[1]+2))):
if cellGrid[cellX][cellY] != None:
neighbours.append(cellGrid[cellX][cellY])
return neighbours
Please help.
The probably most important step is missing in your code:
If new point is valid, new point is listed as spawnpoint and placed into cell in grid.
I suggest to add the point to the cellGrid if it is valid:
if isValid:
cellGrid[newPointIndex[0]][newPointIndex[1]] = newPoint
points.append(newPoint)
spawnpoints.append(newPoint)
spawned = True
break
Further, you have to verify if the cell with the index newPointIndex is not already occupied before a point can be add:
newPointIndex = [int(newPoint.x/cellSize), int(newPoint.y/cellSize)]
if cellGrid[newPointIndex[0]][newPointIndex[1]] != None:
continue
neighbours = FindNeighbours(cellNumberX,cellNumberY,newPointIndex,cellGrid)
Finally there is an issue in the function FindNeighbours. range(start, stop) creates a range for x in start <= x < stop.
So the stop has to be index[0]+3 rather than index[0]+2.
Further the ranges which control the 2 nested for loops, run both from x-2 to y+2 rather than from x-2 to x+2 respectively from y-2 to y+2:
for cellX in range(max(0,(index[0]-2)), min(cellNumberX,(index[1]+2))):
for cellY in range(max(0,(index[0]-2)), min(cellNumberY,(index[1]+2)))
The fixed function has to be:
def FindNeighbours(cellNumberX, cellNumberY, index, cellGrid):
neighbours = []
for cellX in range(max(0, index[0]-2), min(cellNumberX, index[0]+3)):
for cellY in range(max(0, index[1]-2), min(cellNumberY, index[1]+3)):
if cellGrid[cellX][cellY] != None:
neighbours.append(cellGrid[cellX][cellY])
return neighbours
See the result, for a size of 300 x 300 and a radius of 15:
An even better result can be achieve, if always the 1st point of spawnpoints is used to rather than a random point:
# spawnIndex = random.randint(0,len(spawnpoints)-1)
spawnIndex = 0 # 0 rather than random
spawnpoint = spawnpoints[spawnIndex]
I am new to python and still learning the ropes but I am hoping someone with more experience can help me out.
I am trying to write a python script that:
creates four points
creates four rectangles
check if each of the point is in any of the rectangles then write out the results to a output file.
The problem involves two data structures Point and Rectangle class. I have already started to create the Point class and Rectangle classes. Rectangle class should hold relevant data sets created from random module’s random method. As you can tell from my attempts that I am kind of all over the place but I have used #comments to try to get what I am trying to do.
Specific questions I have are:
1) how can I get this script working?
2) What variables or functions am I missing to generate random rectangles and see if specific points are in those rectangles?
## 1. Declare the Point class
class Point:
def __init__(self,x = 0.0, y = 0.0):
self.x = x
self.y = y
pass
## 2. Declare the Rectangle class
class Rectangle:
def __int__(self): ## A rectangle can be determined aby (minX, maxX) (minY, maxY)
self.minX = self.minY = 0.0
self.maxX = self.maxY = 1.0
def contains(self, point): ## add code to check if a point is within a rectangle
"""Return true if a point is inside the rectangle."""
# Determine if a point is inside a given polygon or not
# Polygon is a list of (x,y) pairs. This function
# returns True or False.
def point_in_poly(x,y,poly):
n = len(poly)
inside = False
p1x,p1y = poly[0]
for i in range(n+1):
p2x,p2y = poly[i % n]
if y > min(p1y,p2y):
if y <= max(p1y,p2y):
if x <= max(p1x,p2x):
if p1y != p2y:
xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
if p1x == p2x or x <= xints:
inside = not inside
p1x,p1y = p2x,p2y
return inside
## 3. Generate four points
##define a Point list to keep four points
points = []
##add codes to generate four points and append to the points list
polygon = [(0,10),(10,10),(10,0),(0,0)]
point_x = 5
point_y = 5
## 4. Generate four rectangles
##define a Rectangle list
rects = []
for i in range(4):
rectangle = Rectangle()
## Generate x
x1 = random.random()
x2 = random.random()
## make sure minX != maxX
while(x1 == x2):
x1 = random.random()
if x1<x2:
rectangle.minX=x1
rectangle.maxX=x2
elif x1>x2:
rectangle.minX=x2
rectangle.maxX=x1
rects.append(rectangle)
## Develop codes to generate y values below
## make sure minY != maxY
while(y1 == y2):
y1 = random.random()
if y1<y2:
rectangle.minY=y1
rectangle.maxY=y2
elif y1>y2:
recetangle.minY=y2
racetangle.maxY=y1
## add to the list
rects.append(rectangle)
## 5. Add code to check which point is in which rectangle
resultList = [] ## And use a list to keep the results
for i in range(4):
for j in range(4):
if points[i] in rectangle[j]:
print i
# write the results to file
f=open('Code5_4_1_Results.txt','w')
for result in resultList:
f.write(result+'\n')
f.close()
This is pretty simple math. Given a rectangle with points (x1,y1) and (x2,y2) and assuming x1 < x2 and y1 < y2 (if not, you can just swap them), a point (x,y) is within that rectangle if x1 < x < x2 and y1 < y < y2. Since Python comparison operators can be chained, this is even valid Python code which should produce the correct result (in other languages you'd have to write something like x1 < x and x < x2, etc).
If you want, you can use <= instead of <. Using <= means points on the boundary of the rectangle (eg, the point (x1,y1)) count as being inside it, while using < so means such points are outside it.
It is better to write a separate function to do the job. Here's my function. You can just copy it if you want
def pointInRect(point,rect):
x1, y1, w, h = rect
x2, y2 = x1+w, y1+h
x, y = point
if (x1 < x and x < x2):
if (y1 < y and y < y2):
return True
return False