Adding a countdown timer in python - python

I'll admit, i am a newbie with python, but here is my issue.
the version is 2.6.5 (i know i'ts an old version but there's reasons to this) and livewires is used
Bascially this game has a bunch of colored balloons in which you need to click them to make them disappear. Adjacent balloons of the same color disappear along with the clicked balloon. Once the balloons are cleared it moves on to the next level.
I need to create a timer on the top right of my screen. This timer needs to countdown in seconds (from 30 might be a good start.) However no matter what i try, either the timer does not display or the numbers are overlap eachother. I would like to know how to do this, as it has been driving me up the wall as of late.
...Of course it also needs to end the game if it reaches zero and add more time if the level is complete...
But for now i just want to focus on displaying the timer and having it count down to zero on screen.
class Timer(games.Sprite):
""" countdown timer """
def __init__(self):
timer_message = games.Text(
value = 30,
size = 50,
color = red,
x = 600,
y = 30
)
def start(self):
while self.timer_message.value != 0:
time.sleep(1)
self.timer_message.value -= 1
game.screen.add(timer_message)
Alright. I fixed the "compressing balloons table" (accidentally deleted the self_update lol) problem, but now it is saying that "global name timer_message is not defined"... despite the fact that it says timer_message = games.Text
I would paste the whole code, but i can't get the indentation right (this is my first time using this website.)

So, I understand it's been some time and if you don't need an answer anymore that's alright.
For now it's hard to answer your question in general because I don't understand the structure of the rest of your code or how you're displaying graphics. However, I can tell you while you're getting the
global name timer_message is not defined
error. It's because when you define timer_message within the __init__ function you are defining it within the local scope of the function but not for the class. In order to make it accessible to the class you need to assign to self.timer_message.
This is a consequence of how python imitates object oriented programming, but making this change should address your immediate error.

Related

How do I get this input to work inside of an inherited class in Python?

So, to preface this I’m self learning python and I’m trying to build a Tic-Tac-Toe game using the command line as an interface. The issue that I have is that I can’t get the input inside one of the inherited class for the player to work (so the game itself doesn’t work aside from the 3x3 board showing up on the command line)
The section of code that I’m having issues with goes as such:
class HumanPlayer(Player):
def __init__(self, letter):
super().__init__(letter)
def get_move(self, game):
valid_square = False
val = None
while not valid_square:
square = input(self.letter + ' s turn. Input move (0-9):')
# we are going to check that this is a correct value by trying to cast
# it into an integer, and if it's not, then we will say its invalid
# if that spot is not available on the board, then we also say it's invalid
try:
val = int(square)
if val not in game.available_moves:
raise ValueError
valid_square = True # if these are successful, then oh yeah!
except ValueError:
print ('Invalid Square. Try Again. ')
return val
I’ve tried to make sure that my spacing is correct within this class, but now I’m not sure what else to do. Any help, suggestions, or the like would be appreciated since I’m learning to program in general
Thanks!
Although there is nothing wrong with an object-oriented approach, and it can be the (or at least a) right approach for many problems, it looks like your program has "classes because of classes". It's probably easier if you don't bother with the object-orientation too much at this stage and focus on the main gameplay loop.
Try to imagine how the game should progress: you start the game, you make a move, another player makes a move, this continues until the game decides either play has won and then perhaps you can start a new game. And the other player might be an "AI" (tic tac toe doesn't require much intelligence) or another live player.
Your code covers what needs to happen for a single player to enter a valid square and it appears you have another class somewhere that's a Game, an instance of which has an attribute available_moves that contains all the currently valid moves. Or perhaps that Game class is the next thing you plan to write.
The main game loop would be to ask players for a move in alternating fashion, update the game board, decide if someone has won yet, and keep doing that. And you'll need to get the whole thing started, some core routine that sets up the game and the players and gets the ball rolling.
If you have a more specific problem getting all that to work, you should post a question about that - but without a more specific problem, it's hard to provide a better answer.
Where is the code for th

Using recursion in pygame

I'm new to python and programming in general and am working on a final project for a python centric class. One of the requirements that I cant seem to figure out how to make work is to integrate recursion into our code in order to show a working knowledge. I've worked up a simple "bullet hell" style game using pygame.
My goal is that when contact is made between a bullet and an enemy, that a series of bullet sets will be launched from the player position as a sort of short-term modifier.
This code runs in the main loop whenever a bullet hits an enemy:
for i in reversed(range(len(bullets))):
for j in reversed(range(len(enemies))):
if bullets[i].collided(enemies[j].rect):
del enemies[j]
del bullets[I]
s.global_score += 100
more_bullets(10)
#print("Hit!")
#print(s.global_score)
break
The "more_bullets" function is the focus of my recursion, and calls this:
def more_bullets(n):
if(n > 0):
spawnx = sq.rect.x+10 + sq.rect.width/2 - 10
b = Square(s.red, spawnx,sq.rect.y, 10,30)
b.direction = 'N'
b.player_speed = 10
bullets.append(b)
spawnx = sq.rect.x-10 + sq.rect.width/2 - 10
b = Square(s.red, spawnx,sq.rect.y, 10,30)
b.direction = 'N'
b.player_speed = 10
bullets.append(b)
pygame.display.update()
more_bullets(n-1)
print(f"Fired x {n}")
The outcome currently is that my debug does print 10 times making me think that the recursion is functioning correctly, however only one set of bullets is firing when the collision occurs. I'm thinking that all 10 bullets are firing faster than I can register it and just stacking on the screen.
Is there an easy-to-use function that might slow down the firing of the bullets? or have messed up something more fundamentally here?
I'm thinking that all 10 bullets are firing faster than I can register it and just stacking on the screen.
You're correct.
I don't believe there's an "easy way" to do what you're asking the way you think. The recursion is immediate, meaning that the function runs 10 times right away when it's called. For it to send out a burst of staggered bullets, you'd need some kind of timer or queue or something along those lines that runs alongside your main loop, and recursion isn't really a natural fit for that. The same will go for any kind of game logic function that plays out over a period of time.
This isn't what's being asked, but here's an idea of what you could do, even though it's kind of a redundant use of recursion: add a parameter to that function that dictates the angle the bullet is being shot. Then, add a little bit to that parameter on every recursive call. That way an arc of bullets will be shot at the same time.

Using onscreen.click() in turtle but only works at the end of the programme

This is the programme I wrote but I'm not sure what's wrong:-
import turtle
import random
bob = turtle.Turtle()
screen = turtle.Screen()
def coord(x,y):
print(x,y)
turtle.onscreenclick(None)
turtle.onscreenclick(coord)
turtle.listen()
print('hello')
turtle.done()
The programme works fine except that the print('hello') part happens first, followed by the on screen event. How do I make the onscreen event occur first before the rest of my programme?
You can simply make the rest of your code part of the coord function:
def coord():
print(x,y)
turtle.onscreenclick(None)
print("Hello")
# The rest of your program goes here.
However, a few things to note:
This isn't an amazing elegant solution, especially if you intend to set up other events further into your code. It can become quite hard to keep track of.
It's imperative that you remove the event binding (in this case the binding of coord to onscreenclick) as soon as it's been used, otherwise you could end up with multiple instances of the same code running at once if someone double-clicked the screen, for example. In your case you've already done this (with turtle.onscreenclick(None)), but it is something to keep in mind.
If you choose to go this route, don't forget to rename the coord function something more representative of what that section of your code will do.

What range does mainloop() have over code?

I would like to make a python tkinter window with custom-moving widgets on a canvas to simulate motion. For now, I have one canvas, and one not-moving oval widget. I am having problems at the base level; mainloop(). I understand that it runs in wait for the user to do something, but I am having a hard time seeing:
How to control/see exactly what code mainloop() is reiterating (where, and only tkinter?);
How to properly interrupt it and return to it from another function, if it doesn't do it itself;
What code should be reiterated? All tkinter objects, or only updating changing ones? Use some kind of update operation instead? Finally;
What is the functionality difference between tkinter.mainloop() and window.mainloop()? Perhaps the previous questions will answer.
I have minor experience with Swift, and started learning the very similar Python yesterday evening. I've tried probably hundred of mutations to my code, which currently is in the test stage. I have moved everything in and out of the apparent range of the mainloop, and even got several hundred tiny Python windows all over the screen. Everything does one of two things: it does nothing, or gives me an error. Since I don't know what is even running, or if it is running, I can't diagnose anything. My goal is simply to move a circle one hundred pixels repeatedly. I've scanned around for sources, but—it may be me—a clear one is scarce. I have my code here all marked up. This page is closest to what I am looking for: Move a ball inside Tkinter Canvas Widget (simple Arkanoid game). Everything appears to be under mainloop. So, everything is redrawn every pass? Here, unfortunately, is my whole script; I can't only show pieces. It, for some reason, only brings up a small window, not a full-screen one. (Edit: I seem to have lost the screen size code)
import tkinter
import time
# Initial values for circle's corners and start idicator ('b'):
x1 = 10
y1 = 10
x2 = 210
y2 = 210
b = 0
# Window ('window')
window = tkinter.Tk()
# Canvas ('area')
area = tkinter.Canvas(window, width=1368, height=650)
area.place(x=0, y=0)
# Ovals to be placed on 'area'
oval1 = area.create_oval(x1,y1,x2,y2,fill='#42befe')
oval2 = area.create_oval(100,10,300,210,fill='#d00000')
# Turns b to 1 to start shifting when 'butt' is pressed:
def startFunc():
b = 1
print('b = 1')
# My button to activate 'startFunc'
butt = tkinter.Button(window, text='Start movement', command=startFunc)
butt.pack()
# Adjusts the x and y coordinates when they are fed in:
def Shift(A, B, C, D):
print('Shift activated.')
window.after(1000)
print('Edit and return:')
A += 100
B += 100
C += 100
D += 100
return(A, B, C, D)
# Problems start about here: my Mainloop section;
# I have little idea how this is supposed to be.
while True:
if b == 1:
# Takes adjusted tuple
n = Shift(x1, y1, x2, y2)
print('Returned edited tuple.')
# Changes coordinates
x1 = n[0]
y1 = n[1]
x2 = n[2]
y2 = n[3]
print(f'{x1}, {y1}, {x2}, and {y2}')
# Reiterate moving oval
oval1 = area.create_oval(x1,y1,x2,y2,fill='#42befe')
#Does this re-run 'window' relations outside here, or only within the 'while'?
window.mainloop()
It ought to show a 1368 by 650 window, not a tiny one. The button does nothing but print, which means the final 'while' is not running, despite the mainloop. It want it to loop inside the 'while' line, which should adjust coordinates and move my blue circle. The iteration may NOT touch the initial values, or else it would reset them.
In effect, calling mainloop is the same as if you added this to your code instead of calling mainloop():
while the_program_is_running():
event = wait_for_event()
process_the_event(event)
As a rule of thumb, mainloop() should be called exactly once after the UI has initialized and you are ready for the user to start interacting with your program. When it exits, you typically won't have any code after it, and your program will exit.
How to control/see exactly what code mainloop() is reiterating (where, and only tkinter?);
I don't know what you mean by "reiterating". It doesn't run any code except it's own internal code. It simply waits for events, and then dispatches them to handlers.
How to properly interrupt it and return to it from another function, if it doesn't do it itself;
It's exceedingly rare to do this in a running program. Typically, calling mainloop is the last thing your program does before the user starts interacting with it, and as soon as it exits your program quits.
However, to answer the specific answer of how to interrupt it, you can call the quit method of the root window. That will cause the most recent call to mainloop() to return.
What code should be reiterated? All tkinter objects, or only updating changing ones? Use some kind of update operation instead?
That question is hard to answer because it doens't make much sense. When you call mainloop(), it will watch for all events on all tkinter objects.
What is the functionality difference between tkinter.mainloop() and window.mainloop()
They have exactly the same effect and behavior. Tkinter oddly chose to make mainloop available from any widget. The most common way to call it is from either the tkinter module itself, or from the root window.
My goal is simply to move a circle one hundred pixels repeatedly.
The normal way to do that is to create a function that moves it one hundred pixels. Then, that function (-- or a function that calls it -- can put itself on an event queue to be run in the future.
For example, the following code will move a canvas object 100 pixels every second until the program exits:
def move_object():
the_canvas.move(item_id, 100, 0)
the_canvas.after(1000, move_object)
When it is called, it will move the item 100 pixels to the right. Then, it will place a new call to itself on the event queue to be picked up and handled in approximately 1000 milliseconds.
There are many working examples of using after on this site, including the question you linked to in your question.
Everything appears to be under mainloop. So, everything is redrawn every pass?
No, not exactly. The only objects that are redrawn are things that need to be redrawn. Moving objects on a canvas, resizing a window, dragging another window over your window, etc, all place an event on the event queue that tells tkinter "this object needs to be redrawn". The processing of that event happens automatically by mainloop. If nothing is happening in your application, nothing gets redrawn by mainloop.
It ought to show a 1368 by 650 window, not a tiny one
That is because you haven't given the main window a size. You've given the canvas a size, but you're using place which won't cause the containing window to grow or shrink to fit. As a beginner, you should completely avoid place and instead use pack or grid, because pack and grid will both automatically size your window to fit everything inside.
While it's tempting to use place for its perceived simplicity, in reality it usually requires you to do a lot more work than if you used one of the other geometry managers, and it results in a GUI that isn't particularly responsive to change.
while True:
You should almost never do this in tkinter. Tkinter -- and almost all event based programs -- rely on a steady flow of events. When you have an infinite loop, it cannot process those events. You can put an explicit call to update the screen inside your loop, but that is inefficient and should be avoided. If you need to do something periodically, create a function that encapsulates the body of your loop, then use after to get mainloop to run it while it is processing events.
window.after(1000)
You should almost never use after this way without a second argument. This usage is functionally no different than calling time.sleep(1) in that it prevents mainloop from processing events. You should structure your code to allow for a steady stream of events to be processed by mainloop.
while True: ... window.mainloop()
You definitely need to avoid calling mainloop inside a loop. A well behaved tkinter program should call mainloop() exactly once.

Why is turtle lightening pixels?

My program for creating a Mandelbrot set has a bug: whenever the pen changes colors, and every 42nd pixel after that, is lighter. This is, rather coincidentally, a mandelbug (yes, I just learned that term), as it is inconsistent for many pixels near an "edge" (it might actually be blurred between the color it's supposed to be and the color the last, or next, pixel is supposed to be), but it's always the 42nd pixel after that one until the next color change. I am using OSX 10.6.8, PYTHON 2.7. When I wrote this program at school, it worked perfectly (Windows), and then I sent it to myself, and worked on it a little more (mostly just making the sample size and therefore image larger), and ran it, I got this bug. EDIT: My bad, I forgot to mention that this only happens with my Mandelbrot program, the few other turtle programs I have at home are fine.
Parts of screenshots (so that you don't have to wait forever while the program runs to see what I'm talking about):
From my first version from home:
From the current version (sideways):
Heres the code:
import turtle
import math
turtle.speed(0)
def benoit(onelen):
turtle.left(90)
for x in range(-2*onelen, onelen):
turtle.up()
turtle.goto(x, int(-1.5*onelen)-1)
turtle.down()
for y in range(int(-1.5*onelen)-1, int(1.5*onelen)-1):
z = complex(0,0)
c = complex(x*1.0/onelen,y*1.0/onelen)
for k in range(20):
z = z*z+c
if abs(z) > 2:
g = .2 + .8*(20-k)/20
break
if k == 19:
g = 0
turtle.pencolor(0,g,0)
turtle.forward(1)
benoit(250)
x = raw_input("Press Enter to Exityadayadayada")
EDIT: A fix has been suggested by DSM, who likes this bug. However, I have no experience editing Python source code, and all the underscores are making me nervous. Can someone tell me specifically what to edit and/or how?
Wow. I think this is one of my favourite bugs ever, and believe it or not, the fact that the number happens to be 42 is actually relevant! Well, peripherally, anyhow..
In turtle.py:
def _goto(self, end):
"""Move the pen to the point end, thereby drawing a line
if pen is down. All other methodes for turtle movement depend
on this one.
[...]
###### vererbung!!!!!!!!!!!!!!!!!!!!!!
self._position = end
if self._creatingPoly:
self._poly.append(end)
if len(self.currentLine) > 42: # 42! answer to the ultimate question
# of life, the universe and everything
self._newLine()
self._update() #count=True)
So the problem comes about when it decides to break a line, apparently for performance reasons:
def _newLine(self, usePos=True):
"""Closes current line item and starts a new one.
Remark: if current line became too long, animation
performance (via _drawline) slowed down considerably.
"""
I was able to "fix" the bug by bumping up the linenumber limit and/or scattering self._pencolor references in places that didn't have any. But you're not crazy, anyway, and it's not really anything that you're doing. :-)
Can i offer a suggestion?
i tried your code and it was taking forever to run which you are aware of but what you may not be aware of is the tracer function... i simply put at the beginning of your code:
wn=turtle.Screen()
wn.tracer(10000)
that also eliminates the need for the speed(0) function :)
Try that and run it again, i did and it rendered the whole image in 62 seconds, i timed it by importing the time module by putting this code at the beginning:
import time
st=time.time()
and this code at the end:
print time.time()-st
Well done by the way, Ive just made my own thats a lot slower and lower quality then yours but was using an array of the square shape and stamping to each location i wanted in the array lol, but will be trying to improve it in the future as i only found out turtle existed less then a week ago.
One last thing, if you type:
from turtle import *
instead of "import turtle" you dont need to put turtle at the beginning of every function call :) same thing goes for every other module.
Ive included the pic of your fractal that took 62 seconds to render on my machine thats not even that powerfulYour code run on my weak machine.
I hope all this helps you greatly. also youll notice i dont have that light line problem, not sure if you fixed that issue in the original code up top?

Categories

Resources