First off, I apologize for the poor wording of this question. I'm not great with the vocabulary and it doesn't help that I have no idea where I'm going wrong with this code. Basically, I was given code that runs the classic Snake Game and was instructed to add 'portals'. These portals must appear at random locations on the screen that are not locations where a 'food' piece or snake segment are already present. My issue lies somewhere in this portion of the assignment-- I can get the two portals to appear, but for some reason a third turtle with no assigned shape or color is also appearing at 0,0. The turtle only appears when I add in the portal code, so I know I've messed up somewhere with my logic. I guess I've been staring at it for too long, because I just cannot for the life of me figure out the issue.
Here is my portal code. Some of the lines may be overly convoluted as I've been messing around with it for a while, but I'm mostly worried about the extra turtle. This is also my first time working with classes. Any help would be greatly appreciated.
class Portal(turtle.Turtle):
def __init__(self,snake):
super().__init__()
self.portals = []
self.count = 0
while self.count != 2:
portal = turtle.Turtle()
portal.shape('triangle')
portal.speed(0)
portal.color('blue')
portal.penup()
self.portals.append(portal)
self.create_portal(snake)
self.count = 1
def create_portal(self,snake):
portal_created = False
while not portal_created:
portal_x = random.randint(x_min, x_max)//20*20
portal_y = random.randint(y_min, y_max)//20*20
for segment in snake.segments:
if segment.distance(portal_x, portal_y) < 10 or food.distance(portal_x, portal_y) < 10:
break
else:
portal_created = True
self.portals[self.count].goto(portal_x, portal_y)
Some of the Snake code for added context if necessary:
class Snake:
def __init__(self):
self.segments = []
for i in range(6):
self.grow()
self.head=self.segments[0]
def grow(self):
body = turtle.Turtle()
body.speed(0)
body.shape("square")
body.penup()
self.segments.append(body)
def move(self):
for i in range(len(self.segments)-1,0,-1):
self.segments[i].goto(self.segments[i-1].xcor(), self.segments[i-1].ycor())
self.head.forward(20)
Please let me know if I'm missing any explanation/code that could be of importance. This is class assignment so I'm not looking for complete answers, just a step in the right direction!
EDIT: It does seem I was missing some important information, so here is my code all put together. Everything but the portal-related code was given for the assignment. I haven't attempted the teleportation yet.
import random
import turtle
import time
WINDOW_WIDTH = 500
WINDOW_HEIGHT = 500
x_min = -220
y_min = -220
x_max = 220
y_max = 220
# window size
turtle.setup(WINDOW_WIDTH, WINDOW_HEIGHT)
turtle.tracer(False)
#============= snake ===============
class Snake:
def __init__(self):
self.segments = []
for i in range(6):
self.grow()
self.head=self.segments[0]
def grow(self):
body = turtle.Turtle()
body.speed(0)
body.shape("square")
body.penup()
self.segments.append(body)
def move(self):
for i in range(len(self.segments)-1,0,-1):
self.segments[i].goto(self.segments[i-1].xcor(), self.segments[i-1].ycor())
self.head.forward(20)
def up(self):
if int(self.head.heading()) != 270:
self.head.setheading(90)
def down(self):
if int(self.head.heading()) != 90:
self.head.setheading(270)
def left(self):
if int(self.head.heading()) != 0:
self.head.setheading(180)
def right(self):
if int(self.head.heading()) != 180:
self.head.setheading(0)
#============= food ===============
class Food(turtle.Turtle):
def __init__(self,snake):
super().__init__()
self.color("red")
self.shape("circle")
self.turtlesize(0.8)
self.speed(0)
self.penup()
self.create_food(snake)
def create_food(self,snake):
food_created = False
while not food_created:
food_x = random.randint(x_min, x_max)//20*20
food_y = random.randint(y_min, y_max)//20*20
for segment in snake.segments:
if segment.distance(food_x,food_y) < 10:
break
else:
food_created = True
self.goto(food_x, food_y)
#============= portal ===============
class Portal(turtle.Turtle):
def __init__(self,snake):
super().__init__()
self.portals = []
self.count = 0
while self.count != 2:
portal = turtle.Turtle()
portal.shape('triangle')
portal.speed(0)
portal.color('blue')
portal.penup()
self.portals.append(portal)
self.create_portal(snake)
self.count = 1
def create_portal(self,snake):
portal_created = False
while not portal_created:
portal_x = random.randint(x_min, x_max)//20*20
portal_y = random.randint(y_min, y_max)//20*20
for segment in snake.segments:
if segment.distance(portal_x, portal_y) < 10 or food.distance(portal_x, portal_y) < 10:
break
else:
portal_created = True
self.portals[self.count].goto(portal_x, portal_y)
#============= message ===============
class Message(turtle.Turtle):
def __init__(self):
super().__init__()
self.score = 0
self.speed(0)
self.hideturtle()
self.penup()
self.color("gray")
self.goto(-20,220)
self.write('score: ' str(self.score), font=("Arial", 16, 'normal'))
#============= snake game ===============
snake = Snake()
food = Food(snake)
message = Message()
portal = Portal(snake)
turtle.onkeypress(snake.up, 'Up')
turtle.onkeypress(snake.down, 'Down')
turtle.onkeypress(snake.left, 'Left')
turtle.onkeypress(snake.right, 'Right')
turtle.listen()
is_over = False
while not is_over:
turtle.update()
snake.move()
if snake.head.xcor() < -250 or snake.head.xcor() > 250 or snake.head.ycor() < -250 or snake.head.ycor() > 250:
message.goto(-40,100)
message.write('Game Over', font=("Arial", 16, 'normal'))
is_over = True
for i in range(1,len(snake.segments)):
if snake.head.distance(snake.segments[i]) < 10:
message.goto(-40,100)
message.write('Game Over', font=("Arial", 16, 'normal'))
is_over = True
break
if snake.head.distance(food) < 10:
food.create_food(snake)
message.score = 1
snake.grow()
snake.segments[-1].goto(snake.segments[-2].xcor(),snake.segments[-2].ycor())
message.clear()
message.write('score: ' str(message.score), font=("Arial", 16, 'normal'))
time.sleep(0.1)
turtle.done()
CodePudding user response:
You didn't include the portion of your code where you actually call the Portal class constructor, but I am assuming that you do somewhere in the code.
The issue is that you are creating 3 turtles every time you create a portal. In portal's constructor you have a while loop that executes twice each time creating a Turtle instance and then setting it's shape, speed, color etc... So that is the first two turles
. The third turtle
is the portal itself. Portal
is a subclass of turtle.Turtle
, so calling simply by calling it's constructor you have created an instance of a Turtle.
for example:
portal = Portal(snake)
In the example above, portal
is the third extra turtle that has no shape color, or and is never moved from (0,0)
. This one line of code will create 3 turtles, the 2 inside of the while loop and the portal
object itself.
A solution to this could be to turn create_portal
into a function and simply move the while loop inside of the function.
def create_portal(snake):
portals = []
count = 0
while self.count != 2:
portal = turtle.Turtle()
portal.shape('triangle')
portal.speed(0)
portal.color('blue')
portal.penup()
portals.append(portal)
count = 1
portal_created = False
while not portal_created:
portal_x = random.randint(x_min, x_max)//20*20
portal_y = random.randint(y_min, y_max)//20*20
for segment in snake.segments:
if segment.distance(portal_x, portal_y) < 10 or food.distance(portal_x, portal_y) < 10:
break
else:
portal_created = True
portals[count].goto(portal_x, portal_y)
Alternatively you could keep the portal as a class, but don't subclass the Turtle, and instead have to instance attributes represent each end of the portal. THis is likely the better solution.
For example:
class Portal:
def __init__(self):
self.portal1 = self.create_portal()
self.portal2 = self.create_portal()
def create_portal(self):
portal = turtle.Turtle()
portal.shape('triangle')
portal.speed(0)
portal.color('blue')
portal.penup()
portal_x = random.randint(x_min, x_max)//20*20
portal_y = random.randint(y_min, y_max)//20*20
portal.goto(portal_x, portal_y)
return portal