How can I use the random module in python turtle? - python-3.x

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.

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.

Interference of canvas items and problem in setting coordinates

I'm working on an animation of a moving object, while drawing it's path.
I want to draw the pixels in which the center of the object went through... but guess what? python decided to set the NW anchor of the image with the coordinates I send, instead of the center. I infer it has something to do with the pixels I draw simultaneously (creating a one pixel rectangle). so the image appear on the right of the path bellow... I want the center of it to be on the top of the pixels... adding the main of the code:
from tkinter import*
import time
dt = 0.01
clock_place = (500, 10)
def round_two(t, t0):
return round((t-t0)*100)/100
def round_three(t, t0):
return round((t-t0)*1000)/1000
# showing 'real time motion' for a known path (also cyclic), with
# parametric representation
def paint_known_path(x_pos, y_pos, t_0):
window = Tk()
canvas = Canvas(window, height=700, width=1000)
canvas.pack()
canvas.config(background='black')
tennis_ball = PhotoImage(file='tennis ball.png')
t = t_0
x = x_pos(t_0)
y = y_pos(t_0)
particle = canvas.create_image(x, y, image=tennis_ball)
clock = canvas.create_text(clock_place, text=round_two(t, t_0),
fill='white')
while True:
canvas.create_rectangle(x, y, x, y, outline='red')
canvas.itemconfig(clock, text=round_two(t, t_0))
t += dt
x = x_pos(t)
y = y_pos(t)
canvas.moveto(particle, x, y)
window.update()
if x == x_pos(t_0) and y == y_pos(t_0):
if t - t_0 > 100*dt:
break
time.sleep(dt)
canvas.create_text((500, 100), text='orbit duration: ' +
str(round_three(t, t_0)), fill='white')
window.mainloop()
It turns out to be quite a bit require, but here is the main completion components.
The first additional part that you need to add:
# print('the ten ball height', tennis_ball.height(), tennis_ball.width())
# tennis ball dimensions
tb_hght = tennis_ball.height()
tb_wdth = tennis_ball.width()
mid_point_x = x + tennis_ball.height() / 2
mid_point_y = y + tennis_ball.width() / 2
Secondly, also needed to add some functions to for x_pos and y_pos like this (these are just example functions to make the code work):
def x_pos(a):
# any function of t,
return 100
def y_pos(a):
# any function of t,
return 100
Furthermore, you need to call the function at the end like this:
paint_known_path(x_pos,y_pos,0)
Finally, need to add the mid_point_x and mid_point_y to the path that is drawn (as these will be the image centre points).

Extra pixels left and top of canvas python turtle

I'm not sure why but on the left and top of a canvas I'm getting weird behaviour while creating my snake game using turtle. There's a border of 10 pixels on the left and on the top.
Main Code
import functools
from turtle import Screen
import time
from Snake import Snake
# initialize screen
win = Screen()
WIDTH = HEIGHT = 800
win.setup(width=WIDTH, height=HEIGHT)
win.bgcolor("black")
canvas = win.getcanvas()
root = canvas.winfo_toplevel()
root.overrideredirect(1)
win.tracer(0)
s = Snake()
def main():
segments = s.init_segments()
win.update()
move_keys = ["Right", "Up", "Left", "Down"]
running = True
inc = 0
for k in move_keys:
win.onkeypress(functools.partial(s.move, segments, inc, k), key=k)
inc += 90
win.listen()
while running:
win.update()
time.sleep(0.1)
s.move(segments, segments[0].heading(), "none")
if s.crashed(win.window_width(), win.window_height(), segments):
running = False
break
win.exitonclick()
main()
Snake Code
from turtle import Turtle
class Snake:
def init_segments(self):
segs = []
x_pos = 0
for i in range(3):
t = Turtle(shape="square")
t.color("white")
t.penup()
t.speed(1)
segs.append(t)
if t != segs[0]:
x_pos -= 20
t.goto(x_pos, 0)
return segs
def __no_turn(self, h, d):
if h - d == -180 or h - d == 180:
return True
else:
return False
def __no_skip(self, k, dir):
if (k == "Right" and dir == 0 or k == "Up" and dir == 90
or k == "Left" and dir == 180 or k == "Down" and dir == 270):
return False
else:
return True
def move(self, t_list , dir, k):
for seg_num in range(len(t_list) - 1, -1, -1):
if self.__no_turn(t_list[0].heading(), dir):
continue
elif seg_num > 0:
if self.__no_skip(k, dir):
t_list[seg_num].goto(t_list[seg_num - 1].position())
else:
t_list[seg_num].setheading(dir)
if self.__no_skip(k, dir):
t_list[seg_num].forward(20)
def crashed(self, window_width, window_height, t_list):
head_position = t_list[0].position()
body_positions = []
hw = window_width/2
hh = window_height/2
for i in range(len(t_list)):
body_positions.append(t_list[i].position())
body_positions.pop(0)
for i in range(len(body_positions)):
if head_position == body_positions[i]:
return True
if head_position[0] >= hw:
print(f"Head Position: {head_position[0]} ... Wall Position: {hw}")
return True
elif head_position[0] <= -hw:
print(f"Head Position: {head_position[0]} ... Wall Position: {-hw}")
return True
elif head_position[1] >= hh:
print(f"Head Position: {head_position[1]} ... Wall Position: {hh}")
return True
elif head_position[1] <= -hh:
print(f"Head Position: {head_position[1]} ... Wall Position: {-hh}")
return True
The print lines in this file were added to try and figure out what was going on.
Please help me understand what's going on.
Thanks.
Edit:
Also I didn't notice it earlier, but there's a 1px border on the bottom and right hand sides.
Second Edit to illustrate the problem further.
I added the code for the pills a while ago. What I do is, I spawn every pill in all at once and hide them because you can't destroy a turtle without clearing the screen and it would be weird to get the turtle position every single time and redraw the turtle on the screen where it had been 0.1 seconds previously. Then I got this result.
As you can see the pills are spawning off screen.
The following is the code that I used in order to spawn in all pills and is similar to the code that I use for determining the edges of the screen.
hw = int(WIDTH/2)
hh = int(HEIGHT/2)
p_list = []
for i in range(-hw, hw, 20):
for j in range(-hh, hh, 20):
p = Pill((i, j))
p.show_pill(segments)
p_list.append(p)
What happens when I reorder the win.update()
As you can see here, the right side now crashes one whole segment in. If you look closely, you'll see in this picture that the white of the snake, covers the extra pixel of space that is not covered in the previous picture where it stops on the last pixel of the circle.
You'll also see in the second picture and this one that there is a row of circles on the bottom that are peeking over the edge of the window's border.
If you move win.update() after s.move(segments, segments[0].heading(), "none") instead of before it will work (i.e) crash in the same way in all four walls.
If you don't want the head to get burst into the wall when crashing then modify the crash boundary check accordingly.
I've finally figured out why this happens.
First problem: I was previously led to believe that all turtle shapes are created equal. No they're not. While the default size for a turtle might be 20px by 20px, a square and circle are both defaulted to 21px as they need a center point that is not an even number so that it can divide evenly between the 4 quadrants of turtle's co-ordinate system (which has a single pixel serving as the x and y axes).
How to solve:
Make the canvas a multiple of 21px.
Remember that the canvas, when split into 2 parts has half of a turtle on each edge, which counts as a full turtle but the turtle only completes over the canvas, so we're sitting with an extra column and row of turtles.
Remove the last column and row of turtles and move the entire grid over by one turtle.
If done correctly you'll end up with the result below. Unfortunately, 2px at the right and bottom will be cut off as it's used for Turtle's UI. The top and left face a similar problem in that a single row of pixels are used for Turtle's UI
The snake itself is done in a similar manner in that instead of shifting the starting segment 20px I shift it 11px first and then I appended the other parts of the snake. I calculate it's position by removing the 11px that I shifted it initially. This literally solved most of the problems with the snake apart from stuff that Turtle's ui takes away.

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()

Colors are not filling in certain places

I am utilizing turtle to make a xmastree. The task is to color the individual pieces. The "trunk" portion of code works and will fill correctly. The Layer1(), Layer2(), and Layer3() do not fill with color as they should. Any help would be appreciated.
I have looked through the other problems on stack overflow. I have repositioned my variables. Still, nothing.
""" Lab 9 Exercise 3
Author: Jonathan Wheatley
Define a function drawXmasTree(t, blc, scale = 1) You may add further parameters beyond the first three if you wish (note: give any additional parameters default values!). Your
tree should resemble three filed, superimposed green triangles (containing colored ball ornamets) over a brown trunk.
blc and scale should work as in the preceding exercise. Show results at two different scales.
"""
from turtle import Turtle
scale = 1.25
def drawXmasTree():
a = trunk()
b = Layer1()
c = Layer2()
d = Layer3()
def trunk():
t = Turtle()
t.pencolor("brown")
t.fillcolor("brown")
t.shape("turtle")
t.up()
t.goto((scale * -100), (scale * -100))
t.down()
for count in range(2):
t.begin_fill()
t.forward(scale * 10)
t.left(90)
t.forward(scale *100)
t.left(90)
t.end_fill()
t.hideturtle()
def Layer1():
t = Turtle()
t.pencolor("green")
t.fillcolor("green")
t.shape("turtle")
t.up()
t.goto((scale * -150), 0)
t.down()
for count in range(3):
t.begin_fill()
t.forward(scale * 110)
t. left(120)
t.end_fill()
t.hideturtle()
def Layer2():
t = Turtle()
t.pencolor("green")
t.fillcolor("green")
t.shape("turtle")
t.up()
t.goto((scale * -147), 15)
t.down()
for count in range(3):
t.begin_fill()
t.forward(scale * 104)
t.left(120)
t.end_fill()
t.hideturtle()
def Layer3():
t = Turtle()
t.fillcolor("green")
t.pencolor("green")
t.shape("turtle")
t.up()
t.goto((scale * -145), 30)
t.down()
for count in range(3):
t.begin_fill()
t.forward(scale * 100)
t.left(120)
t.end_fill()
t.hideturtle()
def main():
u = drawXmasTree()
main()
When the code is run the turtle should display, draw in the correct colored line, and then the shape should fill.
So, in running so more tests, I seem to be getting positive results by breaking the begin_fill() and end_fill out of the for loop. I don't know why this was working in the Trunk() section but it does not work in the subsequent sections.
Please, if you find a way to make this code better let me know. I would love to learn something and improve my coding.
Thank you.

Resources