Python User Input Checkerboard - python

For my assignment, I'm trying to make a 5 x 5 checkerboard with the user choice in color and square size. I got how to make square sizes and colors based on the user input and am having some trouble with how to start a loop or how to create a 5 x 5 checkerboard. I'm just not sure what I could do to move the turtle to make a 5x5 board. I have this much done so far, if anyone could help me get started, i would really appreciate it!
import turtle
def main():
length = int(input("Enter a desired length (from 1-150.)"))
keepGoing = 'y'
while keepGoing == 'y':
print("What color would you like to draw?")
print(" Enter 1 for Black")
print(" 2 for Blue")
print(" 3 for Red")
print(" 4 for Green")
choice = int(input(" Your choice?"))
if choice == 1:
square(0,0,length,'black')
elif choice == 2:
square(0,0,length,'blue')
elif choice == 3:
square(0,0,length,'red')
elif choice == 4:
square(0,0,length,'green')
else:
print("ERROR: only enter 1-4.")
def square(x, y, width, color):
turtle.clear()
turtle.penup() # Raise the pen
turtle.goto(x, y) # Move to (X,Y)
turtle.fillcolor(color) # Set the fill color
turtle.pendown() # Lower the pen
turtle.begin_fill() # Start filling
for count in range(4): # Draw a square
turtle.forward(width)
turtle.left(90)
turtle.end_fill()
#calling main function
main()

First, you developed your user interface without having anything to interface to -- you might start the other way around next time. Second, don't reinvent booleans (e.g. while keepGoing == 'y'). Third, for the amount of code it took you to draw one square, we can stamp the entire grid:
from turtle import Turtle, Screen
COLORS = ["Black", "Blue", "Red", "Green"]
GRID = 5
STAMP_UNIT = 20
def main():
length = int(input("Enter a desired length (from 1-150): "))
keepGoing = True
while keepGoing:
print("What color would you like to draw?")
for i, color in enumerate(COLORS, start=1):
print(" Enter {} for {}".format(i, color))
choice = int(input(" Your choice? "))
if 1 <= choice <= len(COLORS):
grid(-length * GRID // 2, -length * GRID // 2, length, COLORS[choice - 1])
keepGoing = False
else:
print("ERROR: only enter 1-{}.".format(len(COLORS)))
def grid(x, y, width, color):
tortoise = Turtle('square', visible=False)
tortoise.shapesize(width / STAMP_UNIT)
tortoise.color(color)
tortoise.penup()
for dy in range(0, GRID):
tortoise.goto(x, y + width * dy)
for dx in range(dy % 2, GRID, 2):
tortoise.setx(x + width * dx)
tortoise.stamp()
screen = Screen()
main()
screen.exitonclick()
USAGE
> python3 test.py
Enter a desired length (from 1-150): 30
What color would you like to draw?
Enter 1 for Black
Enter 2 for Blue
Enter 3 for Red
Enter 4 for Green
Your choice? 2
OUTPUT
This is a perfect example of where stamping can make things simpler and faster than drawing.

Related

A start to tiling the screen with triangles

At the moment, I'm trying to get started trying to tile the python turtle graphics screen with equilateral triangles of random colors. I've made a function which is successfully able to make a triangle starting at say coordinates v_1 = (x, y). Once it's made the triangle, I want the turtle to head to one of the other triangle vertices that are not equal to v_1 and start a new triangle there. I'd like to do all of this in a while loop. However, when I run my code, it seems that the turtle always goes back to the initial position of the first equilateral triangle and makes a new one on top of it. I'm not sure what I'm doing wrong. Here's my code:
from turtle import Turtle, colormode, done
from random import randint
colormode(255)
def make_left_triangle(bob: Turtle, x: float, y: float) -> None:
"""Makes an equilaterial triangle at coordinates (x, y) with the turtle bob moving left."""
r: int = randint(0, 255)
g: int = randint(0, 255)
b: int = randint(0, 255)
rand_n: int = randint(1, 2)
i: int = 0
bob.penup()
bob.goto(x, y)
bob.pendown()
bob.fillcolor(r, g, b)
bob.begin_fill()
while i < 3:
bob.forward(200)
bob.left(120)
i += 1
bob.end_fill()
while rand_n < 3:
bob.forward(200)
bob.left(120)
rand_n += 1
def main() -> None:
bob: Turtle = Turtle()
i: int = 0
rand_n: int = randint(25, 50)
x_coord: float = 0
y_coord: float = 0
while i < rand_n:
make_left_triangle(bob, x_coord, y_coord)
x_coord = bob.pos()[0]
y_coord = bob.pos()[1]
i += 1
done()
if __name__ == "__main__":
main()
Any help on what's going wrong would be greatly appreciated! Thanks
The problem is that the orientation of the bob is preserved between different calls to make_left_triangle. For example, if you draw a triangle ABC:
C
/ \
/ \
A-----B
and then move to vertex C you also turn in the direction of CA. So the next time you'll draw a new triangle CAB, which will be drawn over the initial ABC. You need to reset the orientation of the bob at the start of each make_left_triangle call.

How can I make the turtle function calculate the total length value drawn?

So I need my turtle to draw any regular polygon the user wants where they click on the screen
and also printing out the total length of the polygons they draw.
import turtle
t=turtle.Turtle()
tlength=0
def figure(num, length):
for i in range(num):
t.forward(length)
t.left(360/num)
global tlength
tlength = tlength+length
def drawit(x, y):
shape = int(turtle.textinput("","What Shape?"))
if shape!="0":
length = int(turtle.textinput("","The length of side? "))
t.penup()
t.goto(x, y)
t.pendown()
figure(shape, length)
t.write("total drawn length=", tlength)
s = turtle.Screen()
s.onscreenclick(drawit)
This is what I have so far.. It works well except it wont print out the total length. Any suggestions?
You have some code issues:
t.write("total drawn length=", tlength)
This won't work as write() doesn't take multiple arguments to display but rather just one. Check the documentation.
shape = int(...)
if shape != "0":
You just forced shape to be an int but then test if it's not a string -- this will always be true. Also, the int() call will fail if the user hits "Cancel" and textinput() returns None.
tlength=0
This only gets initialized once, so any subsequent polygons will add the perimeters of the previous polygons to their total length.
Here's rework of your program that fixes most of the above issues and basically works:
from turtle import Screen, Turtle
def figure(sides, length):
perimeter = 0
for _ in range(sides):
turtle.forward(length)
perimeter += length
turtle.left(360 / sides)
return perimeter
def drawit(x, y):
shape = screen.textinput("", "How many sides?")
if not shape:
return
length = screen.textinput("", "The length of one side?")
if not length:
return
turtle.penup()
turtle.goto(x, y)
turtle.pendown()
total_length = figure(int(shape), int(length))
turtle.write("Total drawn length = " + str(total_length))
turtle = Turtle()
turtle.hideturtle()
screen = Screen()
screen.onscreenclick(drawit)
screen.mainloop()

Is there any way I can avoid Turtle slowing down the more lines it has to print?

I made a program to generate a random 2D array of 1s and 0s that I then print as green "land" tiles above a blue background to generate a little 'map'. I've tried to optimise the code as much as I know how but when I try to print anything above 100x100, it slows down by a LOT. If I set it to print line by line, I see that it's taking quite a lot longer to print lines the more of the map turtle has already printed. I don't know why this is (I don't know much about turtle or rendering in general) but I would quite like to know how to solve this problem.
If this is not possible, could I have recommendations for other libraries/languages that I could try this in? I'd like to do more programs like this and I don't mind learning something new. :)
Here is my code:
import turtle
import random
import time
d = turtle.Turtle()
d.ht()
wn = turtle.Screen()
wn.setup(width=0.45,height=0.8,startx=800,starty=50)
wn.bgcolor("#002240")
turtle.tracer(0,0)
d.penup()
interval = 10
def square(col) :
d.pendown()
d.color(col)
d.begin_fill()
for i in range(4) :
d.fd(interval)
d.right(90)
d.end_fill()
d.penup()
size = int(input("Enter map size "))
custom_interval = input("Custom interval? ")
if custom_interval != '' :
interval = int(custom_interval)
int_by_size = interval * size
start_gen = time.time()
data = []
for i in range(size) :
gen_line = []
[gen_line.append(random.randint(0,1)) for i in range(size)]
data.append(gen_line)
end_gen = time.time()
print("GEN DONE IN",end_gen-start_gen)
d.goto(-int_by_size/2,int_by_size/2)
start_draw = time.time()
d.pendown()
d.color("blue")
d.begin_fill()
for i in range(4) :
d.fd(int_by_size)
d.right(90)
d.end_fill()
d.penup()
for y,line in enumerate(data) :
for x,tile in enumerate(line) :
if tile == 1 :
square("green")
d.fd(interval)
else :
d.fd(interval)
#Comment out to print entire map at once
turtle.update()
d.backward(int_by_size)
d.sety(d.ycor()-interval)
end_draw = time.time()
print("DRAW DONE IN",end_draw-start_draw)
wn.exitonclick()
My rework of your code below speeds up the drawing by an order of magnitude by switching from drawing to stamping and other tricks:
from turtle import Screen, Turtle
from random import randint
import time
CURSOR_SIZE = 20
interval = 10
size = int(input("Enter map size: "))
custom_interval = input("Custom interval: ")
if custom_interval != '':
interval = int(custom_interval)
int_by_size = interval * size
screen = Screen()
screen.setup(width=0.45, height=0.8, startx=800, starty=50)
screen.bgcolor('#002240')
screen.tracer(False)
x, y = interval/2 - int_by_size/2, int_by_size/2 - interval/2
start_gen = time.time()
data = [(randint(False, True) for _ in range(size)) for _ in range(size)]
print("GEN DONE IN", time.time() - start_gen)
start_draw = time.time()
turtle = Turtle()
turtle.hideturtle()
turtle.shape('square')
turtle.penup()
turtle.color('blue')
turtle.shapesize(int_by_size / CURSOR_SIZE)
turtle.stamp()
turtle.color('green')
turtle.shapesize(interval / CURSOR_SIZE)
turtle.goto(x, y)
for line in data:
dx = x
for tile in line:
if tile:
turtle.setx(dx)
turtle.stamp()
dx += interval
# Comment out to print entire map at once
screen.update()
y -= interval
turtle.goto(x, y)
print("DRAW DONE IN", time.time() - start_draw)
screen.tracer(True)
screen.exitonclick()
Some of my optimizations you may need to undo, like making the data a list of generators instead of a list of lists (speeds up data generation but is only good for a single pass.) Also, remember that a window size of width=0.45, height=0.8 is relative to the size of your screen -- other folks running on other screens will get a different size window.

Generate "scattered" items, that do not overlap

This is a question that i had to solve, but have been banging my head against a wall for the past 5 days. There must be something really simple that i am missing, or misunderstanding, as the logic seems 100% correct in my head, but it just won't work.
I need to draw "scattered" houses, that do not over lap, 100 of them.
I have to use turtle to draw them, so i have X and Y coordinates.
And X and Y list for verification purposes, to see if a house is already in that place.
What i am doing is drawing a "house" (square with a triangle on top) on the randomized coordinates,
I loop while the number of houses is less than 100.
On each loop i randomize the x and y coordinates, from where i start drawing each house with turtle.
I check if the value is already in the X and Y verification lists, and also if my new X and Y are +/- the size of the house (treating it as a square)
import turtle
import time
import random
t = turtle.Turtle()
turtle.screensize(1920,1000)
x_verif = []
y_verif = []
t.speed(0)
collision = None
def square():
for s in range(0, 4):
t.forward(90)
t.left(90)
def triangle():
for s in range(0, 2):
t.left(-60)
t.forward(52)
def house():
square()
t.left(90)
t.forward(90)
triangle()
def scatter():
print("a")
house()
x1 = random.randint(-850, 850)
y1 = random.randint(-380, 380)
count = 0
while count < 100:
print("a2")
i = 0
u = 0
collision = False
for tries in range (0, 2):
print("a3")
x_verif.append(x1)
y_verif.append(y1)
while u < 100:
print("a4")
print(x_verif, y_verif, x1, y1)
while i < len(x_verif):
x1 = random.randint(-850, 850)
y1 = random.randint(-380, 380)
print(x1, y1)
if x1 not in x_verif and (x1 > x_verif[i]+91 or x1 < x_verif[i]-91):
if y1 not in y_verif and (y1 > y_verif[i]+142 or y1 < y_verif[i]-142):
collision = False
else:
collision = True
if collision == False:
t.penup()
t.hideturtle()
t.setx(x1)
t.sety(y1)
t.pendown()
house()
x_verif.append(x1)
y_verif.append(y1)
count += 1
i+= 1
u += 1
scatter()
Sorry for the ugly code, and the simplicity behind it. I would love to use list comprehensions for this, but i do not know where my logic fails at the moment. This is like my 100th try, as of this version it only draws the initial house and i think it loops infinitely somewhere....
My problem lies in looping through the whole lists for every new value. Do i need to loop through them every time, or is there some IF condition that I can use? Edit: It keeps on looping through random values, but none of them are accepted by the two IF statements that i'm using.
On a side note: With my current code they also change direction of drawing every time... No idea why is that happening....
Edit: I am very thankful for all the solutions! I'm struggling with the note at the beginning of the question. It says that it should only take a couple more lines to do the last one, compared to the first one..... Are they joking?
This turned out to be a trickier problem than I assumed from the description. My approach was to treat, and store, a house as two polygons, a square and a triangle. I randomly test draw (pen up) a house and compare all the points in its polygons to see if they are inside existing house polygons, and vice versa. If no overlap, draw the house for real. The solution is not efficient, but it allows tight packing of houses than a simple diameter-based approach.
The point in triangle routine is based on one from GeeksForGeeks.org
I have one slight fudge factor in my code that still needs to be polished out. But overall it seems to achieve the objective:
from turtle import Screen, Turtle
from random import randint
HOUSES = 100
HOUSE_SIZE = 90
WINDOW_WIDTH, WINDOW_HEIGHT = 1920, 1000
# assumes roof is an isosceles triangle
ROOF_SIDE = HOUSE_SIZE * 3**0.5 / 3
ROOF_HEIGHT = ROOF_SIDE // 2
FONT_SIZE = HOUSE_SIZE // 3
FONT = ('Arial', FONT_SIZE, 'normal')
def square(turtle, identity=None):
turtle.begin_poly()
for _ in range(3):
turtle.forward(HOUSE_SIZE)
turtle.right(90)
turtle.end_poly()
turtle.forward(HOUSE_SIZE/2 - FONT_SIZE/2) # visually finish square
if identity: # label each house with a number
turtle.penup()
turtle.right(90)
turtle.forward(HOUSE_SIZE/2)
turtle.write(identity, align='center', font=FONT)
turtle.backward(HOUSE_SIZE/2)
turtle.left(90)
turtle.pendown()
turtle.forward(HOUSE_SIZE/2 + FONT_SIZE/2) # visually finish square
turtle.right(90) # return to original orientation
return turtle.get_poly()
def triangle(turtle):
turtle.begin_poly()
turtle.forward(HOUSE_SIZE)
turtle.left(150)
turtle.forward(ROOF_SIDE)
turtle.end_poly()
turtle.left(60)
turtle.forward(ROOF_SIDE) # visually finish triangle
turtle.right(210) # return to original orientation
return turtle.get_poly()
def house(turtle, identity=None):
return (square(turtle, identity), triangle(turtle))
def area_of_triangle(p1, p2, p3):
x1, y1 = p1
x2, y2 = p2
x3, y3 = p3
return abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2))) // 2
def is_inside_triangle(point, triangle):
p1, p2, p3 = triangle
a = area_of_triangle(p1, p2, p3)
b = area_of_triangle(point, p2, p3)
c = area_of_triangle(p1, point, p3)
d = area_of_triangle(p1, p2, point)
return abs(a - (b + c + d)) < 5 # 5 == fudge factor, sigh
def is_inside_square(point, square):
x, y = point
p1, p2, p3, p4 = square
_, y1 = p1
x2, _ = p2
_, y3 = p3
x4, _ = p4
return x4 <= x <= x2 and y3 <= y <= y1
def scatter(turtle):
houses = []
count = 0
while count < HOUSES:
x = randint(-WINDOW_WIDTH/2, WINDOW_WIDTH/2 - HOUSE_SIZE)
y = randint(HOUSE_SIZE - WINDOW_HEIGHT/2, WINDOW_HEIGHT/2 - ROOF_HEIGHT)
turtle.penup()
turtle.goto(x, y)
proposed_square, proposed_triangle = house(turtle) # test draw invisible house
turtle.pendown()
collision = False
for point in proposed_square + proposed_triangle: # does proposed house collide with houses?
for square, triangle in houses:
if is_inside_square(point, square) or is_inside_triangle(point, triangle):
collision = True
break
if collision:
break
for square, triangle in houses: # do houses collide with proposed house?
for point in square + triangle:
if is_inside_square(point, proposed_square) or is_inside_triangle(point, proposed_triangle):
collision = True
break
if collision:
break
if not collision:
count += 1
houses.append(house(turtle, identity=count)) # actually draw new house
print(count)
screen = Screen()
screen.screensize(WINDOW_WIDTH, WINDOW_HEIGHT)
screen.tracer(False)
turtle = Turtle()
turtle.hideturtle()
scatter(turtle)
screen.tracer(True)
screen.exitonclick()
This problem is simplified somewhat by the consistent orientation of houses. If the houses were randomly oriented, compass-wise, the square overlap calculations would be more complex.
The solution could be made more efficient by doing triangle overlap with triangle, square overlap with triangle, etc. tests instead of simply "point inside". We could also push the collision logic down into the square() and triangle() routines to throw an error as soon as there's a collsion, rather than completing a house and then testing.
Given the size of the screen area, size of a house, the number of houses, and the random scattering, I believe it may be possible for an idividual run of the program to stall trying to place a house where there may not be available space:
Defo no the best way to do it but
import turtle
import time
import random
t = turtle.Turtle()
turtle.screensize(1920,1000)
x_verif = []
y_verif = []
t.speed(0)
collision = None
def square():
for s in range(0, 4):
t.forward(90)
t.left(90)
def triangle():
for s in range(0, 2):
t.left(-60)
t.forward(52)
def house():
square()
t.left(90)
t.forward(90)
triangle()
t.left(30) #returning to 90 degrres
def scatter():
beenAt = [] #this will hold every place that there is a house
for i in range(100):
t.penup()
loop = True
while loop == True:
area = random.randint(-850, 850)
for i in range(91): #looping for the size of the house
if area+i in beenAt: #if the number chosen plus i is in beenAt stop because we cant use that place
break
if i == 90: #if at the finial part of our loop then draw house
t.goto(area, 0)
t.pendown()
for i in range(area, area + 91):
beenAt.append(i) #add to been at list every place we have drawn
house()
loop = False
scatter()

Can't draw parabolic curve correctly with turtle graphics

I'm making a game like 'angry bird'.
There are two input:power and angle.
I apply those inputs to parabolic curve.
My turtle flies, making a parabolic curve. and my turtle have to hit the target,
but my turtle draws strange curve when angle is greater than 46, angle is 30, 40 etc...
I don't know where is problem....here is my code:
import turtle
import random
import math
g=9.80665
origin_x=-480
origin_y=-180
flag=False
def create_target():
x=random.randint(0,500)
y=random.randint(-200,0)
target=turtle.Turtle()
target.hideturtle()
target.penup()
target.goto(x,y)
target.shape('square')
target.color('red')
target.showturtle()
return target
def create_turtle():
homework=turtle.Turtle()
homework.hideturtle()
homework.penup()
homework.speed(0)
homework.goto(origin_x,origin_y)
homework.pendown()
homework.shape('turtle')
homework.color('blue')
homework.left(45)
homework.showturtle()
return homework
def setting():
'''drawing back ground lines'''
setting=turtle.Turtle()
setting.hideturtle()
setting.speed(0)
turtle.colormode(255)
setting.pencolor(214,214,214)
for y in range(100,-101,-100):
setting.penup()
setting.goto(-500,y)
setting.pendown()
setting.goto(500,y)
for x in range(-375,500,125):
setting.penup()
setting.goto(x,200)
setting.pendown()
setting.goto(x,-200)
def throw_turtle(turtle,target):
angle=int(input("Enter Angle:"))
power=int(input("Enter Power:"))
'''
parabola fomula:
x coordinate: speed(in here, that is power) * cos(anlge)*time
y coordinate: speed*sin(angle)*time - (gravity speed*time**2)/2
'''
for time in range(1,20):
# the origin fomula is for the situation that starts from (0,0). so I think
# I should compensate it, but is it right?
x=power*math.cos(angle)*time + origin_x
y=power*math.sin(angle)*time - (((time**2)*g)/2) + origin_y
if x<origin_x: # I think it has problem...
x-=origin_x
turtle.goto(x,y)
turtle.stamp() #this is for testing
if (x==target.xcor()) and (y==target.ycor()):
print("******Target is HIT!!! ******")
print("End of Game")
flag=True
break
else:
print("You missed...")
turtle.setup(1000,400)
windo=turtle.Screen()
windo.title('Angry Turtle')
setting()
#__main
my_turtle=create_turtle()
while flag==False:
target=create_target()
my_turtle=create_turtle()
my_turtle.speed(6)
throw_turtle(my_turtle,target)
my_turtle.hideturtle()
target.hideturtle()
I think create_target() and create_turtle(), and setting() don't have problem...
Below, I reduce your code to a MVCE (minimal, complete, and verifiable example) to examine the parabolic curve drawing code. The problem I found with it is the usual one of the difference between degrees and radians. The Python math library thinks in radians but provides a conversion function for degrees. The Python turtle library thinks in degress, by default, but can switch to radians using turtle.radians(). Either way is fine but the usage has to be consistent:
from turtle import Turtle, Screen
import math
import random
G = 9.80665
origin_x = -480
origin_y = -180
def create_turtle():
homework = Turtle(shape='turtle')
homework.hideturtle()
homework.penup()
homework.goto(origin_x, origin_y)
homework.pendown()
homework.speed(0)
homework.left(45)
homework.showturtle()
return homework
def throw_turtle(turtle):
angle = int(input("Enter Angle (in degrees): "))
power = int(input("Enter Power: "))
# parabola formula:
# x coordinate: speed(in here, that is power) * cos(angle)*time
# y coordinate: speed*sin(angle)*time - (gravity speed * time**2)/2
for time in range(1, 20):
x = power * math.cos(math.radians(angle)) * time + origin_x
y = power * math.sin(math.radians(angle)) * time - (((time ** 2) * G) / 2) + origin_y
turtle.goto(x, y)
turtle.stamp() # this is for testing
window = Screen()
window.setup(1000, 400)
for _ in range(3):
my_turtle = create_turtle()
my_turtle.color(random.choice(['red', 'green', 'blue', 'purple', 'black']))
throw_turtle(my_turtle)
window.exitonclick()
EXAMPLE
> python3 test.py
Enter Angle (in degrees): 30
Enter Power: 120
Enter Angle (in degrees): 45
Enter Power: 90
Enter Angle (in degrees): 60
Enter Power: 90
>
Now, what more do you want it to do parabolic curve-wise?
Oh Thanks thanks really thanks.......!!!!
but, i have one more problem. that is, the 'if' sentence in throw_turtle function.
my intention for using that 'if' sentence is for check and end the game. but in fact, the user can not exactly correct coordinate of target. so it is impossible that end the game. so the game is endless....
to avoid that, i re-write like this.
def throw_turtle(turtle,target):
angle=int(input("Enter Angle:"))
power=int(input("Enter Power:"))
'''
parabola fomula: x coordinate: speed(in here, that is power) * cos(anlge)*time
y coordinate: speed*sin(angle)*time - (gravity speed*time**2)/2'''
for time in range(1,20):
x=power*math.cos(math.radians(angle))*time + origin_x #the origin fomula is for the situation that starts from (0,0). so i think i should compensate it. but.. is it right?
y=power*math.sin(math.radians(angle))*time - (((time**2)*g)/2) + origin_y
turtle.goto(x,y)
turtle.stamp() #this is for testing min_target_x=target.xcor()-1
max_target_x=target.xcor()+1 #the '1' means target's size
min_target_y=target.ycor()-1
max_target_y=target.ycor()+1
min_target_y=target.ycor()-1
if ((turtle.xcor()>=min_target_x) or (turtle.xcor()<=max_target_x)) and ((turtle.ycor()>=min_target_y) or (turtle.ycor()<=max_target_y)):
print("******Target is HIT!!! ******")
print("End of Game")
flag=True
break
else:
print("You missed...")

Categories