Reputation: 11
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?
Upvotes: 1
Views: 150
Reputation: 41872
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.
Upvotes: 1