#python #python-3.x #turtle-graphics
#python #python-3.x #черепаха-графика
Вопрос:
Я должен написать программу, которая реализует использование графики черепах. Я уже написал большую часть программы, которая включает в себя сетку и создание нескольких черепах, но мне трудно завершить игру, как только черепаха достигает края созданной мной сетки. Вот что у меня есть на данный момент:
import turtle
import random
# Setting up Turtle Graphics Window
turtle.setup(800,600)
window = turtle.Screen()
window.title("Turtles Walking through Grid")
window.bgcolor("black")
# Making the turtle
grid = turtle.getturtle()
grid.shape("classic")
grid.color("white")
grid.speed(10)
# Creating the Grid (Relative Positioning)
grid.penup()
grid.setposition(-300,200)
grid.pendown()
grid.forward(600)
grid.right(90)
grid.forward(400)
grid.right(90)
grid.forward(600)
grid.right(90)
grid.forward(400)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(400)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(400)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(400)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(400)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(400)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(400)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(400)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(400)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(400)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(400)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(400)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(400)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(400)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(400)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(600)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(600)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(600)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(600)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(600)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(600)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(600)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(600)
grid.right(90)
grid.forward(40)
grid.right(90)
grid.forward(600)
grid.left(90)
grid.forward(40)
grid.left(90)
grid.forward(600)
# User Input for Speed
speed = int(input("Enter the speed of the turtles (1-10): "))
# Variable for choosing colors
all_colors = ["red","white","blue","hotpink","purple","lightgreen","yellow"]
# Creating the turtles
def createTurtles(turtle_count):
count = []
for k in range(0, turtle_count):
lil_guys = turtle.Turtle()
lil_guys.shape("turtle")
colors = random.choice(all_colors)
lil_guys.color(colors)
lil_guys.speed(speed)
lil_guys.pendown()
count.append(lil_guys)
return count
# Determine where the Turtle should stop
def off_board(self):
x = self.turtle.xcor()
y = self.turtle.ycor()
return x < -160 or 160 < x or y < -160 or 160 < y
# Set Turtle Amount to 5
count = createTurtles(5)
fun = True
while fun:
for k in range(5):
coin = random.randrange(0, 2)
if coin == 0:
count[k].left(90)
else:
count[k].right(90)
count[k].forward(40)
# Exit on close window
turtle.exitonclick()
Предполагается, что программа завершится, как только одна из пяти черепах достигнет края созданной мной сетки.
Ответ №1:
Выход на краю сетки
Вы можете выйти, когда черепаха достигнет края сетки, следующим образом:
while fun:
for k in range(5):
coin = random.randrange(0, 2)
if coin == 0:
count[k].left(90)
else:
count[k].right(90)
count[k].forward(40)
x = count[k].xcor() # new lines
y = count[k].ycor() # |
# |
if x < -300 or 300 < x or # |
y < -200 or 200 < y: # |
fun = False # |
break # |
Вы на правильном пути, помещая эту логику в off_grid
функцию, но эта функция не должна принимать self
в качестве аргумента (это не экземпляр класса).
Предложения по дизайну
У меня есть несколько общих предложений по дизайну (простите за импровизированный обзор кода):
- Избегайте глобальных переменных; используйте параметры для передачи информации в функции. Это обеспечивает многократное использование функций и их безопасность. Думайте о каждой функции как о черном ящике с регулируемыми ручками (параметрами); этот черный ящик должен работать независимо и не ломаться или работать по-другому, если внешнее состояние непредсказуемо меняется. Это уменьшает количество ошибок и упрощает анализ вашей программы.
- Используйте точные имена переменных.
count
на самом деле это неcount
что-либо, а скорее списокturtle
ов. Осмысленные имена облегчают следование вашей логике и позволяют избежать ошибок и недоразумений. Переменнаяfun
могла бы быть понятнее какrunning
. Переменнаяk
вfor k in range(0, turtle_count):
не используется и обычно записывается как_
на Python. - Предпочитайте
snake_case
для имен функций Python (CamelCase
используется для классов). -
Вместо множества последовательных строк команд используйте цикл для рисования сетки и сохраняйте свой код СУХИМ (не повторяйтесь). Например:
for _ in range(0, height 1, grid_size): turtle.pendown() turtle.forward(width) turtle.penup() turtle.right(90) turtle.forward(grid_size) turtle.right(90) turtle.forward(width) turtle.right(180)
-
Избегайте жесткого кодирования чисел и строк; разместите все эти переменные в верхней части вашей
main
программы и используйте их повсюду. В частности, в этой программе вам нужныheight
,width
иgrid_size
параметры, которые будут определены в одном месте и будут управлять работой всей программы (включая определение того, когда черепаха покинула сетку). Теперь, если я решу, что мне нужен размер сетки 30, высота 200 и ширина 400, например, я могу изменить эти цифры в одном месте, и все просто заработает. - Используйте параметры Python по умолчанию или словари, чтобы уменьшить нагрузку избыточных параметров на функции. Разместите функции в верхней части вашего скрипта и отделите их от
main
. -
Комментарии хороши, но комментарии, когда код уже очевиден, часто добавляют шума:
# Exit on close window turtle.exitonclick()
-
Имейте в виду пользователя: я не знал, что мне нужно было вернуться к терминалу, чтобы ввести черепашью скорость после того, как сетка была нарисована. Я бы предпочел запросить у пользователя черепашью скорость, а затем запустить визуальную часть программы.
Возможный рефакторинг
Собрав все это вместе, вот предлагаемый первый рефакторинг (все еще есть много возможностей для улучшения дизайна, но это должно дать некоторую пищу для размышлений):
import turtle
import random
def create_turtles(
turtle, turtle_count, colors, speed=10, shape="turtle"
):
turtles = []
for _ in range(turtle_count):
tur = turtle.Turtle()
tur.shape(shape)
tur.color(random.choice(colors))
tur.speed(speed)
tur.pendown()
turtles.append(tur)
return turtles
def draw_lines(turtle, turn, length_a, length_b, grid_size):
for _ in range(0, length_a 1, grid_size):
turtle.pendown()
turtle.forward(length_b)
turtle.penup()
turn(90)
turtle.forward(grid_size)
turn(90)
turtle.forward(length_b)
turn(180)
def draw_grid(
turtle, width=600, height=400, grid_size=40,
speed=100, shape="classic", color="white"
):
tur = turtle.getturtle()
tur.shape(shape)
tur.color(color)
tur.speed(speed)
tur.penup()
tur.setposition(-width // 2, height // 2)
draw_lines(tur, tur.right, height, width, grid_size)
tur.setposition(-width // 2, height // 2)
tur.right(90)
draw_lines(tur, tur.left, width, height, grid_size)
turtle.penup()
turtle.ht()
def off_grid(turtle, width, height):
x = turtle.xcor()
y = turtle.ycor()
return x < -width // 2 or x > width // 2 or
y < -height // 2or y > height // 2
if __name__ == "__main__":
grid_size = 40
height = 400
width = 600
all_colors = [
"red", "white", "blue", "hotpink",
"purple", "lightgreen", "yellow"
]
speed = int(input("Enter the speed of the turtles (1-10): "))
turtle.setup(800, 600)
window = turtle.Screen()
window.title("Turtles Walking through Grid")
window.bgcolor("black")
draw_grid(turtle, width, height, grid_size)
turtles = create_turtles(turtle, 5, all_colors, speed)
running = True
while running:
for tur in turtles:
random.choice([tur.left, tur.right])(90)
tur.forward(grid_size)
if off_grid(tur, width, height):
running = False
break
turtle.exitonclick()
Комментарии:
1. Как я отметил в своем ответе, я ценю совет, который вы дали OP ( 1). Один сбой, который вы наследуете от кода операционной системы, — это проблема с четностью. Т.е. черепахи ходят по линиям в одном измерении, между ними — в другом. Если вы измените ширину сетки на 400, то они полностью пройдут по линиям. Это действительно должно не зависеть от размера сетки, и я попытался учесть это в своем примере кода.
Ответ №2:
Я не собираюсь дублировать все отличные советы @ggorlen ( 1), а скорее укажу на некоторые другие проблемы:
-
Ваши черепахи ходят по линиям сетки в одном измерении и ходят между линиями сетки в другом. В моей доработке ниже они ходят по линиям сетки. Для этого требуется вычисление на основе (четности) размеров, которые вы выбираете для своей сетки.
-
В моей доработке движение останавливается, когда черепаха достигает края сетки, что становится более четким, поскольку они идут по линиям сетки.
-
Избегайте использования «white» в качестве цвета черепахи, если вы хотите, чтобы перо было опущено и линии сетки были белыми! То же самое для «черного» цвета черепахи.
-
Если / когда вы можете, не заставляйте пользователя выходить за пределы программы для ввода параметров. Как отмечает @ggorlen, выполнение
input()
перед вызовом turtle помогает. Но в своей доработке я использовалnuminput()
, новое в Python 3, чтобы сохранить все это в графическом интерфейсе. -
Существует множество алгоритмов для рисования сетки в turtle, выберите один и используйте его!
Переработанный код:
from turtle import Screen, Turtle
from random import choice
TURTLE_COUNT = 5
# Variable for choosing colors
ALL_COLORS = ['red', 'green', 'blue', 'magenta', 'yellow', 'cyan', 'purple']
WINDOW_WIDTH, WINDOW_HEIGHT = 800, 600
GRID_WIDTH, GRID_HEIGHT = 600, 400
CELL_SIZE = 40 # should be a divisor of GRID_WIDTH and GRID_HEIGHT, and probably no smaller than CURSOR_SIZE
CURSOR_SIZE = 20
# Creating the turtles
def create_turtles(turtle_count, speed):
turtles = []
for index in range(turtle_count):
lil_guy = Turtle('turtle')
lil_guy.color(ALL_COLORS[index % TURTLE_COUNT])
lil_guy.speed(speed)
lil_guy.penup()
# to place a turtle cleanly on the grid lines, we have to consider the parity of the grid size
lil_guy.goto((GRID_WIDTH / CELL_SIZE % 2) * CELL_SIZE/2, (GRID_HEIGHT / CELL_SIZE % 2) * CELL_SIZE/2)
lil_guy.pendown()
turtles.append(lil_guy)
return turtles
# Determine where the Turtle should stop
def on_edge(turtle):
x, y = turtle.position()
return abs(x) >= (GRID_WIDTH/2 - CELL_SIZE/2) or abs(y) >= (GRID_HEIGHT/2 - CELL_SIZE/2)
# Setting up Turtle Graphics Window
window = Screen()
window.setup(WINDOW_WIDTH, WINDOW_HEIGHT)
window.title("Turtles Walking through Grid")
window.bgcolor('black')
# Create the grid via stamping
grid = Turtle(visible=False)
grid.speed('fastest')
grid.color('white')
grid.penup()
grid.setx(-GRID_WIDTH/2)
grid.shapesize(GRID_HEIGHT*2 / CURSOR_SIZE, 1/CURSOR_SIZE)
for _ in range(GRID_WIDTH // CELL_SIZE 1):
grid.stamp()
grid.forward(CELL_SIZE)
grid.setheading(90)
grid.setposition(0, -GRID_HEIGHT/2)
grid.shapesize(GRID_WIDTH*2 / CURSOR_SIZE, 1/CURSOR_SIZE)
for _ in range(GRID_HEIGHT // CELL_SIZE 1):
grid.stamp()
grid.forward(CELL_SIZE)
# User Input for Speed
user_speed = window.numinput("Turtle Speed", "Enter a value (1-10)", default=5, minval=1, maxval=10)
# Set Turtle Amount
turtles = create_turtles(TURTLE_COUNT, user_speed)
finished = False
while not finished:
for k in range(5):
angle = choice([90, -90])
turtles[k].left(angle)
turtles[k].forward(CELL_SIZE)
finished = on_edge(turtles[k])
if finished:
break
# Exit on close window
window.exitonclick()