Завершение игры, когда черепаха достигает края сетки - PullRequest
0 голосов
/ 09 марта 2019

Мне нужно написать программу, которая реализует использование Turtle Graphics. Я уже написал большую часть программы, которая включает в себя сетку и создание нескольких черепах, но мне трудно закончить игру, когда черепаха достигнет края сетки, которую я создал. Вот что у меня есть:

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

Программа должна завершиться, как только одна из пяти черепах достигнет края созданной мною сетки.

Ответы [ 2 ]

1 голос
/ 10 марта 2019

Я не собираюсь дублировать все замечательные советы @ ggorlen (+1), а скорее укажу на некоторые другие вопросы:

  • Ваши черепахи проходят по линиям сетки в одном измерении и проходят между линиями сетки в другом измерении. В моей переделке ниже они ходят по линиям сетки. Это требует расчета на основе (паритета) размеров, выбранных для вашей сетки.

  • В моей переделке движение останавливается, когда черепаха достигает края сетки, которая более ясна, поскольку они ходят по линиям сетки.

  • Старайтесь не использовать «белый» в качестве цвета черепахи, если вы хотите, чтобы перо было перо и белые линии сетки! То же самое для "черного" цвета черепахи.

  • Если / когда вы можете, не заставляйте пользователя выходить за пределы программы для ввода параметров. Как отмечает @ggorlen, выполнение input() перед вызовом черепахи помогает. Но в своей переделке я использовал numinput(), новый в Python 3, чтобы сохранить все это в графическом интерфейсе.

  • Существует множество алгоритмов рисования сетки в черепахе, выберите один и используйте его!

переработанный код:

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()
1 голос
/ 09 марта 2019

Выход на краю сетки

Вы можете выйти, когда черепаха достигнет края сетки, следующим образом:

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 используется дляклассы).
  • Вместо множества последовательных командных команд используйте цикл для рисования сетки и сохраняйте свой код DRY (не повторяйте себя).Например:

    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'sпараметры по умолчанию или словари, чтобы уменьшить нагрузку чрезмерных параметров на функции.Разместите функции в верхней части скрипта и отделите их от 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()
...