Cannot get correct recursion in function - python-3.x

In my show() function I can't get the correct amount of recursions and boxes showing. It only goes 1 level deep then stops. Any idea on how to fix it? I'll post all my code to give you some background. When it runs it won't continue to show other boxes that don't have bombs around them or numbered boxes. Not sure what is going wrong as I believe the code is correct but I didn't know how to debug the recursion function. I thought since it could be because being called only once in another function it might limit the recursion. But that does not make sense. I want to get this working to see if it would be possible to run a CSP type algorithm against it. Thanks for the help.
import pygame as pg
import random
pg.init()
HEIGHT, WIDTH = 400, 400
gameloop = True
TILESIZE = 25
class Tile:
def __init__(self, pos):
self.pos = pos
self.bomb = False
self.number = 0
self.show = False
def printAttr(self):
print(self.bomb, self.pos, self.number)
def create_bomb(diction):
b = []
for i in range(1,41):
x = random.randint(0, 15)
y = random.randint(0, 15)
while (x,y) in b:
x = random.randint(0, 15)
y = random.randint(0, 15)
b.append((x,y))
print(len(b))
for item in b:
diction[item].bomb = True
if not diction[item].bomb:
neighbors = [
(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1), (x + 1, y + 1),
(x + 1, y - 1), (x - 1, y + 1), (x - 1, y - 1)
]
neighbors = [neighbor for neighbor in neighbors if validate_cell(neighbor)]
for q in neighbors:
if not diction[q].bomb:
diction[q].number += 1
else:
continue
def validate_cell(neighbor):
if neighbor[0] < 0 or neighbor[1] < 0:
return False
elif neighbor[0] >= 16 or neighbor[1] >= 16:
return False
else:
return True
def create_number(pos, diction):
if not diction[pos].bomb:
neighbors = [
(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1), (x + 1, y + 1),
(x + 1, y - 1), (x - 1, y + 1), (x - 1, y - 1)
]
neighbors = [neighbor for neighbor in neighbors if validate_cell(neighbor)]
count = 0
for item in neighbors:
if diction[item].bomb:
count += 1
else:
continue
if count >= 0:
diction[pos].number = count
def create_board_surf(dis, diction): #creating boaurd
for x in range(16):
for y in range(16):
if diction[(x,y)].show == True:
rect = pg.Rect(x * TILESIZE, y * TILESIZE, TILESIZE, TILESIZE)
pg.draw.rect(dis, pg.Color("grey"), rect, 5)
if diction[(x,y)].number > 0:
rect = pg.Rect(x * TILESIZE+7, y * TILESIZE-3, TILESIZE, TILESIZE)
font = pg.font.SysFont("timesnewroman", 25)
num = diction[(x,y)].number
text = font.render(str(num), False, pg.Color("black"))
dis.blit(text, rect)
else:
rect = pg.Rect(x * TILESIZE, y * TILESIZE, TILESIZE, TILESIZE)
pg.draw.rect(dis, pg.Color("grey"), rect, 2)
# if diction[(x,y)].bomb:
# rect = pg.Rect(x * TILESIZE, y * TILESIZE, TILESIZE, TILESIZE)
# font = pg.font.SysFont("timesnewroman", 25)
# text = font.render("B", False, pg.Color("black"))
# dis.blit(text, rect)
def chosen(pos):
if diction[pos].bomb == True:
diction[pos].show = True
gameloop = False
return gameloop
else:
show(pos)
gameloop = True
return gameloop
def show(pos):
if diction[pos].number == 0 and not diction[pos].show and not diction[pos].bomb:
diction[pos].show = True
neighbors = [
(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1), (x + 1, y + 1),
(x + 1, y - 1), (x - 1, y + 1), (x - 1, y - 1)
]
neighbor1= [neighbor for neighbor in neighbors if validate_cell(neighbor)]
for item in neighbor1:
show(item)
return
if diction[pos].number > 0:
diction[pos].show = True
return
diction = {}
for x in range(16):
for y in range(16):
diction[(x, y)] = Tile([x, y])
create_bomb(diction)
for x in range(16):
for y in range(16):
create_number((x,y), diction)
dis = pg.display.set_mode((HEIGHT, WIDTH))
pg.display.update()
while gameloop:
for event in pg.event.get():
if event.type == pg.QUIT:
gameloop = False
elif event.type == pg.MOUSEBUTTONDOWN:
x, y = [int(v // TILESIZE) for v in pos]
gameloop = chosen((x,y))
pos = pg.Vector2(pg.mouse.get_pos())
dis.fill(pg.Color("white"))
create_board_surf(dis,diction)
pg.display.flip()

Your show-method doesnt know the (updated) value of the variables x and y, so it sets them to the value they had during the first call to show (note that it is only because they get defined as global variables that these initial values of x and y are visible throughout your call-stack - had your main game-loop been in a separate method you would probably have been warned that they were not initialized). Modify your show method as follows
def show(pos):
if diction[pos].number == 0 and not diction[pos].show and not diction[pos].bomb:
diction[pos].show = True
x=pos[0]
y=pos[1]
neighbors = [
(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1), (x + 1, y + 1),
(x + 1, y - 1), (x - 1, y + 1), (x - 1, y - 1)
]
neighbor1= [neighbor for neighbor in neighbors if validate_cell(neighbor)]
for item in neighbor1:
show(item)
return
if diction[pos].number > 0:
diction[pos].show = True
return
and I would expect your program to work.

Related

DFS vs. Kruskal runtime (maze generation)

I have written two algorithms for creating unique mazes, one of them using depth-first-search (DFS) and the other using Kruskal's. The DFS algorithm performs as expected, however Kruskal's algorithm runs marginally slower than DFS and I do not know why.
I had written Kruskal's algorithm in Python.
I suspect the random.choice() function seems to be the underlying problem. The difference in runtime becomes noticeable when (r, c) > 30.
Here is the code for Kruskal's algorithm:
# Create a list of all possible edges
def create_edges(r, c):
edges = []
for y in range(r):
for x in range(c):
i = (y, x)
for d in ((0, 1), (0, -1), (1, 0), (-1, 0)):
p = tuple(map(sum, zip(d, i)))
py = p[0]
px = p[1]
if px in range(c) and py in range(r):
edges.append([i, p])
return edges
def kruskal(r, c, sz):
path = []
# Create a list of parent root nodes
roots = {(y, x) : [(y, x)] for y in range(r) for x in range(c)}
edges = create_edges(r, c)
while edges:
# Choose a random edge
edge = random.choice(edges)
parent = edge[0]
child = edge[1]
parent_set = get_set(roots, parent)
child_set = get_set(roots, child)
# Check if the parent / child are already in the same set
if parent_set == child_set:
rev_edge = edge.reverse()
if rev_edge in edges:
edges.remove(rev_edge)
edges.remove(edge)
continue
roots[parent_set] += roots[child_set]
roots.pop(child_set)
path.extend((parent, child))
rev_edge = edge.reverse()
if rev_edge in edges:
edges.remove(rev_edge)
edges.remove(edge)
return path
def get_set(roots, member):
s = None
for parent, children in roots.items():
if member in children:
s = parent
return s
def create_maze(t, r, c, sz):
maze = [['|_' for _ in range(c)] for _ in range(r)]
for cell in maze: cell.append('| ')
wd = {'DOWN' : ( 1, 0),
'UP' : (-1, 0),
'LEFT' : ( 0, -1),
'RIGHT': ( 0, 1)}
for n in range(len(t) - 1):
a = n
b = n + 1
p1 = t[a]
p2 = t[b]
ay, ax = p1[0], p1[1]
by, bx = p2[0], p2[1]
w = tuple(numpy.array(p2) - numpy.array(p1))
if w in wd.values():
k = list(wd.keys())[list(wd.values()).index(w)]
if k == 'DOWN': maze[ay][ax] = maze[ay][ax].replace('_', ' ')
if k == 'UP': maze[by][bx] = maze[by][bx].replace('_', ' ')
if k == 'LEFT': maze[ay][ax] = maze[ay][ax].replace('|', ' ')
if k == 'RIGHT': maze[by][bx] = maze[by][bx].replace('|', ' ')
return maze
def print_maze(maze, r, c, delay = 0):
s, l = min((r, c)), max((r, c))
a = 1 / (4 * r * c)
e = (1 / (s * l)) ** 2
delay = (a * 2.718 ** (-1 * e)) ** 0.5
time.sleep(delay)
print(' _' * c)
for iy in range(r):
for ix in range(c + 1):
print(maze[iy][ix], end = '')
print('')
print('')
def main():
r = 30
c = 30
sz = r * c
path = kruskal(r, c, sz)
maze = create_maze(path, r, c, sz)
print_maze(maze, r, c)
if __name__ == "__main__":
main()

Turtle onclic doesn't work as expected, moving object that was not clicked

Want to make a ball to change trajectory when I click on it. But in my case when I click on some ball, movement not always happens on the ball that was clicked but on another one. Tried to change place for onclic method, but always the same. print shows that function is called for wrong object. Don't know how to make it right.
import random
import turtle
def my_function(x, y):
print(x, y)
xd[index] = -xd[index]
print (myballs)
window = turtle.Screen()
window.delay(5)
message = turtle.Turtle()
message.hideturtle()
MAXX, MAXY = window.screensize()
BALLSIZE = 1
border = turtle.Turtle()
border.hideturtle()
border.speed(0)
border.up()
border.goto(MAXX, MAXY)
border.down()
border.pensize(1)
border.color('red')
border.goto(MAXX, -MAXY)
border.goto(-MAXX, -MAXY)
border.goto(-MAXX, MAXY)
border.goto(MAXX, MAXY)
balls = []
balls.append(turtle.Turtle())
balls.append(turtle.Turtle())
x = [0] * len(balls)
y = [0] * len(balls)
xd = [0] * len(balls)
yd = [0] * len(balls)
for myballs in balls:
x, y = random.randint(-MAXX + 1, MAXX - 1), random.randint(-MAXY + 1, MAXY - 1)
myballs.hideturtle()
myballs.speed(0)
myballs.up()
myballs.shapesize(BALLSIZE)
myballs.shape('circle')
myballs.goto(x, y)
myballs.showturtle()
index = balls.index(myballs)
speed = 1
xd[index] = speed
yd[index] = speed
myballs.onclick(my_function)
while True:
for myballs in balls:
index = balls.index(myballs)
x, y = myballs.pos()
if x+BALLSIZE*10 >= MAXX or x-BALLSIZE*10 <= -MAXX:
xd[index] = -xd[index]
if y+BALLSIZE*10 >= MAXY or y-BALLSIZE*10 <= -MAXY:
yd[index] = -yd[index]
x = x + xd[index]
y = y + yd[index]
myballs.goto(x, y)
First, import math module:
import math
then change your function to this:
def my_function(x, y):
print(x, y)
for i,ball in enumerate(balls):
ball_x, ball_y = ball.pos()
if math.hypot(abs(x-ball_x),
abs(y-ball_y)) < BALLSIZE*10:
xd[i] = -xd[i]
print(i, ball)
return

Calculate the number of paths in a grid from top left to bottom right

I need to calculate the number of paths from top left to right bottom where a valid path is path that crosses all the squares in the grid (and exactly once for every square)
I'm using the backtracking technique. Unfortunately, count is 0 in the end of the calculation. Printing t, I see that it never gets to n-1.
What's wrong with my algorithm?
n = 4
count = 0
m = [[False for x in range(n)] for y in range(n)]
def num_of_paths(m, x, y, t):
print(t)
global count
# check if we reached target
if x == (n - 1) and y == (n - 1):
if t < (n * n):
# not on time, prune the tree here
return
elif t == n * n:
# completed a full path in the grid and on time
count += 1
if t > n * n:
return
# Right
if x + 1 < n and m[x + 1][y] == False:
m[x + 1][y] = True
num_of_paths(m, x + 1, y, t + 1)
m[x + 1][y] = False
# Left
if x - 1 > 0 and m[x - 1][y] == False:
m[x - 1][y] = True
num_of_paths(m, x - 1, y, t + 1)
m[x - 1][y] = False
# Down
if y + 1 < n and m[x][y + 1] == False:
m[x][y + 1] = True
num_of_paths(m, x, y + 1, t + 1)
m[x][y + 1] = False
# Up
if y - 1 > 0 and m[x][y - 1] == False:
m[x][y - 1] = True
num_of_paths(m, x, y - 1, t + 1)
m[x][y - 1] = False
num_of_paths(m, 0, 0, 0)
print(count)
There are the following issues:
The starting cell is not marked with m[0][0] = True, so after going right, the algorithm will go left again, and actually visit that cell twice. To resolve this, you can move the code for managing the m values away from where you have it now (4 times) and apply it to the current cell (once). This includes the if m[..][..] check, and the assignments of True and False.
The if conditions that relate to the left and up directions should compare the coordinate with >= 0, not with > 0: a zero value for a coordinate is still within range.
t should start with 1, since you compare its value with n * n. Or else you should compare with n * n - 1. In my correction below I will start with t = 1.
Not a real problem, but after doing count += 1 it would make sense to immediately return, since there is no possibility anymore to extend the path further.
Some other remarks:
When n is even, there is no valid path, so even when corrected, the function is bound to return 0 in that case
The number of paths this algorithm visits is exponential, O(2n²). For n > 6, don't wait for it...
Here is a corrected version of your code. Comments should clarify what was changed and why:
n = 5
count = 0
m = [[False for x in range(n)] for y in range(n)]
def num_of_paths(m, x, y, t):
global count
# Moved the "visited" check to here. No need to add `== True`.
if m[x][y]:
return
if x == (n - 1) and y == (n - 1):
if t < (n * n):
return
else: # Removed the unnecessary condition here
count += 1
# Added a return here
return
# Removed an if-block of which the condition could never be true
# Moved the "visited" marking to here:
m[x][y] = True
if x + 1 < n:
num_of_paths(m, x + 1, y, t + 1)
# Corrected "> 0" to ">= 0"
if x - 1 >= 0:
num_of_paths(m, x - 1, y, t + 1)
if y + 1 < n:
num_of_paths(m, x, y + 1, t + 1)
# Corrected "> 0" to ">= 0"
if y - 1 >= 0:
num_of_paths(m, x, y - 1, t + 1)
# Moved the "visited" unmarking to here:
m[x][y] = False
# Corrected the last argument
num_of_paths(m, 0, 0, 1)
print(count)
this code is working
n = 3
count=0
m = [[False for x in range(n)] for y in range(n)]
def num_of_paths(m,x,y):
# setting (x,y) position in m = True as we have crossed this square now
m[y][x]=True
global count
# check if we reached target
if x == (n - 1) and y == (n - 1):
# check if we haven't missed any square
for i in m:
if False in i:
m[y][x]=False
return
# increment count if we visited all squares
count+=1
m[y][x]=False
return
# setting up legel directions in which current point(x,y) should head next
dir={'up','down','left','right'}
if x==0:
dir-={'left'}
if x==n-1:
dir-={'right'}
if y==0:
dir-={'up'}
if y==n-1:
dir-={'down'}
# now we have all legal directions that (x,y) could go to
# now iterate over all possible directions of (x,y)
for i in dir:
if i=='left': # left means (x,y) will change to (x-1,y) i.e. change is (-1,0)
if m[y][x-1]==False: # it means left of (x,y) havent yet crossed i.e. it is legel to visit now
num_of_paths(m,x-1,y)
# similiarly for other possible directions
if i=='right':
if m[y][x+1]==False:
num_of_paths(m,x+1,y)
if i=='up':
if m[y-1][x]==False:
num_of_paths(m,x,y-1)
if i=='down':
if m[y+1][x]==False:
num_of_paths(m,x,y+1)
num_of_paths(m,0,0)
print(count)
let me know if there is some issue

Random walk simulation in python 3.6

I have been trying to simulate a random walk using the code below
import random
def random_walk(n):
""" Return coordiantes after 'n' block random walk"""
x, y = 0, 0
# y = 0
for i in range(n):
(dx, dy) = random.choice([(0, 1), (0, -1), (1, 0), (-1, 0)])
x = x+dx
y = y+dy
return(x, y)
for i in range(25):
walk = random_walk(10)
print(walk, "Distance from origin:",
abs(walk[0]) + abs(walk[1]))
I am always getting output as 1.No matter how much i increase number of walks or
number of blocks walked.I am not able to figure out what am i doing wrong
Just an indenting problem in the first for loop.
import random
def random_walk(n):
""" Return coordiantes after 'n' block random walk"""
x, y = 0, 0
# y = 0
for i in range(n):
(dx, dy) = random.choice([(0, 1), (0, -1), (1, 0), (-1, 0)])
x = x+dx
y = y+dy
return(x, y)
for i in range(25):
walk = random_walk(10)
print(walk, "Distance from origin:", abs(walk[0]) + abs(walk[1]))

How to modify my K-Means clustering algorithm to increase the dimensions upto 8?

I have created my k means algorithm for 2 dimensions. I want to modify it for 8 dimensions i.e. the datapoints can take 8-dimensional values and finally return 8-dimensional centroid values.
The code is following :
import random
import math
# Input varibles
#k = 3
#Threshold = 1
DATA = [[2, 1, 1, 2, 1, 1, 1, 5], [ 6, 8, 1, 3, 4, 3, 7, 1],[4, 1, 3, 2, 1, 3, 1, 1],[3, 1, 1, 2, 1, 2, 1, 1],[3 ,1 ,1 ,1, 1, 2, 1, 1],[6, 1, 1, 1, 1, 7, 1, 1],[6, 10, 2, 8, 10, 7, 3, 3]]
BIG_NUMBER = math.pow(10, 10)
data = []
centroids = []
class DataPoint:
def __init__(self, x, y):
self.x = x
self.y = y
def set_x(self, x):
self.x = x
def get_x(self):
return self.x
def set_y(self, y):
self.y = y
def get_y(self):
return self.y
def set_cluster(self, clusterNumber):
self.clusterNumber = clusterNumber
def get_cluster(self):
return self.clusterNumber
class Centroid:
def __init__(self, x, y):
self.x = x
self.y = y
def set_x(self, x):
self.x = x
def get_x(self):
return self.x
def set_y(self, y):
self.y = y
def get_y(self):
return self.y
# Initializing The Centroids
def initialize_centroids(k,DATA):
#find data range in x and y
max_x = max(x for x,y in DATA)
max_y = max(y for x,y in DATA)
min_x = min(x for x,y in DATA)
min_y = min(y for x,y in DATA)
#chosse random x and y between this data range
#assign to centroids
for j in range(k):
#x = random.choice(DATA)
random_x = random.uniform(min_x,max_x)
random_y = random.uniform(min_y,max_y)
centroids.append(Centroid(random_x, random_y))
#print("(", centroids[j].get_x(), ",", centroids[j].get_y(), ")")
return centroids
# Assigning Datapoints to nearest Centroids
def initialize_datapoints(k,DATA):
for i in range(len(DATA)):
newpoint = DataPoint(DATA[i][0], DATA[i][1])
bestMinimum = BIG_NUMBER
data.append(newpoint)
for j in range(k):
distance = get_distance(newpoint.get_x(), newpoint.get_y(), centroids[j].get_x(), centroids[j].get_y())
if(distance < bestMinimum):
bestMinimum = distance
newpoint.set_cluster(j)
return
# Calculating Euclidean distance
def get_distance(dataPointX, dataPointY, centroidX, centroidY):
return math.sqrt(math.pow((centroidY - dataPointY), 2) + math.pow((centroidX - dataPointX), 2))
# Updating Centroid and Clusters till the threshold is met
def update_centroids_n_clusters(k,DATA,Threshold):
dist = 0.0
#print ("a")
for j in range(k):
prev_x = centroids[j].get_x()
prev_y = centroids[j].get_y()
totalX = 0
totalY = 0
totalInCluster = 0
for z in range(len(data)):
if (data[z].get_cluster() == j):
totalX += data[z].get_x()
totalY += data[z].get_y()
totalInCluster += 1
if (totalInCluster > 0):
s_x = (totalX / totalInCluster)
s_y = (totalY / totalInCluster)
centroids[j].set_x(s_x)
centroids[j].set_y(s_y)
x1 = centroids[j].get_x()
y1 = centroids[j].get_y()
x2 = prev_x
y2 = prev_y
dist += get_distance(x1,y1,x2,y2)
conv_val = (1/k)*dist
if(conv_val >= Threshold):
for i in range(len(DATA)):
bestMinimum = BIG_NUMBER
currentCluster = 0
for j in range(k):
distance = get_distance(data[i].get_x(), data[i].get_y(), centroids[j].get_x(), centroids[j].get_y())
if (distance < bestMinimum):
bestMinimum = distance
currentCluster = j
data[i].set_cluster(currentCluster)
update_centroids_n_clusters(k, DATA, Threshold)
return
# Performing K_Means
def Kmeans(k, DATA, Threshold):
initialize_centroids(k,DATA)
initialize_datapoints(k, DATA)
update_centroids_n_clusters(k, DATA, Threshold)
for i in range(k):
p = 0
print()
print("Centroid ", i, " is at")
print("(",centroids[i].get_x(), ",", centroids[i].get_y(), ")")
print("Cluster ", i, " includes:")
for j in range(len(DATA)):
if (data[j].get_cluster() == i):
#print("(", data[j].get_x(), ", ", data[j].get_y(), ")")
p += 1
print(p,"points")
return
Kmeans(3,DATA,0.1)
How should I modify my class Centroid and class DataPoint in this code? Thanks!!
Note: The code is in Python 3
Use arrays instead of x and y.
You want e.g. your distance function to be
def distance(array1, array2):
return (array1 - array2)**2
(assuming you use numpy)

Resources