Create a bar to decrease or increase a variable pygame - python

As you can see in the picture I inserted (from a game called Victoria 2), I want to create a bar in pygame to modify a variable (the number of pounds below the bar) depending on which part of the bar the gamer clicks (if they click on the left part of the bar, the red circle moves there and decrease the variable and if click on the right part, the circle moves there and increase the variable).
I tried:
if event.type==pygame.MOUSEBUTTONDOWN:
if mouse_rect.colliderect(bar_rect) and event.button==1:
circle_rect.x=mouse_rect.x
if circle_rect.x+mouse_rect.x:
budget=budget+1
if circle_rect.x-mouse_rect.x:
budget=budget-1
The circle moves, but the variable budget increases always, no matter if clicked on left or right, and it only increases one by one (because I don't know any other way to do it and what I want is a bigger or smaller increase depending on how far you click from the circle).
Any help?
Thanks!!!
Victoria 2 is a strategy game which allows you to balance the budget of your Goverment through a bar. Depending on where you clicked of the bar (left or right), the budget increases or decreases.

You need to look at how far along the slider you've clicked. Here's some untested code to show what I mean:
if event.type==pygame.MOUSEBUTTONDOWN:
if mouse_rect.colliderect(bar_rect) and event.button==1:
# move circle to clicked point
circle_rect.centerx=mouse_rect.x
# calculate distance from the slider origin
delta = circle_rect.centerx - bar_rect.left
# determine the percentage of the total budget
budget_pct = delta // (circle_rect.width)
# recalculate budget
budget = budget_pct * budget_max

Related

Weird jumping of polygon object

I have the following code for jumping of polygon:
def animate_up(canvas,id_rect,t,height_canvas,old_h_t=0):
if t<V_0/G: #jumping,max height
#t+=0.001
t+=(V_0/G)/20 #18=60*t-10*(0.5)*t^2 --> 36-120t+10t^2=0
t=float(Decimal(str(t)).quantize(Decimal('.01'), rounding=ROUND_UP))
new_h_t=V_0*t-G*(t**2)/2
diff_h_t=new_h_t-old_h_t #diff heights
canvas.move(id_rect,0,-diff_h_t)
#spin rect
if MODE=='SPIN': #spinning in the air
#if t>1.5:
#exit()
#else:
spin(canvas,id_rect)
root.after(15,lambda:animate_up(canvas,id_rect,t,height_canvas,new_h_t))
else: #calc time to fall down and activate fall down
t_to_fall=math.sqrt(old_h_t/(0.5*G))
animate_down(canvas,id_rect,0,height_canvas,t_to_fall)
def animate_down(canvas,id_rect,t,height_canvas,t_to_fall,old_h_t=0):
if t<t_to_fall: #falling
t+=(V_0/G)/20
t=float(Decimal(str(t)).quantize(Decimal('.01'), rounding=ROUND_UP))
new_h_t=G*(t**2)/2
h_t=new_h_t-old_h_t #diff heights
canvas.move(id_rect,0,h_t)
if MODE=='SPIN': #spinning in the air
spin(canvas,id_rect)
root.after(15,lambda:animate_down(canvas,id_rect,t,height_canvas,t_to_fall,new_h_t))
else:
#fix small spin(?) error after placing on platform
canvas.move(id_rect,0,467.5-canvas.coords(id_rect)[1])
let's say my parameters are:
G=15.0
V_0=35
MODE=='NORMAL' #the spin part is not relevant to the question in the momemt
With these parameters the jumping seems normal,but if I want a higher jumping,let's say V_0=80,then the polygon starting to lose some of it's upper part of the shape (pixels) when it is near the highest point of the jumping.
Same thing happening when the object fall and is near the bottom of the platform,it starting to lose it's lower parts of the shape (pixels).
explain of arguments:
canvas- Canvas object
id_rect- id (int) of the polygon object that is drawn in my canvas
t=time #start jumping/falling t=0
height_canvas= height of my canvas #relevant to other part of the code that not shown here
old_h_t=height after using the jumping/falling formula
pictures to illustrate:
Upper part of the pentagon is missing near highest point of jumping
Lower part of the pentagon is missing near platform after falling(maybe it's hard to see but the change is from bottom edge):
How to fix this problem?
In addition,the jumping is very elastic,it seems that the object is stretching while jumping/falling,is there a way to change that?(maybe that's because of the illusion of missing parts?)

Tkinter map mouse coordinates to figure size

I'm using Tkinter where an ASCII figure is printed on a label. I want it to change depending on where I click with the mouse, but I do not know how to tackle that problem. How would I map the mouse's coordinates so that it is restricted by a range, say [-n, n]? I am printing my mouse's (event.x, event.y) and I need those values restricted by the earlier stated interval. So instead of the values I get by moving my mouse ranging from 300 to 400, how can I map them to range from -15 to 15, for example?
Thank you in advance!
Edit: Here is what the program looks like - the center is the (0, 0) coordinate and I only want to be able to click on the sphere to rotate it. Therefore, I want to have my interval range from [-r, r] when clicking width the mouse.
Depends on your intervals. lets assume you have [-15,15], and mouse movement from 0-300, you map -15,15 onto [-150,150], which means that every spatial entity you move your mouse (1/150) is 15/150 = 0.1 step in the scale of your choice [-n,n], which you multiply with your mouse coordinate to get the corresponding value within your range [-n, n]. therefore its n*(n/m) with n being target interval and m being the coordinate interval. Why negative values? You must determine your [0,0] for your coordinate system, and this also depends on whether you want to increase size only or also shrink the figure. Maybe give some additional information or a code snippet!

How can I make a circular visual object with its area evenly divided among three or more colors?

Background:
A little new to python and psychopy. I’m trying to build a function that would sort of functionally replicate a roulette wheel and I’ve essentially done so. This function will revolve a small circle around a large circle and pressing 'space' while it is revolving will initiate an exponential deceleration in the revolution velocity of the smaller circle. In order to model the roulette wheel (the larger circle), I used the visual.RadialStim component from PsychoPy. It really was designed, as I understand it, to conveniently build the spinning checkerboard visuals often used as controls in fMRI experiments. However, that means that it evenly divides the wheel into alternating sections of color which is really helpful for my purposes. Here's a visualization of the task in action:
Problem:
I'd like to be able to place more than two colors on the wheel, but I'm not sure that it's possible with visual.RadialStim. Looking through the documentation, I can't see anything that helps, though, I did come across this old thread where Jon seems to suggest that it is possible, but I frankly can't make heads or tails of it and I think the OP left it unresolved as well. Does anyone know if my suspicions about RadialStim are correct (i.e., can't use more than two colors)? Alternatively, does anyone have another recommended solution to replacing it so that I could maybe get 3 or 4 colors modeled on this larger circle?
Code:
PLEASE NOTE - for those unfamiliar with PsychoPy, it is a collection functions specially built for creating reseach studies and requires any code that uses it to be run in a PsychoPy terminal (rather than just any old Python terminal). PsychoPy can run any Python package, but a Python terminal cannot run PsychoPy code, so if you tried to run this on your own without PsychoPy, it likely will not work.
# Psychopy modules
from psychopy import core, event, visual, gui
# Needed to calculate the trajectory of the revolving ball
import math
# Needed to calculcate the deceleration of the revolving ball
import random
# Specifying which monitor to use
monitor=0
# The speed with which the ball revolves around the wheel
speed = 0.125
# The radius of the wheel around which the ball is revolving
wheel_radius=0.45
# How many frames per second the animation should use
fps = 30
# Specifying Window & Screen Information -----
win = visual.Window(size=(1024, 768),
fullscr=True,
screen= monitor,
winType='pyglet',
allowGUI=True,
allowStencil=False,
monitor='testMonitor',
color=[0,0,0],
colorSpace='rgb',
blendMode='avg',
useFBO=True,
units='height')
# Noting the starting position of the revolving ball
position = 0.0
# Noting whether the ball is decelerating
decelerate = False
# Creating the ball
ball = visual.Circle(win, edges=100,radius=0.02, fillColor='white', lineColor=None, pos=[position,position])
# Creating the wheel
wheel = visual.RadialStim(win, pos=(0,0), size=((wheel_radius * 2), (wheel_radius * 2)),
color =('red', 'blue', 'white'), angularRes=300,
angularCycles=6, radialCycles = 0, opacity= 0.8, autoLog=False)
# While speed is greater than 0:
while speed > 0:
# Change the position of the ball according to the current value of position
ball.pos = [((math.sin(position)/10) * (wheel_radius * 10)),
((math.cos(position)/10) * (wheel_radius * 10))]
# Produce the visualization of the wheel
wheel.draw()
# Produce the visualization of the ball
ball.draw()
# If the participant hasn't asked to stop the spinner yet
if decelerate == False:
# Continue spinning the ball around the wheel according to the specified speed
position += speed
# If the participant has asked to stop the spinner
if decelerate == True:
# Randomly select a value between 0.005 and 0.035
rand_dec = random.uniform(0.005,0.035)
# Reduce speed to be a percentage (99.5% - 96.5%) of its last value
# Randomizing the the value of the deceleration will hopefully prevent
# participants from being able to predict where the ball will stop. Also
# making speed a fraction or what it once was, rather than using a linear value
# will better model friction and exponential decay in the real world
speed *= 1 - rand_dec
# Continue spinning the ball around the wheel according to the new speed
position += speed
# If speed drops below 0.001
if speed < 0.001:
# Round speed down to 0
speed = 0
# If escape is pressed, end the task
if event.getKeys('escape'):
break
# If space is pressed, begin slowing the ball
if event.getKeys('space'):
decelerate = True
# Refresh the screen according to the core.wait rate allowing for objects and visualizations
# to change position
win.flip()
# How long psychopy should wait before updating the screen
core.wait(1/fps)
# close the window
win.close()
Try as I could, I could not get the texture approach to work, but I settled on a far less eloquent solution. By reducing the opacity of the RadialStim and overlaying another RadialStim of a complementary color at half opacity, and situated at a 30-degree angle, I was able to more or less create the appearance of four colors. Not thrilled, but it'll do for now. Looking forward to someone else showing me up.
# Psychopy modules
from psychopy import core, event, visual, gui
# Needed to calculate the trajectory of the revolving ball
import math
# Needed to calculcate the deceleration of the revolving ball
import random
# Needed to create colors for the roulette wheel
import numpy as np
# Specifying which monitor to use
monitor=0
# The speed with which the ball revolves around the wheel
speed = 0.125
# The radius of the wheel around which the ball is revolving
wheel_radius=0.45
# How many frames per second the animation should use
fps = 30
# Specifying Window & Screen Information -----
win = visual.Window(size=(1024, 768),
fullscr=True,
screen= monitor,
winType='pyglet',
allowGUI=True,
allowStencil=False,
monitor='testMonitor',
color=[0,0,0],
colorSpace='rgb',
blendMode='avg',
useFBO=True,
units='height')
# Noting the starting position of the revolving ball
position = 0.0
# Noting whether the ball is decelerating
decelerate = False
# Creating the ball
ball = visual.Circle(win, edges=100,radius=0.02, fillColor='white', lineColor=None, pos=[position,position])
# Creating the wheel
wheel_base = visual.RadialStim(win, pos=(0,0), size=((wheel_radius * 2), (wheel_radius * 2)),
color ='yellow', angularRes=300,
angularCycles=3, radialCycles = 0, opacity= 0.9, autoLog=False)
wheel_layer = visual.RadialStim(win, pos=(0,0), size=((wheel_radius * 2), (wheel_radius * 2)),
color ='red', angularRes=300, ori=30,
angularCycles=3, radialCycles = 0, opacity= 0.5, autoLog=False)
# While speed is greater than 0:
while speed > 0:
# Change the position of the ball according to the current value of position
ball.pos = [((math.sin(position)/10) * (wheel_radius * 10)),
((math.cos(position)/10) * (wheel_radius * 10))]
# Produce the visualization of the wheel
wheel_base.draw()
wheel_layer.draw()
# Produce the visualization of the ball
ball.draw()
# If the participant hasn't asked to stop the spinner yet
if decelerate == False:
# Continue spinning the ball around the wheel according to the specified speed
position += speed
# If the participant has asked to stop the spinner
if decelerate == True:
# Reduce speed to be a percentage (99.5% - 96.5%) of its last value
# Randomizing the the value of the deceleration will hopefully prevent
# participants from being able to predict where the ball will stop. Also
# making speed a fraction or what it once was, rather than using a linear value
# will better model friction and exponential decay in the real world
speed *= 1 - rand_dec
# Continue spinning the ball around the wheel according to the new speed
position += speed
# If speed drops below 0.001
if speed < 0.001:
# Round speed down to 0
speed = 0
# If escape is pressed, end the task
if event.getKeys('escape'):
break
# If space is pressed, begin slowing the ball
if event.getKeys('space'):
decelerate = True
# Randomly select a value between 0.005 and 0.035
rand_dec = random.uniform(0.005,0.035)
# Refresh the screen according to the core.wait rate allowing for objects and visualizations
# to change position
win.flip()
# How long psychopy should wait before updating the screen
core.wait(1/fps)
# close the window
win.close()

Need help designing fitness evaluation for a NEAT algorithm-based neural network

I am working on a neural network based on the NEAT algorithm that learns to play an Atari Breakout clone in Python 2.7, and I have all of the pieces working, but I think the evolution could be greatly improved with a better algorithm for calculating species fitness.
The inputs to the neural network are:
X coordinate of the center of the paddle
X coordinate of the center of the ball
Y coordinate of the center of the ball
ball's dx (velocity in X)
ball's dy (velocity in Y)
The outputs are:
Move paddle left
Move paddle right
Do not move paddle
The parameters I have available to the species fitness calculation are:
breakout_model.score - int: the final score of the game played by the species
breakout_model.num_times_hit_paddle - int: the number of times the paddle hit the ball
breakout_model.hits_per_life - int: the number of times the paddle hit the ball per life, in the form of a list; e.g. first element is the value for the first life, 2nd element is the value for the 2nd life, and so on up to 4
breakout_model.avg_paddle_offset_from_ball - decimal: the average linear distance in the X direction between the ball and the center of the paddle
breakout_model.avg_paddle_offset_from_center - decimal: the average linear distance in the X direction between the center of the frame and the center of the paddle
breakout_model.time - int: the total duration of the game, measured in frames
breakout_model.stale - boolean: whether or not the game was artificially terminated due to staleness (e.g. ball gets stuck bouncing directly vertical and paddle not moving)
If you think I need more data about the final state of the game than just these, I can likely implement a way to get it very easily.
Here is my current fitness calculation, which I don't think is very good:
def calculate_fitness(self):
self.fitness = self.breakout_model.score
if self.breakout_model.num_times_hit_paddle != 0:
self.fitness += self.breakout_model.num_times_hit_paddle / 10
else:
self.fitness -= 0.5
if self.breakout_model.avg_paddle_offset_from_ball != 0:
self.fitness -= (1 / self.breakout_model.avg_paddle_offset_from_ball) * 100
for hits in self.breakout_model.hits_per_life:
if hits == 0:
self.fitness -= 0.2
if self.breakout_model.stale:
self.fitness = 0 - self.fitness
return self.fitness
Here is what I think the fitness calculation should do, semantically:
The score, obviously, should have the most significant impact on the overall fitness. Maybe a score of 0 should slightly negatively affect the fitness?
The number of times that the paddle hit the ball per life should have some effect, but not as significant of a contribution/weight. e.g. if that number is 0, it didn't even really try to hit the ball at all during that life, so it should have a negative effect
The number of times that the paddle hit the ball total should also have some effect, and its contribution should be based on the score. e.g. if it didn't hit the ball many times and also didn't score many points, that should have a significant negative effect; if it didn't hit the ball many times but scored a high number of points, that should have a significant positive effect. Overall, (I think) the closer to equal this value is to the game score, the less contribution/weight this value should have on fitness
The average distance in the X direction between the center of the frame and the center of the paddle should basically encourage a central "resting" position for paddle
If the game was ended artificially due to staleness, either this should have a significant negative effect, or it should automatically force the fitness to be 0.0; I'm not sure which case would be better
I'm not sure how to operate on all these values to make them affect the overall fitness appropriately.
Thanks in advance for any help you can provide.
I would minimize the conditional logic in your fitness function, using it only in those cases where you want to force the fitness score to 0 or a major penalty. I would just decide how much weight each component of the score should have and multiply. Negative components just add complexity to understanding the fitness function, with no real benefit; the model learns from the relative difference in scores. So my version of the function would look something like this:
def fitness(...):
if total_hits == 0:
return 0
return (game_score/max_score) * .7 \
+ game_score/total_hits * .2 \
+ game_score_per_life/hits_per_life * .1
(Aside: I didn't include "distance from center of frame" because I think that's cheating; if staying near the center is a good thing to do to maximize play efficiency, then the agent should learn that on it's own. If you sneak all the intelligence into the fitness function, then your agent isn't intelligent at all.)

How do I create collision detections for my bouncing balls?

I have coded an animation (in python) for three beach balls to bounce around a screen. I now wish to have them all collide and be able to bounce off each other. I would really appreciate any help that can be offered.
import pygame
import random
import sys
class Ball:
def __init__(self,X,Y):
self.velocity = [1,1]
self.ball_image = pygame.image.load ('Beachball.jpg'). convert()
self.ball_boundary = self.ball_image.get_rect (center=(X,Y))
self.sound = pygame.mixer.Sound ('Thump.wav')
self.rect = self.ball_image.get_rect (center=(X,Y))
if __name__ =='__main__':
width = 800
height = 600
background_colour = 0,0,0
pygame.init()
window = pygame.display.set_mode((width, height))
pygame.display.set_caption("Bouncing Ball animation")
num_balls = 3
ball_list = []
for number in range(num_balls):
ball_list.append( Ball(random.randint(10, (width - 10)),random.randint(10, (height - 10))) )
while True:
for event in pygame.event.get():
print event
if event.type == pygame.QUIT:
sys.exit(0)
window.fill (background_colour)
for ball in ball_list:
if ball.ball_boundary.left < 0 or ball.ball_boundary.right > width:
ball.sound.play()
ball.velocity[0] = -1 * ball.velocity[0]
if ball.ball_boundary.top < 0 or ball.ball_boundary.bottom > height:
ball.sound.play()
ball.velocity[1] = -1 * ball.velocity[1]
ball.ball_boundary = ball.ball_boundary.move (ball.velocity)
window.blit (ball.ball_image, ball.ball_boundary)
pygame.display.flip()
Collision detection for arbitrary shapes is usually quite tricky since you have to figure out if any pixel collides.
This is actually easier with circles. If you have two circles of radius r1 and r2, a collision has occurred if the distance between the centers is less than r1+r2.
The distance between the two centers (x1,y1) and (x2,y2) can be calculated and compared as:
d = sqrt((y2-y1) * (y2-y1) + (x2-x1) * (x2-x1));
if (d < r1 + r2) { ... bang ... }
Or, as jfclavette points out, square roots are expensive so it may be better to calculate using just simple operations:
dsqrd = (y2-y1) * (y2-y1) + (x2-x1) * (x2-x1);
if (dsqrd < (r1+r2)*(r1+r2)) { ... bang ... }
The tricky bit comes in calculating the new movement vectors (the rate at which (x,y) changes over time for a given object) since you need to take into account the current movement vectors and the point of contact.
I think as a first cut, you should just reverse the movement vectors to test if the collision detection works first.
Then ask another question - it's better to keep individual questions specific so answers can be targeted.
Detecting a collision is only the first step. Let's break that down.
The fastest thing to do is calculate their square bounding boxes and see if those collide. Two of the sides need to cross (top of 1 and bottom or 2, and left of 1 and right of 2, or vice versa) in order for the bounding boxes to overlap. No overlap, no collision.
Now, when they do overlap, you need to calculate the distance between them. If this distance is more than the sums of the radii of the balls, then no collision.
Okay! We have two balls colliding. Now what? Well, they have to bounce off each other. Which way they bounce depends on a few factors.
The first is their elasticity. Two rubber balls bouncing off each other rebound differently than two glass balls.
The second is their initial velocity. Inertia states that they'll want to keep going in mostly the same direction they started in.
The third is the mass of the balls. A ball with smaller mass will rebound off a much larger mass with a higher velocity.
Let's deal with the second and third factors first, since they are intertwined.
Two balls will rarely hit exactly dead on. Glancing blows are far more likely. In any case, the impact will happen along the normal of the tangent where the balls collide. You need to calculate the vector component of both along this normal given their initial velocities. This will result in a pair of normal velocities that both balls will bring to the collision. Add up the sum and store it somewhere handy.
Now we have to figure out what each ball will take away from it. The resulting normal velocity of each ball is inversely proportional to the given ball's mass. That is to say, take the reciprocal of each ball's mass, add both masses together, and then parcel out the resultant normal velocity away from the collision based on the ratio of the ball's mass to the sum of the reciprocal of both ball's masses. Then add the tangential velocity to this, and you get the resultant velocity of the ball.
Elasticity is mostly the same, except it requires some basic calculus due to the fact that the balls are still moving even as they compress. I'll leave it to you to find the relevant math.
Detecting collisions was covered well by Pax's answer. With respect to having objects bounce off one another, I suggest checking out the following links concerning elastic collisions, inelastic collisions, and coefficients of restitution.
EDIT: I just noticed that this was covered in another SO question, albeit not specifically for Python. You should also check there for some good links.
I think there is somehthing simpler that you guys are missing espeically considering he's using pygame.
Calling the get_rect function can set probably boundraies for the images and Rect that is created, is used for calculating the position of the image and if there are more than one object in the animation, it can be used for detecting collisions.
colliderect & rect can be used, problem is i have no idea how you would implement it especially for an unkown number of balls.
Keeping in mind it's python.
Back in the good old times when CPU cycles were a premium coders used a simple trick to detect collision: they used such colours that they could tell from the pixel colour if it was background or an object. This was done on at least some C64 games.
Don't know if you are willing to go this route, though..
First you need to check collision with rect.colliderect(other_rect)
after that if they are colliding, you can check pixel perfect collision. So you don't mess with object's radius or shape.
For pixel perfect collision checking, I use Masks:
Make both mask objects with mask.from_surface, then put them to Mask.overlap function.
I made a python collision detection if statement, here it is:
if beach ball 1 x < beach ball 2 x + beach ball 1 width and beach ball 1 x + beach ball 2 width > beach ball 2 x and beach ball 1 y < beach ball 2 y + beach ball 1 height and beach ball 2 height + beach ball 1 y > beach ball 2 y:
#put needed code here
In your case, with 3 bouncing balls, you will have to make 2 if statements of this format for each ball to make sure that the collision detection is flawless. I hope this helps.

Categories

Resources