Weighted version of random choice with 1 of 1 guaranteed choices - python

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

Related

How to properly add gradually increasing/decreasing space between objects?

I've trying to implement transition from an amount of space to another which is similar to acceleration and deceleration, except i failed and the only thing that i got from this was this infinite stack of mess, here is a screenshot showing this in action:
you can see a very black circle here, which are in reality something like 100 or 200 circles stacked on top of each other
and i reached this result using this piece of code:
def Place_circles(curve, circle_space, cs, draw=True, screen=None):
curve_acceleration = []
if type(curve) == tuple:
curve_acceleration = curve[1][0]
curve_intensity = curve[1][1]
curve = curve[0]
#print(curve_intensity)
#print(curve_acceleration)
Circle_list = []
idx = [0,0]
for c in reversed(range(0,len(curve))):
for p in reversed(range(0,len(curve[c]))):
user_dist = circle_space[curve_intensity[c]] + curve_acceleration[c] * p
dist = math.sqrt(math.pow(curve[c][p][0] - curve[idx[0]][idx[1]][0],2)+math.pow(curve [c][p][1] - curve[idx[0]][idx[1]][1],2))
if dist > user_dist:
idx = [c,p]
Circle_list.append(circles.circles(round(curve[c][p][0]), round(curve[c][p][1]), cs, draw, screen))
This place circles depending on the intensity (a number between 0 and 2, random) of the current curve, which equal to an amount of space (let's say between 20 and 30 here, 20 being index 0, 30 being index 2 and a number between these 2 being index 1).
This create the stack you see above and isn't what i want, i also came to the conclusion that i cannot use acceleration since the amount of time to move between 2 points depend on the amount of circles i need to click on, knowing that there are multiple circles between each points, but not being able to determine how many lead to me being unable to the the classic acceleration formula.
So I'm running out of options here and ideas on how to transition from an amount of space to another.
any idea?
PS: i scrapped the idea above and switched back to my master branch but the code for this is still available in the branch i created here https://github.com/Mrcubix/Osu-StreamGenerator/tree/acceleration .
So now I'm back with my normal code that don't possess acceleration or deceleration.
TL:DR i can't use acceleration since i don't know the amount of circles that are going to be placed between the 2 points and make the time of travel vary (i need for exemple to click circles at 180 bpm of one circle every 0.333s) so I'm looking for another way to generate gradually changing space.
First, i took my function that was generating the intensity for each curves in [0 ; 2]
Then i scrapped the acceleration formula as it's unusable.
Now i'm using a basic algorithm to determine the maximum amount of circles i can place on a curve.
Now the way my script work is the following:
i first generate a stream (multiple circles that need to be clicked at high bpm)
this way i obtain the length of each curves (or segments) of the polyline.
i generate an intensity for each curve using the following function:
def generate_intensity(Circle_list: list = None, circle_space: int = None, Args: list = None):
curve_intensity = []
if not Args or Args[0] == "NewProfile":
prompt = True
while prompt:
max_duration_intensity = input("Choose the maximum amount of curve the change in intensity will occur for: ")
if max_duration_intensity.isdigit():
max_duration_intensity = int(max_duration_intensity)
prompt = False
prompt = True
while prompt:
intensity_change_odds = input("Choose the odds of occurence for changes in intensity (1-100): ")
if intensity_change_odds.isdigit():
intensity_change_odds = int(intensity_change_odds)
if 0 < intensity_change_odds <= 100:
prompt = False
prompt = True
while prompt:
min_intensity = input("Choose the lowest amount of spacing a circle will have: ")
if min_intensity.isdigit():
min_intensity = float(min_intensity)
if min_intensity < circle_space:
prompt = False
prompt = True
while prompt:
max_intensity = input("Choose the highest amount of spacing a circle will have: ")
if max_intensity.isdigit():
max_intensity = float(max_intensity)
if max_intensity > circle_space:
prompt = False
prompt = True
if Args:
if Args[0] == "NewProfile":
return [max_duration_intensity, intensity_change_odds, min_intensity, max_intensity]
elif Args[0] == "GenMap":
max_duration_intensity = Args[1]
intensity_change_odds = Args[2]
min_intensity = Args[3]
max_intensity = Args[4]
circle_space = ([min_intensity, circle_space, max_intensity] if not Args else [Args[0][3],circle_space,Args[0][4]])
count = 0
for idx, i in enumerate(Circle_list):
if idx == len(Circle_list) - 1:
if random.randint(0,100) < intensity_change_odds:
if random.randint(0,100) > 50:
curve_intensity.append(2)
else:
curve_intensity.append(0)
else:
curve_intensity.append(1)
if random.randint(0,100) < intensity_change_odds:
if random.randint(0,100) > 50:
curve_intensity.append(2)
count += 1
else:
curve_intensity.append(0)
count += 1
else:
if curve_intensity:
if curve_intensity[-1] == 2 and not count+1 > max_duration_intensity:
curve_intensity.append(2)
count += 1
continue
elif curve_intensity[-1] == 0 and not count+1 > max_duration_intensity:
curve_intensity.append(0)
count += 1
continue
elif count+1 > 2:
curve_intensity.append(1)
count = 0
continue
else:
curve_intensity.append(1)
else:
curve_intensity.append(1)
curve_intensity.reverse()
if curve_intensity.count(curve_intensity[0]) == len(curve_intensity):
print("Intensity didn't change")
return circle_space[1]
print("\n")
return [circle_space, curve_intensity]
with this, i obtain 2 list, one with the spacing i specified, and the second one is the list of randomly generated intensity.
from there i call another function taking into argument the polyline, the previously specified spacings and the generated intensity:
def acceleration_algorithm(polyline, circle_space, curve_intensity):
new_circle_spacing = []
for idx in range(len(polyline)): #repeat 4 times
spacing = []
Length = 0
best_spacing = 0
for p_idx in range(len(polyline[idx])-1): #repeat 1000 times / p_idx in [0 ; 1000]
# Create multiple list containing spacing going from circle_space[curve_intensity[idx-1]] to circle_space[curve_intensity[idx]]
spacing.append(np.linspace(circle_space[curve_intensity[idx]],circle_space[curve_intensity[idx+1]], p_idx).tolist())
# Sum distance to find length of curve
Length += abs(math.sqrt((polyline[idx][p_idx+1][0] - polyline[idx][p_idx][0]) ** 2 + (polyline [idx][p_idx+1][1] - polyline[idx][p_idx][1]) ** 2))
for s in range(len(spacing)): # probably has 1000 list in 1 list
length_left = Length # Make sure to reset length for each iteration
for dist in spacing[s]: # substract the specified int in spacing[s]
length_left -= dist
if length_left > 0:
best_spacing = s
else: # Since length < 0, use previous working index (best_spacing), could also jsut do `s-1`
if spacing[best_spacing] == []:
new_circle_spacing.append([circle_space[1]])
continue
new_circle_spacing.append(spacing[best_spacing])
break
return new_circle_spacing
with this, i obtain a list with the space between each circles that are going to be placed,
from there, i can Call Place_circles() again, and obtain the new stream:
def Place_circles(polyline, circle_space, cs, DoDrawCircle=True, surface=None):
Circle_list = []
curve = []
next_circle_space = None
dist = 0
for c in reversed(range(0, len(polyline))):
curve = []
if type(circle_space) == list:
iter_circle_space = iter(circle_space[c])
next_circle_space = next(iter_circle_space, circle_space[c][-1])
for p in reversed(range(len(polyline[c])-1)):
dist += math.sqrt((polyline[c][p+1][0] - polyline[c][p][0]) ** 2 + (polyline [c][p+1][1] - polyline[c][p][1]) ** 2)
if dist > (circle_space if type(circle_space) == int else next_circle_space):
dist = 0
curve.append(circles.circles(round(polyline[c][p][0]), round(polyline[c][p][1]), cs, DoDrawCircle, surface))
if type(circle_space) == list:
next_circle_space = next(iter_circle_space, circle_space[c][-1])
Circle_list.append(curve)
return Circle_list
the result is a stream with varying space between circles (so accelerating or decelerating), the only issue left to be fixed is pygame not updating the screen with the new set of circle after i call Place_circles(), but that's an issue i'm either going to try to fix myself or ask in another post
the final code for this feature can be found on my repo : https://github.com/Mrcubix/Osu-StreamGenerator/tree/Acceleration_v02

My Ordered Dict is not working as I expected

So basically I have this code:
from collections import OrderedDict as OD
person = OD({})
for num in range(10):
person[num] = float(input())
tall = max(person.values())
short = min(person.values())
key_tall = max(person.keys())
key_short = min(person.keys())
print(f'The shortest person is the person number {key_short} who is {short}meters tall')
print(f'The tallest person is the person number {key_tall} who is {tall}meters tall')
And in theory when I put 10 people on my dictionary, being the first number 1, going all the way to 9, and the last one being 0, the output should be:
The shortest person is the person number 9 who is 0.0m meters tall
The tallest person is the person number 8 who is 9.0m meters tall
But in fact it prints:
The shortest person is the person number 0 who is 0.0m meters tall
The tallest person is the person number 9 who is 9.0m meters tall
And for some reason when the values of my dictionary go to 1 all the way to 10, it works fine.
Any ideas on why this is happening and how to fix it?
key_tall = max(person.keys())
key_short = min(person.keys())
Your keys are the integers 0..9 so it's expected that you'll get 9 and 0 for these two values, since you're asking for the min/max key without regard to the values.
You seem to be after the key of the person that has the highest/lowest value, but that's not what that code will give you.
If you're after the indexes of the items with the largest values, you can do something like:
indexes_tall = [idx for idx in range(len(person)) if person[idx] == max(person.keys())]
This will give you a list of the indexes matching the highest value, which you can then process as you see fit. As one example:
from collections import OrderedDict as OD
person = OD({})
for num in range(10):
person[num] = float((num + 1) % 10) # effectively your input
tall = max(person.values())
short = min(person.values())
keys_tall = [str(idx + 1) for idx in range(len(person)) if person[idx] == max(person.keys())]
keys_short = [str(idx + 1) for idx in range(len(person)) if person[idx] == min(person.keys())]
print(f'The shortest height of {short}m is held by: {" ".join(keys_short)}')
print(f'The tallest height of {tall}m is held by: {" ".join(keys_tall)}')
will give you:
The shortest height of 0.0m is held by: 10
The tallest height of 9.0m is held by: 9

Count number of steps of turtle in a list - 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.

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

Modify the length of a sequence based on two list lengths

here is my code:
from psychopy import visual, event, gui
import random, os
from random import shuffle
from PIL import Image
import glob
# import images sequences and randomize in the same order
a = glob.glob("DDtest/targetimagelist1/*")
b = glob.glob("DDtest/distractorimagelist1/*")
c = glob.glob("DDtest/targetimagelist2/*")
d = glob.glob("DDtest/distractorimagelist3/*")
indices = random.sample(range(len(a)), len(a))
a = map(a.__getitem__, indices)
b = map(b.__getitem__, indices)
c = map(c.__getitem__, indices)
d = map(d.__getitem__, indices)
def loop():
# randomizes the location of the stimuli
loc = [1, 2]
location = random.choice(loc)
if location == 1:
pos1 = [-.05,-.05]
pos2 = [.05, .05]
else:
pos1 = [.05, .05]
pos2 = [-.05, -.05]
# randomizes the image lists
type = [1,2]
trialtype = random.choice(type)
if trialtype == 1:
target = a
distractor = b
else:
target = c
distractor = d
# Create window and stimuli.
win = visual.Window(size=(1280, 800), fullscr=True, screen=0, monitor='testMonitor', color=[-1,-1,-1]) # removed a default value
targetstim = visual.ImageStim(win=win, pos=pos2, size=[0.5,0.5])
targetstim.autoDraw = True
distractorstim = visual.ImageStim(win=win, pos=pos1, size=[0.5,0.5])
distractorstim.autoDraw = True
distractorstim.image = distractor[i]
targetstim.image = target[i]
# Display and wait for answer
win.flip()
event.waitKeys(keyList = ['space'])
# loop
for i in range(len(a)):
loop()
So here is my problem: I have 64 images in each file. The current program terminates when the 64 images have been displayed (length is based on the amount of images in 'a'). What I would like is for the file to terminate when all images have been displayed (128 trials). Is there anyway to do this? I will appreciate any help or guidance. :)
Edit:
I tried doing this to the loop:
# loop
for i in range(len(a)*2):
loop()
What happens when I do this, the images cycle as they did before except when I get a little past 64 (65-67) it tries to call an image that is outside the range, and this causes an "IndexError:list index out of range." Basically I need some way to index one list from 1-64, and the other 65-128, and then just randomly generate the order while ensuring that the index for list a and b are the same.
Here's how I would recommend addressing this - create a separate counter for each trial type. Every time you use an image set from one of the trial types, use the counter for that trial type as the index, then increment that counter. When you select the trial type, select it at random unless one of the trial type image sets has been exhausted, in which case pick the other one.
Here's the code - there are three places where modifications are necessary:
type = [1,2]
if trialImageCounters[1] == len(a):
trialtype = 2 # If the maximum number of type-1 images have been used the trial is automatically type-2.
elif trialImageCounters[2] == len(a):
trialtype = 1 # If the maximum number of type-2 images have been used the trial is automatically type-1.
else:
trialtype = random.choice(type) # If neither type-1 or type-2 images are all used up, pick a random type.
if trialtype == 1:
target = a
distractor = b
else:
target = c
distractor = d
...
# The image index is taken from the counter for the selected type of trial
distractorstim.image = distractor[trialImageCounters[trialtype]]
targetstim.image = target[trialImageCounters[trialtype]]
trialImageCounters[trialtype] += 1
...
# loop
trialImageCounters = {1:0, 2:0} # Create a different image counter for each trial type
for i in range(len(a)*2):
loop()

Categories