Why are the balls so unstable? - python

This is a physics simulation constraining balls in a circular area.
I made the original code in Scratch and converted it to Python in Pygame.
When I run the simulation, all the balls were shaking, compared to the original code.
I constrained the velocity to be maximum 20, but it didn't help.
I created substeps for each frame, but that wasn't helping either.
import pygame
import math
import random
screen = pygame.display.set_mode((16*120,9*120))
pygame.display.set_caption('')
clock = pygame.time.Clock()
mx = 16*60
my = 9*60
global x
global y
x = []
y = []
xv = []
yv = []
prevx = []
prevy = []
for i in range(20):
x.append(random.randint(-200,200))
y.append(random.randint(-200,200))
xv.append(0)
yv.append(0)
prevx.append(0)
prevy.append(0)
r = 25
size = 300
sub = 20
maxvel = 20
global dist
global dx
global dy
global d
#points at something
def pointat(px,py):
global d
dx = px-x[i]
dy = py-y[i]
if dy == 0:
if dx < 0:
d = -90
else:
d = 90
else:
if dy < 0:
d = 180+math.atan(dx/dy)
else:
d = math.atan(dx/dy)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
screen.fill((10,10,10))
pygame.draw.circle(screen,(100,100,100),(mx,my),size)
for i in range(len(x)):
pygame.draw.circle(screen,(255,255,255),(mx+x[i],my+y[i]*-1),r)
for i in range(len(x)):
prevx[i] = x[i]
prevy[i] = y[i]
for j in range(sub):
x[i] += xv[i]/sub
y[i] += yv[i]/sub
y[i] -= 1/sub
a = 0
for k in range(len(x)-1):
if a == i:
a += 1
dist = math.sqrt(((x[i]-x[a])*(x[i]-x[a]))+((y[i]-y[a])*(y[i]-y[a])))
if dist < r*2:
pointat(x[a],y[a])
x[i] += (math.sin(d)*(dist-(r*2)))
y[i] += (math.cos(d)*(dist-(r*2)))
x[a] -= (math.sin(d)*(dist-(r*2)))
y[a] -= (math.cos(d)*(dist-(r*2)))
dist = math.sqrt((x[i]*x[i])+(y[i]*y[i]))
if dist > size-r:
pointat(0,0)
x[i] += (math.sin(d)*(dist-(size-r)))
y[i] += (math.cos(d)*(dist-(size-r)))
a += 1
xv[i] = x[i]-prevx[i]
yv[i] = y[i]-prevy[i]
if xv[i] > maxvel:
xv[i] = maxvel
if xv[i] < -maxvel:
xv[i] = -maxvel
if yv[i] > maxvel:
yv[i] = maxvel
if yv[i] < -maxvel:
yv[i] = -maxvel
pygame.display.update()
clock.tick(60)

The angle of the trigonometric functions in the math module is measured in Radian, but not in Degrees. d should not be a global variable, you can just return the angle from pointat:
def pointat(px,py):
dx = px-x[i]
dy = py-y[i]
if dy == 0:
if dx < 0:
d = -math.pi/2
else:
d = math.pi/2
else:
if dy < 0:
d = math.pi+math.atan(dx/dy)
else:
d = math.atan(dx/dy)
return d
while True:
# [...]
for i in range(len(x)):
# [...]
for j in range(sub):
# [...]
for k in range(len(x)-1):
if a == i:
a += 1
dist = math.sqrt(((x[i]-x[a])*(x[i]-x[a]))+((y[i]-y[a])*(y[i]-y[a])))
if dist < r*2:
d = pointat(x[a],y[a])
x[i] += (math.sin(d)*(dist-(r*2)))
y[i] += (math.cos(d)*(dist-(r*2)))
x[a] -= (math.sin(d)*(dist-(r*2)))
y[a] -= (math.cos(d)*(dist-(r*2)))
dist = math.sqrt((x[i]*x[i])+(y[i]*y[i]))
if dist > size-r:
d = pointat(0,0)
x[i] += (math.sin(d)*(dist-(size-r)))
y[i] += (math.cos(d)*(dist-(size-r)))
a += 1
Overall, the code in the loops can be simplified a lot. I suggest to refactor your code as follows:
while True:
# [...]
for i in range(len(x)):
prevx[i] = x[i]
prevy[i] = y[i]
for j in range(sub):
x[i] += xv[i]/sub
y[i] += yv[i]/sub
y[i] -= 1/sub
for a in range(len(x)):
if a == i:
continue
dx = x[i]-x[a]
dy = y[i]-y[a]
dist = math.sqrt(dx*dx + dy*dy)
if dist < r*2 and dist != 0:
x[i] -= dx/dist * (dist-r*2)
y[i] -= dy/dist * (dist-r*2)
x[a] += dx/dist * (dist-r*2)
y[a] += dy/dist * (dist-r*2)
dist = math.sqrt(x[i]*x[i] + y[i]*y[i])
if dist > size-r:
x[i] -= x[i]/dist * (dist-(size-r))
y[i] -= y[i]/dist * (dist-(size-r))
xv[i] = max(-maxvel, min(maxvel, x[i]-prevx[i]))
yv[i] = max(-maxvel, min(maxvel, y[i]-prevy[i]))

Related

Why function colis(collision) doesn't work?

Function doesn't work
I tried to make a game, and at the some point all start going wrong.(my english isn't best, i know, sorry)
I use library pygame for my project. I made a function which detect the contact of objects, but when i start the game function doesn't work. I tried to start it without function and then all start working. I'm wondering why the function doesn't work. I hope that you can tell me something about it.
In the file sets i wrote some variables like speed.
from sets import *
import pygame as pg
import time
pg.init()
sc = pg.display.set_mode((1280,720))
test = pg.Surface((100,100))
test.fill((122,122,122))
block = pg.Surface((50,50))
block.fill((0,0,0))
def colis(x,y,player_width,player_height,width,height,xx,yy):
if x + player_width > xx:
if x < xx + width - 20:
if y + player_height > yy + 10:
if y < yy + height - 10:
x = xx - player_width
if x < xx + width:
if x > xx:
if y + player_height > yy + 10:
if y < yy + height - 10:
x = xx + width
if y < yy + height:
if y > yy + 20:
if x + player_width > xx:
if x < xx + width:
y = yy + height
if y + player_height > yy:
if y < yy + height - 20:
if x + player_width > xx:
if x < xx + width:
y = yy - player_height
while True:
clock.tick(60)
for event in pg.event.get():
if event.type == pg.QUIT:
run = False
pg.QUIT
elif event.type == pg.KEYDOWN:
if event.key == pg.K_w:
w = 1
if event.key == pg.K_a:
a = 1
if event.key == pg.K_s:
s = 1
if event.key == pg.K_d:
d = 1
if w > 0:
w += speed
if w > 10:
w = 0
if a > 0:
a += speed
if a > 10:
a = 0
if s > 0:
s += speed
if s > 10:
s = 0
if d > 0:
d += speed
if d > 10:
d = 0
x += d - a
y += s - w
colis(x,y,50,50,100,100,200,200)
pg.display.update()
sc.fill((255,255,255))
sc.blit(block,(x,y))
sc.blit(test,(xx,yy))
Your function colis doesn't do anything. There is no return value. All it does is change the values of a few local variables, variables which go out of scope when the function finishes. Thus the function call colis(x,y,50,50,100,100,200,200) has no effect. The values of x and y are not changed by the function call.
Instead, return x and y from the function:
def colis(x,y,player_width,player_height,width,height,xx,yy):
if x + player_width > xx:
if x < xx + width - 20:
if y + player_height > yy + 10:
if y < yy + height - 10:
x = xx - player_width
if x < xx + width:
if x > xx:
if y + player_height > yy + 10:
if y < yy + height - 10:
x = xx + width
if y < yy + height:
if y > yy + 20:
if x + player_width > xx:
if x < xx + width:
y = yy + height
if y + player_height > yy:
if y < yy + height - 20:
if x + player_width > xx:
if x < xx + width:
y = yy - player_height
return x,y
(I eliminated most of the blank lines since all those blank lines makes it harder to read).
After that change, replace colis(x,y,50,50,100,100,200,200) by
x,y = colis(x,y,50,50,100,100,200,200)
Disclaimer: I don't have pygame installed so am unable to see if there are any other problems with your code, but making the above changes will at least give you code which makes more sense.

Drawing a tan-wave in paint using pyautogui and math

I am having a difficulty in implementing drawing a tan function in python since it doesn't look smooth, and when having reached 90/180, it doesn't look nice, so im looking for some improvements on this part.
def tan_wave():
pyautogui.click()
theta = 0
step = 10
length = 10
curr_x, curr_y = pyautogui.position()
prev = (curr_x, curr_y)
decrease_flag = False
while theta <= 180:
if (theta % 90) == 0:
theta -= 0.99999
decrease_flag = True
x = curr_x + theta
y = curr_y - math.tan(math.radians(theta))*length
print(x, y, theta)
pyautogui.click(*prev)
pyautogui.dragTo(x, y)
pyautogui.click(x, y)
prev = (x, y)
if decrease_flag:
theta += 0.99999
decrease_flag = False
theta += step
print('Done')
tan_wave()

How to move pygame rects in a list?

So I am trying to create a series of "boids" at random locations, which fly at random speeds, but I am having some trouble moving the rects which are in a list, although I can draw them. I am using a provided vector module, the entire code and the module can be found here. The png I am using for the sprites.
Update: I got a rect moving, by using the instance position vector instead of the class vector. But now only one boid is drawn. I suspect that more boids are drawn at the same exact position.
class Boid():
def __init__(self, screen):
self.bird = pygame.image.load("birdie.png")
self._pos = Vector2D(random.randint(0, screen.get_width()),
random.randint(0, screen.get_height()))
self._vel = Vector2D((random.randint(1, 10) / 5.0),
(random.randint(1, 10) / 5.0))
self.speed = random.randint(1, 5)
self.bird_rect = self.bird.get_rect(center=(self._pos.x, self._pos.y))
self._boids = []
def add_boid(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self._boids.append(Boid(screen))
def move_boids(self):
s = Screen()
#self.bird_rect.move_ip(self._vel.x, self._vel.y)
self._pos += (self._vel * self.speed)
#bounds check
if self._pos.x + self.bird_rect.width >= s.width:
self._pos.x = s.width - self.bird_rect.width
self._vel.x *= -1
elif self._pos.x <= 0:
self._pos.x = 0
self._vel.x *= -1
if self._pos.y - self.bird_rect.height <= 0:
self._pos.y = self.bird_rect.height
self._vel.y *= -1
elif self._pos.y >= s.height:
self._pos.y = s.height - self.bird_rect.height
self._vel.y *= -1
def draw_boids(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
print(len(self._boids))
for boid in self._boids:
self.boidRect = pygame.Rect(self.bird_rect)
#edit: changed boid._pos.x and y to self._pos.x and y
self.boidRect.x = self._pos.x
self.boidRect.y = self._pos.y
screen.blit(self.bird, self.boidRect)
You have to iterate over all boids in the self._boids list and update their _pos and bird_rect attributes to move them.
def move_boids(self):
s = Screen()
for boid in self._boids:
boid._pos += boid._vel * boid.speed
boid.bird_rect.center = boid._pos
# Bounds check.
if boid._pos.x + boid.bird_rect.width >= s.width:
boid._pos.x = s.width - boid.bird_rect.width
boid._vel.x *= -1
elif boid._pos.x <= 0:
boid._pos.x = 0
boid._vel.x *= -1
if boid._pos.y - boid.bird_rect.height <= 0:
boid._pos.y = boid.bird_rect.height
boid._vel.y *= -1
elif boid._pos.y >= s.height:
boid._pos.y = s.height - boid.bird_rect.height
boid._vel.y *= -1
You can also simplify the draw method a bit.
def draw_boids(self):
# Blit all boids at their rects.
for boid in self._boids:
screen.blit(boid.bird, boid.bird_rect)

TypeError: 'NoneType' object is not iterable when implementing Perceptron, see code below

I have a problem with my perceptron codes.I receive this when I execute my code. I checked my two txt files and I am pretty sure the two of them are definitely ok. So can someone help? Thanks a lot
Traceback (most recent call last):
File "perceptron.py", line 160, in <module>
test()
File "perceptron.py", line 133, in test
w,k,i = p.perceptron_train('train.txt')
TypeError: 'NoneType' object is not iterable
Here is my code
import numpy as np
import matplotlib.pyplot as plt
class Data():
def __init__(self,x,y):
self.len = len(x)
self.x = x
self.y = y
class Perceptron():
def __init__(self,N,X):
self.w = np.array([])
self.N = N
self.X =X
def prepare_training(self,file):
file = open(file,'r').readlines()
self.dic = set([])
y = []
vocab = {}
for i in range(len(file)):
words = file[i].strip().split()
y.append(int(words[0])*2-1)
for w in set(words[1:]):
if w in vocab:
vocab[w].add(i)
if i < self.N and len(vocab[w]) >= self.X:
self.dic.add(w)
elif i < self.N:
vocab[w] = set([i])
x = np.zeros((len(file),len(self.dic)))
self.dic = list(self.dic)
for i in range(len(self.dic)):
for j in vocab[self.dic[i]]:
x[j][i] = 1
self.training = Data(x[:self.N],y[:self.N])
self.validation = Data(x[self.N:],y[self.N:])
return x,y
def update_weight(self,x,y):
self.w = self.w + x * y
def perceptron_train(self,data):
x,y = self.prepare_training(data)
self.w = np.zeros(len(self.dic),int)
passes = 0
total_passes = 100
k = 0
while passes < total_passes:
print('passes:',passes)
mistake = 0
for i in range(self.N):
check = y[i] * np.dot(self.w,x[i])
if (check == 0 and (not
np.array_equal(x[i],np.zeros(len(self.dic),int)))) or (check < 0):
self.update_weight(x[i],y[i])
mistake += 1
k += 1
passes += 1
print('mistake:',mistake)
if mistake == 0:
print('converge at pass:',passes)
print('total mistakes:', k)
return self.w, k, passes
def perceptron_error(self,w,data):
error = 0
for i in range(data.len):
if data.y[i] * np.dot(w,data.x[i]) < 0:
error += 1
return error/data.len
def test(self,report):
x = np.zeros(len(self.dic),int)
for i in range(len(self.dic)):
if self.dic[i] in report:
x[i] = 1
if np.dot(self.w,x) > 0:
return 1
else:
return 0
def perceptron_test(self,data):
test = open(data,'r').readlines()
y = []
mistake = 0
for t in test:
y0 = int(t.strip().split()[0])
report = set(t.strip().split()[1:])
r = self.test(report)
y.append(r)
if (y0 != r):
mistake += 1
return y,mistake/len(test)
def predictive_words(self):
w2d = {}
for i in range(len(self.dic)):
try:
w2d[self.w[i]].append(self.dic[i] + " ")
except:
w2d[self.w[i]] = [self.dic[i] + " "]
key = list(w2d.keys())
key.sort()
count = 0
most_positive = ""
most_negative = ""
for i in range(len(key)):
for j in range(len(w2d[key[i]])):
most_negative += w2d[key[i]][j]
count += 1
if count == 5:
break
if count == 5:
break
count = 0
for i in range(len(key)):
for j in range(len(w2d[key[len(key)-i-1]])):
most_positive += w2d[key[len(key)-i-1]][j]
count += 1
if count == 5:
break
if count == 5:
break
return most_positive,most_negative
def test():
p = Perceptron(500,30)
w,k,i = p.perceptron_train('train.txt')
print(p.perceptron_error(w,p.validation))
normal,abnormal = p.predictive_words()
print('Normal:\n',normal)
print('Abnormal:\n',abnormal)
print(p.perceptron_test('test.txt'))
def plot_error():
x = [100,200,400,500]
y = []
for n in x:
p = Perceptron(n,10)
w,k,i = p.perceptron_train('train.txt')
y.append(p.perceptron_error(w,p.validation))
plt.plot(x,y)
plt.show()
def plot_converge():
x = [100,200,400,500]
y = []
for n in x:
p = Perceptron(n,10)
w,k,i = p.perceptron_train('train.txt')
y.append(i)
plt.plot(x,y)
plt.show()
test()
perceptron_train has the implicit return value None if mistakes!=0, so that's what you're seeing here.

Longer execution time

Below code is taking excessively long time when executed for larger inputs such as x = 23!!!! and y = 5!!!!!. The output of the code should be comparison between x and y. small example regarding the inputs is, if input is 3!!, this means (3!)!) = 6! = 720.
def large()
t = int(input())
i = 0
fact_str = ""
fact_stry = ""
res, resy = 1, 1
counter = 0
countery = 0
while(i < t):
x = input()
y = input()
for char in x:
if char == '!' and counter == 0:
fact_str = int(fact_str)
for num in range(fact_str, 0, -1):
res = res * num
counter = counter + 1
elif counter > 0:
for num in range(res-1, 0, -1):
res = res * num
else:
fact_str = str(fact_str)
fact_str = fact_str + char
for chary in y:
if chary == '!' and countery == 0:
fact_stry = int(fact_stry)
for numy in range(fact_stry, 0, -1):
resy = resy * numy
countery = countery + 1
elif countery > 0:
for num in range(resy-1, 0, -1):
resy = resy * numy
else:
print("at esle", resy)
fact_stry = str(fact_stry)
fact_stry = fact_stry + chary
if len(str(x)) == len(str(fact_str)):
fact_str = int(fact_str)
x = fact_str
else:
x = res
if len(str(y)) == len(str(fact_stry)):
fact_stry = int(fact_stry)
y = fact_stry
else:
y = res
x = int(x)
y = int(y)
if x == y:
print("x=y")
elif x > y:
print("x>y")
else:
print("x<y")
i = i + 1
large()
can this code be optimized so that it can execute faster if inputs are large values ? Or python is limited to smaller inputs ?

Categories

Resources