Python Forum
Python 100 line Challenge
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Python 100 line Challenge
#1
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.
Reply
#2
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
Reply
#3
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()
Reply
#4
You might like this: https://python-forum.io/thread-37300-pos...#pid157645
Reply
#5
(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. :-)
Reply
#6
(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^^.
Reply
#7
(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.
Reply
#8
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
I welcome all feedback.
The only dumb question, is one that doesn't get asked.

My Scripts
CookBook - Shmup - PyQt5 Music Player


Reply
#9
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()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.

My Scripts
CookBook - Shmup - PyQt5 Music Player


Reply
#10
(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^^.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Python 25 Line Challenge codingCat 34 3,803 May-18-2022, 07:17 PM
Last Post: codingCat
  Zen Python Challenge ichabod801 3 3,342 Aug-13-2018, 12:02 AM
Last Post: ichabod801

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020