How to plot out grid lines in Python Turtle module? - python

so currently i'm trying to plot out a block maze by reading from a .txt file and then displaying it in Python's Turtle Library. Currently, my code is only able to draw out the boxes, but not the grid lines surrounding the boxes. Is there anyway to deal with this problem, cuze i tried to see the docs and they only suggested turtle.Turtle.fillcolor, which doesnt really seems right.
Here's my current code:
# import the necessary library
import turtle
# read the .txt file here!
with open("map01.txt") as f:
content = f.readlines()
content = [x.strip() for x in content]
# create the map here!
window = turtle.Screen()
window.bgcolor("white") # set the background as white(check if this is default)
window.title("PIZZA RUNNERS") # create the titlebar
window.setup(700,700)
# create pen
class Pen(turtle.Turtle):
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square")
self.color('grey')
self.penup()
self.speed(0)
# create maze_list
maze = []
# add the maze to maze
maze.append(content)
# create conversion from the list to the map in turtle
def setup_maze(level):
for y in range(len(level)):
for x in range(len(level[y])):
# get the character at each x,y coordinate
character = level[y][x]
# calculate the screen x, y coordinates
screen_x = -288 + (x * 24)
screen_y = 288 - (y * 24)
# check if it is a wall
if character == "X":
pen.goto(screen_x, screen_y)
pen.stamp()
# create class instances
pen = Pen()
# set up the maze
setup_maze(maze[0])
# main game loop
while True:
pass
And this is how the current text file i am reading from looks like:
XXXXXXXXXXXX
X.........eX
X.XXX.XXX..X
X.XsX.X.XX.X
X.X......X.X
X.XXXXXXXX.X
X..........X
XXXXXXXXXXXX
The 's' and 'e' are supposed to represent the starting point and the ending point, which is not implemented yet, so it can be ignored. The dots represent the path, and the X represents walls. The current map(or txt) dimension is 8 rows by 12 columns.
Right now my output looks like this:
I wanted it to look something like this(referring to the addition of grids, not the same pattern maze):

Assuming what you desire is:
Then we need to resize the square cursor from its default size of 20 to your tile size of 24:
from turtle import Screen, Turtle
TILE_SIZE = 24
CURSOR_SIZE = 20
class Pen(Turtle):
def __init__(self):
super().__init__()
self.shape('square')
self.shapesize(TILE_SIZE / CURSOR_SIZE)
self.color('grey')
self.penup()
self.speed('fastest')
def setup_maze(level):
''' Conversion from the list to the map in turtle. '''
maze_height, maze_width = len(level), len(level[0])
for y in range(maze_height):
for x in range(maze_width):
# get the character at each x,y coordinate
character = level[y][x]
# check if it is a wall
if character == 'X':
# calculate the screen x, y coordinates
screen_x = (x - maze_width) * TILE_SIZE
screen_y = (maze_width - y) * TILE_SIZE
pen.goto(screen_x, screen_y)
pen.stamp()
screen = Screen()
screen.setup(700, 700)
screen.title("PIZZA RUNNERS")
maze = []
with open("map01.txt") as file:
for line in file:
maze.append(line.strip())
pen = Pen()
setup_maze(maze)
screen.mainloop()
If you're looking for something more like:
Then change the line:
self.color('grey')
in the code above to:
self.color('black', 'grey')
Finally, if you desire:
Then we need to make a couple of small changes to the code above:
class Pen(Turtle):
def __init__(self):
super().__init__()
self.shape('square')
self.shapesize(TILE_SIZE / CURSOR_SIZE)
self.pencolor('black')
self.penup()
self.speed('fastest')
def setup_maze(level):
''' Conversion from the list to the map in turtle. '''
maze_height, maze_width = len(level), len(level[0])
for y in range(maze_height):
for x in range(maze_width):
# get the character at each x,y coordinate
character = level[y][x]
# check if it is a wall or a path
pen.fillcolor(['white', 'grey'][character == 'X'])
# calculate the screen x, y coordinates
screen_x = (x - maze_width) * TILE_SIZE
screen_y = (maze_width - y) * TILE_SIZE
pen.goto(screen_x, screen_y)
pen.stamp()

Related

How to make sprite (turtle) move through the best path cell by cell

Right now I am creating a maze path finding algorithm using breadth first search. The algorithm to find the 'best path' seems to work. The problem however is that I am not sure how to make the sprite (turtle) move through that best path.
Here is code:
import turtle
import time
import sys
from collections import deque
bs = turtle.Screen()
bs.bgcolor("black")
bs.setup(1300,700)
class Building(turtle.Turtle): # Define building class
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square") # Shape of the building
self.color("grey") # Colour of the building
self.penup() # Lift the pen up so it does not leave a trail
self.speed('fast') # Sets the speed that the building is drawn on the screen
class Road(turtle.Turtle): # Define road class
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square") # Shape of the road
self.color("white") # Colour of the road
self.penup() # Lift the pen up so it does not leave a trail
self.hideturtle()
self.speed('fast') # Sets the speed that the road is drawn on the screen
class Start(turtle.Turtle): # Define start class
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square") # Shape of the start point
self.color('green') # Colour of the start point
self.penup() # Lift the pen up so it does not leave a trail
self.hideturtle()
self.speed('fast')
class Route(turtle.Turtle):
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square")
self.color("yellow")
self.penup()
self.hideturtle()
self.speed('fast')
class End(turtle.Turtle): # Define end class
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square") # Shape of the end point
self.color("red") # Colour of the end point
self.penup() # Lift the pen up so it does not leave a trail
self.hideturtle()
self.speed('fast')
class Searcher(turtle.Turtle):
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square")
self.color("white")
self.penup()
self.hideturtle()
self.speed('fast')
class sprite(turtle.Turtle): # Define sprite class
noOfSteps = 0 # Declare variable noOfSteps and instantiate it as 0
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("turtle") # Shape of the sprite
self.color("purple") # Colour of the sprite
self.penup() # Lift the pen up so it does not leave a trail
self.hideturtle()
self.speed('fast')
def moveSprite(self):
start.color('green')
start.stamp()
searcher.color('red')
searcher.stamp()
self.goto(start_x, start_y)
self.showturtle()
# self.goto(solution[x,y])
# Read maze txt file
def readMaze(mazeSet, filename):
mazeFile = open(filename, "r")
lines = mazeFile.readlines()
for line in lines:
line = line.strip()
row = [c for c in line]
mazeSet.append(row)
mazeSet = [] # This declares the maze as an empty list
readMaze(mazeSet, "CA1_Map.txt") # This reads the maze into the list
# Setting up of maze
def setupMaze(mazeSet):
global start_x, start_y, end_x, end_y
m_height, m_width = len(mazeSet), len(mazeSet[0]) # Define maze height and maze width
for y in range(m_height): # Select each line in the maze
for x in range(m_width): # Identify each character in the line
character = mazeSet[y][x] # Assign the maze reference to the variable 'character'
screen_x = ((x - m_width) * 24) + 150 # Assign screen_x to screen starting position for x coords
screen_y = ((m_width - y) * 24) - 200 # Assign screen_y to screen starting position for y coords
if character == "X":
building.goto(screen_x, screen_y)
building.stamp()
walls.append((screen_x, screen_y))
if character == "." or character == 'e':
path.append((screen_x, screen_y))
if character == "e":
end_x, end_y = screen_x, screen_y
searcher.color('red')
searcher.goto(screen_x, screen_y)
searcher.stamp()
searcher.color('white')
finish.append((screen_x, screen_y))
if character == "s":
start_x, start_y = screen_x, screen_y
start.goto(screen_x,screen_y)
start.stamp()
def search(x,y):
frontier.append((x, y))
solution[x,y] = x,y
while len(frontier) > 0:
time.sleep(0)
x, y = frontier.popleft()
if(x - 24, y) in path and (x - 24, y) not in visited:
cell = (x - 24, y)
solution[cell] = x, y
frontier.append(cell)
visited.add((x-24, y))
if (x, y - 24) in path and (x, y - 24) not in visited:
cell = (x, y - 24)
solution[cell] = x, y
frontier.append(cell)
visited.add((x, y - 24))
if(x + 24, y) in path and (x + 24, y) not in visited:
cell = (x + 24, y)
solution[cell] = x, y
frontier.append(cell)
visited.add((x + 24, y))
if(x, y + 24) in path and (x, y + 24) not in visited:
cell = (x, y + 24)
solution[cell] = x, y
frontier.append(cell)
visited.add((x, y + 24))
searcher.goto(x,y)
searcher.stamp()
def correctRoute(x, y):
route.goto(x, y)
route.stamp()
while (x, y) != (start_x, start_y):
route.goto(solution[x, y])
route.stamp()
x, y = solution[x, y]
def endProgram():
bs.exitonclick()
sys.exit()
# Main program
# Setting up classes
building = Building()
road = Road()
start = Start()
end = End()
searcher = Searcher()
route = Route()
sprite = sprite()
# Setting up lists
walls = []
path = []
finish = []
visited = set()
frontier = deque()
solution = {}
setupMaze(mazeSet)
search(start_x, start_y)
correctRoute(end_x, end_y)
while True:
sprite.moveSprite()
The image of the maze currently looks like this:
So what I need to do is make the purple turtle (kind of hard to see) move from the green square to red square through the best path (yellow squares).
Step 1: At the function where the program determines the correct route,
record each coordinate into a list,
and return the list (inverted) so it can be accessed outside the function:
def correctRoute(x, y):
routes = [(x, y)]
route.goto(x, y)
route.stamp()
while (x, y) != (start_x, start_y):
route.goto(solution[x, y])
route.stamp()
x, y = solution[x, y]
routes.append((x, y))
return routes[::-1]
Step 2: Give the moveSprite function another parameter, and the parameter will be the list of coordinates.
the list will then be split into two parts: the starting coordinates and the path. Before calling self.showturtle(),
make sure that the turtle is in its proper starting position first.
Using a for loop, loop through the path list with time.sleep and make the turtle go to each of the coordinates:
def moveSprite(self, routes):
starting, path = routes[0], routes[1:]
start.color('green')
start.stamp()
searcher.color('red')
searcher.stamp()
self.goto(starting)
self.showturtle()
for path in path:
time.sleep(0.3)
self.goto(path)
Step 3: At the very bottom of your code, assign the correctRoute call to a variable to retrive the coordinates.
Remove the while loop, and put the list of coordinates into the moveSprite function:
routes = correctRoute(end_x, end_y)
sprite.moveSprite(routes)
All together:
import turtle
import time
import sys
from collections import deque
bs = turtle.Screen()
bs.bgcolor("black")
bs.setup(1300,700)
class Building(turtle.Turtle): # Define building class
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square") # Shape of the building
self.color("grey") # Colour of the building
self.penup() # Lift the pen up so it does not leave a trail
self.speed('fast') # Sets the speed that the building is drawn on the screen
class Road(turtle.Turtle): # Define road class
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square") # Shape of the road
self.color("white") # Colour of the road
self.penup() # Lift the pen up so it does not leave a trail
self.hideturtle()
self.speed('fast') # Sets the speed that the road is drawn on the screen
class Start(turtle.Turtle): # Define start class
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square") # Shape of the start point
self.color('green') # Colour of the start point
self.penup() # Lift the pen up so it does not leave a trail
self.hideturtle()
self.speed('fast')
class Route(turtle.Turtle):
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square")
self.color("yellow")
self.penup()
self.hideturtle()
self.speed('fast')
class End(turtle.Turtle): # Define end class
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square") # Shape of the end point
self.color("red") # Colour of the end point
self.penup() # Lift the pen up so it does not leave a trail
self.hideturtle()
self.speed('fast')
class Searcher(turtle.Turtle):
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("square")
self.color("white")
self.penup()
self.hideturtle()
self.speed('fast')
class sprite(turtle.Turtle): # Define sprite class
noOfSteps = 0 # Declare variable noOfSteps and instantiate it as 0
def __init__(self):
turtle.Turtle.__init__(self)
self.shape("turtle") # Shape of the sprite
self.color("purple") # Colour of the sprite
self.penup() # Lift the pen up so it does not leave a trail
self.hideturtle()
self.speed('fast')
def moveSprite(self, routes):
starting, path = routes[0], routes[1:]
start.color('green')
start.stamp()
searcher.color('red')
searcher.stamp()
self.goto(starting)
self.showturtle()
for path in path:
time.sleep(0.3)
self.goto(path)
# Read maze txt file
def readMaze(mazeSet, filename):
mazeFile = open(filename, "r")
lines = mazeFile.readlines()
for line in lines:
line = line.strip()
row = [c for c in line]
mazeSet.append(row)
mazeSet = [] # This declares the maze as an empty list
readMaze(mazeSet, "CA1_Map.txt") # This reads the maze into the list
# Setting up of maze
def setupMaze(mazeSet):
global start_x, start_y, end_x, end_y
m_height, m_width = len(mazeSet), len(mazeSet[0]) # Define maze height and maze width
for y in range(m_height): # Select each line in the maze
for x in range(m_width): # Identify each character in the line
character = mazeSet[y][x] # Assign the maze reference to the variable 'character'
screen_x = ((x - m_width) * 24) + 150 # Assign screen_x to screen starting position for x coords
screen_y = ((m_width - y) * 24) - 200 # Assign screen_y to screen starting position for y coords
if character == "X":
building.goto(screen_x, screen_y)
building.stamp()
walls.append((screen_x, screen_y))
if character == "." or character == 'e':
path.append((screen_x, screen_y))
if character == "e":
end_x, end_y = screen_x, screen_y
searcher.color('red')
searcher.goto(screen_x, screen_y)
searcher.stamp()
searcher.color('white')
finish.append((screen_x, screen_y))
if character == "s":
start_x, start_y = screen_x, screen_y
start.goto(screen_x,screen_y)
start.stamp()
def search(x,y):
frontier.append((x, y))
solution[x,y] = x,y
while len(frontier) > 0:
time.sleep(0)
x, y = frontier.popleft()
if(x - 24, y) in path and (x - 24, y) not in visited:
cell = (x - 24, y)
solution[cell] = x, y
frontier.append(cell)
visited.add((x-24, y))
if (x, y - 24) in path and (x, y - 24) not in visited:
cell = (x, y - 24)
solution[cell] = x, y
frontier.append(cell)
visited.add((x, y - 24))
if(x + 24, y) in path and (x + 24, y) not in visited:
cell = (x + 24, y)
solution[cell] = x, y
frontier.append(cell)
visited.add((x + 24, y))
if(x, y + 24) in path and (x, y + 24) not in visited:
cell = (x, y + 24)
solution[cell] = x, y
frontier.append(cell)
visited.add((x, y + 24))
searcher.goto(x,y)
searcher.stamp()
def correctRoute(x, y):
routes = [(x, y)]
route.goto(x, y)
route.stamp()
while (x, y) != (start_x, start_y):
route.goto(solution[x, y])
route.stamp()
x, y = solution[x, y]
routes.append((x, y))
return routes[::-1]
def endProgram():
bs.exitonclick()
sys.exit()
# Main program
# Setting up classes
building = Building()
road = Road()
start = Start()
end = End()
searcher = Searcher()
route = Route()
sprite = sprite()
# Setting up lists
walls = []
path = []
finish = []
visited = set()
frontier = deque()
solution = {}
setupMaze(mazeSet)
search(start_x, start_y)
routes = correctRoute(end_x, end_y)
sprite.moveSprite(routes)
If you want the turtle to change its direction at each turn, use this as the moveSprite function:
def moveSprite(self, routes):
starting, routes = routes[0], routes[1:]
start.color('green')
start.stamp()
searcher.color('red')
searcher.stamp()
self.goto(starting)
self.showturtle()
for path1, path2 in zip(routes, routes[1:]):
self.goto(path1)
time.sleep(0.3)
if path1[0] == path2[0]: # If the x coordinates of two consecutive moves are equal, that means that the turtle is moving along the `y` axis
if path1[1] > path2[1]:
self.setheading(270)
else:
self.setheading(90)
else: # If the x coordinates of two consecutive moves aren't equal, that means that the turtle is moving along the `x` axis
if path1[0] > path2[0]:
self.setheading(180)
else:
self.setheading(0)
self.goto(path2)

How to remove the black sprite in the middle of my screen?

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.

Python Turtle Write Value in Containing Box

I want to be able to create some turtles which display values by subclassing turtle.Turtle.
These turtles should display their value as text centered in their own shape. I also want to be able to position the turtles with accuracy, so setting/determining their width and height relative to a given font size is important.
This is my attempt so far:
I think this answer is relevant: How to know the pixel size of a specific text on turtle graphics in python? but it is quite old, and the bounding box it draws around the text is not in the correct position using python 3.8.
import turtle
FONT_SIZE = 32
class Tile(turtle.Turtle):
def __init__(self):
super().__init__(shape="square")
self.penup()
def show_value(self, val):
self.write(val, font=("Arial", FONT_SIZE, "bold"), align="center")
screen = turtle.Screen()
vals = [5, 7, 8, 2]
for i in range(len(vals)):
tile = Tile()
tile_size = (FONT_SIZE / 20)
tile.shapesize(tile_size)
tile.fillcolor("red" if i % 2 == 0 else "blue")
tile.setx(i * FONT_SIZE)
tile.show_value(vals[i])
turtle.done()
It would be very helpful to have Turtle Objects containing text such
as integer values, which can be used to display a variety of puzzles
and games, and can have their own click handlers attached.
Here's the rub, and the (two) reason(s) that approaches using stamp() as suggested in other answers won't work. First, you can't click on a hidden turtle:
from turtle import *
def doit(x, y):
print("Just do it!")
yertle = Turtle()
# comment out the following line if you want `onlick()` to work
yertle.hideturtle()
yertle.shape('square')
yertle.stamp()
yertle.onclick(doit)
done()
Stamps are not clickable entities. Second, you can't even click on a turtle that's behind ink left by this, or another, turtle:
from turtle import *
def doit(x, y):
print("Just do it!")
yertle = Turtle()
yertle.shape('square')
yertle.fillcolor('white')
yertle.onclick(doit)
myrtle = Turtle()
myrtle.shape('turtle')
myrtle.penup()
myrtle.sety(-16)
# comment out the following line if you want `onlick()` to work
myrtle.write('X', align='center', font=('Courier', 32, 'bold'))
myrtle.goto(100, 100) # move myrtle out of the way of clicking
done()
If you click on the letter 'X', nothing happens unless you manage to hit a portion of the square just beyond the letter. My belief is that although we think of the 'X' as dead ink over our live turtle, at the tkinter level they are both similar, possibly both capable of receiving events, so one obscures the click on the other.
So how can we do this? The approach I'm going to use is make a tile a turtle with an image where the images are generate by writing onto bitmaps:
tileset.py
from turtle import Screen, Turtle, Shape
from PIL import Image, ImageDraw, ImageFont, ImageTk
DEFAULT_FONT_FILE = "/Library/Fonts/Courier New Bold.ttf" # adjust for your system
DEFAULT_POINT_SIZE = 32
DEFAULT_OUTLINE_SIZE = 1
DEFAULT_OUTLINE_COLOR = 'black'
DEFAULT_BACKGROUND_COLOR = 'white'
class Tile(Turtle):
def __init__(self, shape, size):
super().__init__(shape)
self.penup()
self.size = size
def tile_size(self):
return self.size
class TileSet():
def __init__(self, font_file=DEFAULT_FONT_FILE, point_size=DEFAULT_POINT_SIZE, background_color=DEFAULT_BACKGROUND_COLOR, outline_size=DEFAULT_OUTLINE_SIZE, outline_color=DEFAULT_OUTLINE_COLOR):
self.font = ImageFont.truetype(font_file, point_size)
self.image = Image.new("RGB", (point_size, point_size))
self.draw = ImageDraw.Draw(self.image)
self.background_color = background_color
self.outline_size = outline_size
self.outline_color = outline_color
def register_image(self, string):
width, height = self.draw.textsize(string, font=self.font)
image = Image.new("RGB", (width + self.outline_size*2, height + self.outline_size*2), self.background_color)
draw = ImageDraw.Draw(image)
tile_size = (width + self.outline_size, height + self.outline_size)
draw.rectangle([(0, 0), tile_size], outline=self.outline_color)
draw.text((0, 0), string, font=self.font, fill="#000000")
photo_image = ImageTk.PhotoImage(image)
shape = Shape("image", photo_image)
Screen()._shapes[string] = shape # underpinning, not published API
return tile_size
def make_tile(self, string):
tile_size = self.register_image(string)
return Tile(string, tile_size)
Other than its image, the only differences a Tile instance has from a Turtle instance is an extra method tile_size() to return its width and height as generic turtles can't do this in the case of images. And a tile's pen is up at the start, instead of down.
I've drawn on a couple of SO questions and answers:
Dump characters (glyphs) from TrueType font (TTF) into bitmaps
How do you set a turtle's shape to a PIL image
And while I'm at it, this answer has been updated to be more system independent:
How to know the pixel size of a specific text on turtle graphics in python?
To demonstrate how my tile sets work, here's the well-know 15 puzzle implemented using them. It creates two tile sets, one with white backgrounds and one with red (pink) backgrounds:
from tileset import TileSet
from turtle import Screen
from functools import partial
from random import shuffle
SIZE = 4
OFFSETS = [(-1, 0), (0, -1), (1, 0), (0, 1)]
def slide(tile, row, col, x, y):
tile.onclick(None) # disable handler inside handler
for dy, dx in OFFSETS:
try:
if row + dy >= 0 <= col + dx and matrix[row + dy][col + dx] == None:
matrix[row][col] = None
row, col = row + dy, col + dx
matrix[row][col] = tile
width, height = tile.tile_size()
x, y = tile.position()
tile.setposition(x + dx * width, y - dy * height)
break
except IndexError:
pass
tile.onclick(partial(slide, tile, row, col))
screen = Screen()
matrix = [[None for _ in range(SIZE)] for _ in range(SIZE)]
white_tiles = TileSet(background_color='white')
red_tiles = TileSet(background_color='pink')
tiles = []
parity = True
for number in range(1, SIZE * SIZE):
string = str(number).rjust(2)
tiles.append(white_tiles.make_tile(string) if parity else red_tiles.make_tile(string))
parity = not parity
if number % SIZE == 0:
parity = not parity
shuffle(tiles)
width, height = tiles[0].tile_size()
offset_width, offset_height = width * 1.5, height * 1.5
for row in range(SIZE):
for col in range(SIZE):
if row == SIZE - 1 == col:
break
tile = tiles.pop(0)
width, height = tile.tile_size()
tile.goto(col * width - offset_width, offset_height - row * height)
tile.onclick(partial(slide, tile, row, col))
matrix[row][col] = tile
screen.mainloop()
If you click on a number tile that's next to the blank space, it will move into the blank space, otherwise nothing happens. This code doesn't guarantee a solvable puzzle -- half won't be solvable due to the random shuffle. It's just a demonstration, the fine details of it, and the tiles themselves, are left to you.

How to stop the frame moving when I call the function again?

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.

Turn-based game map in python

I am making a turn-based game in python and I have some problems with game map.
This is the code:
import pygame, sys
from pygame.locals import *
pygame.init()
class Map(object):
def __init__(self, x, y, SURF):
self.x = x
self.y = y
self.SURF = SURF
def createNewMap(self, D1, D2):
create a D1xD2 map
self.Map = []
self.D1 = D1
self.D2 = D2
for n in range(self.D1):
current = ['']*self.D2
self.Map.append(current)
return self.Map
def drawMap(self):
draw the D1xD2 map from (x,y) position. grass.gif represent 1 grass box. the map contain D1xD2 grass boxes.
imgObj = pygame.image.load('images\\textures\grass.gif')
imgObj = pygame.transform.scale(imgObj, (40, 40))
xc = self.x
yc = self.y
for itemY in self.Map:
for itemX in self.Map[self.Map.index(itemY)]:
self.SURF.blit(imgObj, (xc, yc))
xc += 40
yc += 40
xc = self.x
i want when the mouse is above a box, i want to changes it with another texture box(grass1.gif). how can i do this?
You can do that by getting the current mouse position and then checking whether that is in the bounds of the box. Here is an unrelated example:
mouse_pos = pygame.mouse.get_pos()
for rectange in my_objects_list:
if rectangle.collidepoint(mouse_pos):
#do something ...

Categories

Resources