问题
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