Pygame for loop iterates once, and then does not iterate again - python-3.x

Essentially, what I'm doing is taking the enemies list (on line 1), which is holding a list of coordinates and iterate through each pair in the enemies list at the bottom.
I want to go through each enemy in the list, get the y coordinate, add 10 and then go to the next enemy and add 10, so on and so forth. For some reason, it adds 10 ONCE and then stops, and the enemies do not fall down the screen. I don't know why this is happening. Why is it not running through the for loop anymore? Thank you so much for any help.
NOTE: I removed some code at the top for the sake of being less confusing. The update() function is just the pygame flip function.
enemies = [[100,0], [150,0]]
while True:
for enemy in enemies:
x = enemy[0]
y = enemy[1]
y += 10
pygame.draw.rect(screen, (255,0,0), (x, y,10,10))
# uses flip to update the screen
update()
# FPS
clock.tick(20)

You're trying to modify a local variable, not the value in the list. You need to write:
enemy[1] += 10
Since integers are immutable (they cannot be changed), the line y = enemy[1] can be thought of as "copy the value from enemy[1] into y".

Related

Any way to refactor this in a neater way? Specifically lines 7-11 in relation to line 39? Using Pygame

I am teaching this to 5th graders so it needs to be as simple as possible. However, as you see inline 39 I am using stored variables for the function parameters. However is there an easier way? Let's say I wanted to put in a 2nd character I would have to put that long list of variables again?
Should I create a character class? If so, how would I plug that into a parameter?
import pygame
pygame.init()
win = pygame.display.set_mode((1000, 1000))
#Character Details
x = 100
y = 5
width = 10
height = 10
vel = 20
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
#Movement Details
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= vel #
if keys[pygame.K_RIGHT]:
x += vel
if keys[pygame.K_UP]:
y -= vel
if keys[pygame.K_DOWN]:
y += vel
#Without this character movement is additive.
win.fill((0, 0, 0))
pygame.draw.rect(win, (245, 100, 50), (x, y, width, height))
pygame.display.update()
```
You could make the variables a Rect object.
player1 = pygame.Rect(100,5,10,10) #x,y,w,h
player2 = pygame.Rect(200,20,10,10)
...
pygame.draw.rect(win,(245,100,50),player1)
pygame.draw.rect(win,(245,100,50),player2)
you could also use a list and it would work just as fine, but with the Rect, it calculates extra attributes for you like
player1.center #get center of the player, both x and y
player.right #get right side of player, same as x position + width
you can also move using these
player1.center = (200,200) #moves the center to (200,200)
But you would need to have a velocity variable for each player, which you could have a list
#The Big Kahuna has already suggested that you can replace one of the parameters to the pygame.draw.rect() with a rect representing the player. That is specifically answering your question and you should do that.
Since you are teaching this to others I wanted to suggest some other things as well.
The first is a tweak to what #The Big Kahuna said. He correctly suggested that you should use the the pygame rect class, which you should. Instead of passing the four parameters like that, Rect allows you you create rect's by calling it with the position and size parameters grouped together. I.e you can group the two position parameters and the two size parameters. Like this:
size = (10, 10) # width, height
player1 = pygame.Rect((100, 5), size)
player2 = pygame.Rect((200, 20), size)
You could separate the position out as well if desired.
The other thing that you can do (and the real reason that I am actually commenting here) to make it cleaner is to look at the other parameter to pygame.draw.rect(). The (245,100,50) is the color you are drawing (see the docs here). Instead of just using numbers like that, it can be made more clear by assigning that color to a variable somewhere near the top of your program and using the variable in the draw call. Like this:
orangey = (245, 100, 50)
...
pygame.draw.rect(win, orangey, player1)
In this case I called the color orangey, because when I displayed it it looked kind of orangey to me. but obviously you can name it whatever is appropriately descriptive.
You can use multiple colors to distinguish the player rect's like this:
orange = pygame.Color("orange")
purple = pygame.Color("purple")
...
pygame.draw.rect(win, orange, player1)
pygame.draw.rect(win, purple, player2)
You can see a good way to find defined colors like the ones I showed by looking at the answer to the question here.

Remove elements while in a for loop

I have a simpel Card Game, which I am currently working on for my thesis.
The Rules are simpel. You have a deck of 52 Cards, from 1 to 10 and jack, queen, knight.
You draw a card from your Deck. If its a Number it gets added to your Account. If you draw a jack, queen or knight, your account gets reset to 0. After every draw you can decide if you want to draw again or stop.
For this game, i programmed a code with the help of this site.
It should give the probability, that you draw exactly "target".
So for example, the probability to draw, so that you have 1 Point in your account,
is 4/52, since you have four 1´s. The Programm does give me exactly this value.
But. The probabiltity, that you have exactly 2 points in your account is
4/52 + 4/52*3/51. You can either draw a 2 with prob of 4/52 or a 1 and another 1 with prob 4/52*3/51.
Here the code messes up. It calculates the probability to have exactly 2 points in your account as
4/52 + 4/52*4/51 and i dont get why?
Can anyone help me?
import collections
import numpy as np
def probability(n, s, target):
prev = {0: 1} # previous roll is 0 for first time
for q in range(n):
cur = collections.defaultdict(int) # current probability
for r, times in prev.items():
cards = [card for card in range(1, 11)] * 4
for i in cards[:]:
cards.remove(i)
# if r occurred `times` times in the last iteration then
# r+i have `times` more possibilities for the current iteration.
cur[r + i] += times
prev = cur # use this for the next iteration
return (cur[t]*np.math.factorial(s-n)) / (np.math.factorial(s))
if __name__ == '__main__':
s = 52
for target in range(1, 151):
prob = 0
for n in range(1, 52):
prob += probability(n, s, target)
print(prob)
EDIT: I am fairly sure, that the line
for i in [i for i in cards]:
is the problem. Since cards.remove(i) removes the drawn card, but i doesnt care and can draw it anyway.
EDIT 2: Still searching. I tried the suggestions in this two qestions
How to remove list elements in a for loop in Python?
and
How to remove items from a list while iterating?
Nothing worked so far as it should.
I'm assuming with probability(n, s, target) you want to calculate the probability if you draw exactly n out of s cards that the sum of values is exactly target.
Then you will have a problem with n>=2. If I understand this right, for every iteration in the loop
for q in range(n):
you save in cur[sum] the number of ways to reach sum after drawing one card (p=0), two cards (p=1) and so on. But when you set p=1 you don't "remember" which card you have already drawn as you set
cards = [i for i in range(1, 11)] * 4
afterwards. So if you have drawn a "1" first (four possibilities) you have again still four "1"s you can draw out of your deck, which will give you your 4/52*4/51.
As a side note:
Shouldn't there be some kind of check if i==11 since that should reset your account?
I have solved it. After like a 4 Days.
This is the Code:
import numpy as np
def probability(cards, target, with_replacement = False):
x = 0 if with_replacement else 1
def _a(idx, l, r, t):
if t == sum(l):
r.append(l)
elif t < sum(l):
return
for u in range(idx, len(cards)):
_a(u + x, l + [cards[u]], r, t)
return r
return _a(0, [], [], target)
if __name__ == '__main__':
s = 52 # amount of cards in your deck
cards = [c for c in range(1, 11)] * 4
prob = 0
for target in range(1, 151): # run till 150 points
prob = probability(cards, target, with_replacement = False)
percentage = 0
for i in range(len(prob)):
percentage += np.math.factorial(len(prob[i])) * np.math.factorial(s-len(prob[i]))/(np.math.factorial(s))
print(percentage)
This Code is the Solution to my Question. Therefore this Thread can be closed.
For those who want to know, what it does as a tl;dr version.
You have a List (in this case Cards). The Code gives you every possible Combination of Elements in the List such as the Sum over the elements equals the target Value. Furthermore it also gives the Probability in the above mentioned Cardgame to draw a specific Value. The above mentioned game is basically the pig dice game but with cards.

Get just the x or y pos with pygame.mouse.get_pos

I want to know how to get the x-coordinate position or the y-coordinate position of the mouse individually on pygame.
Like just the x and just the y. I think It would use
pygame.mouse.get_pos
Pygame doesn't have an API that will get you only one coordinate, you always get both. But it returns them in a 2-tuple, so you can index to get just one value if you want to:
x = pygame.mouse.get_pos()[0]
If there's any chance you might need the y coordinate as well, it might make sense to unpack as normal anyway, and just ignore the y value in the part of the code where you don't need it:
x, y = pygame.mouse.get_pos()
# do stuff with x, ignore y
if something_rare_happens():
# do stuff with y too
It might even be clearer to do the unpacking even if you'll never use y, but that's really up to you.

How to pull tuples out of a list and make a turtle connect it as coordinates?

Write a function called connectTheDots that takes in a list of tuples as its input and an optional color input as well. The default color value should be black. Each tuple is a coordinate pair (x, y) for a turtle. The function will have the turtle trace out a picture by starting at the first coordinate and then moving to each coordinate in turn.
Your function should do the following:
a. Create a turtle, setting the turtle’s color and speed appropriately
b. Check if the input list is empty: if it is empty then nothing else should happen!
c. Without leaving a line behind, move the turtle to the first location given in the list. Then start leaving a line again. Note: recall how to pull values out of a list, and also know that the goto method can take a single (x, y) tuple as its input: myTurtle.goto( (25, 25) ) will move myTurtle to x = 25 and y = 25.
d. After the turtle is at the starting coordinate, move it to each coordinate in the list in turn.
This is what I have been able to do so far:
def connectTheDots(list1, color ="black"):
myTurtle = turtle.Turtle()
myTurtle.speed(1)
myTurtle.goto(list1[0])
for x,y in list1[1:]: #I'm unsure if this is correct
myTurtle.goto(x,y)
You have most of what you need but are probably making it more complicated than needed and are missing some small details.
For step "a" you need to explicitly set the color (you passed it in just fine). You are probably better off using a symbolic speed instead of a numeric one.
For step "b", if you have a proper for ... in loop, you don't need to explicitly check if the list is empty as the loop won't run if it is. Your splitting off the first item myTurtle.goto(list1[0]) works against you here as there may not be one, causing an IndexError.
For step "c" you need to add another command. Turtles start life in the center of the screen with their pens down. You need to raise the pen up after creating your turtle. But you don't need to explicitly move to the starting position, let your loop handle that.
The trick we'll use for step "c" and step "d" is to put the pen down after the goto() in the loop. The first time, this actually puts the pen down, after that, it's a harmless no-op:
import turtle
def connectTheDots(coordinates, color="black"):
myTurtle = turtle.Turtle()
myTurtle.speed("slowest")
myTurtle.color(color)
myTurtle.penup()
for coordinate in coordinates:
myTurtle.goto(coordinate)
myTurtle.pendown() # redundant after first iteration
dots = ((34, 56), (100, 240), (230, 105), (34, 56))
connectTheDots(dots, "green")
turtle.done()
If it bothers you that we're putting the pen down unnecessarily in the loop, then we can replace myTurtle.pendown() with:
if not myTurtle.isdown():
myTurtle.pendown()

Python 3: Find Index of Square in grid, based on Mouse Coordinates

This may have been asked before, but for my lack of correct English terms end up leaving me here. (I'm Finnish) This may be asked before, but what else could I have done?
But I have pygame code, which renders partion of bigger 'map'. I want to have behaviour to 'click' a squre and 'select' it.
The broblem is, how do I find the index of image I am currently overlapping with mouse?
Codelike close to what I have now
#...setup code...
map = [[0,0,0,0], [0,1,0,0], [0,0,0,0]]
while:
render()
#render completely fills the screen with images based on map's objects
mousepos=pyagem.mouse.get_pos()
selectedMapSquare=???
You just have to divide the absolute (screen) coordinates with the size of your squares. So, if the size of your squares is e.g. 32, you can use something like
x, y = pygame.mouse.get_pos()
# TODO: use a constant
w_x, w_y = x / 32, y /32
Now w_x is the index of the x axis, and w_y is the index of the y axis:
# TODO: bound/error checking
tile_under_mouse = map[w_y][w_x]

Resources