Posts: 23
Threads: 2
Joined: May 2022
The goal of the 25 line challenge was to see how much could be squished into a very minimal space. It was amazing to see how low people could go. I was particularly impressed with menator01's managing to cram Rock Paper Scissors into five lines. I am amazed with out much I learned about the workings of Python.
But 25 line is too limiting. The challenge became a practice in obfuscating code, and all but eliminated the possibility of graphics and games. As a teacher I want keep the examples small enough that a student can grasp the ideas contained in the code, while still leaving room to make the code itself comprehensible. Hence the expansion of the challenge to 100 lines.
Genius arises when you have a good idea, not enough resources.
Posts: 23
Threads: 2
Joined: May 2022
Here is a first offering. Something I have dubbed a chaos spinner. Credit for the trig needed to rotate the spinners goes to its original author. My contribution was randomization and having it reset periodically... and of course converting it to Python.
Enjoy.
#Choas Spinner - Original code by Math Man in SmallBasic - Randomizing update by codingCat aka Matthew L. Parets for the 50 line challange. Python conversion in 2022.
from tkinter import * #tools to build the window
from random import * #tools for choosing random numbers
from time import * #tools for checking the time, and pausing the app
from math import * #tools for sin,cos
#--Record when a key is pressed
def onKeyDown(event):
global lastkey, appOn
lastkey = event.keysym
if lastkey == "Escape" : appOn = False #Close app when escape key is pressed
#--Allow for a graceful exit
def onShutdown():
global appOn
appOn = False
win = Tk() #Build a window
win.title('Chaos Spinner') #Set the title
win.geometry("640x480") #default window size
win.state('zoomed') #maximize the window
win.config(bg="#000000") #background color of black
win.update() #update the window to show the user
winwid = win.winfo_width() #get the resulting height and width for calculations
winhei = win.winfo_height()
canv = Canvas(win, width = winwid, height = winhei, bg="black") #build a canvas to draw on
canv.pack() #add the canvas to the window
win.bind("<KeyPress>", onKeyDown) #set the key down event
win.protocol("WM_DELETE_WINDOW",onShutdown) #Set the shut down event
appOn = True #run the program while true
while appOn: #keep going until canceled by the app window closeing
lastkey = "" #reset the key press, changes design when space is pressed
amount = randint(4,16) #choose the number of segments in the pen
total = 0 #track the length of the pen
angle = [] #reset the lists that describe the pen and its location
dist = []
rate = []
x = []
y = []
angrng = randint(1,12) * 15 #occationally limit the angle range to allow for more sweeping shapes
for i in range(0,amount): #for each pen segment
angle.append(randint(-angrng,angrng)) #Randomly choose the speed around the circle for each pen segment
dist.append(randint(16,round((winhei/1.1)/amount))) #randomly choose the distance from the center - dividing by 1.1 ensures that most will stay on the screen
total = total + dist[i] #keep track of the distance
rate.append(randint(-10,10) * 2) #change speed of the angle
for i in range(0,amount): #randomly distribute what is left of distance from center to edge
rndpos = randint(0,amount-1)#random position
dist[rndpos] = dist[rndpos] + (((winhei / 1.1) - total) // amount)
x.append(winwid // 2) #place the pen in the center of the window
y.append(winhei // 2)
for i in range(1,amount): #for each segment of the pen
x.append(round(x[i-1]+dist[i]*cos(radians(angle[i])))) #set its location based on its angle and distance from middle
y.append(round(y[i-1]+dist[i]*sin(radians(angle[i]))))
x.append(x[amount-1]) #duplicate the last entry. This will help us draw connecting lines
y.append(y[amount-1])
pen = [] #list to hold the lines of the pen
for i in range(0,amount): pen.append(None) #fill it with lots of nothing
line = [] #list to hold the connecting lines and point circles
while appOn and lastkey != "space" and (time_ns() // 100000000) % 150 != 0 : #while window is open, the space has not been pressed, and if seconds has not elapsed
for i in range(1,amount): #for each pen segment
if pen[i] != None: #Is there already a pen segment here?
canv.delete(pen[i]) #delete the old pen segment
angle[i] = angle[i]+rate[i] #Calcualte the position of the new pen segment
x[i] = x[i-1]+dist[i]*cos(radians(angle[i]))
y[i] = y[i-1]+dist[i]*sin(radians(angle[i]))
pen[i] = canv.create_line(x[i-1],y[i-1],x[i],y[i],fill="green") #create the new pen segment
color = ["#"+''.join([choice('ABCDEF0123456789') for i in range(6)])] #choose a random color (random hex value)
line.append(canv.create_line(x[amount-1],y[amount-1],x[amount],y[amount], fill=color)) #draw a line between the end of the pen, and the ends last location
x[amount] = x[amount-1] #update the end of the pen
y[amount] = y[amount-1]
line.append(canv.create_oval(x[amount-1]-3,y[amount-1]-3,x[amount-1]+3,y[amount-1]+3, outline=color)) #draw a circle at the end of the pen
canv.update() #update the window so the user can see the change
sleep(0.025) #sleep for a tick to let other stuff happen in the OS
for s in line: canv.delete(s) #delete all of the connecting lines and circles
for l in pen: canv.delete(l) #delete the old pen
#once the window has been closed, clean up all of the resources and close the window.
win.destroy()
menator01 likes this post
Posts: 23
Threads: 2
Joined: May 2022
Another offering. This one is a little reflex game. Again, showing how expanding the challenge to 100 lines provides a lot more flexibility using GUI objects while keeping the code easy to follow.
#Stop it! Originally developed for Smallbasic in May 2016, converted to Python in May of 2022. (C)opyright codingCat AKA Matthew L. Parets -- Released under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
#Hit the space bar to stop the ball. The closer you get to the middle ofthe basket, the higher your score.
import tkinter, time
#functions and events ----------------------------------------------------------------------------------------------------
def SpeedIncrease(): #At the end of every frame, decrease frame size and speed up
global speed,framelength,framestart
if time.time_ns() // 1000000 - framestart > framelength: #Have we reached the end of the frame?
if framelength > 1: #Frames can only get so short, stop at 1 millisecond
speed = speed * 2 #Double the speed
if speed > 0.7: #Not too fast.
speed = 0.7 #Set to half a pixel per frame
framelength = framelength * 0.80 #Decrease the length of frame by 20%
framestart = time.time_ns() // 1000000 #Start the next frame.
def DisplayFinishfinishMsg(): #Display the win/lose message
canvas.create_line(ballx,0,ballx,winhei, fill="#dddddd", width=2, dash=(2, 1,1,1)) #highlight the location of the of the ball
canvas.create_line(ballx+ballsize,0,ballx+ballsize,winhei, fill="#dddddd", width=2, dash=(2, 1,1,1))
scoreMsg = 0
finishMsg = ""
if ballx + ballsize < targetX: #Stopped before the basket
finishMsg = "Too Soon!"
scoreMsg = 0
elif ballx > targetX + targetWidth: #Stopped after the basic
finishMsg = "Missed it!"
scoreMsg = 0
elif ballx < targetX and ballx + ballsize > targetX: #Stoped just inside
finishMsg = "Just Made it!"
scoreMsg = round(1000 * (ballsize - (targetX - ballx)))
elif ballx + ballsize > targetX + targetWidth and ballx < targetX + targetWidth:
finishMsg= "Almost Missed it!" #Stopped almost outside
scoreMsg = round(1000 * (ballsize - ((ballx + ballsize) - (targetX + targetWidth))))
else: #Nailed it - fully inside
finishMsg = "Bulls Eye"
scoreMsg = chr(8734)
scoreMsg = "Score = " + str(scoreMsg)
canvas.create_text(4,25, text = finishMsg, anchor="nw", font=("Tahoma",65,"bold","italic"),fill="#333333")
canvas.create_text(0,25, text = finishMsg, anchor="nw", font=("Tahoma",65,"bold","italic"),fill="green")
canvas.create_text(4,winhei - 25, text = scoreMsg, anchor="sw", font=("Tahoma",65,"bold","italic"),fill="#333333")
canvas.create_text(0,winhei - 25, text = scoreMsg, anchor="sw", font=("Tahoma",65,"bold","italic"),fill="blue")
#** Key press event - Update the last key variable
def onKeyDown(event):
global lastkey
lastkey = event.keysym
#** Shut down event. Called when window is closed. Having this event prevents the window itself from closing
#** until the main processing loop stops, and the destroy function is called.
def onShutdown():
global gameon
gameon = False
#Initial setup ---------------------------------------------------------------------------------------------------------------
winwid, winhei = 800,500 #The size of the window
win = tkinter.Tk() #build the window
win.title("Stop it") #give the game a name
win.geometry(f"{winwid}x{winhei}+0+0") #set the size and location of window -- looking for a super power? Google "python fstrings"
lastkey="" #the last key pressed, set in the key down event
ballsize = 50 #size of player
ballx, bally = 0,winhei / 2 - ballsize/2 #location of player (centered to the left)
targetSize = ballsize * 1.25 #the size and location of the target
targetWidth = targetSize * 1.125
targetX = winwid - targetSize * 2.5
targetY = winhei/2 - targetSize/2
speed = 0.00075 #player speed at the start of the game (very slow)
framelength = 500 #length of the frame at start of game (very long)
gameon = True #game is on until the window is closed
canvas = tkinter.Canvas(win,bg="#ffffcc",width=800, height=600) #add a work area to the window
canvas.pack(expand=True)
ball= canvas.create_oval(ballx,bally, ballx+ballsize,bally+ballsize, outline="#bb0000", fill="red", width=3) #add the player and target
targetX + (targetWidth / 2) - (ballsize / 2)
target = canvas.create_rectangle(targetX,targetY, targetX+targetWidth,targetY+targetSize, outline="#0000bb", fill="blue", width=3)
win.bind("<KeyPress>", onKeyDown)
win.protocol("WM_DELETE_WINDOW",onShutdown)
# Main processing / Game loop. Repeats once per frame. ---------------------------------------------------------------------
framestart = time.time_ns() // 1000000 #current millisecond count. Why note nanoseconds? Why divide by 1000000? Because no one wants to deal in billions of a second
secstart = time.time_ns() // 1000000
while gameon and lastkey != "space" and ballx < winwid - ballsize: #continue until - the window is close, the space bar is pressed, or the ball reaches the far end of the screen
ballx = ballx + speed #update the balls location
canvas.coords(ball, ballx,bally, ballx+ballsize,bally+ballsize) #move ball to new location
SpeedIncrease() #increase the speed, ball goes a little faster each round
win.update() #update the graphics in the window (does not happen automatically)
DisplayFinishfinishMsg()
while gameon: #hold the window open until the player closes it
win.update()
time.sleep(0.25)
#** Main processing loop is done, close the window
win.destroy()
Posts: 11,643
Threads: 450
Joined: Sep 2016
Posts: 23
Threads: 2
Joined: May 2022
(May-28-2022, 09:56 PM)Larz60+ Wrote: You might like this: https://python-forum.io/thread-37300-pos...#pid157645
I have recently seen this... its a great video. I love gravity simulators. It hadn't occurred to me to convert it to python. i will have to take a look at the code.
I should be able to adapt Chaos Spinner to it without too much trouble. :-)
Posts: 26
Threads: 1
Joined: Mar 2022
(May-27-2022, 02:19 PM)codingCat Wrote: Here is a first offering. Something I have dubbed a chaos spinner. Credit for the trig needed to rotate the spinners goes to its original author. My contribution was randomization and having it reset periodically... and of course converting it to Python.
Enjoy.
#Choas Spinner - Original code by Math Man in SmallBasic - Randomizing update by codingCat aka Matthew L. Parets for the 50 line challange. Python conversion in 2022.
from tkinter import * #tools to build the window
from random import * #tools for choosing random numbers
from time import * #tools for checking the time, and pausing the app
from math import * #tools for sin,cos
#--Record when a key is pressed
def onKeyDown(event):
global lastkey, appOn
lastkey = event.keysym
if lastkey == "Escape" : appOn = False #Close app when escape key is pressed
#--Allow for a graceful exit
def onShutdown():
global appOn
appOn = False
win = Tk() #Build a window
win.title('Chaos Spinner') #Set the title
win.geometry("640x480") #default window size
win.state('zoomed') #maximize the window
win.config(bg="#000000") #background color of black
win.update() #update the window to show the user
winwid = win.winfo_width() #get the resulting height and width for calculations
winhei = win.winfo_height()
canv = Canvas(win, width = winwid, height = winhei, bg="black") #build a canvas to draw on
canv.pack() #add the canvas to the window
win.bind("<KeyPress>", onKeyDown) #set the key down event
win.protocol("WM_DELETE_WINDOW",onShutdown) #Set the shut down event
appOn = True #run the program while true
while appOn: #keep going until canceled by the app window closeing
lastkey = "" #reset the key press, changes design when space is pressed
amount = randint(4,16) #choose the number of segments in the pen
total = 0 #track the length of the pen
angle = [] #reset the lists that describe the pen and its location
dist = []
rate = []
x = []
y = []
angrng = randint(1,12) * 15 #occationally limit the angle range to allow for more sweeping shapes
for i in range(0,amount): #for each pen segment
angle.append(randint(-angrng,angrng)) #Randomly choose the speed around the circle for each pen segment
dist.append(randint(16,round((winhei/1.1)/amount))) #randomly choose the distance from the center - dividing by 1.1 ensures that most will stay on the screen
total = total + dist[i] #keep track of the distance
rate.append(randint(-10,10) * 2) #change speed of the angle
for i in range(0,amount): #randomly distribute what is left of distance from center to edge
rndpos = randint(0,amount-1)#random position
dist[rndpos] = dist[rndpos] + (((winhei / 1.1) - total) // amount)
x.append(winwid // 2) #place the pen in the center of the window
y.append(winhei // 2)
for i in range(1,amount): #for each segment of the pen
x.append(round(x[i-1]+dist[i]*cos(radians(angle[i])))) #set its location based on its angle and distance from middle
y.append(round(y[i-1]+dist[i]*sin(radians(angle[i]))))
x.append(x[amount-1]) #duplicate the last entry. This will help us draw connecting lines
y.append(y[amount-1])
pen = [] #list to hold the lines of the pen
for i in range(0,amount): pen.append(None) #fill it with lots of nothing
line = [] #list to hold the connecting lines and point circles
while appOn and lastkey != "space" and (time_ns() // 100000000) % 150 != 0 : #while window is open, the space has not been pressed, and if seconds has not elapsed
for i in range(1,amount): #for each pen segment
if pen[i] != None: #Is there already a pen segment here?
canv.delete(pen[i]) #delete the old pen segment
angle[i] = angle[i]+rate[i] #Calcualte the position of the new pen segment
x[i] = x[i-1]+dist[i]*cos(radians(angle[i]))
y[i] = y[i-1]+dist[i]*sin(radians(angle[i]))
pen[i] = canv.create_line(x[i-1],y[i-1],x[i],y[i],fill="green") #create the new pen segment
color = ["#"+''.join([choice('ABCDEF0123456789') for i in range(6)])] #choose a random color (random hex value)
line.append(canv.create_line(x[amount-1],y[amount-1],x[amount],y[amount], fill=color)) #draw a line between the end of the pen, and the ends last location
x[amount] = x[amount-1] #update the end of the pen
y[amount] = y[amount-1]
line.append(canv.create_oval(x[amount-1]-3,y[amount-1]-3,x[amount-1]+3,y[amount-1]+3, outline=color)) #draw a circle at the end of the pen
canv.update() #update the window so the user can see the change
sleep(0.025) #sleep for a tick to let other stuff happen in the OS
for s in line: canv.delete(s) #delete all of the connecting lines and circles
for l in pen: canv.delete(l) #delete the old pen
#once the window has been closed, clean up all of the resources and close the window.
win.destroy()
Hello,
This is not well written tkinter code. With tkinter you use a callback (use after()) to loop...
I see other clumsinesses...
I speak Python but I don't speak English (I just read it a little). If I express myself badly, please blame the translator^^.
Posts: 23
Threads: 2
Joined: May 2022
(May-29-2022, 06:06 PM)Coricoco_fr Wrote: Hello,
This is not well written tkinter code. With tkinter you use a callback (use after()) to loop...
I see other clumsinesses...
I actually developed this code to specifically avoid using after. When teaching coding to new students, something like an event call back can be completely incomprehensible. By using a while loop and leaning into update() and sleep() it is much easier to see the flow of the code.
Would you be interested in restructuring and reposting the code to use after() rather than the while loop? I would love to see your take on the event driven design, as well as seeing the fixes to the other perceived clumsiness's.
Posts: 826
Threads: 94
Joined: Sep 2019
Jun-04-2022, 10:25 AM
(This post was last modified: Jun-04-2022, 10:25 AM by menator01.)
Here is my go at it using after.
import tkinter as tk
import time
class View:
def __init__(self, parent):
self.parent = parent
self.canvas = tk.Canvas(self.parent, bg='#ffffcc')
self.canvas.pack(expand=True, fill='both')
self.ballx, self.bally = 2, 275
self.ball_size, self.targetsize = 50, 50 * 1.25
self.targetx, self.targety = 800 - self.targetsize * 2.5, 300 - self.targetsize/2
self.target = self.canvas.create_rectangle(self.targetx, self.targety,
self.targetx+self.targetsize*1.125, self.targety+self.targetsize,
outline='#0000bb', fill='blue')
self.ball = self.canvas.create_oval(self.ballx, self.bally, self.ballx+self.ball_size,
self.bally+self.ball_size, outline='#bb0000', fill='red', width=3)
self.text = self.canvas.create_text(10, 15, font=('tahoma 50 bold'), anchor='nw')
self.text2 = self.canvas.create_text(10, 540, font=('tahoma 50 bold'), anchor='w')
class Controller:
def __init__(self, view):
self.view = view
self.view.parent.bind('<space>', self.ballstop)
self.counter, self.speed = 0, 0.2
self.key = False
self.msg = None
self.color = None
self.score = None
self.view.btn = tk.Button(self.view.parent, text='Reset', command=self.reset)
self.view.btn.pack_forget()
self.update()
def update(self):
if self.key != True:
if self.counter > 1000:
self.speed = 1.35
self.counter += 1
self.view.ballx = self.view.ballx + self.speed
if self.view.ballx > 725:
self.view.ballx = 725
self.view.canvas.coords(self.view.ball, self.view.ballx, self.view.bally,
self.view.ballx+self.view.ball_size, self.view.bally+self.view.ball_size)
if self.counter == 1400:
self.msg = 'Missed It!'
self.score = 0
self.color = 'red'
self.key = True
self.view.btn.pack(side='bottom')
else:
if self.view.ballx + self.view.ball_size < self.view.targetx:
self.msg = 'Too Soon!'
self.score = 0
self.color = 'red'
elif self.view.ballx > self.view.targetx + self.view.targetsize:
self.msg = 'Missed It!'
self.score = 0
self.color = 'red'
elif self.view.ballx < self.view.targetx and self.view.ballx + self.view.ball_size > self.view.targetx:
self.msg = 'Just Made It!'
self.score = 50
self.color= 'orange'
elif self.view.ballx + self.view.ball_size > self.view.targetx + self.view.targetsize and self.view.ballx < self.view.targetx + self.view.targetsize:
self.msg = 'Almost Missed It!'
self.score = 50
self.color = 'orange'
elif self.view.ballx + self.view.ball_size > self.view.targetx and self.view.ballx + self.view.ball_size < self.view.targetx + self.view.targetsize:
self.msg = 'Bulls Eye!'
self.score = 100
self.color = 'blue'
self.message(self.msg, self.color, f'Score: {self.score}')
self.view.parent.after(1, self.update)
def reset(self):
self.key = False
self.view.canvas.itemconfigure(self.view.text, text='')
self.view.canvas.itemconfigure(self.view.text2, text='')
self.view.ballx, self.speed, self.counter = 2, 0.2, 0
self.view.btn.pack_forget()
self.msg, self.score = None, None
def ballstop(self, event):
if event.keysym:
self.key = True
self.view.btn.pack(side='bottom')
def message(self, msg, color, score):
self.view.canvas.itemconfigure(self.view.text, text=msg, fill=color)
self.view.canvas.itemconfigure(self.view.text2, text=score, fill=color)
if __name__ == '__main__':
root = tk.Tk()
root.geometry('800x600+200+200')
controller = Controller(View(root))
root.mainloop()
Coricoco_fr likes this post
Posts: 826
Threads: 94
Joined: Sep 2019
Jun-08-2022, 09:52 AM
(This post was last modified: Jun-08-2022, 09:52 AM by menator01.)
One more approach using after. It's a shot though.
import tkinter as tk
def window():
window.canvas = getattr(window, 'canvas', tk.Canvas())
window.canvas.pack(fill='both', expand=True)
window.canvas.configure(bg='ivory')
window.canvas.update()
window.width = window.canvas.winfo_width()
window.height = window.canvas.winfo_height()
window.font = ('times 30 bold')
def target():
target.center = getattr(target, 'center', (window.width-150, round(window.height/2)))
target.linex = window.canvas.create_line(0, target.center[1], 800, target.center[1], fill='black')
target.liney = window.canvas.create_line(target.center[0], 0, target.center[0], window.height, fill='black')
target.target = window.canvas.create_rectangle(target.center[0]-30, target.center[1]-30, \
target.center[0]+30, target.center[1]+30, fill='blue')
def ball():
ball.ballx = 30
ball.speed = 0.2
ball.ball = window.canvas.create_oval(ball.ballx-25, target.center[1]-25, \
ball.ballx+25, target.center[1]+25, fill='red')
def ballstop(stop=False):
ballstop.msg = getattr(ballstop, 'msg', None)
ballstop.score = getattr(ballstop, 'score', None)
ballstop.stop = stop
if round(ball.ballx)+25 < target.center[0]-30:
ballstop.msg = 'Too Soon!'
ballstop.score = 0
elif round(ball.ballx)-25 > target.center[0]+30:
ballstop.msg = 'Missed It!'
ballstop.score = 0
elif round(ball.ballx)-25 < target.center[0]-30 and round(ball.ballx)+25 > target.center[0]-30:
ballstop.msg = 'Just Made It!'
ballstop.score = 50
elif round(ball.ballx)-25 < target.center[0]+30 and round(ball.ballx)+25 > target.center[0]+30:
ballstop.msg = 'Almost Missed!'
ballstop.score = 50
else:
ballstop.msg = 'Bulls Eye!'
ballstop.score = 100
def update():
ball.ballx = ball.ballx + ball.speed
root.bind('<space>', lambda stop: ballstop(stop=True))
if round(ball.ballx) >= 200:
ball.speed = 1.35
if round(ball.ballx) == 750:
ball.ballx = 750
ballstop.msg = 'Missed'
ballstop.stop = True
window.canvas.coords(ball.ball, ball.ballx-25, target.center[1]-25, ball.ballx+25, target.center[1]+25)
if ballstop.stop != True:
root.after(1, update)
else:
color = 'red' if ballstop.score == 0 else ('orange' if ballstop.score == 50 else 'blue')
window.canvas.create_text(10, 30, font=window.font, text=ballstop.msg, fill=color, anchor='w')
window.canvas.create_text(10, window.height-100, font=window.font, text=f'Score: {ballstop.score}', fill=color, anchor='w')
root.after_cancel(root)
root = tk.Tk()
root.geometry('800x600+200+200')
root.resizable(False, False)
window()
target()
ball()
ballstop()
update()
root.mainloop()
with reset button
import tkinter as tk
def window():
window.canvas = getattr(window, 'canvas', tk.Canvas())
window.canvas.pack(fill='both', expand=True)
window.canvas.configure(bg='ivory')
window.canvas.update()
window.width = window.canvas.winfo_width()
window.height = window.canvas.winfo_height()
window.font = ('times 30 bold')
window.button = tk.Button(window.canvas, text='Reset')
window.button.pack_forget()
def target():
target.center = getattr(target, 'center', (window.width-150, round(window.height/2)))
target.linex = window.canvas.create_line(0, target.center[1], 800, target.center[1], fill='black')
target.liney = window.canvas.create_line(target.center[0], 0, target.center[0], window.height, fill='black')
target.target = window.canvas.create_rectangle(target.center[0]-30, target.center[1]-30, \
target.center[0]+30, target.center[1]+30, fill='blue')
def ball():
ball.ballx = 30
ball.speed = 0.2
ball.ball = window.canvas.create_oval(ball.ballx-25, target.center[1]-25, \
ball.ballx+25, target.center[1]+25, fill='red', tags=('ball',))
def ballstop(stop=False):
ballstop.msg = getattr(ballstop, 'msg', None)
ballstop.score = getattr(ballstop, 'score', None)
ballstop.stop = stop
if round(ball.ballx)+25 < target.center[0]-30:
ballstop.msg = 'Too Soon!'
ballstop.score = 0
elif round(ball.ballx)-25 > target.center[0]+30:
ballstop.msg = 'Missed It!'
ballstop.score = 0
elif round(ball.ballx)-25 < target.center[0]-30 and round(ball.ballx)+25 > target.center[0]-30:
ballstop.msg = 'Just Made It!'
ballstop.score = 50
elif round(ball.ballx)-25 < target.center[0]+30 and round(ball.ballx)+25 > target.center[0]+30:
ballstop.msg = 'Almost Missed!'
ballstop.score = 50
else:
ballstop.msg = 'Bulls Eye!'
ballstop.score = 100
def update():
ball.ballx = ball.ballx + ball.speed
root.bind('<space>', lambda stop: ballstop(stop=True))
if round(ball.ballx) >= 200:
ball.speed = 1.35
if round(ball.ballx) == 750:
ball.ballx = 750
ballstop.msg = 'Missed'
ballstop.stop = True
window.canvas.coords(ball.ball, ball.ballx-25, target.center[1]-25, ball.ballx+25, target.center[1]+25)
if ballstop.stop != True:
root.after(1, update)
else:
color = 'red' if ballstop.score == 0 else ('orange' if ballstop.score == 50 else 'blue')
window.canvas.create_text(10, 30, font=window.font, text=ballstop.msg, fill=color, anchor='w', tags=('msg',))
window.canvas.create_text(10, window.height-100, font=window.font, text=f'Score: {ballstop.score}', fill=color, anchor='w', tags=('score',))
window.button.pack(side='bottom', pady=10)
window.button['command'] = reset
root.after_cancel(root)
def reset():
window.button.pack_forget()
window.canvas.delete('all')
window()
target()
ball()
ballstop()
update()
root = tk.Tk()
root.geometry('800x600+200+200')
root.resizable(False, False)
window()
target()
ball()
ballstop()
update()
root.mainloop()
Posts: 26
Threads: 1
Joined: Mar 2022
(Jun-04-2022, 10:25 AM)menator01 Wrote: Here is my go at it using after.
import tkinter as tk
import time
class View:
def __init__(self, parent):
self.parent = parent
self.canvas = tk.Canvas(self.parent, bg='#ffffcc')
self.canvas.pack(expand=True, fill='both')
self.ballx, self.bally = 2, 275
self.ball_size, self.targetsize = 50, 50 * 1.25
self.targetx, self.targety = 800 - self.targetsize * 2.5, 300 - self.targetsize/2
self.target = self.canvas.create_rectangle(self.targetx, self.targety,
self.targetx+self.targetsize*1.125, self.targety+self.targetsize,
outline='#0000bb', fill='blue')
self.ball = self.canvas.create_oval(self.ballx, self.bally, self.ballx+self.ball_size,
self.bally+self.ball_size, outline='#bb0000', fill='red', width=3)
self.text = self.canvas.create_text(10, 15, font=('tahoma 50 bold'), anchor='nw')
self.text2 = self.canvas.create_text(10, 540, font=('tahoma 50 bold'), anchor='w')
class Controller:
def __init__(self, view):
self.view = view
self.view.parent.bind('<space>', self.ballstop)
self.counter, self.speed = 0, 0.2
self.key = False
self.msg = None
self.color = None
self.score = None
self.view.btn = tk.Button(self.view.parent, text='Reset', command=self.reset)
self.view.btn.pack_forget()
self.update()
def update(self):
if self.key != True:
if self.counter > 1000:
self.speed = 1.35
self.counter += 1
self.view.ballx = self.view.ballx + self.speed
if self.view.ballx > 725:
self.view.ballx = 725
self.view.canvas.coords(self.view.ball, self.view.ballx, self.view.bally,
self.view.ballx+self.view.ball_size, self.view.bally+self.view.ball_size)
if self.counter == 1400:
self.msg = 'Missed It!'
self.score = 0
self.color = 'red'
self.key = True
self.view.btn.pack(side='bottom')
else:
if self.view.ballx + self.view.ball_size < self.view.targetx:
self.msg = 'Too Soon!'
self.score = 0
self.color = 'red'
elif self.view.ballx > self.view.targetx + self.view.targetsize:
self.msg = 'Missed It!'
self.score = 0
self.color = 'red'
elif self.view.ballx < self.view.targetx and self.view.ballx + self.view.ball_size > self.view.targetx:
self.msg = 'Just Made It!'
self.score = 50
self.color= 'orange'
elif self.view.ballx + self.view.ball_size > self.view.targetx + self.view.targetsize and self.view.ballx < self.view.targetx + self.view.targetsize:
self.msg = 'Almost Missed It!'
self.score = 50
self.color = 'orange'
elif self.view.ballx + self.view.ball_size > self.view.targetx and self.view.ballx + self.view.ball_size < self.view.targetx + self.view.targetsize:
self.msg = 'Bulls Eye!'
self.score = 100
self.color = 'blue'
self.message(self.msg, self.color, f'Score: {self.score}')
self.view.parent.after(1, self.update)
def reset(self):
self.key = False
self.view.canvas.itemconfigure(self.view.text, text='')
self.view.canvas.itemconfigure(self.view.text2, text='')
self.view.ballx, self.speed, self.counter = 2, 0.2, 0
self.view.btn.pack_forget()
self.msg, self.score = None, None
def ballstop(self, event):
if event.keysym:
self.key = True
self.view.btn.pack(side='bottom')
def message(self, msg, color, score):
self.view.canvas.itemconfigure(self.view.text, text=msg, fill=color)
self.view.canvas.itemconfigure(self.view.text2, text=score, fill=color)
if __name__ == '__main__':
root = tk.Tk()
root.geometry('800x600+200+200')
controller = Controller(View(root))
root.mainloop()
(Jun-08-2022, 09:52 AM)menator01 Wrote: One more approach using after. It's a shot though.
import tkinter as tk
def window():
window.canvas = getattr(window, 'canvas', tk.Canvas())
window.canvas.pack(fill='both', expand=True)
window.canvas.configure(bg='ivory')
window.canvas.update()
window.width = window.canvas.winfo_width()
window.height = window.canvas.winfo_height()
window.font = ('times 30 bold')
def target():
target.center = getattr(target, 'center', (window.width-150, round(window.height/2)))
target.linex = window.canvas.create_line(0, target.center[1], 800, target.center[1], fill='black')
target.liney = window.canvas.create_line(target.center[0], 0, target.center[0], window.height, fill='black')
target.target = window.canvas.create_rectangle(target.center[0]-30, target.center[1]-30, \
target.center[0]+30, target.center[1]+30, fill='blue')
def ball():
ball.ballx = 30
ball.speed = 0.2
ball.ball = window.canvas.create_oval(ball.ballx-25, target.center[1]-25, \
ball.ballx+25, target.center[1]+25, fill='red')
def ballstop(stop=False):
ballstop.msg = getattr(ballstop, 'msg', None)
ballstop.score = getattr(ballstop, 'score', None)
ballstop.stop = stop
if round(ball.ballx)+25 < target.center[0]-30:
ballstop.msg = 'Too Soon!'
ballstop.score = 0
elif round(ball.ballx)-25 > target.center[0]+30:
ballstop.msg = 'Missed It!'
ballstop.score = 0
elif round(ball.ballx)-25 < target.center[0]-30 and round(ball.ballx)+25 > target.center[0]-30:
ballstop.msg = 'Just Made It!'
ballstop.score = 50
elif round(ball.ballx)-25 < target.center[0]+30 and round(ball.ballx)+25 > target.center[0]+30:
ballstop.msg = 'Almost Missed!'
ballstop.score = 50
else:
ballstop.msg = 'Bulls Eye!'
ballstop.score = 100
def update():
ball.ballx = ball.ballx + ball.speed
root.bind('<space>', lambda stop: ballstop(stop=True))
if round(ball.ballx) >= 200:
ball.speed = 1.35
if round(ball.ballx) == 750:
ball.ballx = 750
ballstop.msg = 'Missed'
ballstop.stop = True
window.canvas.coords(ball.ball, ball.ballx-25, target.center[1]-25, ball.ballx+25, target.center[1]+25)
if ballstop.stop != True:
root.after(1, update)
else:
color = 'red' if ballstop.score == 0 else ('orange' if ballstop.score == 50 else 'blue')
window.canvas.create_text(10, 30, font=window.font, text=ballstop.msg, fill=color, anchor='w')
window.canvas.create_text(10, window.height-100, font=window.font, text=f'Score: {ballstop.score}', fill=color, anchor='w')
root.after_cancel(root)
root = tk.Tk()
root.geometry('800x600+200+200')
root.resizable(False, False)
window()
target()
ball()
ballstop()
update()
root.mainloop()
with reset button
import tkinter as tk
def window():
window.canvas = getattr(window, 'canvas', tk.Canvas())
window.canvas.pack(fill='both', expand=True)
window.canvas.configure(bg='ivory')
window.canvas.update()
window.width = window.canvas.winfo_width()
window.height = window.canvas.winfo_height()
window.font = ('times 30 bold')
window.button = tk.Button(window.canvas, text='Reset')
window.button.pack_forget()
def target():
target.center = getattr(target, 'center', (window.width-150, round(window.height/2)))
target.linex = window.canvas.create_line(0, target.center[1], 800, target.center[1], fill='black')
target.liney = window.canvas.create_line(target.center[0], 0, target.center[0], window.height, fill='black')
target.target = window.canvas.create_rectangle(target.center[0]-30, target.center[1]-30, \
target.center[0]+30, target.center[1]+30, fill='blue')
def ball():
ball.ballx = 30
ball.speed = 0.2
ball.ball = window.canvas.create_oval(ball.ballx-25, target.center[1]-25, \
ball.ballx+25, target.center[1]+25, fill='red', tags=('ball',))
def ballstop(stop=False):
ballstop.msg = getattr(ballstop, 'msg', None)
ballstop.score = getattr(ballstop, 'score', None)
ballstop.stop = stop
if round(ball.ballx)+25 < target.center[0]-30:
ballstop.msg = 'Too Soon!'
ballstop.score = 0
elif round(ball.ballx)-25 > target.center[0]+30:
ballstop.msg = 'Missed It!'
ballstop.score = 0
elif round(ball.ballx)-25 < target.center[0]-30 and round(ball.ballx)+25 > target.center[0]-30:
ballstop.msg = 'Just Made It!'
ballstop.score = 50
elif round(ball.ballx)-25 < target.center[0]+30 and round(ball.ballx)+25 > target.center[0]+30:
ballstop.msg = 'Almost Missed!'
ballstop.score = 50
else:
ballstop.msg = 'Bulls Eye!'
ballstop.score = 100
def update():
ball.ballx = ball.ballx + ball.speed
root.bind('<space>', lambda stop: ballstop(stop=True))
if round(ball.ballx) >= 200:
ball.speed = 1.35
if round(ball.ballx) == 750:
ball.ballx = 750
ballstop.msg = 'Missed'
ballstop.stop = True
window.canvas.coords(ball.ball, ball.ballx-25, target.center[1]-25, ball.ballx+25, target.center[1]+25)
if ballstop.stop != True:
root.after(1, update)
else:
color = 'red' if ballstop.score == 0 else ('orange' if ballstop.score == 50 else 'blue')
window.canvas.create_text(10, 30, font=window.font, text=ballstop.msg, fill=color, anchor='w', tags=('msg',))
window.canvas.create_text(10, window.height-100, font=window.font, text=f'Score: {ballstop.score}', fill=color, anchor='w', tags=('score',))
window.button.pack(side='bottom', pady=10)
window.button['command'] = reset
root.after_cancel(root)
def reset():
window.button.pack_forget()
window.canvas.delete('all')
window()
target()
ball()
ballstop()
update()
root = tk.Tk()
root.geometry('800x600+200+200')
root.resizable(False, False)
window()
target()
ball()
ballstop()
update()
root.mainloop()
Hello.
The use of getattr is superfluous. It makes the code unnecessarily heavy...
I speak Python but I don't speak English (I just read it a little). If I express myself badly, please blame the translator^^.
|