How do I end a loop on Turtle using the following code? - python-3.x

import turtle
flower = turtle.Turtle()
flower.color("black", "red")
def draw_flower(d, n, p):
for num in range(n):
flower.forward(d)
flower.lt(180 - (180 * (n-2))/n)
for tilt in range(p):
flower.lt(360/p)
draw_flower(d, n, p)
draw_flower(50, 8, 4)
turtle.done()
d is the length of each side, n is the number of total sides, p is the number of times I want it to repeat. The first loop will draw a polygon. I want to repeat that for p times until it shows something like a flower, and I want to break it out.
I know there are other coding ways to make it such that it does not loop. But based on the above code, how do I break out of it when it draws exactly the number p that I want it to draw?

Related

How can I simplify my python turtle game to avoid it from taking larger memory as the running time increases?

This is a color-flipping game based on the python turtle module.
The game is composed of a rectangular board divided into a number of flat colored tiles of various colors. The goal of the game is to turn all tiles to the same color by choosing one tile on the board and then changing its color to another color. The newly selected color will spread to all its neighboring tiles which match the original color of the chosen tile. The player continues selecting another tile and changing its color to another until all the tiles within the board flipped to the same color. In addition to the rectangular board, an array of colors is shown below the board. These are the colors to which the player can choose to flip the color of the selected tile.
As I started to run my game, the memory it took up was getting larger and larger as the running time increased. And it's getting slower and slower. I think it might be due to the while loop at the end of my code. But I'm not sure. How can I modify it to make it faster?
from turtle import *
from random import choice
from functools import partial
# set game dimension to be 5 x 5
g_dim = 5
# use random.choice() to create the color for the game
g_game = [choice(['#0000FF', '#FF0000', '#FFFF00', '#008000', '#00FFFF']) for i in range(25)]
# provide the option to flip during the game
optionColor = ['#0000FF', '#FF0000', '#FFFF00', '#008000', '#00FFFF']
# show a set of colors as option for user to flip
def promptColorToFlip(optionColor, # a list that contains a set of the color for user to choose from
height=100, # the height of the option tiles
width=100 # the width of the option tiles
):
# the coordinates of the first tiles
x = -200
y = -200
for i in range(len(optionColor)):
tile = prototype.clone()
tile.goto(i * width + x, -(height+50) + y)
tile.color('white', optionColor[i])
tile.onclick(partial(returnChosenColor, i))
# return the index of the select-to-flip-to color in the optionColor list
def returnChosenColor(userChosenColor, # the index of the select-to-flip-to color in the optionColor list
x, y # take the positional arguments from the onclick() function to avoid errors, no significant meaning
):
global userOption
userOption = userChosenColor
def refreshScreen(game, rows=5, columns=5, height=100, width=100):
x = -200
y = -200
for column in range(columns):
for row in range(rows):
square = prototype.clone()
square.goto(column * (5+width) + x , row * (5+height) + y)
square.onclick(partial(userChosenTile, row, column))
if state['framed'] == row*5+column:
square.color('black', game[row*5+column])
else:
square.color('white', game[row*5+column])
update()
def userChosenTile(ROW, COL, x, y):
global state, R, C
state['framed'] = ROW*5+COL
R = ROW
C = COL
def flipColor(row, col, game, orig, to):
print('excuted')
global userOption, state, R, C
if orig == to:
return game
if row < 0 or row >= g_dim:
return
if col < 0 or col >= g_dim:
return
idx = row*g_dim+col
if game[idx] != orig:
return
print(idx, 'excuted')
game[idx] = to
flipColor(row-1, col, game, orig, to)
flipColor(row+1, col, game, orig, to)
flipColor(row, col-1, game, orig, to)
flipColor(row, col+1, game, orig, to)
state = {'framed':None}
R = None
C = None
userOption = None
return game
# initialize the game status
state = {'framed':None} # stores the number of the last selected tile, which will be framed with a black border
R = None # the row of the last selected tile
C = None # the column of the last selected tile
userOption = None # the index of the select-to-flip-to color in the optionColor list
# create a prototype of the tiles
prototype = Turtle()
prototype.shape('square')
prototype.shapesize(5, 5, 5)
prototype.penup()
# disable auto screen refresh
tracer(False)
# run the game
while True:
# the try and except block here is to prevent error from raising when user terminate the progarm
try:
promptColorToFlip(optionColor)
refreshScreen(g_game)
if state['framed'] is not None and R is not None and C is not None and userOption is not None:
g_game = flipColor(R, C, g_game, g_game[state['framed']], optionColor[userOption])
except:
pass
I haven't fully worked through your somewhat complex code, but I'm pretty sure I see the source of your progressive slowdown as the game runs for a while. It's right here, in your refreshScreen function:
for column in range(columns):
for row in range(rows):
square = prototype.clone()
...
This code makes a new turtle for each square of your board on every frame. Those turtles never go away, so you just keep on stacking up more and more of them, which slows down the performance of the whole game over time. The solution is probably to use only one turtle for each location, and keep references to them so that you can change their colors when necessary, rather than making them anew each time.

Pass a variable into a for range loop and then grow the value?

In Turtle graphics I'm trying to create a series of boxes, one within the next. My main question is how does one pass values into a for i in range(4): loop and have the values increase or decrease by a value? Here, I've created two boxes but I'd like to make the second smaller and fit in the first?
import turtle as t
def block(x, y, length, scale):
for i in range(2):
t.up()
t.goto(x,y)
t.down()
for i in range(4):
t.forward(length * scale)
t.right(90)
block(100, 100, 100, 1)
t.mainloop()
You need to make your starting x and y coordinates and side length variables, change them every time through the loop, and move the turtle every time. Something like this:
import turtle as t
x = 100
y = 100
side = 100
decrease = 10
num_rect = 2
for i in range (num_rect):
t.up()
t.goto(x, y)
t.down()
for i in range(4):
t.forward(side)
t.right(90)
x += decrease / 2
y -= decrease / 2
side -= decrease
t.mainloop()

Trouble understanding modulus on a negative in 'Conways Game of Life'

I'm going over the book 'automate the boring stuff with python' and cannot understanding a simple expression with the % operator. The expression is leftCoord = (x - 1) % WIDTH which on the first iteration of the loop evaluates to (0 - 1) % 60. In my mind the % operator should evaluate to the remainder of a division. Why does it evaluate to 9?
This is the part of the program that precedes the expression in question:
import random,time,copy
WIDTH = 60
HEIGHT = 20
# Create a list of list for the cells:
nextCells = []
for x in range(WIDTH):
column = [] # Create a new column.
for y in range(HEIGHT):
if random.randint(0,1) == 0:
column.append('#') # Add a living cell.
else:
column.append(' ') # Add a dead cell.
nextCells.append(column) # nextCells is a list of column lists.
while True: # Main program loop.
print('\n\n\n\n\n') # Separate each step with newlines.
currentCells = copy.deepcopy(nextCells)
# Print currentCells on the screen:
for y in range(HEIGHT):
for x in range(WIDTH):
print(currentCells[x][y], end='') # Print the # or space.
print() # Print a newline at the end of the row.
# Calculate the next step's cells based on current step's cells:
for x in range(WIDTH):
for y in range(HEIGHT):
# Get neighboring coordinates:
# % WIDTH ensures leftCoord is always between 0 and WIDTH -1
leftCoord = (x - 1) % WIDTH
rightCoord = (x + 1) % WIDTH
aboveCoord = (y - 1) % HEIGHT
belowCoord = (y + 1) % HEIGHT
For the sake of example, let's assume that you're using a table of 10x10.
The % operator isn't so intuitive when the first number is smaller than the second. Try going into the interactive python shell and running 4 % 10. Try 8 % 10. Notice how you always get the same number back? That's because the answer to the division is 0... with your whole number being left over as remainder. For most numbers in the table, the modulus doesn't do anything at all.
Now try -1 % 10 (simulating what this would do for the top row). It gives you 9, indicating the bottom row. If you run 10 % 10 (simulating the bottom row), it gives you 0, indicating the top row. Effectively, this makes the table "wrap"... the cells in the top row affect the bottom and vice versa. It also wraps around the sides.
Hope this helps!

How can I use the random module in python turtle?

I'm trying to make a game like the original snake and I want the "food" to go to random places but I'm not really sure how to get it to work with it in a class. There are some other errors in the code but want to focus on the player and the food right now.
import turtle
import random
"""-------"""
t=turtle.Turtle()
s=turtle.Screen()
cube=turtle.Turtle
"""------------"""
WIDTH, HEIGHT=300, 300
DISTANCE=5
"""--------------"""
s.setup(WIDTH,HEIGHT)
"""------------"""
t.width(1)
s.bgcolor("dark green")
"""-----------------"""
class Border():
def check():
x, y = t.position()
if not -WIDTH / 2 < x < WIDTH / 2 or not -HEIGHT / 2 < y < HEIGHT / 2:
t.undo() # undo error
t.speed(0)
t.left(180) # turn around
t.forward(10) # redo movement but in new direction
t.speed(3)
"""-------------"""
randint=random.randint
x=random.randint(cube.xcor, 0)
y=random.randint(0,cube.ycor)
"""---------------"""
class Food():
def block():
cube.color("red")
for i in range(4):
cube.goto(x, -x, y, -y)
cube.begin_fill()
cube.forward(20)
cube.right(90)
cube.end_fill()
"""---------------"""
class Player():
def move_up():
player=False
while player==False:
for i in range(1):
t.forward(DISTANCE)
t.clear()
def move_left():
t.speed(0)
t.left(90)
t.speed(3)
def move_right():
t.speed(0)
t.right(90)
t.speed(3)
"""------------"""
collistion_check=Border()
player1=Player()
s.onkey(Player.move_up,"up")
s.onkey(Player.move_left,"left")
s.onkey(Player.move_right,"right")
s.listen()
Generally speaking, your program is a disaster. As far as your immediate problem with random numbers, this code has an issue:
randint=random.randint
x=random.randint(cube.xcor, 0)
y=random.randint(0,cube.ycor)
Syntax-wise, this should be something more like:
from random import randint
...
x = randint(cube.xcor(), 0)
y = randint(0, cube.ycor())
I.e. it's not clear why you set the variable randint since you don't use it and xcor() and ycor() are methods and require parenthesis.
If your goal is to locate the food randomly on the screen, I'd go with:
x = randint(cube.xcor()/2 - WIDTH/2, WIDTH/2 - cube.xcor()/2)
y = randint(cube.ycor()/2 - HEIGHT/2, HEIGHT/2 - cube.ycor()/2)
Adding variables as you see fit to reduce the redundancies. The biggest problem I see with the remainder of your code is this method:
def move_up():
player=False
while player==False:
for i in range(1):
t.forward(DISTANCE)
t.clear()
It's not clear how player ever becomes True so once you hit the up arrow, you're into an infinite loop. But I assume you'll focus on this once you get the food issue resolved.

TkInter python - creating points on a canvas to obtain a Sierpinsky triangle

I want to make a program which plots a Sierpinsky triangle (of any modulo). In order to do it I've used TkInter. The program generates the fractal by moving a point randomly, always keeping it in the sides. After repeating the process many times, the fractal appears.
However, there's a problem. I don't know how to plot points on a canvas in TkInter. The rest of the program is OK, but I had to "cheat" in order to plot the points by drawing small lines instead of points. It works more or less, but it doesn't have as much resolution as it could have.
Is there a function to plot points on a canvas, or another tool to do it (using Python)? Ideas for improving the rest of the program are also welcome.
Thanks. Here's what I have:
from tkinter import *
import random
import math
def plotpoint(x, y):
global canvas
point = canvas.create_line(x-1, y-1, x+1, y+1, fill = "#000000")
x = 0 #Initial coordinates
y = 0
#x and y will always be in the interval [0, 1]
mod = int(input("What is the modulo of the Sierpinsky triangle that you want to generate? "))
points = int(input("How many points do you want the triangle to have? "))
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
canvas = Canvas(window, height = 700, width = 808, bg = "#FFFFFF") #The dimensions of the canvas make the triangle look equilateral
canvas.pack()
for t in range(points):
#Procedure for placing the points
while True:
#First, randomly choose one of the mod(mod+1)/2 triangles of the first step. a and b are two vectors which point to the chosen triangle. a goes one triangle to the right and b one up-right. The algorithm gives the same probability to every triangle, although it's not efficient.
a = random.randint(0,mod-1)
b = random.randint(0,mod-1)
if a + b < mod:
break
#The previous point is dilated towards the origin of coordinates so that the big triangle of step 0 becomes the small one at the bottom-left of step one (divide by modulus). Then the vectors are added in order to move the point to the same place in another triangle.
x = x / mod + a / mod + b / 2 / mod
y = y / mod + b / mod
#Coordinates [0,1] converted to pixels, for plotting in the canvas.
X = math.floor(x * 808)
Y = math.floor((1-y) * 700)
plotpoint(X, Y)
tkengine.mainloop()
If you are wanting to plot pixels, a canvas is probably the wrong choice. You can create a PhotoImage and modify individual pixels. It's a little slow if you plot each individual pixel, but you can get dramatic speedups if you only call the put method once for each row of the image.
Here's a complete example:
from tkinter import *
import random
import math
def plotpoint(x, y):
global the_image
the_image.put(('#000000',), to=(x,y))
x = 0
y = 0
mod = 3
points = 100000
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
the_image = PhotoImage(width=809, height=700)
label = Label(window, image=the_image, borderwidth=2, relief="raised")
label.pack(fill="both", expand=True)
for t in range(points):
while True:
a = random.randint(0,mod-1)
b = random.randint(0,mod-1)
if a + b < mod:
break
x = x / mod + a / mod + b / 2 / mod
y = y / mod + b / mod
X = math.floor(x * 808)
Y = math.floor((1-y) * 700)
plotpoint(X, Y)
tkengine.mainloop()
You can use canvas.create_oval with the same coordinates for the two corners of the bounding box:
from tkinter import *
import random
import math
def plotpoint(x, y):
global canvas
# point = canvas.create_line(x-1, y-1, x+1, y+1, fill = "#000000")
point = canvas.create_oval(x, y, x, y, fill="#000000", outline="#000000")
x = 0 #Initial coordinates
y = 0
#x and y will always be in the interval [0, 1]
mod = int(input("What is the modulo of the Sierpinsky triangle that you want to generate? "))
points = int(input("How many points do you want the triangle to have? "))
tkengine = Tk() #Window in which the triangle will be generated
window = Frame(tkengine)
window.pack()
canvas = Canvas(window, height = 700, width = 808, bg = "#FFFFFF") #The dimensions of the canvas make the triangle look equilateral
canvas.pack()
for t in range(points):
#Procedure for placing the points
while True:
#First, randomly choose one of the mod(mod+1)/2 triangles of the first step. a and b are two vectors which point to the chosen triangle. a goes one triangle to the right and b one up-right. The algorithm gives the same probability to every triangle, although it's not efficient.
a = random.randint(0,mod-1)
b = random.randint(0,mod-1)
if a + b < mod:
break
#The previous point is dilated towards the origin of coordinates so that the big triangle of step 0 becomes the small one at the bottom-left of step one (divide by modulus). Then the vectors are added in order to move the point to the same place in another triangle.
x = x / mod + a / mod + b / 2 / mod
y = y / mod + b / mod
#Coordinates [0,1] converted to pixels, for plotting in the canvas.
X = math.floor(x * 808)
Y = math.floor((1-y) * 700)
plotpoint(X, Y)
tkengine.mainloop()
with a depth of 3 and 100,000 points, this gives:
Finally found a solution: if a 1x1 point is to be placed in pixel (x,y), a command which does it exactly is:
point = canvas.create_line(x, y, x+1, y+1, fill = "colour")
The oval is a good idea for 2x2 points.
Something remarkable about the original program is that it uses a lot of RAM if every point is treated as a separate object.

Resources