pythonturtle-graphicspython-turtle

How to break already set function with ontimer in Turtle Graphic


I am creating a snake game with food teleporting around map. I use ontimer with recursion at the end of function to emulate mole staying at a certain position for a time. I also make the mole to move if turtle ate it. But then, mole teleportation is called two time because of the already set function by ontimer.

def mole_teleport(is_loop=True):
        rand_x = random.randint(-250, 250)
        rand_y = random.randint(-250, 250)
        mole.teleport(rand_x, rand_y)
        if is_loop == False:
            return
        else:
            screen.ontimer(mole_teleport, 3000)

tim = Player() #user_turtle
mole = Turtle("circle")

mole_teleport()

screen = Screen()
screen.setup(600, 600)

screen.listen()
screen.onkey(tim.turn_left, "Left")
screen.onkey(tim.turn_right, "Right")

game_is_on = True
while game_is_on:
    tim.move()
    if mole.distance(tim) < 20:
        mole_teleport(is_loop=False)

my-ideal-diagram I tried to block recursion with argument. What I expect is teleportation timer to be refreshed from start after the turtle has eaten the mole and call mole_teleportation()

EDIT: Player() Class

from turtle import Turtle

SPEED = 3

class Player(Turtle):
    
    def __init__(self) -> None:
        super().__init__()
        self.speed("fastest")
        self.shape("turtle")
        self.penup()
    
    def move(self):
        self.forward(SPEED)
    
    
    def turn_left(self):
        self.left(90)
        
    def turn_right(self):
        self.right(90)

Solution

  • turtle is created with tkitner which has similar function task_id = root.after(millisecond, function) and it gives taks_id which you can use to delete task, and later you could create new task - so you would have only one timer-loop.

    There are even method to access root in turtle but I would do it without ontimer but using time.time() in while-loop to check if mole_time_to_teleport <= time.time() and to teleport mole, and set new time mole_time_to_teleport += 3.

    And when snake ate mole then it can simply set mole_time_to_teleport = time.time() to teleport mole at once - and function will also set mole_time_to_teleport += 3.


    I tried to recreate Player but it slow, so I also use click on mole to eat it.

    from turtle import *
    import random
    import time
    
    class Player(Turtle):
    
        def __init__(self):
            super().__init__()
            self.penup()
            
        def move_forward(self):
            self.forward(10)
    
        def move_backward(self):
            self.backward(10)
            
        def turn_left(self):
            self.left(10)
    
        def turn_right(self):
            self.right(10)
            
    def mole_teleport():
        global mole_time_to_teleport
    
        if mole_time_to_teleport <= time.time():
            rand_x = random.randint(-250, 250)
            rand_y = random.randint(-250, 250)
            mole.goto(rand_x, rand_y)
            mole_time_to_teleport += 3  # set next teleport after 3 seconds
            
    def eat_mole(x=None, y=None):
        global mole_time_to_teleport
            
        print('eat')
        mole_time_to_teleport = time.time()  # reset to current time 
        #mole_teleport()  # while-loop will run `mole_teleport()`  so I don't need it here
    
        
    tim = Player()     
    mole = Turtle("circle")
    mole.penup()
    
    mole_time_to_teleport = time.time()  # it will teleport at once, and it will set start position
    
    screen = Screen()
    screen.setup(600, 600)
    
    screen.listen()
    screen.onkey(tim.turn_left, "Left")
    screen.onkey(tim.turn_right, "Right")
    screen.onkey(tim.move_forward, "Up")
    screen.onkey(tim.move_backward, "Down")
    
    mole.onclick(eat_mole)  # click on mole to eat it
    
    game_is_on = True
    while game_is_on:
        mole_teleport()  # run in every loop 
        
        if mole.distance(tim) < 20:
            eat_mole()
            
        time.sleep(.04)  # slow down code - to use less CPU: 1s/.04 = 25 (Frames Per Second)
        
        screen.update()  # check events and execute `onclick`
    

    EDIT:

    The same but with class Mole and list of moles

    from turtle import *
    import random
    import time
    
    # --- classes ---
    
    class Player(Turtle):
    
        def __init__(self, screen):
            super().__init__()
            self.penup()
    
            screen.onkey(self.turn_left, "Left")
            screen.onkey(self.turn_right, "Right")
            screen.onkey(self.move_forward, "Up")
            screen.onkey(self.move_backward, "Down")
            
        def move_forward(self):
            self.forward(10)
    
        def move_backward(self):
            self.backward(10)
            
        def turn_left(self):
            self.left(10)
    
        def turn_right(self):
            self.right(10)
         
            
    class Mole(Turtle):
    
        def __init__(self, shape, color="black"):
            super().__init__(shape)
            self.penup()
            self.color(color)
            self.time_to_teleport = time.time()
            
            self.onclick(self.eat)  
    
        def check_teleport(self):
            if self.time_to_teleport <= time.time():
                rand_x = random.randint(-250, 250)
                rand_y = random.randint(-250, 250)
                #self.speed(0)
                self.goto(rand_x, rand_y)
                #self.speed(6)
                self.time_to_teleport += 3  # set next teleport after 3 seconds
                
        def eat(self, x=None, y=None):
            print('eat')
            self.time_to_teleport = time.time()
            
    # --- functions ---
        
    # --- main ---
    
    screen = Screen()
    screen.setup(600, 600)
        
    tim = Player(screen)     
    
    moles = [
        Mole("circle", "red"),
        Mole("square", "green"),
        Mole("triangle", "blue"),
    ]    
    
    screen.listen()
    
    game_is_on = True
    while game_is_on:
        for mole in moles:
            mole.check_teleport()  # run in every loop 
    
        for mole in moles:
            if mole.distance(tim) < 20:
                mole.eat()
            
        time.sleep(.04)  # slow down code - to use less CPU: 1s/.04 = 25 (Frames Per Second)
        
        screen.update()  # check events and execute `onclick`