The Point of the game is to make all the circles disappear when they Collide but for some reason some circles aren't disappearing? - Thank you for your help in advance!
import turtle
import random
import math
import time
# Setting up the Screen
ms = turtle.Screen()
ms.bgcolor("red")
ms.title("Space Rocket Minigame #Rafa94")
# Using class functions/Methods
# subclass
class Border(turtle.Turtle):
def __init__(self): # class constrcutor
turtle.Turtle.__init__(self) # adding our Objects attributes all starting with "self"
self.penup()
self.hideturtle()
self.speed(0)
self.color("silver")
self.pensize(5)
def draw_border(self):
self.penup()# getting our pen to start drawing
self.goto(-300, -300)
self.pendown()
self.goto(-300, 300)
self.goto(300, 300)
self.goto(300, -300)
self.goto(-300, -300)
class Player(turtle.Turtle): # since it got inherited this class becomes a Superclass
def __init__(self): # self is our only argument here but it will have multiple attributes
turtle.Turtle.__init__(self) # since we are using the Turtle module, we are able to use it's built in functions
self.penup()# our attributes
self.speed(0)
self.shape("triangle")
self.color("black")
self.velocity = 0.1
def move(self):
self.forward(self.velocity)
# Border Checking
if self.xcor() > 290 or self.xcor() < -290: # Left side is -290 Right side is 290 we also want the coordinates x and y to be below 300 to not go over our border
self.left(60)
if self.ycor() > 290 or self.ycor() < -290:
self.left(60)
def turnleft(self):
self.left(30)
def turnright(self):
self.right(30)
def increasespeed(self):
self.velocity += 1
class Goal(turtle.Turtle): # Sub Class
def __init__(self):
# since we are using the Turtle module we are able to use it's built in functions
turtle.Turtle.__init__(self)
self.penup() # our attributes
self.speed(0)
self.shape("circle")
self.color("green")
self.velocity = 3 #xcor #ycor
self.goto(random.randint(-250, 250), random.randint(-250, 250)) # we are making our turtle "go to" X & Y coordinates by -250 and 250 only randomly. We also have our random module here aswell
self.setheading(random.randint(0, 360)) # setting the heading to see in which direction i want it to go
def jump(self): # Jump = Collidee
self.goto(random.randint(-250, 250), random.randint(-250, 250)) # "jump" stands for Collidee so if the circle "jumps" with player it will move to random postion by 250 and -25
self.setheading(random.randint(0, 360)) # from where it collidee's it goes 360 moves location 360 Right
def move(self): # we copyed the same method cause it will be doing the same movements as the player we want it to go "forward" with our set "speed" & also check for our borders we set
self.forward(self.velocity)
# Border Checking
if self.xcor() > 290 or self.xcor() < -290: # Left side is -290 Right side is 290 we also want the coordinates x and y to be below 300 to not go over our border
self.left(60)
if self.ycor() > 290 or self.ycor() < -290:
self.left(60)
# Collision checking function/Method
# Uses the Pythgorean Theorem to measure the distance between two objects
def isCollision(t1, t2): # t1 = turtle1 t2 = turtle also when a function starts with "is" isCollision most likely it will be a Boolean of True or False
a = t1.xcor()-t2.xcor() # xcor = Right -xcor = Left/ when they collide the xcor is 0
b = t1.ycor()-t2.ycor() # ycor = Right -ycor = Left/ when they collide the ycor is 0
distance = math.sqrt((a ** 2) + (b ** 2))
if distance < 30:
return True
else:
return False
# Create class instances
player = Player() # after creating a class must make instances to call it in other words make an Object of the class
border = Border() # sub class
#goal = Goal() #sub class
# Draw our border
border.draw_border()
#create multiple goals
goals = [] # Creating a list of goals
for count in range(6): # We are making the code repeat 6 times
goals.append(Goal()) # each time the code runs it puts a goal the end 6 times
# Set keyboard bindings
ms.listen()
ms.onkey(player.turnleft, "Left")
ms.onkey(player.turnright, "Right")
ms.onkey(player.increasespeed, "Up")
# speed game up
ms.tracer(0.1)
# main loop
while True:
ms.update()
player.move() # these two are class methods
#goal.move() # the reason we copyed like we said is cause it's gunna have the exact same movements as our player!
# we want the goal to be True to in our while loop in order for the code to be excuted
for goal in goals:
# Basically saying If there is a collision between the player and goal we the want the goal to "jump" / Function in our while True loop
goal.move()
if isCollision(player, goal):
goal.jump() # baiscally saying if the goal collide's move or "jump" to other location
time.sleep(0.005)
As far as I can tell, your circles (goals) disappear and reappear elsewhere as you designed them to do. One possibiliy is that since you move the circles to a random location, that location can be close to where they disappeared from, making it appear that nothing happened.
Generally, your code is a mess. Below is my rewrite of it to bring some structure and style to it. As well as take more advantage of what turtle offers:
from turtle import Screen, Turtle
from random import randint
WIDTH, HEIGHT = 600, 600
CURSOR_SIZE = 20
class Border(Turtle):
def __init__(self): # class initializer
super().__init__(visible=False)
self.penup()
self.color("silver")
self.pensize(5)
def draw_border(self):
self.goto(-WIDTH/2, -HEIGHT/2)
self.pendown()
for _ in range(2):
self.forward(WIDTH)
self.left(90)
self.forward(HEIGHT)
self.left(90)
class Player(Turtle):
def __init__(self):
super().__init__(shape="triangle")
self.color("black")
self.penup()
self.velocity = 0.1
def move(self):
self.forward(self.velocity)
if not (CURSOR_SIZE - WIDTH/2 < self.xcor() < WIDTH/2 - CURSOR_SIZE and CURSOR_SIZE - HEIGHT/2 < self.ycor() < HEIGHT/2 - CURSOR_SIZE):
self.undo() # undo forward motion
self.left(60)
self.forward(self.velocity) # redo forward motion in new heading
def turn_left(self):
self.left(30)
def turn_right(self):
self.right(30)
def increase_speed(self):
self.velocity += 1
class Goal(Turtle):
def __init__(self):
super().__init__(shape="circle")
self.color("green")
self.penup()
self.velocity = 3
self.jump()
def jump(self):
while self.distance(player) < CURSOR_SIZE * 10: # make sure we move far away from player
self.goto(randint(CURSOR_SIZE - WIDTH/2, WIDTH/2 - CURSOR_SIZE), randint(CURSOR_SIZE - HEIGHT/2, HEIGHT/2 - CURSOR_SIZE))
self.setheading(randint(0, 360))
def move(self):
self.forward(self.velocity)
if not (CURSOR_SIZE - WIDTH/2 < self.xcor() < WIDTH/2 - CURSOR_SIZE and CURSOR_SIZE - HEIGHT/2 < self.ycor() < HEIGHT/2 - CURSOR_SIZE):
self.undo() # undo forward motion
self.left(60)
self.forward(self.velocity) # redo forward motion in new heading
def isCollision(t1, t2):
return t1.distance(t2) < CURSOR_SIZE
def move():
player.move()
for goal in goals:
goal.move()
if isCollision(player, goal):
goal.jump()
screen.update()
screen.ontimer(move, 10)
# Setting up the Screen
screen = Screen()
screen.bgcolor("red")
screen.title("Space Rocket Minigame #cdlane")
screen.tracer(False)
# Create class instances
Border().draw_border()
player = Player()
# Create multiple goals
goals = [Goal() for _ in range(6)]
# Set keyboard bindings
screen.onkey(player.turn_left, "Left")
screen.onkey(player.turn_right, "Right")
screen.onkey(player.increase_speed, "Up")
screen.listen()
move()
screen.mainloop()
Related
I'm developing the Space Invaders game and I want to create a list of turtles as enemies, in a separate class. I want to move this list from right to left, to the edge of the screen, until the end of the game (while loop). How do to ?
main.py:
from turtle import Turtle, Screen
from enemy import Enemy
def space_invaders():
# SCREEN :
screen = Screen()
screen.setup(width=800, height=800)
screen.title("Space Invaders")
screen.bgcolor("gray1")
screen.tracer(0)
# INSTANCE OF CLASSES :
enemy = Enemy()
# CREATES & DISPLAY ENEMIES :
enemy.create_enemies()
# WHILE THE GAME IS PLAYING :
play_game = True
while play_game:
screen.update()
# enemy.move_enemies_left()
sleep(0.05)
# enemy.move_enemies_right()
sleep(0.05)
space_invaders()
enemy.py:
from turtle import Turtle
class Enemy(Turtle):
def __init__(self):
super().__init__()
self.nb_enemy = 10
self.enemies = []
def create_enemies(self):
x = 0
for nb in range(self.nb_enemy):
enemy = Turtle()
enemy.shapesize(1.2)
enemy.shape("triangle")
enemy.shapesize(stretch_wid=2, stretch_len=1)
enemy.color("red")
enemy.right(90)
enemy.penup()
enemy.goto(x=-225 + x, y=345)
enemy.color("purple")
self.enemies.append(enemy)
x += 50
def move_enemies_left(self):
x = 2
for nb in range(5):
for enemy in self.enemies:
enemy.goto(x=enemy.xcor() - x, y=enemy.ycor())
def move_enemies_right(self):
x = 2
for nb in range(5):
for enemy in self.enemies:
enemy.goto(x=enemy.xcor() + x, y=enemy.ycor())
def refresh_enemies(self):
self.nb_enemy -= 1
I want to keep the classes separate from the main.py and be able to trigger the functions of the enemy class from the while loop. Is this the right thing to do or do I have to create the enemy movement function from main.py ?
Trying to make a simple maze game in Python Turtle. Using the stamp method I have made the outline of the first level, and it loads perfectly. I want to add a second level and access it, but I don't know how. Any help will be appreciated.
P.S - I am quite new to stack overflow so idk how much code I should put here, but expecting help, I am posting the full code. Thank in advance
import turtle
import math
wn = turtle.Screen()
wn.bgcolor("black")
wn.title("A maze game")
wn.setup(700,700)
class Pen(turtle.Turtle):
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square")
self.color("white")
self.penup()
self.speed(0)
class Treasure(turtle.Turtle):
def __init__(self, x, y):
turtle.Turtle.__init__(self)
self.shape("square")
self.color(color_1)
self.penup()
self.speed(0)
self.gold = 100
self.goto(x, y)
def destroy(self):
self.goto(2000, 2000)
self.hideturtle()
def change_color(self):
self.color(color_2)
color_1 = ("white")
color_2 = ("gold")
class Player(turtle.Turtle):
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("circle")
self.color("red")
self.penup()
self.speed(0)
self.gold = 0
def go_up(self):
move_to_x = player.xcor()
move_to_y = player.ycor() + 24
if (move_to_x, move_to_y) not in walls:
self.goto(move_to_x, move_to_y)
def go_down(self):
move_to_x = player.xcor()
move_to_y = player.ycor() - 24
if (move_to_x, move_to_y) not in walls:
self.goto(move_to_x, move_to_y)
def go_left(self):
move_to_x = player.xcor() - 24
move_to_y = player.ycor()
if (move_to_x, move_to_y) not in walls:
self.goto(move_to_x, move_to_y)
def go_right(self):
move_to_x = player.xcor() + 24
move_to_y = player.ycor()
if (move_to_x, move_to_y) not in walls:
self.goto(move_to_x, move_to_y)
def is_close_to(self, other):
a = self.xcor()-other.xcor()
b = self.ycor()-other.ycor()
distance = math.sqrt((a ** 2) + (b ** 2))
if distance < 50:
return True
else:
return False
levels = [""]
level_1 = [
"XXXXXXXXXXXXXXXXXXXXXXXXX",
"X P T X",
"XXXXXXXXXXXXXXXXXXXXXXXXX"
]
treasures = []
levels.append(level_1)
def setup_maze(level):
for y in range(len(level)):
for x in range(len(level[y])):
character = level[y][x]
screen_x = -288 + (x * 24)
screen_y = 288 - (y * 24)
if character == "X":
pen.goto(screen_x, screen_y)
pen.stamp()
walls.append((screen_x, screen_y))
if character == "P":
player.goto(screen_x, screen_y)
if character == "T":
treasures.append(Treasure(screen_x, screen_y))
pen = Pen()
player = Player()
walls = []
setup_maze(levels[1])
turtle.listen()
turtle.onkey(player.go_left,"Left")
turtle.onkey(player.go_right,"Right")
turtle.onkey(player.go_up,"Up")
turtle.onkey(player.go_down,"Down")
wn.tracer(0)
while True:
for treasure in treasures:
if player.is_close_to(treasure):
treasure.change_color()
wn.update()
To load a new level, you need to reset the current state of the world to a known base state, then setup the new level. You seem to have figured out the latter, and the former involves clearing the screen, re-initializing its properties, and clearing the game state.
First, you need to clear the window. As this answer tells us, this can be done by calling turtle.Screen().reset(), or in your case wm.reset().
After that's done, you need to reinitialize all the properties of the screen, like setting the bgcolor and title and other things like this. Right now these happen at the beginning of the file, but you'll need to do them multiple times through your program's lifecycle, so you should place them in a separate function, like init_screen, so that you can call it once you've reset it.
Finally, you need to reset the game state. In your case, this would be your treasures and walls lists, but as you add more features this may include any other data that does not need to persist between one level and the next. This data actually becomes invalid as soon as you call setup_maze, so it would make sense to first clear them and then setup the new level in the inner loops.
I have a maze game that comprises of 3 different class, which includes 2 maze solving algorithms and a class for players to move around with the up, down, left and right key. So the way to toggle between the classes is to use a tab key. But however, when i reach the class where users can manually control the sprite, there will always be a random black arrow that looks like its going through spasm in the middle of my window. Even though i can manage to control the orange sprite, but that black arrow is still there. Is there anyway that i can remove it? I tried using hideturtle to hide the arrow, but to no avail.
I highly suspect that it was due to this part of my code that resulted in the appearance of the black arrowhead. But i wasn't able to find a replacement for that.
while True:
ManualMovements()
This is how the black sprite looks like right now:
My current code:
from turtle import * # import the turtle library
# define the tile_size and cursor_size for usage later on
TILE_SIZE = 24
CURSOR_SIZE = 20
screen = Screen() # instantiate the Screen class from turtle
screen.setup(700, 700) # determine the size of the turtle pop out window
class Wall(Turtle): # create Wall class to plot out the walls
def __init__(self):
super().__init__() # inherit from Turtle(parent class)
self.hideturtle() # hide the cursor
self.shape('square') # define the shape of the object we wanna draw out
self.shapesize(TILE_SIZE / CURSOR_SIZE) # define the size of the square
self.pencolor('black') # define the color that we are going to plot the grids out
self.penup() # to prevent the pen from leaving a trace
self.speed('fastest') # get the fastest speed
class Path(Turtle): # create Path class to plot out the path
def __init__(self):
super().__init__() # inherit from Turtle(parent class)
self.hideturtle() # hide the cursor
self.shape('square') # define the shape of the object we wanna draw out
self.shapesize(TILE_SIZE / CURSOR_SIZE) # define the size of the square
self.pencolor('white') # define the color that we are going to plot the grids out
self.penup() # to prevent the pen from leaving a trace
self.speed('fastest') # get the fastest speed
class Sprite(Turtle): # create Sprite class to define the turtle and its characteristics
def __init__(self):
super().__init__() # inherit from Turtle(parent class)
self.shape('turtle') # define the shape of the object we wanna draw out
self.shapesize((TILE_SIZE / CURSOR_SIZE)-0.4) # define the size of the square
self.color('orange') # define the color that we are going to plot the turtle
self.penup() # to prevent the pen from leaving a trace
self.speed('slowest') # set speed to slowest to observe the sprite movement
class ManualMovements(Sprite): # create ManualMovement class to let the user manually control the sprite
def __init__(self):
super().__init__()
self.moves = 0
self.hideturtle()
self.screen.onkeypress(self.go_up, "Up")
self.screen.onkeypress(self.go_down, "Down")
self.screen.onkeypress(self.go_left, "Left")
self.screen.onkeypress(self.go_right, "Right")
def go_up(self):
xcor = sprite.xcor()
ycor = sprite.ycor()
if (xcor, ycor + TILE_SIZE) not in walls:
sprite.setheading(90)
sprite.goto(xcor, ycor + TILE_SIZE)
def go_down(self):
xcor = sprite.xcor()
ycor = sprite.ycor()
if (xcor, ycor - TILE_SIZE) not in walls:
sprite.setheading(270)
sprite.goto(xcor, ycor - TILE_SIZE)
def go_left(self):
xcor = sprite.xcor()
ycor = sprite.ycor()
if (xcor - TILE_SIZE, ycor) not in walls:
sprite.setheading(180)
sprite.goto(xcor - TILE_SIZE, ycor)
def go_right(self):
xcor = sprite.xcor()
ycor = sprite.ycor()
if (xcor + TILE_SIZE, ycor) not in walls:
sprite.setheading(0)
sprite.goto(xcor + TILE_SIZE, ycor)
def setup_maze(level): # create a setup_maze function so that we can plot out the map in turtle
# declare maze_height and maze_width first as the limits for the entire maze
maze_height, maze_width = len(level), len(level[0])
# get the center point for each maze
center_horizontal_point = (maze_width + 1) / 2
center_vertical_point = (maze_height + 1) / 2
for y in range(maze_height): # for loop to limit the entire maze
for x in range(maze_width):
character = level[y][x] # get the character at each x,y coordinate
# calculate the screen x, y coordinates
screen_x = ((x - maze_width) * TILE_SIZE) + (center_horizontal_point * TILE_SIZE)
screen_y = ((maze_height - y) * TILE_SIZE) - (center_vertical_point * TILE_SIZE)
if character == "X":
maze.fillcolor('grey')
maze.goto(screen_x, screen_y)
maze.stamp()
walls.append((screen_x, screen_y)) # add coordinates for the wall to the list
else:
maze.fillcolor('white')
maze.goto(screen_x, screen_y)
maze.stamp()
paths.append((screen_x, screen_y)) # add coordinates for the path to the list
if character == "e":
maze.fillcolor(['white', 'red'][character == 'e'])
maze.goto(screen_x, screen_y) # proceed on to the coordinates on turtle
maze.stamp() # stamp out the boxes
finish.append((screen_x, screen_y)) # add coordinates for the endpoint to the list
if character == 's': # if statement to determine if the character is s
maze.fillcolor('green')
maze.goto(screen_x, screen_y)
maze.stamp() # stamp out the boxes
start.append((screen_x, screen_y)) # add coordinates for the startpoint to the list
sprite.goto(screen_x, screen_y) # move the sprite to the location where it is supposed to start
def endProgram(): # exit the entire program upon clicking anywhere in the turtle window
screen.exitonclick()
grid = [] # create a grid list to store the labels while reading from the txt file
walls = [] # create walls coordinate list
start = []
finish = [] # enable the finish array
paths = []
maze = Wall() # enable the Wall class
sprite = Sprite() # enable the Sprite class
path = Path()
with open("map02.txt") as file: # open the txt file and read contents and append it to maze
for line in file:
grid.append(line.strip())
setup_maze(grid) # call the setup maze function
start_x, start_y = (start[0])[0], (start[0])[1]
sprite.seth(0)
sprite.goto(start_x, start_y)
while True:
ManualMovements()
screen.listen() # we need this in order to allow turtle to detect what key we are pressing and respond to it
screen.mainloop()
A sample txt file map:
XXXXXXXXXXXXXXXXXXXX
Xe.................X
XXXXXXX...X..XXXXX.X
XXXXXX....X.....XXXX
XXX.......X...X.XXXX
XXXXXX....X.....XXXX
X.........X...XXXXXX
X.XXXXXX..X...XXXXXX
X.X.......X..XXXXXXX
X.X...XXXX.........X
X.XXXXXsXX..XXXXXXXX
X..............XXXXX
XXXXXXXXXXXXXXXXXXXX
EDIT: Some edits have been made. Please relook again. In order to execute the code, you have to press tab once before pressing the up, down, left, right buttons from my side. I also have removed the 2 classes to prevent confusion and also since i pinpointed that this part of the code is where my error should come from. The current code above does replicate the same error too.
The problem is that your ManualMovements class is wrong-headed. Rather than a bizarre helper class, it should be a subclass of Sprite. (Akin to a fully automated Sprite subclass.) I've reworked your code accordingly below as well as made other fixes and optimizations -- pick and choose as you see fit:
from turtle import Screen, Turtle
# define some global constants for use later
TILE_SIZE = 24
CURSOR_SIZE = 20
class Wall(Turtle):
''' class to plot out walls '''
def __init__(self):
super().__init__(shape='square') # inherit from parent class
self.hideturtle()
self.shapesize(TILE_SIZE / CURSOR_SIZE) # define the size of the square
self.penup() # prevent the pen from leaving a trace
self.speed('fastest')
class Path(Wall):
''' class to plot out the path '''
def __init__(self):
super().__init__() # inherit from parent class
self.pencolor('white') # define the color that we are going to plot the grids out
class Sprite(Turtle):
''' class to define turtle sprite and its characteristics '''
def __init__(self):
super().__init__(shape='turtle') # inherit from parent class
self.shapesize((TILE_SIZE / CURSOR_SIZE) - 0.4) # define the size of the square
self.color('orange') # color that we are going to plot the turtle
self.penup() # prevent the pen from leaving a trace
self.speed('slowest') # set speed to slowest to observe the sprite movement
class ManualSprite(Sprite):
''' class to let the user manually control the sprite '''
def __init__(self):
super().__init__()
screen.onkeypress(self.go_up, 'Up')
screen.onkeypress(self.go_down, 'Down')
screen.onkeypress(self.go_left, 'Left')
screen.onkeypress(self.go_right, 'Right')
def go_up(self):
screen.onkeypress(None, 'Up') # disable handler inside handler
xcor = int(self.xcor())
ycor = int(self.ycor())
if (xcor, ycor + TILE_SIZE) not in walls:
self.setheading(90)
self.sety(ycor + TILE_SIZE)
screen.onkeypress(self.go_up, 'Up') # reenable handler on exit
def go_down(self):
screen.onkeypress(None, 'Down')
xcor = int(self.xcor())
ycor = int(self.ycor())
if (xcor, ycor - TILE_SIZE) not in walls:
self.setheading(270)
self.sety(ycor - TILE_SIZE)
screen.onkeypress(self.go_down, 'Down')
def go_left(self):
screen.onkeypress(None, 'Left')
xcor = int(self.xcor())
ycor = int(self.ycor())
if (xcor - TILE_SIZE, ycor) not in walls:
self.setheading(180)
self.setx(xcor - TILE_SIZE)
screen.onkeypress(self.go_left, 'Left')
def go_right(self):
screen.onkeypress(None, 'Right')
xcor = int(self.xcor())
ycor = int(self.ycor())
if (xcor + TILE_SIZE, ycor) not in walls:
self.setheading(0)
self.setx(xcor + TILE_SIZE)
screen.onkeypress(self.go_right, 'Right')
def setup_maze(level):
''' plot out the map as a maze in turtle '''
# declare the limits for the entire maze
maze_height, maze_width = len(level), len(level[0])
# get the center point for each maze
center_horizontal_point = (maze_width + 1) / 2
center_vertical_point = (maze_height + 1) / 2
start = finish = None
for y in range(maze_height):
for x in range(maze_width):
character = level[y][x] # get the character at each coordinate
# calculate the screen x, y coordinates
screen_x = int((x - maze_width) * TILE_SIZE + center_horizontal_point * TILE_SIZE)
screen_y = int((maze_height - y) * TILE_SIZE - center_vertical_point * TILE_SIZE)
maze.goto(screen_x, screen_y)
if character == 'X':
maze.fillcolor('grey')
walls.append((screen_x, screen_y)) # add coordinates for the wall
else:
paths.append((screen_x, screen_y)) # add coordinates for the path
if character == 'e':
maze.fillcolor('red')
finish = (screen_x, screen_y)
elif character == 's':
maze.fillcolor('green')
start = (screen_x, screen_y)
else:
maze.fillcolor('white')
maze.stamp()
return start, finish
grid = [] # create a grid list to store the labels while reading from the txt file
with open("map02.txt") as file: # open the txt file and read contents and append it to maze
for line in file:
grid.append(line.strip())
screen = Screen() # extract the Screen class from turtle
screen.setup(700, 700) # set the size of the turtle window
sprite = ManualSprite() # instantiate a Sprite instance
walls = [] # walls coordinate list
paths = []
maze = Wall() # instantiate the Wall class
path = Path()
start, finish = setup_maze(grid)
sprite.setheading(0)
sprite.goto(start) # move the sprite to the start location
screen.listen() # allow turtle to detect key press and respond to it
screen.mainloop()
I've tossed comments that simply echo the obvious. Also, your logic didn't take into account the imprecision of floating point coordinates which makes them difficult to compare -- I've converted those comparisons to int.
For my homework I have to draw a rectangle frame and the turtle should be a dot and moving to a random destination.
When I press the space bar(which starts and stops the simulation), the frame starts changing position bouncing with the dot. The dot is also not moving but only bounce in the center.
'''
import turtle
import random
#used to infect
class Virus:
def __init__(self, colour, duration):
self.colour = colour
self.duration = duration
## This class represents a person
class Person:
def __init__(self, world_size):
self.world_size = world_size
self.radius = 7
self.location = turtle.position()
self.destination = self._get_random_location()
#random locations are used to assign a destination for the person
#the possible locations should not be closer than 1 radius to the edge of the world
def _get_random_location(self):
x = random.randint(-349, 349)
y = random.randint(-249, 249)
return (x, y)
#draw a person using a dot. Use colour if implementing Viruses
def draw(self):
turtle.penup()
turtle.home()
turtle.pendown()
turtle.dot(self.radius*2)
#returns true if within 1 radius
def reached_destination(self):
self.location = turtle.position()
distX = abs(abs(self.destination[0])-abs(self.location[0]))
distY = abs(abs(self.destination[1])- abs(self.location[1]))
if distX and distY < self.radius:
return True
else:
pass
#Updates the person each hour.
#- moves each person by calling the move method
#- if the destination is reached then set a new destination
#- progress any illness
def update(self):
self.move()
if self.reached_destination():
self._get_random_location()
else:
self.move()
#moves person towards the destination
def move(self):
turtle.setheading(turtle.towards(self.destination))
turtle.forward(self.radius/2)
class World:
def __init__(self, width, height, n):
self.size = (width, height)
self.hours = 0
self.people = []
self.add_person()
#add a person to the list
def add_person(self):
person = Person(1)
self.people.append(person)
#simulate one hour in the world.
#- increase hours passed.
#- update all people
#- update all infection transmissions
def simulate(self):
self.hours += 1
for item in self.people:
item.update()
#Draw the world. Perform the following tasks:
# - clear the current screen
# - draw all the people
# - draw the box that frames the world
# - write the number of hours and number of people infected at the top of the frame
def draw(self):
turtle.clear()
turtle.hideturtle()
turtle.penup()
turtle.right(180)
turtle.forward(250)
turtle.right(90)
turtle.forward(350)
turtle.left(180)
turtle.pendown()
turtle.forward(700)
turtle.left(90)
turtle.forward(500)
turtle.left(90)
turtle.forward(700)
turtle.left(90)
turtle.forward(500)
turtle.right(180)
turtle.forward(500)
turtle.write(f'Hours: {self.hours}', False, 'left')
turtle.update()
for item in self.people:
item.draw()
#---------------------------------------------------------
#Should not need to alter any of the code below this line
#---------------------------------------------------------
class GraphicalWorld:
""" Handles the user interface for the simulation
space - starts and stops the simulation
'z' - resets the application to the initial state
'x' - infects a random person
'c' - cures all the people
"""
def __init__(self):
self.WIDTH = 800
self.HEIGHT = 600
self.TITLE = 'COMPSCI 130 Project One'
self.MARGIN = 50 #gap around each side
self.PEOPLE = 200 #number of people in the simulation
self.framework = AnimationFramework(self.WIDTH, self.HEIGHT, self.TITLE)
self.framework.add_key_action(self.setup, 'z')
self.framework.add_key_action(self.infect, 'x')
self.framework.add_key_action(self.cure, 'c')
self.framework.add_key_action(self.toggle_simulation, ' ')
self.framework.add_tick_action(self.next_turn)
self.world = None
def setup(self):
""" Reset the simulation to the initial state """
print('resetting the world')
self.framework.stop_simulation()
self.world = World(self.WIDTH - self.MARGIN * 2, self.HEIGHT - self.MARGIN * 2, self.PEOPLE)
self.world.draw()
def infect(self):
""" Infect a person, and update the drawing """
print('infecting a person')
self.world.infect_person()
self.world.draw()
def cure(self):
""" Remove infections from all the people """
print('cured all people')
self.world.cure_all()
self.world.draw()
def toggle_simulation(self):
""" Starts and stops the simulation """
if self.framework.simulation_is_running():
self.framework.stop_simulation()
else:
self.framework.start_simulation()
def next_turn(self):
""" Perform the tasks needed for the next animation cycle """
self.world.simulate()
self.world.draw()
## This is the animation framework
## Do not edit this framework
class AnimationFramework:
"""This framework is used to provide support for animation of
interactive applications using the turtle library. There is
no need to edit any of the code in this framework.
"""
def __init__(self, width, height, title):
self.width = width
self.height = height
self.title = title
self.simulation_running = False
self.tick = None #function to call for each animation cycle
self.delay = 1 #smallest delay is 1 millisecond
turtle.title(title) #title for the window
turtle.setup(width, height) #set window display
turtle.hideturtle() #prevent turtle appearance
turtle.tracer(0, 0) #prevent turtle animation
turtle.listen() #set window focus to the turtle window
turtle.mode('logo') #set 0 direction as straight up
turtle.penup() #don't draw anything
turtle.setundobuffer(None)
self.__animation_loop()
def start_simulation(self):
self.simulation_running = True
def stop_simulation(self):
self.simulation_running = False
def simulation_is_running(self):
return self.simulation_running
def add_key_action(self, func, key):
turtle.onkeypress(func, key)
def add_tick_action(self, func):
self.tick = func
def __animation_loop(self):
try:
if self.simulation_running:
self.tick()
turtle.ontimer(self.__animation_loop, self.delay)
except turtle.Terminator:
pass
gw = GraphicalWorld()
gw.setup()
turtle.mainloop()
'''
The turtle dot should be bouncing slowly to the random location and the frame should stay still when I press the space bar. And I know the code is long sorry about that.
the frame starts changing position bouncing with the dot. The dot is
also not moving but only bounce in the center.
I've reworked your Person and World classes below to address these two issues. This simulation model you're given uses a single turtle which means we have to play by certain rules:
Each Person must keep track of their own position and heading
Only the draw() method should have the pen down, all other Person methods should have the pen up if using the turtle to do calculations
Whenever a Person uses the turtle, you can't assume anything about it as another Person was just using the turtle so you must set your heading, position and pen state
The changed portion of your posted code:
FONT = ('Arial', 16, 'normal')
# This class represents a person
class Person():
def __init__(self, world_size):
self.world_size = world_size
self.radius = 7
self.location = turtle.position()
self.destination = self._get_random_location()
turtle.penup()
turtle.setposition(self.location)
turtle.setheading(turtle.towards(self.destination))
self.heading = turtle.heading()
# random locations are used to assign a destination for the person
# the possible locations should not be closer than 1 radius to the edge of the world
def _get_random_location(self):
x = random.randint(self.radius - 349, 349 - self.radius)
y = random.randint(self.radius - 249, 249 - self.radius)
return (x, y)
# draw a person using a dot. Use colour if implementing Viruses
def draw(self):
x, y = self.location
# use .circle() not .dot() as the latter causes an extra update (flicker)
turtle.penup()
turtle.setposition(x, y - self.radius)
turtle.pendown()
turtle.begin_fill()
turtle.circle(self.radius)
turtle.end_fill()
# returns true if within 1 radius
def reached_destination(self):
distX = abs(self.destination[0] - self.location[0])
distY = abs(self.destination[1] - self.location[1])
return distX < self.radius and distY < self.radius
# Updates the person each hour.
# - moves each person by calling the move method
# - if the destination is reached then set a new destination
# - progress any illness
def update(self):
self.move()
if self.reached_destination():
self.destination = self._get_random_location()
turtle.penup()
turtle.setposition(self.location)
turtle.setheading(turtle.towards(self.destination))
self.heading = turtle.heading()
# moves person towards the destination
def move(self):
turtle.penup()
turtle.setheading(self.heading)
turtle.setposition(self.location)
turtle.forward(self.radius / 2)
self.location = turtle.position()
class World:
def __init__(self, width, height, n):
self.size = (width, height)
self.hours = 0
self.people = []
for _ in range(n):
self.add_person()
# add a person to the list
def add_person(self):
person = Person(1)
self.people.append(person)
# simulate one hour in the world.
# - increase hours passed.
# - update all people
# - update all infection transmissions
def simulate(self):
self.hours += 1
for item in self.people:
item.update()
# Draw the world. Perform the following tasks:
# - clear the current screen
# - draw all the people
# - draw the box that frames the world
# - write the number of hours and number of people infected at the top of the frame
def draw(self):
turtle.clear() # also undoes hideturtle(), etc.
turtle.hideturtle()
turtle.setheading(0)
turtle.penup()
turtle.setposition(-350, -250)
turtle.pendown()
for _ in range(2):
turtle.forward(500)
turtle.right(90)
turtle.forward(700)
turtle.right(90)
for item in self.people:
item.draw()
turtle.penup()
turtle.setposition(-350, -250)
# leave this to the end as .write() forces an extra update (flicker)
turtle.write(f'Hours: {self.hours}', move=False, align='left', font=FONT)
turtle.update()
I've also included some tweaks to reduce flicker. I'm sure there's lots more work to be done.
If we wanted to run this simulation faster, we'd make each Person a separate turtle (e.g. via inheritance) and use the reshaped turtle itself as it's presence on the screen and not draw the Person. And we'd throw separate turtles at the outline frame and the text to simplify updates.
I'm learning Object Orientated Python and understand the main principals of classes and creating objects from classes however I need something explained Re: the pygame code below. I'm struggling to get my head around what's happening when sprite lists are being created and the two lines of code under the code which creates the ball object (allsprites.add etc). In other words what are sprites and why are lists of them created? Why isn't the ball object just created from the class on its own? Why does it need to be added to a sprite list?? What's going on? Any explanation would be greatly appreciated.
"""
Sample Breakout Game
Sample Python/Pygame Programs
Simpson College Computer Science
http://programarcadegames.com/
http://simpson.edu/computer-science/
"""
# --- Import libraries used for this program
import math
import pygame
# Define some colors
black = (0, 0, 0)
white = (255, 255, 255)
blue = (0, 0, 255)
# Size of break-out blocks
block_width = 23
block_height = 15
class Block(pygame.sprite.Sprite):
"""This class represents each block that will get knocked out by the ball
It derives from the "Sprite" class in Pygame """
def __init__(self, color, x, y):
""" Constructor. Pass in the color of the block,
and its x and y position. """
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create the image of the block of appropriate size
# The width and height are sent as a list for the first parameter.
self.image = pygame.Surface([block_width, block_height])
# Fill the image with the appropriate color
self.image.fill(color)
# Fetch the rectangle object that has the dimensions of the image
self.rect = self.image.get_rect()
# Move the top left of the rectangle to x,y.
# This is where our block will appear..
self.rect.x = x
self.rect.y = y
class Ball(pygame.sprite.Sprite):
""" This class represents the ball
It derives from the "Sprite" class in Pygame """
# Speed in pixels per cycle
speed = 10.0
# Floating point representation of where the ball is
x = 0.0
y = 180.0
# Direction of ball (in degrees)
direction = 200
width = 10
height = 10
# Constructor. Pass in the color of the block, and its x and y position
def __init__(self):
# Call the parent class (Sprite) constructor
pygame.sprite.Sprite.__init__(self)
# Create the image of the ball
self.image = pygame.Surface([self.width, self.height])
# Color the ball
self.image.fill(white)
# Get a rectangle object that shows where our image is
self.rect = self.image.get_rect()
# Get attributes for the height/width of the screen
self.screenheight = pygame.display.get_surface().get_height()
self.screenwidth = pygame.display.get_surface().get_width()
def bounce(self, diff):
""" This function will bounce the ball
off a horizontal surface (not a vertical one) """
self.direction = (180 - self.direction) % 360
self.direction -= diff
def update(self):
""" Update the position of the ball. """
# Sine and Cosine work in degrees, so we have to convert them
direction_radians = math.radians(self.direction)
# Change the position (x and y) according to the speed and direction
self.x += self.speed * math.sin(direction_radians)
self.y -= self.speed * math.cos(direction_radians)
# Move the image to where our x and y are
self.rect.x = self.x
self.rect.y = self.y
# Do we bounce off the top of the screen?
if self.y <= 0:
self.bounce(0)
self.y = 1
# Do we bounce off the left of the screen?
if self.x <= 0:
self.direction = (360 - self.direction) % 360
self.x = 1
# Do we bounce of the right side of the screen?
if self.x > self.screenwidth - self.width:
self.direction = (360 - self.direction) % 360
self.x = self.screenwidth - self.width - 1
# Did we fall off the bottom edge of the screen?
if self.y > 600:
return True
else:
return False
class Player(pygame.sprite.Sprite):
""" This class represents the bar at the bottom that the player controls. """
def __init__(self):
""" Constructor for Player. """
# Call the parent's constructor
pygame.sprite.Sprite.__init__(self)
self.width = 75
self.height = 15
self.image = pygame.Surface([self.width, self.height])
self.image.fill((white))
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.screenheight = pygame.display.get_surface().get_height()
self.screenwidth = pygame.display.get_surface().get_width()
self.rect.x = 0
self.rect.y = self.screenheight-self.height
def update(self):
""" Update the player position. """
# Get where the mouse is
pos = pygame.mouse.get_pos()
# Set the left side of the player bar to the mouse position
self.rect.x = pos[0]
# Make sure we don't push the player paddle
# off the right side of the screen
if self.rect.x > self.screenwidth - self.width:
self.rect.x = self.screenwidth - self.width
# Call this function so the Pygame library can initialize itself
pygame.init()
# Create an 800x600 sized screen
screen = pygame.display.set_mode([800, 600])
# Set the title of the window
pygame.display.set_caption('Breakout')
# Enable this to make the mouse disappear when over our window
pygame.mouse.set_visible(0)
# This is a font we use to draw text on the screen (size 36)
font = pygame.font.Font(None, 36)
# Create a surface we can draw on
background = pygame.Surface(screen.get_size())
# Create sprite lists
blocks = pygame.sprite.Group()
balls = pygame.sprite.Group()
allsprites = pygame.sprite.Group()
# Create the player paddle object
player = Player()
allsprites.add(player)
# Create the ball
ball = Ball()
allsprites.add(ball)
balls.add(ball)
# The top of the block (y position)
top = 80
# Number of blocks to create
blockcount = 32
# --- Create blocks
# Five rows of blocks
for row in range(5):
# 32 columns of blocks
for column in range(0, blockcount):
# Create a block (color,x,y)
block = Block(blue, column * (block_width + 2) + 1, top)
blocks.add(block)
allsprites.add(block)
# Move the top of the next row down
top += block_height + 2
# Clock to limit speed
clock = pygame.time.Clock()
# Is the game over?
game_over = False
# Exit the program?
exit_program = False
# Main program loop
while exit_program != True:
# Limit to 30 fps
clock.tick(30)
# Clear the screen
screen.fill(black)
# Process the events in the game
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit_program = True
# Update the ball and player position as long
# as the game is not over.
if not game_over:
# Update the player and ball positions
player.update()
game_over = ball.update()
# If we are done, print game over
if game_over:
text = font.render("Game Over", True, white)
textpos = text.get_rect(centerx=background.get_width()/2)
textpos.top = 300
screen.blit(text, textpos)
# See if the ball hits the player paddle
if pygame.sprite.spritecollide(player, balls, False):
# The 'diff' lets you try to bounce the ball left or right
# depending where on the paddle you hit it
diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2)
# Set the ball's y position in case
# we hit the ball on the edge of the paddle
ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1
ball.bounce(diff)
# Check for collisions between the ball and the blocks
deadblocks = pygame.sprite.spritecollide(ball, blocks, True)
# If we actually hit a block, bounce the ball
if len(deadblocks) > 0:
ball.bounce(0)
# Game ends if all the blocks are gone
if len(blocks) == 0:
game_over = True
# Draw Everything
allsprites.draw(screen)
# Flip the screen and show what we've drawn
pygame.display.flip()
pygame.quit()
You don't need to add the balls and blocks to sprite lists - it's just a matter of convenience. You could manually check each ball for a collision, but it's easier to just tell pygame to check them all for you
# See if the ball hits the player paddle
if pygame.sprite.spritecollide(player, balls, False):
# The 'diff' lets you try to bounce the ball left or right
# depending where on the paddle you hit it
diff = (player.rect.x + player.width/2) - (ball.rect.x+ball.width/2)
# Set the ball's y position in case
# we hit the ball on the edge of the paddle
ball.rect.y = screen.get_height() - player.rect.height - ball.rect.height - 1
ball.bounce(diff)
You could draw each thing to the screen separately on each frame, but it's easier just to tell pygame to do it for you:
# Draw Everything
allsprites.draw(screen)
Things can be in more than one list as required, for example a ball is added to the balls list so that you can easily check for collisions, but also added to the allsprites list so that you can easily draw everything on the screen
# Create the ball
ball = Ball()
allsprites.add(ball)
balls.add(ball)
Edit:
An important distinction is that allsprites is actually a sprite.Group. It has a list of sprites inside it, but it also has other methods like draw.
To address your question of "what is a Sprite", it's simply a thing that gets drawn on screen. pygame methods like sprite.Group.draw expect a list of things with certain attributes - such as update. The easiest way to make sure that you provide all of those attributes with the right names is to subclass Sprite, however this is also a (strongly recommended) convenience thing - for instance, this is from the pygame source code:
While it is possible to design sprite and group classes that don't
derive from the Sprite and AbstractGroup classes below, it is strongly
recommended that you extend those when you add a Sprite or Group
class.
So what specifically does subclassing Sprite get you? Let's take a look at the source. Here's how to find the source code for a python module:
>>> import pygame.sprite
>>> pygame.sprite.__file__
'c:\\Python27\\lib\\site-packages\\pygame\\sprite.py'
>>>
Every python module has a __file__ attribute that tells you where the source is located (well not quite every). If you open it up in your editor, and scroll down, you see the class definition for Sprite:
class Sprite(object):
"""simple base class for visible game objects
pygame.sprite.Sprite(*groups): return Sprite
The base class for visible game objects. Derived classes will want to
override the Sprite.update() and assign a Sprite.image and
Sprite.rect attributes. The initializer can accept any number of
Group instances to be added to.
When subclassing the Sprite, be sure to call the base initializer before
adding the Sprite to Groups.
"""
def __init__(self, *groups):
self.__g = {} # The groups the sprite is in
if groups: self.add(groups)
def add(self, *groups):
"""add the sprite to groups
Sprite.add(*groups): return None
Any number of Group instances can be passed as arguments. The
Sprite will be added to the Groups it is not already a member of.
"""
has = self.__g.__contains__
for group in groups:
if hasattr(group, '_spritegroup'):
if not has(group):
group.add_internal(self)
self.add_internal(group)
else: self.add(*group)
def remove(self, *groups):
"""remove the sprite from groups
Sprite.remove(*groups): return None
Any number of Group instances can be passed as arguments. The Sprite will
be removed from the Groups it is currently a member of.
"""
has = self.__g.__contains__
for group in groups:
if hasattr(group, '_spritegroup'):
if has(group):
group.remove_internal(self)
self.remove_internal(group)
else: self.remove(*group)
def add_internal(self, group):
self.__g[group] = 0
def remove_internal(self, group):
del self.__g[group]
def update(self, *args):
"""method to control sprite behavior
Sprite.update(*args):
The default implementation of this method does nothing; it's just a
convenient "hook" that you can override. This method is called by
Group.update() with whatever arguments you give it.
There is no need to use this method if not using the convenience
method by the same name in the Group class.
"""
pass
def kill(self):
"""remove the Sprite from all Groups
Sprite.kill(): return None
The Sprite is removed from all the Groups that contain it. This won't
change anything about the state of the Sprite. It is possible to continue
to use the Sprite after this method has been called, including adding it
to Groups.
"""
for c in self.__g.keys():
c.remove_internal(self)
self.__g.clear()
def groups(self):
"""list of Groups that contain this Sprite
Sprite.groups(): return group_list
Return a list of all the Groups that contain this Sprite.
"""
return self.__g.keys()
def alive(self):
"""does the sprite belong to any groups
Sprite.alive(): return bool
Returns True when the Sprite belongs to one or more Groups.
"""
return (len(self.__g) != 0)
def __repr__(self):
return "<%s sprite(in %d groups)>" % (self.__class__.__name__, len(self.__g))
So in summary, you don't have to subclass Sprite - you could just provide all of these methods on your own - but it's easier if you do ;)