Turtle graphics script keeps crashing when code is run - python

I am creating a project that loads an image and converts it to 1 and zeros it will then draw this using turtle. However, every time I run it tells me that it has stopped working after the first column has been completed. If the problem is with the processing power of my computer I would like to know if there is a way to switch to a GPU to achieve the task. Any help would be greatly appreciated. Thanks
def ShowMaze(possibleRoutes):
turtle.delay(0)
for x in range(0,len(Maze)):
for y in range(0,len(Maze[0])):
if Maze[x][y]==3:
Maze[x][y]=0
for x in range(0,len(Maze)):
turtle.forward(-5)
turtle.right(90)
turtle.forward(5/len(Maze[0]))
turtle.left(90)
for y in range(0,len(Maze[0])):
if Maze[x][y]==1:
turtle.fillcolor("black")
turtle.begin_fill()
elif Maze[x][y]==0:
turtle.fillcolor("white")
turtle.begin_fill()
elif Maze[x][y]==4:
turtle.fillcolor("green")
turtle.begin_fill()
elif Maze[x][y]==5:
turtle.fillcolor("red")
turtle.begin_fill()
for i in range(0,4):
turtle.forward(5/len(Maze[0]))
turtle.left(90)
turtle.end_fill()
turtle.forward(5/len(Maze[0]))
input()
for ii in range(1,len(possibleRoutes)-1):
turtle.pu()
turtle.home()
turtle.forward(-250)
turtle.forward((250/len(Maze))*possibleRoutes[ii][1])
turtle.right(90)
turtle.forward((250/len(Maze))*possibleRoutes[ii][0]+(250/len(Maze)))
turtle.left(90)
turtle.fillcolor("blue")
turtle.pd()
turtle.begin_fill()
for x in range(0,4):
turtle.forward(250/len(Maze[0]))
turtle.left(90)
turtle.end_fill()
im = Image.open('D:/MazeSolver/ExampleMazePicture.JPG') # Can be many different formats.
pix = im.load()
size=250
Maze=[]
length=im.size[0] # Get the width and hight of the Maze for iterating over
for x in range(0,size,8):
print("Row",x)
row=[]
for y in range(0,size,2):
pix = im.load()
if pix[x,y]>=(200,200,200):
node=0
elif pix[x,y][0]>200 and pix[x,y][2]<200 and pix[x,y][1]<200:
node=4
print("End")
elif pix[x,y][1]>200 and pix[x,y][0]<50 and pix[x,y][2]<50:
node=5
print("Start")
elif pix[x,y]<=(50,50,50):
node=1
else:
print(pix[x,y])
row.append(node)
Maze.append([row])
ShowMaze(Maze)

This code is a mess. You input a JPEG maze image, called Maze, into a two dimensional array and pass it to ShowMaze(Maze) to show that you've read it in correctly. But ShowMaze() accesses Maze globally and thinks its argument is ShowMaze(possibleRoutes) where possibleRoutes through the maze were never calculated? Also: the X and Y sense of Maze seems inverted; the rows of the maze have an extra layer of list wrapped around them for no apparent reason; there's dead code included; you're not reading it in as 1s and 0s but rather four different color codes; the drawing code seems hopeless.
I've reworked your code to simply read the maze into a list of lists and then display it with turtle using stamping instead of drawing to both simplify and speed up the code:
from turtle import Screen, Turtle
from PIL import Image
CURSOR_SIZE = 20
PIXEL_SIZE = 5
COLORS = {0: 'white', 1: 'black', 4: 'green', 5: 'red'}
def ShowMaze(maze):
height, width = len(maze), len(maze[0])
screen = Screen()
screen.setup(width * PIXEL_SIZE, height * PIXEL_SIZE)
screen.setworldcoordinates(0, height, width, 0)
turtle = Turtle('square', visible=False)
turtle.shapesize(PIXEL_SIZE / CURSOR_SIZE)
turtle.penup()
screen.tracer(False)
for y in range(height):
for x in range(width):
color = maze[y][x]
if color in COLORS:
turtle.fillcolor(COLORS[color])
else:
turtle.fillcolor("orange") # error color
turtle.stamp()
turtle.forward(1)
turtle.goto(0, turtle.ycor() + 1)
screen.tracer(True)
screen.mainloop()
image = Image.open('ExampleMazePicture.JPG') # Can be many different formats.
width, height = image.size # Get the width and height of the Maze for iterating over
pixels = image.load()
maze = []
for y in range(0, width, 4):
print("Row:", y)
row = []
for x in range(0, width, 4):
node = -1
pixel = pixels[x, y]
if pixel >= (200, 200, 200):
node = 0
elif pixel[0] > 200 and pixel[1] < 200 and pixel[2] < 200:
node = 4
print("End")
elif pixel[0] < 50 and pixel[1] > 200 and pixel[2] < 50:
node = 5
print("Start")
elif pixel <= (50, 50, 50):
node = 1
else:
print(pixel)
row.append(node)
maze.append(row)
ShowMaze(maze)
Output based on using "Figure 1.6: Picobot’s maze." from this page as input:
Hopefully this should provide you a starting point for the program you're ultimately trying to develop.

Related

Python turtle drawing equal cells in a rectangle

I'm trying to use turtle in order to draw a rectangle, and then 32 equal cells inside it. But I somehow can't get it right, I don't know why.
Here is the code I've written:
import turtle, tkinter, datetime, time
turtle.setx(-400)
turtle.sety(200)
turtle.pendown()
turtle.pencolor('#00807c')
for i in range (0,4):
x = 800
if i%2 == 0:
turtle.fd(x)
else:
turtle.fd(x/2)
turtle.right(90)
def cells(position):
for i in range (0,4):
x = 100
turtle.fd(x)
turtle.right(90)
if turtle.pos() == position:
turtle.fd(x)
position = turtle.pos()
for j in range(0, 8):
cells(turtle.pos())
turtle.done()
The result is weird, it only draws three or four cells and then the program ends.
I'd be grateful if somebody could possibly help me with this problem. Thanks.
I've rewritten your code and I don't understand why you're using a function. I've used 3 loops:
loop 3(4):
loop 2(8):
loop 1(4):
The first loop repeats himself 4 times and draws the sides of 1 square
The second loop runs the first loop 8 times, so it draws 8 squares next to each other
The third loop runs the second loop 4 times, so it draws 4 lines of 8 squares.
And that makes a field of 32 cells.
My code:
import turtle, tkinter, datetime, time
turtle.penup()
turtle.hideturtle()
turtle.setx(-400)
turtle.sety(200)
turtle.pendown()
turtle.pencolor('#00807c')
turtle.speed(0)
turtle.pendown()
for i in range (4):
x = 800
if i%2 == 0:
turtle.fd(x)
else:
turtle.fd(x/2)
turtle.right(90)
for w in range (4):
for i in range (8):
for i in range (4):
x = 100
turtle.fd(x)
turtle.right(90)
turtle.forward(x)
turtle.penup()
turtle.goto(-400,turtle.ycor()-100)
turtle.pendown()
turtle.done()
PS: I've also changed a few things like:
I hid the turtle
I changed the speed (to maximum)
I added a turtle.penup()-command before I moved the turtle in the beginning, so you don't see a black line.
Kind regards
spyrolix
You can create two functions, to simplify the logic: One to draw a square, and one to position the turtle at the place to draw a square. Then, using a little bit of index arithmetic in two nested loops (one for rows, and one for columns), use the indices values, and the side length of a square to draw at the correct location:
Maybe like this:
import turtle, tkinter
def draw_square(side):
"""draws a square of side=side starting at the current turtle location
"""
turtle.pendown()
turtle.setheading(0)
for _ in range(4):
turtle.forward(side)
turtle.left(90)
turtle.penup()
def draw_grid(rows, cols):
"""positions the turtle at the correct location,
in order to draw a grid of squares
"""
for jdx in range(rows):
for idx in range(cols):
turtle.penup()
turtle.goto(startx + side*idx, starty + side*jdx)
draw_square(side)
turtle.pencolor('#00807c')
side = 20
startx, starty = 0, 0 # this can be changed,
# other locations used are relative to this starting point
turtle.penup()
turtle.goto(startx, starty)
rows, cols = 4, 8 # this can be changed
draw_grid(rows, cols)
turtle.goto(startx, starty) # return to the starting point
turtle.done()
This is a situation where I would switch from drawing to stamping to simplify the code and speed it up:
from turtle import Screen, Turtle
WIDTH, HEIGHT = 800, 400
SQUARE = 100
CURSOR_SIZE = 20
def cells(t):
x, y = t.position()
for dy in range(HEIGHT // SQUARE):
turtle.goto(x, y + dy * SQUARE)
for dx in range(WIDTH // SQUARE):
turtle.stamp()
turtle.forward(SQUARE)
screen = Screen()
turtle = Turtle()
turtle.hideturtle()
turtle.shape('square')
turtle.shapesize(HEIGHT / CURSOR_SIZE, WIDTH / CURSOR_SIZE)
turtle.color('#00807c', 'white')
turtle.speed('fastest')
turtle.penup()
turtle.stamp() # draw outer perimeter
turtle.shapesize(SQUARE / CURSOR_SIZE)
turtle.goto(SQUARE/2 - WIDTH/2, SQUARE/2 - HEIGHT/2)
cells(turtle) # draw inner squares
screen.exitonclick()
I'd also remove magic numbers from the body of the code and declare them at the start to make them easier to adjust.

How to show two lines overlapping in turtle graphics

Screenshot of output image
I made a random walk program using turtle and I want it show where the two turtles cross paths by changing the color.
`import turtle as T
import random as R
t = T.Turtle()
u = T.Turtle()
t.speed(0)
u.speed(0)
t.hideturtle()
u.hideturtle()
t.color("red")
u.color("blue")
def randWalk(num):
for i in range(0, num):
x = R.choice((-1,1))
X = R.choice((-1,1))
y = R.choice((-1,1))
Y = R.choice((-1,1))
t.forward(x)
u.forward(X)
if y == 1:
t.left(90)
t.forward(1)
else:
t.right(90)
t.forward(1)
if Y == 1:
u.left(90)
u.forward(1)
else:
u.right(90)
u.forward(1)
randWalk(4000)`
Turtle can't interrogate what color is currently on the screen, so one way to approach this might be to have some sort of backing store where you keep track of what color was written to what pixel. Here's a rough example using Python lists:
from turtle import Screen, Turtle
from random import choice
WIDTH, HEIGHT = 300, 300
PEN_COLORS = ['red', 'blue']
OVERLAP_COLOR = 'green'
def randWalk(number):
for _ in range(number):
for turtle in turtles:
direction = choice((-1, 1))
turtle.forward(direction)
x, y = map(round, turtle.position())
old_color = color = turtle.pencolor()
turtle.undo() # undo forward()
if grid[y][x] and grid[y][x] != color:
color = OVERLAP_COLOR
turtle.pencolor(color)
turtle.goto(x, y) # redo forward()
turtle.pencolor(old_color)
grid[y][x] = color
choice((turtle.left, turtle.right))(90)
screen.update()
screen = Screen()
screen.setup(WIDTH, HEIGHT)
screen.tracer(False)
grid = [[None] * WIDTH for _ in range(HEIGHT)]
turtles = []
for color in PEN_COLORS:
turtle = Turtle()
turtle.hideturtle()
turtle.pencolor(color)
turtles.append(turtle)
randWalk(4000)
screen.tracer(True)
screen.exitonclick()
There is additional complication in the code as turtle walks a floating point plane but we need to coerce it to an integer plane to accommodate our backing store and reduce undesirable drawing artifacts.

Python turtle graphics repeatedly goes 'out of bounds'

I'm trying to have Turtle in python draw a user dictated amount of triangles sudo-randomly. I'm having some trouble keeping my turtle in the bounds I have defined. For example, the bounds of the turtle are
border = 300
def drawBorder(border):
t.pensize(3)
x = 0
while x <= 4:
t.pendown()
t.forward(border)
t.left(90)
t.penup()
x = x + 1
which draws a 300x300 square.
The following function should - when the X or Y coordinate + the
length of the triangle it is going to draw is outside the border - for
the first two iterations try to turn the turtle 180 degrees (facing
the opposite direction) and move it forward twice the distance it was
initially going to move it forward. If this fails to bring the turtle
within the bounds of the border the turtle should return to the middle
of the border - in this case, that is (150,150). This does not always
happen. Due to the 'random' nature of the generation, most times the
turtle ends up outside of the border, though sometimes it does draw
all triangles within the border.
if t.xcor() + lengthOfTriangle > border or t.xcor() + lengthOfTriangle < 0 or \
t.ycor() + lengthOfTriangle > border or t.ycor() + lengthOfTriangle < 0:
x = 0
while x < 2:
t.penup()
t.left(180)
t.forward(2 * lengthOfTriangle)
t.pendown()
x = x + 1
else:
t.penup()
if t.xcor() > border:
t.seth(180)
if t.xcor() < border:
t.seth(0)
t.forward(t.xcor() - (t.xcor() + border/2))
if t.ycor() > border:
t.seth(90)
if t.ycor() < border:
t.seth(270)
t.forward(t.ycor() - (t.ycor() + border/2))
print("Turtle was going to be out of bounds. Xcor would be: ", t.xcor() + lengthOfTriangle,
". And Ycor would be: ", t.ycor() + lengthOfTriangle)
return drawFigureRec(numTriangles, lengthOfTriangle=random.randint(1, 20),
distanceTriangle=random.randint(1, 40),
angleTriangle=random.randint(0, 360), sum=sum)
If you need context for the function variables and such I have linked a pastebin here.
Here is a picture showing the problem.The turtle should stay within the 'bounds' (red square), but goes outside as shown by the console's output
There are several issues with your code. For example, this boundary checking logic completely ignores the current heading of the turtle:
if t.xcor() + lengthOfTriangle > border or ... t.ycor() + lengthOfTriangle > border or ...
I.e. it always assumes +45 degrees so can't work. You can either do
the trigonometry or, simply move the turtle to the new postion and
test if it's out of bounds. If out of bounds, you can move it back,
adjust and try again. The turtle.undo() method is helpful here.
Your drawFigure*() functions sometimes explicitly return values,
sometimes not. Once a function explicitly returns a value, it should
explictly return one from all points of exit, not implicitly return
None sometimes.
Your square border has five sides:
x = 0
while x <= 4:
since the last one overdraws the first, this isn't visually obvious.
Let's try a simpler iterative approach where we use turtle.circle() to draw our triangles but think of each triangle as a circle when determining if it's in bounds. We'll also center our drawing instead of just using the upper right quadrant and positive numbers:
from turtle import Screen, Turtle
from random import randint
NUMBER_TRIANGLES = 80
PEN_SIZE = 3
BORDER = 300
def drawBorder(border):
turtle.pensize(PEN_SIZE)
turtle.penup()
turtle.goto(-border/2, -border/2)
turtle.pendown()
for _ in range(4):
turtle.forward(border)
turtle.left(90)
turtle.penup()
turtle.home()
def drawFigure(numTriangles):
if not 0 < numTriangles < 500:
exit("Number of triangles is out of range!")
for _ in range(numTriangles):
radius = randint(3, 20)
distance = randint(2, 60)
turtle.forward(distance)
while not(radius - BORDER/2 < turtle.xcor() < BORDER/2 - radius and radius - BORDER/2 < turtle.ycor() < BORDER/2 - radius):
turtle.undo() # undo last forward()
turtle.left(37) # rotate and try again (relative prime angle)
distance += 1 # increase distance slightly on failure
turtle.forward(distance)
angle = randint(1, 360)
turtle.setheading(angle)
turtle.right(90) # turtle represents center so need to adjust
turtle.forward(radius) # as turtle.circle() doesn't think that way
turtle.left(90)
turtle.pendown()
turtle.circle(radius, steps=3)
turtle.penup()
turtle.left(90) # undo adjustment to move turtle back to center
turtle.forward(radius)
turtle.right(90)
screen = Screen()
screen.tracer(False)
turtle = Turtle()
turtle.speed('fastest')
drawBorder(BORDER)
drawFigure(NUMBER_TRIANGLES)
turtle.hideturtle()
screen.tracer(True)
screen.mainloop()
I'll not include the recursive equivalent as this really isn't a recursive problem. You can force it to be one as your assignment requires.

How to get python to generate random size?

I need to make my python code draw my stars in different sizes at random. I've tried every method I know but unable to figure it out...still trying to completely understand loops.
import turtle
import random
for n in range(60):
turtle.penup()
turtle.goto(random.randint(-400, 400), random.randint(-400, 400))
turtle.pendown()
red_amount = random.randint( 0, 100) / 100.0
blue_amount = random.randint(0 , 100) / 100.0
green_amount = random.randint( 0, 100) / 100.0
turtle.pencolor((red_amount, green_amount, blue_amount))
turtle.pensize(random.randint(1, 10))
for i in range(6):
turtle.begin_fill()
turtle.forward(50)
turtle.right(144)
turtle.end_fill()
All it does currently is draw stars at the same size.
You've added code to randomize the color, starting position and pen width but there's none to change the size of the stars. The size is controlled by this statement:
turtle.forward(50)
but you don't want to simply replace the 50 with another call to the random module as your stars will come out irregular. You need to compute a random size before the loop and then use that size in the forward() call in the loop:
import turtle
import random
for n in range(60):
turtle.penup()
turtle.goto(random.randint(-400, 400), random.randint(-400, 400))
turtle.pendown()
red_amount = random.random()
blue_amount = random.random()
green_amount = random.random()
turtle.color(red_amount, green_amount, blue_amount)
turtle.pensize(random.randint(1, 10))
size = random.randint(25, 100)
turtle.begin_fill()
for i in range(5):
turtle.forward(size)
turtle.right(144)
turtle.end_fill()
turtle.done()
I've also made a few style changes to your code while I was at it.

How to put if and then statements while creating snowflakes in python

--im a beginner ..so im not sure how to make sure that the snowflakes don't overlap. Thanks!
import turtle
turtle.right(90)
turtle.penup()
turtle.goto(-700,300)
turtle.pendown()
def snowflakebranch(n):
turtle.forward(n*4)
for i in range(3):
turtle.backward(n)
turtle.right(45)
turtle.forward(n)
turtle.backward(n)
turtle.left(90)
turtle.forward(n)
turtle.backward(n)
turtle.right(45)
def snowflake(n):
for i in range(8):
snowflakebranch(n)
turtle.backward(n)
turtle.right(45)
import random
turtle.colormode(255)
turtle.tracer(0)
for i in range(35):
r = random.randint(0, 255)
g = random.randint(0, 255)
b = random.randint(0, 255)
turtle.color(r, g, b)
x = random.randint(-500, 500)
y = random.randint(-500, 500)
d = random.randint(6, 16)
snowflake(d)
turtle.penup()
turtle.goto(x, y)
#turtle.forward(250)
turtle.pendown()
turtle.update()
One approach would be to calculate a bounding rectangle (or circle) for each snowflake. Save these as a list or a set. Whenever you plan to make a new snowflake, first check if its bounding rectangle (or circle) overlaps with the bounds of any previous snowflakes. If it does, don't draw it. If it doesn't, draw it and save its bounds too. An incomplete outline of this approach:
import turtle
import random
def snowflakebranch(n):
turtle.forward(n * 4)
for _ in range(3):
turtle.backward(n)
turtle.right(45)
turtle.forward(n)
turtle.backward(n)
turtle.left(90)
turtle.forward(n)
turtle.backward(n)
turtle.right(45)
def snowflake(n):
for _ in range(8):
snowflakebranch(n)
turtle.backward(n)
turtle.right(45)
def overlapping(bounds_list, bounds):
for previous in bounds_list:
if overlap(previous, bounds):
return True
return False
def overlap(b1, b2):
# return True or False if these two rectanges or circles overlap
pass
turtle.penup()
turtle.colormode(255)
turtle.tracer(0)
previous_bounds = []
i = 0
while i < 35:
x = random.randint(-500, 500)
y = random.randint(-500, 500)
turtle.goto(x, y)
r = random.randint(0, 255)
g = random.randint(0, 255)
b = random.randint(0, 255)
turtle.color(r, g, b)
turtle.pendown()
d = random.randint(6, 16)
# work out the bounding rectangle or circle based on 'd', 'x' & 'y'
# e.g. (x, y, width & height) or (x, y, radius)
bounds = ( ... )
if not overlapping(previous_bounds, bounds):
snowflake(d)
turtle.update()
previous_bounds.append(bounds)
i += 1
turtle.penup()
turtle.done()
An image of non-overlapping snowflakes using the above logic with the bounding circles also displayed:
I actually like the look of your overlapping snowflakes. Even if you want overlap, the above logic will allow you to control how much overlap.

Categories