当海龟到达网格边缘时结束游戏

时间:2019-03-09 18:42:23

标签: python python-3.x turtle-graphics

我必须编写一个程序来实现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 个答案:

答案 0 :(得分: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作为参数(它不是类的实例)。


设计建议

我有一些一般的设计建议(原谅即兴代码审查):

  • 避免全局变量;使用参数将信息传递给函数。这样可以使功能re-usable保持安全。将每个功能想像成一个带有可调节旋钮(参数)的黑匣子;如果外部状态发生不可预测的变化,则该黑匣子应该独立工作,并且不会损坏或工作不同。这样可以减少错误,并使程序更易于推理。
  • 使用准确的变量名。 count实际上不是任何事物的count,而是turtle的列表。有意义的名称使您更容易遵循自己的逻辑,避免出现错误和误解。变量fun可以更清楚地表示为runningk中的变量for k in range(0, turtle_count):未使用,在Python中通常写为_
  • snake_case首选为function names in PythonCamelCase用于类)。
  • 使用循环来绘制网格并保留代码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)
    
  • 避免使用hardcoding数字和字符串;将所有这些变量放在main程序的顶部,并在整个过程中使用它们。具体来说,在此程序中,您需要heightwidthgrid_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 :(得分:1)

我不会重复@ggorlen的所有出色建议(+1),而是指出其他一些问题:

  • 您的海龟在一个维度上在网格线上行走,在另一个维度上在网格线之间行走。在我下面的修改中,他们在网格线上 行走。这需要根据您为网格选择的(奇偶性)大小进行计算。

  • 在我的返工中,当海龟到达网格的边缘时,运动停止了,因为它们在网格线上行走,因此更加清晰。

  • 如果要使用笔向下和白色网格线,请避免使用“白色”作为乌龟色!同为乌龟颜色的“黑色”。

  • 如果可以的话,请避免让用户走出程序来输入参数。如@ggorlen所述,在调用乌龟之前先进行input()会有所帮助。但是在我的返工中,我使用了Python 3中新增的numinput(),将其全部保留在GUI中。

  • 有很多算法可以在乌龟中绘制网格,然后选择其中一个并使用它!

重做的代码:

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