Count number of steps of turtle in a list - python - python

I am now three weeks into learning python and I'm stuck.
This is my code: (after that comes my question)
from turtle import Screen, Turtle
from random import randint, choice
def person_characteristics(people):
"""
Gives the turtle 'person' all it's characteristics / values.
"""
for person in people:
person.shape('circle')
person.shapesize(0.2)
person.speed('fastest')
person.penup()
x = randint(-200, 200) #turtle gets a random position
y = randint(-200, 200)
person.setpos(x, y)
person.showturtle()
def population(population_size):
"""
Makes a population, by making a list of turtles (persons).
population_size = type(int)
"""
people = []
for _ in range(population_size):
people.append(Turtle(visible=False))
return people
def random_walk(person, step_size, area_size):
"""
Makes the person walk around randomly within the borders.
step_size = type(int) -> determines how big of a step each person takes.
area_size = type(int) -> determines how big the area is where the persons are in.
"""
if -area_size < person.xcor() < area_size and -area_size < person.ycor() < area_size: #if person is within the borders then it moves randomly
person.right(randint(0, 360))
person.forward(step_size)
else:
person.right(180) #if person is outside the borders it turns around
person.forward(step_size)
def infect_random(people):
"""
Random person gets infected (a red color)
people = a list of persons achieved from de function population()
"""
infected = choice(people)
infected.color('red')
return infected
screen = Screen()
people = population(100)
person_characteristics(people)
infected_people = []
initial_infected = infect_random(people)
infected_people.append(initial_infected)
counted_infections = 1
#count_steps = 0
#healed_people = []
for _ in range(10): # determines the number of steps = time
for person in people:
random_walk(person, 30, 400)
for infected_person in infected_people:
if person.pencolor() != 'red' and person.distance(infected_person) < 30: #if a person gets close to the initial infected person it also
person.color('red') #gets infected & added to the list of infected persons
infected_people.append(person)
#count_steps +=1
#if count_steps = 5:
#infected_person.color('green')
#healed_people.append(infected_person)
#infected_people.remove(infected_person)
break
count_susceptible = len(people) - len(infected_people) #counts number of susceptible people
count_infected = len(infected_people) #counts number of infected people
print(count_susceptible)
print(count_infected)
screen.exitonclick()
I want to turn the infected_person green (=healed) and at the turtle to the list healed_people (& remove from the list infected_people) after it made 5 steps. My idea was to do this with a if statement however this does not work. My idea is in the code above. I know why it doesn't work: now it counts the total number of steps of every infected_person, instead of separately.
I think there probably is a very simple solution but I am very new to python so I have no idea how to do this. Can anyone help?
Thanks in advance!
(I prefer not to use a Class since I didn't learn that yet :)

from turtle import Screen, Turtle
from random import randint, choice
def person_characteristics(people):
"""
Gives the turtle 'person' all it's characteristics / values.
"""
for person in people:
person.shape('circle')
person.shapesize(0.2)
person.speed('fastest')
person.penup()
x = randint(-200, 200) #turtle gets a random position
y = randint(-200, 200)
person.setpos(x, y)
person.showturtle()
def population(population_size):
"""
Makes a population, by making a list of turtles (persons).
population_size = type(int)
"""
people = []
for _ in range(population_size):
people.append(Turtle(visible=False))
return people
def random_walk(person, step_size, area_size):
"""
Makes the person walk around randomly within the borders.
step_size = type(int) -> determines how big of a step each person takes.
area_size = type(int) -> determines how big the area is where the persons are in.
"""
if -area_size < person.xcor() < area_size and -area_size < person.ycor() < area_size: #if person is within the borders then it moves randomly
person.right(randint(0, 360))
person.forward(step_size)
else:
person.right(180) #if person is outside the borders it turns around
person.forward(step_size)
def infect_random(people):
"""
Random person gets infected (a red color)
people = a list of persons achieved from de function population()
"""
infected = choice(people)
infected.color('red')
return infected
screen = Screen()
people = population(100)
person_characteristics(people)
infected_people = []
people_steps=[0 for _ in range (len(people))]
initial_infected = infect_random(people)
infected_people.append(initial_infected)
counted_infections = 1
for _ in range(10): # determines the number of steps = time
for person in people:
random_walk(person, 30, 400)
people_steps[people.index(person)]+=1
if people_steps[people.index(person)]==5 and person.pencolor()=='red':
person.color('green')
infected_people.remove(person)
for infected_person in infected_people:
if person.pencolor() != 'red' and person.distance(infected_person) < 30: #if a person gets close to the initial infected person it also
person.color('red') #gets infected & added to the list of infected persons
infected_people.append(person)
people_steps[people.index(person)]=0
break
count_susceptible = len(people) - len(infected_people) #counts number of susceptible people
count_infected = len(infected_people) #counts number of infected people
print(count_susceptible)
print(count_infected)
screen.exitonclick()
You can use an auxiliary array to save the number of people's steps. If you create it with the same size you can work with the indexes. For example:
people=[person1, person2, person3]
people_steps=[number_of_steps1, number_of_steps2, number_of_steps3]
This is just a graphical representation.
But it is better to do it with a class so that the number of steps is part of it as an attribute.
I hope this helps. If you have any suggestions or questions, please let me know.

Related

Average time to hit a given line on 2D random walk on a unit grid

I am trying to simulate the following problem:
Given a 2D random walk (in a lattice grid) starting from the origin what is the average waiting time to hit the line y=1-x
import numpy as np
from tqdm import tqdm
N=5*10**3
results=[]
for _ in tqdm(range(N)):
current = [0,0]
step=0
while (current[1]+current[0] != 1):
step += 1
a = np.random.randint(0,4)
if (a==0):
current[0] += 1
elif (a==1):
current[0] -= 1
elif (a==2):
current[1] += 1
elif (a==3):
current[1] -= 1
results.append(step)
This code is slow even for N<10**4 I am not sure how to optimize it or change it to properly simulate the problem.
Instead of simulating a bunch of random walks sequentially, lets try simulating multiple paths at the same time and tracking the probabilities of those happening, for instance we start at position 0 with probability 1:
states = {0+0j: 1}
and the possible moves along with their associated probabilities would be something like this:
moves = {1+0j: 0.25, 0+1j: 0.25, -1+0j: 0.25, 0-1j: 0.25}
# moves = {1: 0.5, -1:0.5} # this would basically be equivelent
With this construct we can update to new states by going over the combination of each state and each move and update probabilities accordingly
def simulate_one_step(current_states):
newStates = {}
for cur_pos, prob_of_being_here in current_states.items():
for movement_dist,prob_of_moving_this_way in moves.items():
newStates.setdefault(cur_pos+movement_dist, 0)
newStates[cur_pos+movement_dist] += prob_of_being_here*prob_of_moving_this_way
return newStates
Then we just iterate this popping out all winning states at each step:
for stepIdx in range(1, 100):
states = simulate_one_step(states)
winning_chances = 0
# use set(keys) to make copy so we can delete cases out of states as we go.
for pos, prob in set(states.items()):
# if y = 1-x
if pos.imag == 1 - pos.real:
winning_chances += prob
# we no longer consider this a state that propogated because the path stops here.
del states[pos]
print(f"probability of winning after {stepIdx} moves is: {winning_chances}")
you would also be able to look at states for an idea of the distribution of possible positions, although totalling it in terms of distance from the line simplifies the data. Anyway, the final step would be to average the steps taken by the probability of taking that many steps and see if it converges:
total_average_num_moves += stepIdx * winning_chances
But we might be able to gather more insight by using symbolic variables! (note I'm simplifying this to a 1D problem which I describe how at the bottom)
import sympy
x = sympy.Symbol("x") # will sub in 1/2 later
moves = {
1: x, # assume x is the chances for us to move towards the target
-1: 1-x # and therefore 1-x is the chance of moving away
}
This with the exact code as written above gives us this sequence:
probability of winning after 1 moves is: x
probability of winning after 2 moves is: 0
probability of winning after 3 moves is: x**2*(1 - x)
probability of winning after 4 moves is: 0
probability of winning after 5 moves is: 2*x**3*(1 - x)**2
probability of winning after 6 moves is: 0
probability of winning after 7 moves is: 5*x**4*(1 - x)**3
probability of winning after 8 moves is: 0
probability of winning after 9 moves is: 14*x**5*(1 - x)**4
probability of winning after 10 moves is: 0
probability of winning after 11 moves is: 42*x**6*(1 - x)**5
probability of winning after 12 moves is: 0
probability of winning after 13 moves is: 132*x**7*(1 - x)**6
And if we ask the OEIS what the sequence 1,2,5,14,42,132... means it tells us those are Catalan numbers with the formula of (2n)!/(n!(n+1)!) so we can write a function for the non-zero terms in that series as:
f(n,x) = (2n)! / (n! * (n+1)!) * x^(n+1) * (1-x)^n
or in actual code:
import math
def probability_of_winning_after_2n_plus_1_steps(n, prob_of_moving_forward = 0.5):
return (math.factorial(2*n)/math.factorial(n)/math.factorial(n+1)
* prob_of_moving_forward**(n+1) * (1-prob_of_moving_forward)**n)
which now gives us a relatively instant way of calculating relevant parameters for any length, or more usefully ask wolfram alpha what the average would be (it diverges)
Note that we can simplify this to a 1D problem by considering y-x as one variable: "we start at y-x = 0 and move such that y-x either increases or decreases by 1 each move with equal chance and we are interested when y-x = 1. This means we can consider the 1D case by subbing in z=y-x.
Vectorisation would result in much faster code, approximately ~90K times faster. Here is the function that would return step to hit y=1-x line starting from (0,0) and trajectory generation on the 2D grid with unit steps .
import numpy as np
def _random_walk_2D(sim_steps):
""" Walk on 2D unit steps
return x_sim, y_sim, trajectory, number_of_steps_first_hit to y=1-x """
random_moves_x = np.insert(np.random.choice([1,0,-1], sim_steps), 0, 0)
random_moves_y = np.insert(np.random.choice([1,0,-1], sim_steps), 0, 0)
x_sim = np.cumsum(random_moves_x)
y_sim = np.cumsum(random_moves_y)
trajectory = np.array((x_sim,y_sim)).T
y_hat = 1-x_sim # checking if hit y=1-x
y_hit = y_hat-y_sim
hit_steps = np.where(y_hit == 0)
number_of_steps_first_hit = -1
if hit_steps[0].shape[0] > 0:
number_of_steps_first_hit = hit_steps[0][0]
return x_sim, y_sim, trajectory, number_of_steps_first_hit
if number_of_steps_first_hit is -1 it means trajectory does not hit the line.
A longer simulation and repeating might give the average behaviour, but the following one tells if it does not escape to Infiniti it hits line on average ~84 steps.
sim_steps= 5*10**3 # 5K steps
#Repeat
nrepeat = 40000
hit_step = [_random_walk_2D(sim_steps)[3] for _ in range(nrepeat)]
hit_step = [h for h in hit_step if h > -1]
np.mean(hit_step) # ~84 step
Much longer sim_steps will change the result though.
PS:
Good exercise, hope that this wasn't a homework, if it was homework, please cite this answer if it is used.
Edit
As discussed in the comments current _random_walk_2D works for 8-directions. To restrict it to cardinal direction we could do the following filtering:
cardinal_x_y = [(t[0], t[1]) for t in zip(random_moves_x, random_moves_y)
if np.abs(t[0]) != np.abs(t[1])]
random_moves_x = [t[0] for t in cardinal_x_y]
random_moves_y = [t[1] for t in cardinal_x_y]
though this would slow it down the function a bit but still will be super fast compare to for loop solutions.

Weighted version of random choice with 1 of 1 guaranteed choices

So I have this bit of code that takes 5 weighted rarity levels and randomly selects one. These rarity levels all have 10 corresponding image files, when a rarity is selected, the code then randomly selects an image file of that rarity.
What I would like to do is over the course of X amount of rolls, guarantee 1 specific image file of the Epic rarity is always selected, but only once. I'm assuming this would need a check of some kind.
I'm sure this can be done, but I am just having a hard time wrapping my head around how to approach it. Any help is greatly appreciated.
# randomly selects a weighted level of rarity
def quality():
A = 'Common'
B = "Uncommon"
C = "Rare"
D = "Legendary"
E = "Epic"
loot = [ (A,50), (B,30), (C,14), (D,5), (E,1) ]
choices = []
for item, weight in loot:
choices.extend( [item]*weight )
roll = random.choice(choices)
return roll
# randomly adds Aura from the selected rarity
def Aura():
#print(random.choice(os.listdir(os.path.join('/PNG/Aura/', q ))))
layQ = os.path.join('/PNG/Test/Aura/', quality() )
qLen = len(fnmatch.filter(os.listdir(layQ), '*.png'))
if qLen > 0:
png = random.choice(os.listdir(layQ))
roll = os.path.join('PNG/Test/Aura/', layQ, png)
return roll

Simulating the transition of packages in a distribution line

I am new to python and am trying to run a simulation of a warehouse logistics. The problem is composed of four main agents:
a shed, trucks, motorcycles and a distribution line. The truck enters the shed in one side with a specified amount of boxes, it goes to center of the shed, stops and start unloading the boxes to the distribution line, the distribution line moves the boxes to the other side of the shed where motorcycles pickup one box each.
The objective is to vary the size of the shed and distribution line to find the shape that can deliver more boxes in fixed amount of time (or compute the time taken to distribute a fixed amount of boxes, as in my code for now)
The distribution line is a rectangle, a grid with variable amount of rows and columns, depending on the size of the shed, let's say each cell has 0,50m on each side.
In the code I simulated the truck passing through the shed, and the amount of trucks passing as iterations, the problems is:
how to simulate the boxes moving through the grid (distribution line) from one side to the other, maybe accumulating in the stock until a bike arrives, and have the motorcycles "grab" them and go out after the boxes arrive?
I tried to count the boxes with "+= 1" function but I don't know why it's not working (would not be very realistic as well)
This is the main code:
import time
from Vehicles import Truck, Motorbike
bike1 = Motorbike(10, 1)
truck1 = Truck(10, int(input("Enter how many loads the truck has: ")))
num_iterations = int(input("Enter number of iterations: "))
start = time.time()
shed_width = 4
shed_length = 12
truck_path = int(shed_length * truck1.truck_speed/2)
for n in range(num_iterations):
truck_middle = False
while truck_middle is not True:
for i in range(truck_path):
x = 100/truck_path
if i == truck_path/2:
truck_middle = True
else:
#the bar here is to just have some visual feedback while the code runs
print("\r[%-60s] %d%%" % ('=' * i, x * i), end='')
time.sleep(0.1)
print("\ntruck is in the middle")
truck_middle = True
# while truck_middle is True:
# box = 0
# if box < truck1.truck_load:
# box += 1
# else:
# truck_middle = False
print("This was iteration: " + str(n+1))
time.sleep(0.01)
end = time.time()
print("\nDone! \nThe simulation took " + str(end - start) + " seconds to complete!")
I also created a class in a file called "Vehicles" for the truck and the motorcycles, where I can define their speed and the load they can carry:
class Truck:
def __init__(self, truck_speed, truck_load):
self.truck_speed = truck_speed
self.truck_load = truck_load
class Motorbike:
def __init__(self, motorbike_speed, motorbike_load):
self.motorbike_speed = motorbike_speed
self.motorbike_load = motorbike_load
I am open to code suggestions, indications of libraries and other resources I can search and study, any help will be much appreciated! thanks!
box = 0
while truck_middle == True:
if box < truck1.truck_load:
box += 1
else:
truck_middle = False
In your way, box will always be 1 and truck_middle is always True, and it goes in a dead loop

Python Negamax Algorithm

I have about as simple of a negamax algorithm as possible, for evaluating positions in Tic Tac Toe. The state of the game is stored as an array in numpy, with X's pieces represented by 1, and O's pieces represented by four.
I was testing this just now, and found:
a = np.zeros(9).reshape(3,3)
negaMax(a, 6, 1) # Returned zero as it should
negaMax(a, 7, 1) # Returns 100
Meaning that my algorithm thinks it has found a way for X to win in seven plies in a game of Tic Tac Toe, which is obviously impossible against decent play. I can't work out how to have it print the best moves it has found, so am having real trouble debugging this. What am I doing wrong?
def winCheck(state):
"""Takes a position, and returns the outcome of that game"""
# Sums which correspond to a line across a column
winNums = list(state.sum(axis=0))
# Sums which correspond to a line across a row
winNums.extend(list(state.sum(axis=1)))
# Sums which correspond to a line across the main diagonal
winNums.append(state.trace())
# Sums which correspond to a line across the off diagonal
winNums.append(np.flipud(state).trace())
if Square.m in winNums:
return 'X'
elif (Square.m**2 + Square.m) in winNums:
return 'O'
elif np.count_nonzero(state) == Square.m**2:
return 'D'
else:
return None
def moveFind(state):
"""Takes a position as an nparray and determines the legal moves"""
moveChoices = []
# Iterate over state, to determine which squares are empty
it = np.nditer(state, flags=['multi_index'])
while not it.finished:
if it[0] == 0:
moveChoices.append(it.multi_index)
it.iternext()
return moveChoices
def moveSim(state, move, player):
"""Create the state of the player having moved without interfering with the board"""
simState = state.copy()
if player == 1:
simState[move] = 1
else:
simState[move] = gamecfg.n + 1
return simState
def positionScore(state):
"""The game is either won or lost"""
if winCheck(state) == 'X':
return 100
elif winCheck(state) == 'O':
return -100
else:
return 0
def negaMax(state, depth, colour):
"""Recursively find the best move via a negamax search"""
if depth == 0:
return positionScore(state) * colour
highScore = -100
moveList = moveFind(state)
for move in moveList:
score = -negaMax(moveSim(state, move, colour), depth -1, colour * -1)
highScore = max(score, highScore)
return highScore
Your code does not consider the game to stop when a line of 3 symbols is made.
This means that it is playing a variant of tic-tac-toe where X wins if he makes a line of 3 even after O has made a line of 3.
For this variant, the program has correctly found that it is possible for X to always win!
(I came across the same situation with a chess program I made where the computer was happy to sacrifice its king if it would reach checkmate a little later...)

Quickly counting particles in grid

I've written some python code to calculate a certain quantity from a cosmological simulation. It does this by checking whether a particle in contained within a box of size 8,000^3, starting at the origin and advancing the box when all particles contained within it are found. As I am counting ~2 million particles altogether, and the total size of the simulation volume is 150,000^3, this is taking a long time.
I'll post my code below, does anybody have any suggestions on how to improve it?
Thanks in advance.
from __future__ import division
import numpy as np
def check_range(pos, i, j, k):
a = 0
if i <= pos[2] < i+8000:
if j <= pos[3] < j+8000:
if k <= pos[4] < k+8000:
a = 1
return a
def sigma8(data):
N = []
to_do = data
print 'Counting number of particles per cell...'
for k in range(0,150001,8000):
for j in range(0,150001,8000):
for i in range(0,150001,8000):
temp = []
n = []
for count in range(len(to_do)):
n.append(check_range(to_do[count],i,j,k))
to_do[count][1] = n[count]
if to_do[count][1] == 0:
temp.append(to_do[count])
#Only particles that have not been found are
# searched for again
to_do = temp
N.append(sum(n))
print 'Next row'
print 'Next slice, %i still to find' % len(to_do)
print 'Calculating sigma8...'
if not sum(N) == len(data):
return 'Error!\nN measured = {0}, total N = {1}'.format(sum(N), len(data))
else:
return 'sigma8 = %.4f, variance = %.4f, mean = %.4f' % (np.sqrt(sum((N-np.mean(N))**2)/len(N))/np.mean(N), np.var(N),np.mean(N))
I'll try to post some code, but my general idea is the following: create a Particle class that knows about the box that it lives in, which is calculated in the __init__. Each box should have a unique name, which might be the coordinate of the bottom left corner (or whatever you use to locate your boxes).
Get a new instance of the Particle class for each particle, then use a Counter (from the collections module).
Particle class looks something like:
# static consts - outside so that every instance of Particle doesn't take them along
# for the ride...
MAX_X = 150,000
X_STEP = 8000
# etc.
class Particle(object):
def __init__(self, data):
self.x = data[xvalue]
self.y = data[yvalue]
self.z = data[zvalue]
self.compute_box_label()
def compute_box_label(self):
import math
x_label = math.floor(self.x / X_STEP)
y_label = math.floor(self.y / Y_STEP)
z_label = math.floor(self.z / Z_STEP)
self.box_label = str(x_label) + '-' + str(y_label) + '-' + str(z_label)
Anyway, I imagine your sigma8 function might look like:
def sigma8(data):
import collections as col
particles = [Particle(x) for x in data]
boxes = col.Counter([x.box_label for x in particles])
counts = boxes.most_common()
#some other stuff
counts will be a list of tuples which map a box label to the number of particles in that box. (Here we're treating particles as indistinguishable.)
Using list comprehensions is much faster than using loops---I think the reason is that you're basically relying more on the underlying C, but I'm not the person to ask. Counter is (supposedly) highly-optimized as well.
Note: None of this code has been tested, so you shouldn't try the cut-and-paste-and-hope-it-works method here.

Categories