How can I make a turtle do something when it gets close to another turtle?

独自空忆成欢 提交于 2021-01-28 20:32:54

问题


Good afternoon,

I am simulating a virus outbreak with the use of turtles. I have come up with the following code, my question will be after the code:

import turtle
import random
import time

def make_population(amount):
    """
    Creates a list representing a population with a certain amount of people.
    """
    population = []
    for person in range(amount):
        population.append(turtle.Turtle())
    for person in population:
        person.shape("circle")
        person.shapesize(0.2)
    return population

def random_move(person):
    """
    Makes a turtle move forward a random amount and then turn a random amount.
    """
    person.forward(random.randint(0,20))
    person.right(random.randint(-180,180))

def check_boundary(person):
    """
    Checks if a turtle is still within the given boundaries.
    """
    if -250 <= person.xcor() <= 250 and -250 <= person.ycor() <= 250:
        return
    person.setpos(random.randint(-200,200),random.randint(-200,200))

def infect_random(population):
    """
    Gets a random item from the population list and turns one red
    """
    infected = random.choice(population)
    infected.color("red")
    return infected

def infect_person(person):
    """
    Makes the turtle infected
    """
    infected_person = person.color("red")
    return infected_person

def simulation(amount, moves = 0):
    """
    Simulates a virus outbreak
    """
    border = 500
    window = turtle.Screen()
    turtle.setup(500,500)
    turtle.tracer(0)
    population = make_population(amount)
    for person in population:
        person.penup()
        person.setpos(random.randint(-250,250),random.randint(-250,250))
    turtle.update()
    infected = infect_random(population)
    for move in range(moves):
        turtle.tracer(0)
        for person in population:
            random_move(person)
            if person.distance(infected) < 50:
                infect_person(person)
            check_boundary(person)
        turtle.update()
        time.sleep(0.5)

    window.exitonclick()

So when the simulation starts I infect 1 random person, and if other turtles get close, e.g. within 50 pixels, they will get infected too and turn red. However, these newly 'infected' turtles won't infect other turtles since they are not 'infected' as compared to the initial turtle. I have tried changing it to infected = infect_person(person) but this just gives me an error. I am stuck for a while now and was wondering if there is anyone who can help. I have also thought about making two lists: population and infected_population maybe that can solve my issue but I couldn't figure out how to implement that in the rest of my code.

Thanks in advance


回答1:


I believe the solution is separating low level turtle operations into a Person subclass of Turtle from high level operations on people in the simulation:

from turtle import Screen, Turtle
from random import randint, choice
from time import sleep

class Person(Turtle):
    population = []

    def __init__(self):
        super().__init__(shape='circle')

        self.shapesize(0.2)
        self.penup()
        self.setpos(randint(-250, 250), randint(-250, 250))

        Person.population.append(self)

    @classmethod
    def all_infected(cls):
        return [person for person in cls.population if person.infected()]

    def infect(self):
        self.color('red')

    def infected(self):
        return self.pencolor() == 'red'

    def random_move(self):
        """
        Makes a turtle move forward a random amount and then turn a random amount.
        """

        self.right(randint(-180, 180))
        self.forward(randint(0, 20))

        # checks if turtle is still within the given boundaries.

        if not (-250 < self.xcor() < 250 and -250 < self.ycor() < 250):
            self.undo()  # undo forward()

def make_population(amount):
    """
    Creates a list representing a population with a certain amount of people.
    """

    for _ in range(amount):
        Person()

def infect_random():
    """
    Gets a random item from the population list and turns one red
    """

    person = choice(Person.population)
    person.infect()

def simulation(amount=20, moves=100):
    """
    Simulates a virus outbreak
    """

    make_population(amount)

    infect_random()

    screen.update()

    for _ in range(moves):
        for person in Person.population:
            person.random_move()

            if not person.infected():
                for infected in Person.all_infected():
                    if person.distance(infected) < 50:
                        person.infect()

        screen.update()
        sleep(0.5)

screen = Screen()
screen.setup(500, 500)
screen.tracer(0)

simulation()

screen.exitonclick()

We could go further with turtle timer events to make the people more autonomous instead of the for _ in range(moves): loop.




回答2:


I believe you have made a small example but we miss information about data structure, is person a class?

You do not reassign the person as infected.

When you infected the first people

infected = infect_random(population)

you assign it as infected, but when you infect other persone you don't, you turn it in red return the person:

def infect_person(person):
    """
    Makes the turtle infected
    """
    infected_person = person.color("red")
    return infected_person

but hen In your code you don't assign it,

infect_person(person)

I suggest either to use a way to know who is infected or who is not. For example: If you have used POO :

  • you can add a field is_infected

  • else use a list that keeps the indices of the person being infected?

Doing that you will have to change the way you test if someone near is infected. For all person near a person if one is infected then I am becoming infected...



来源:https://stackoverflow.com/questions/61634600/how-can-i-make-a-turtle-do-something-when-it-gets-close-to-another-turtle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!