简体   繁体   中英

Slow python turtle program

I was trying to make this snake game (no food yet) by using turtle. At length of 1 it still fast but as it goes longer it runs slower and slower.

from turtle import Turtle, Screen

SIZE = 500
GRID_NUM = 20
GRID_SIZE = SIZE // GRID_NUM

screen = Screen()
screen.setup(SIZE, SIZE)
screen.bgcolor('black')
screen.setworldcoordinates(10, 8, SIZE + 2, SIZE)

class Snake:
  def __init__(self):
    self.head = init_turtle()
    self.head.setpos((GRID_SIZE // 2 + GRID_SIZE * 9, GRID_SIZE // 2 + GRID_SIZE * 10))
    self.body = []
    self.direction = 0 # 0 = left, 1 = up, 2 = right, 3 = down
    self.speed = 100 * len(self.body) if len(self.body) > 0 else 100

  def levelup(self):
    if len(self.body) == 0:
      self.body.append(self.head.clone())
    else:
      self.body.append(self.body[len(self.body) - 1].clone())
    
  def update(self):
    for i in range(len(self.body) - 1, -1 , -1):
      if i == 0:
        self.body[i].setpos(self.head.pos())
      else:
        self.body[i].setpos(self.body[i - 1].pos())
  

def init_turtle():
  turtle = Turtle()
  turtle.shape('square')
  turtle.shapesize(1.25)
  turtle.color('red')
  turtle.pencolor('#404040')
  turtle.speed('fastest')
  turtle.up()
  return turtle
def init_border():
  def border():
    turtle = init_turtle()
    for _ in range(4):
      turtle.down()
      turtle.forward(SIZE)
      turtle.left(90)
      yield(0)
    turtle.penup()
    turtle.hideturtle()

  def horizontal():
    turtle = init_turtle()
    for x in range(GRID_SIZE, SIZE, GRID_SIZE):
      turtle.goto(x, 0)
      turtle.pendown()
      turtle.goto(x, SIZE)
      turtle.penup()
      yield(0)
    turtle.hideturtle()

  def vertical():
    turtle = init_turtle()
    for y in range(GRID_SIZE, SIZE, GRID_SIZE):
      turtle.goto(0, y)
      turtle.pendown()
      turtle.goto(SIZE, y)
      turtle.penup()
      yield(0)
    turtle.hideturtle()

  generator1 = border()
  generator2 = horizontal()
  generator3 = vertical()
  while(next(generator1, 1) + next(generator2, 1) + next(generator3, 1) < 3):
    pass

def start_game():
  snake = Snake()

  def pop():
    snake.body.pop()
  def right():
    snake.direction = 0
  def up():
    snake.direction = 1
  def left():
    snake.direction = 2
  def down():
    snake.direction = 3
  def exit():
    screen.bye()

  screen.onkey(pop, 'q')
  screen.onkey(left, 'Left')
  screen.onkey(up, 'Up')
  screen.onkey(right, 'Right')
  screen.onkey(down, 'Down')
  screen.onkey(exit, 'Escape')
  screen.onkey(snake.levelup, 'Return')
  screen.listen()

  while True:
    snake.update()
    snake.head.setheading(snake.direction * 90)
    snake.head.forward(GRID_SIZE)
    print('snake', snake.head.pos())


init_border()
start_game()
screen.mainloop()

At first i think the cause of the problem was defines a lot of function but i don't know. Also i am new to python so i don't know any kind of optimizing methods.

Any ideas to optimize this program?

We can speed up long snakes by doing the drawing off screen and sending explicit updates. Below is a rework of your code that does just that and includes a temporary patch to make the snake move in a square so you can just hit to see how much the snake is affected by additional body segments:

from turtle import Turtle, Screen

SIZE = 525
GRID_NUM = 20
GRID_SIZE = SIZE // GRID_NUM

RIGHT, UP, LEFT, DOWN = range(4)

class Snake:
    def __init__(self):
        self.head = init_turtle()
        self.head.setpos(GRID_NUM//2, GRID_NUM//2)
        self.direction = LEFT
        self.body = []

    def levelup(self):
        if self.body:
            self.body.append(self.body[-1].clone())
        else:
                self.body.append(self.head.clone())

    def update(self):
        for i in range(len(self.body) - 1, -1, -1):
            if i == 0:
                self.body[i].setposition(self.head.position())
            else:
                self.body[i].setposition(self.body[i - 1].position())

def init_turtle():
    turtle = Turtle()
    turtle.shape('square')
    print(GRID_SIZE)
    turtle.shapesize((GRID_SIZE - 1) / 20)
    turtle.color('#404040', 'red')
    turtle.speed('fastest')
    turtle.penup()

    return turtle

def init_border():
    def border():
        turtle = init_turtle()

        turtle.goto(-0.5, -0.5)

        for _ in range(4):
            turtle.pendown()
            turtle.forward(GRID_NUM)
            turtle.left(90)

        turtle.penup()
        turtle.hideturtle()

    def vertical():
        turtle = init_turtle()

        for x in range(GRID_NUM):
            turtle.goto(x - 0.5, -0.5)
            turtle.pendown()
            turtle.goto(x - 0.5, GRID_NUM - 0.5)
            turtle.penup()
            yield 0

        turtle.hideturtle()

    def horizontal():
        turtle = init_turtle()

        for y in range(GRID_NUM):
            turtle.goto(-0.5, y + 0.5)
            turtle.pendown()
            turtle.goto(GRID_NUM - 0.5, y + 0.5)
            turtle.penup()
            yield 0

        turtle.hideturtle()

    border()
    generator1 = horizontal()
    generator2 = vertical()

    while next(generator1, 1) + next(generator2, 1) < 2:
        pass

def start_game():
    snake = Snake()

    def right():
        snake.direction = RIGHT

    def up():
        snake.direction = UP

    def left():
        snake.direction = LEFT

    def down():
        snake.direction = DOWN

    screen.onkey(snake.body.pop, 'q')
    screen.onkey(left, 'Left')
    screen.onkey(up, 'Up')
    screen.onkey(right, 'Right')
    screen.onkey(down, 'Down')
    screen.onkey(screen.bye, 'Escape')
    screen.onkey(snake.levelup, 'Return')
    screen.listen()

    while True:
        snake.update()
        snake.head.setheading(snake.direction * 90)
        snake.head.forward(1)

        # Run snake around in a square for testing purposes
        x, y = snake.head.position()

        if round(x) == 1 and snake.direction == LEFT:
            up()
        elif round(y) == GRID_NUM - 2 and snake.direction == UP:
            right()
        elif round(x) == GRID_NUM - 2 and snake.direction == RIGHT:
            down()
        elif round(y) == 1 and snake.direction == DOWN:
            left()

        screen.update()

screen = Screen()
screen.setup(SIZE, SIZE)
screen.bgcolor('black')
screen.setworldcoordinates(-0.75, -0.75, GRID_NUM + 0.25, GRID_NUM + 0.25)

init_border()

screen.tracer(False)

start_game()

screen.mainloop()  # never reached!

I'd recommend both approaches as the main loop will only get more complicated (slower) as you add food and border detection.

I changed your coordinate system to make it snake centric . That is, forward(1) moves one square on the grid rather than having to do the math. If you're going to mess with the coordinates anyway, why not tweak them to your best advantage.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM