Working in Python 2.7.
I'm trying to plot a histogram for the numbers generated by 50 run-throughs of my random walk. But when I use pylab.hist(batting_average, bins = 10), I get a weird multi-colored histogram that goes up close to 500, but with only 50 runs of the walk, the maximum it should be able to go on the y-axis would be 50.
Here's my code:
a = ['Hit', 'Out']
b = [.3, .7]
def battingAverage(atBats, some_list=a, probabilities=b):
num_hits = 0
num_outs = 0
current_BA = []
for i in range(1,atBats):
if random_pick(a, b) == 'Hit':
num_hits += 1
else:
num_outs +=1
BA = float(num_hits)/(float(num_hits)+float(num_outs))
current_BA.append(BA)
return current_BA
def printBAs():
for i in range(50):
batting_average = battingAverage(501)
pylab.hist(batting_average, bins=10)
What's wrong with my histogram!?
Let me know if anything needs clarification, and I'll do my best.
The argument passed to battingAverage is 501... and is the number of at-bats. You're doing 50 histograms with 500 at-bats per histogram.
(Oh, and you need to fix the formatting of your code... the indentation is messed up.)
Your code doesn't do what you think it does.
I think you're wanting battingAverage to return the final batting average, but it returns a list of batting averages, one for each at-bat.
Then you're plotting that list.
I think you want to return a single number from battingAverage, and you want to accumulate the list in the printBAs() function, and move pylab.hist out of the for loop.
I don't suppose this is homework?
In other words, I think you want something like this:
a = ['Hit', 'Out']
b = [.3, .7]
def battingAverage(atBats, results=a, probabilities=b):
num_hits = 0
num_outs = 0
for i in range(atBats):
if random_pick(results, probabilities) == 'Hit':
num_hits += 1
else:
num_outs +=1
BA = float(num_hits)/(float(num_hits)+float(num_outs))
return BA
def printBAs():
batting_averages = [battingAverage(500) for i in range(50)]
pylab.hist(batting_averages, bins=10)
Though that code still needs cleanup...
Related
Objective:
To visualize the population size of a particular organism over finite time.
Assumptions:
The organism has a life span of age_limit days
Only Females of age day_lay_egg days can lay the egg, and the female is allowed to lay an egg a maximum of max_lay_egg times. Each breeding session, a maximum of only egg_no eggs can be laid with a 50% probability of producing male offspring.
Initial population of 3 organisms consist of 2 Female and 1 Male
Code Snippets:
Currently, the code below should produced the expected output
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
def get_breeding(d,**kwargs):
if d['lay_egg'] <= kwargs['max_lay_egg'] and d['dborn'] > kwargs['day_lay_egg'] and d['s'] == 1:
nums = np.random.choice([0, 1], size=kwargs['egg_no'], p=[.5, .5]).tolist()
npol=[dict(s=x,d=d['d'], lay_egg=0, dborn=0) for x in nums]
d['lay_egg'] = d['lay_egg'] + 1
return d,npol
return d,None
def to_loop_initial_population(**kwargs):
npol=kwargs['ipol']
nday = 0
total_population_per_day = []
while nday < kwargs['nday_limit']:
# print(f'Executing day {nday}')
k = []
for dpol in npol:
dpol['d'] += 1
dpol['dborn'] += 1
dpol,h = get_breeding(dpol,**kwargs)
if h is None and dpol['dborn'] <= kwargs['age_limit']:
# If beyond the age limit, ignore the parent and update only the decedent
k.append(dpol)
elif isinstance(h, list) and dpol['dborn'] <= kwargs['age_limit']:
# If below age limit, append the parent and its offspring
h.extend([dpol])
k.extend(h)
total_population_per_day.append(dict(nsize=len(k), day=nday))
nday += 1
npol = k
return total_population_per_day
## Some spec and store all setting in a dict
numsex=[1,1,0] # 0: Male, 1: Female
# s: sex, d: day, lay_egg: Number of time the female lay an egg, dborn: The organism age
ipol=[dict(s=x,d=0, lay_egg=0, dborn=0) for x in numsex] # The initial population
age_limit = 45 # Age limit for the species
egg_no=3 # Number of eggs
day_lay_egg = 30 # Matured age for egg laying
nday_limit=360
max_lay_egg=2
para=dict(nday_limit=nday_limit,ipol=ipol,age_limit=age_limit,
egg_no=egg_no,day_lay_egg=day_lay_egg,max_lay_egg=max_lay_egg)
dpopulation = to_loop_initial_population(**para)
### make some plot
df = pd.DataFrame(dpopulation)
sns.lineplot(x="day", y="nsize", data=df)
plt.xticks(rotation=15)
plt.title('Day vs population')
plt.show()
Output:
Problem/Question:
The time to complete the execution time increases exponentially with nday_limit. I need to improve the efficiency of the code. How can I speed up the running time?
Other Thoughts:
I am tempted to apply joblib as below. To my surprise, the execution time is worse.
def djob(dpol,k,**kwargs):
dpol['d'] = dpol['d'] + 1
dpol['dborn'] = dpol['dborn'] + 1
dpol,h = get_breeding(dpol,**kwargs)
if h is None and dpol['dborn'] <= kwargs['age_limit']:
# If beyond the age limit, ignore the that particular subject
k.append(dpol)
elif isinstance(h, list) and dpol['dborn'] <= kwargs['age_limit']:
# If below age limit, append the parent and its offspring
h.extend([dpol])
k.extend(h)
return k
def to_loop_initial_population(**kwargs):
npol=kwargs['ipol']
nday = 0
total_population_per_day = []
while nday < kwargs['nday_limit']:
k = []
njob=1 if len(npol)<=50 else 4
if njob==1:
print(f'Executing day {nday} with single cpu')
for dpols in npol:
k=djob(dpols,k,**kwargs)
else:
print(f'Executing day {nday} with single parallel')
k=Parallel(n_jobs=-1)(delayed(djob)(dpols,k,**kwargs) for dpols in npol)
k = list(itertools.chain(*k))
ll=1
total_population_per_day.append(dict(nsize=len(k), day=nday))
nday += 1
npol = k
return total_population_per_day
for
nday_limit=365
Your code looks alright overall but I can see several points of improvement that are slowing your code down significantly.
Though it must be noted that you can't really help the code slowing down too much with increasing nday values, since the population you need to keep track of keeps growing and you keep re-populating a list to track this. It's expected as the number of objects increase, the loops will take longer to complete, but you can reduce the time it takes to complete a single loop.
elif isinstance(h, list) and dpol['dborn'] <= kwargs['age_limit']:
Here you ask the instance of h every single loop, after confirming whether it's None. You know for a fact that h is going to be a list, and if not, your code will error anyway even before reaching that line for the list not to have been able to be created.
Furthermore, you have a redundant condition check for age of dpol, and then redundantly first extend h by dpol and then k by h. This can be simplified together with the previous issue to this:
if dpol['dborn'] <= kwargs['age_limit']:
k.append(dpol)
if h:
k.extend(h)
The results are identical.
Additionally, you're passing around a lot of **kwargs. This is a sign that your code should be a class instead, where some unchanging parameters are saved through self.parameter. You could even use a dataclass here (https://docs.python.org/3/library/dataclasses.html)
Also, you mix responsibilities of functions which is unnecessary and makes your code more confusing. For instance:
def get_breeding(d,**kwargs):
if d['lay_egg'] <= kwargs['max_lay_egg'] and d['dborn'] > kwargs['day_lay_egg'] and d['s'] == 1:
nums = np.random.choice([0, 1], size=kwargs['egg_no'], p=[.5, .5]).tolist()
npol=[dict(s=x,d=d['d'], lay_egg=0, dborn=0) for x in nums]
d['lay_egg'] = d['lay_egg'] + 1
return d,npol
return d,None
This code contains two responsibilities: Generating a new individual if conditions are met, and checking these conditions, and returning two different things based on them.
This would be better done through two separate functions, one which simply checks the conditions, and another that generates a new individual as follows:
def check_breeding(d, max_lay_egg, day_lay_egg):
return d['lay_egg'] <= max_lay_egg and d['dborn'] > day_lay_egg and d['s'] == 1
def get_breeding(d, egg_no):
nums = np.random.choice([0, 1], size=egg_no, p=[.5, .5]).tolist()
npol=[dict(s=x, d=d['d'], lay_egg=0, dborn=0) for x in nums]
return npol
Where d['lay_egg'] could be updated in-place when iterating over the list if the condition is met.
You could speed up your code even further this way, if you edit the list as you iterate over it (it is not typically recommended but it's perfectly fine to do if you know what you're doing. Make sure to do it by using the index and limit it to the previous bounds of the length of the list, and decrement the index when an element is removed)
Example:
i = 0
maxiter = len(npol)
while i < maxiter:
if check_breeding(npol[i], max_lay_egg, day_lay_egg):
npol.extend(get_breeding(npol[i], egg_no))
if npol[i]['dborn'] > age_limit:
npol.pop(i)
i -= 1
maxiter -= 1
Which could significantly reduce processing time since you're not making a new list and appending all elements all over again every iteration.
Finally, you could check some population growth equation and statistical methods, and you could even reduce this whole code to a calculation problem with iterations, though that wouldn't be a sim anymore.
Edit
I've fully implemented my suggestions for improvements to your code and timed them in a jupyter notebook using %%time. I've separated out function definitions from both so they wouldn't contribute to the time, and the results are telling. I also made it so females produce another female 100% of the time, to remove randomness, otherwise it would be even faster. I compared the results from both to verify they produce identical results (they do, but I removed the 'd_born' parameter cause it's not used in the code apart from setting).
Your implementation, with nday_limit=100 and day_lay_egg=15:
Wall time 23.5s
My implementation with same parameters:
Wall time 18.9s
So you can tell the difference is quite significant, which grows even farther apart for larger nday_limit values.
Full implementation of edited code:
from dataclasses import dataclass
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
#dataclass
class Organism:
sex: int
times_laid_eggs: int = 0
age: int = 0
def __init__(self, sex):
self.sex = sex
def check_breeding(d, max_lay_egg, day_lay_egg):
return d.times_laid_eggs <= max_lay_egg and d.age > day_lay_egg and d.sex == 1
def get_breeding(egg_no): # Make sure to change probabilities back to 0.5 and 0.5 before using it
nums = np.random.choice([0, 1], size=egg_no, p=[0.0, 1.0]).tolist()
npol = [Organism(x) for x in nums]
return npol
def simulate(organisms, age_limit, egg_no, day_lay_egg, max_lay_egg, nday_limit):
npol = organisms
nday = 0
total_population_per_day = []
while nday < nday_limit:
i = 0
maxiter = len(npol)
while i < maxiter:
npol[i].age += 1
if check_breeding(npol[i], max_lay_egg, day_lay_egg):
npol.extend(get_breeding(egg_no))
npol[i].times_laid_eggs += 1
if npol[i].age > age_limit:
npol.pop(i)
maxiter -= 1
continue
i += 1
total_population_per_day.append(dict(nsize=len(npol), day=nday))
nday += 1
return total_population_per_day
if __name__ == "__main__":
numsex = [1, 1, 0] # 0: Male, 1: Female
ipol = [Organism(x) for x in numsex] # The initial population
age_limit = 45 # Age limit for the species
egg_no = 3 # Number of eggs
day_lay_egg = 15 # Matured age for egg laying
nday_limit = 100
max_lay_egg = 2
dpopulation = simulate(ipol, age_limit, egg_no, day_lay_egg, max_lay_egg, nday_limit)
df = pd.DataFrame(dpopulation)
sns.lineplot(x="day", y="nsize", data=df)
plt.xticks(rotation=15)
plt.title('Day vs population')
plt.show()
Try structuring your code as a matrix like state[age][eggs_remaining] = count instead. It will have age_limit rows and max_lay_egg columns.
Males start in the 0 eggs_remaining column, and every time a female lays an egg they move down one (3->2->1->0 with your code above).
For each cycle, you just drop the last row, iterate over all the rows after age_limit and insert a new first row with the number of males and females.
If (as in your example) there only is a vanishingly small chance that a female would die of old age before laying all their eggs, you can just collapse everything into a state_alive[age][gender] = count and a state_eggs[eggs_remaining] = count instead, but it shouldn't be necessary unless the age goes really high or you want to run thousands of simulations.
use numpy array operation as much as possible instead of using loop can improve your performance, see below codes tested in notebook - https://www.kaggle.com/gfteafun/notebook03118c731b
Note that when comparing the time the nsize scale matters.
%%time
# s: sex, d: day, lay_egg: Number of time the female lay an egg, dborn: The organism age
x = np.array([(x, 0, 0, 0) for x in numsex ] )
iparam = np.array([0, 1, 0, 1])
total_population_per_day = []
for nday in range(nday_limit):
x = x + iparam
c = np.all(x < np.array([2, nday_limit, max_lay_egg, age_limit]), axis=1) & np.all(x >= np.array([1, day_lay_egg, 0, day_lay_egg]), axis=1)
total_population_per_day.append(dict(nsize=len(x[x[:,3]<age_limit, :]), day=nday))
n = x[c, 2].shape[0]
if n > 0:
x[c, 2] = x[c, 2] + 1
newborns = np.array([(x, nday, 0, 0) for x in np.random.choice([0, 1], size=egg_no, p=[.5, .5]) for i in range(n)])
x = np.vstack((x, newborns))
df = pd.DataFrame(total_population_per_day)
sns.lineplot(x="day", y="nsize", data=df)
plt.xticks(rotation=15)
plt.title('Day vs population')
plt.show()
I am writing a programme to generate 6 numbers (lotto style). I want to then generate a second number and compare the two and see how long it takes (in terms of counts) before the two sets of numbers match.
This is my code :
import random
range_of_numbers = [i for i in range(1,60)]
def draw_a_ticket():
total_numbers = range_of_numbers = [i for i in range(1,60)]
draw = []
i = 0
while i < 6:
num = random.choice(total_numbers)
total_numbers.remove(num)
draw.append(num)
i += 1
return draw
draw = draw_a_ticket()
draw1 = draw_a_ticket()
counter = 0
while draw[0:2] != draw1[0:2]: # I am using [0:2] to reduce the complexity/find match sooner
counter += 1
draw = draw1
draw1 = draw_a_ticket()
print(f"{counter} : Draw:{draw} - Draw1:{draw1}")
The code above works fine. But I am trying to be more pythonic and use list comprehensions to generate the numbers sets.
Ive tried the following - but I get an invalid syntax:
draw = [i = set(random.randint(1,60)) in range(1,7)]
print(draw)
The key features I am trying to achieve in a list comprehension is to:
generate 6 unique random integers between 1 and 59
store these in a list.
Thanks for any help.
for your question generate 6 unique random integers between 1 and 59 and store them in list you can use random.sample()
Return a k length list of unique elements chosen from the population
sequence or set. Used for random sampling without replacement.
try this :
draw=random.sample(range(1,59),6)
for all your program you can do it like this :
import random
def draw_a_ticket():
return random.sample(range(1,60),6)
draw = draw_a_ticket()
draw1 = draw_a_ticket()
counter = 0
while draw[0:2] != draw1[0:2]: # I am using [0:2] to reduce the complexity/find match sooner
counter += 1
draw = draw1
draw1 = draw_a_ticket()
print(f"{counter} : Draw:{draw} - Draw1:{draw1}")
if you want your program select draw only once you can append the generated draw to a list of selected draws :
like this :
import random
selected_draw=[]
def draw_a_ticket():
draw=random.sample(range(1,60),6)
if draw in selected :
draw_a_ticket()
selected_draw.append(draw)
return draw
draw = draw_a_ticket()
draw1 = draw_a_ticket()
counter = 0
while draw[0:2] != draw1[0:2]: # I am using [0:2] to reduce the complexity/find match sooner
counter += 1
draw = draw1
draw1 = draw_a_ticket()
print(f"{counter} : Draw:{draw} - Draw1:{draw1}")
Your second approach is fine, except for that you are trying to do assignment in a list comprehension and converting an int from random.randint to a set. Your draw_ticket function should be like this:
def draw_ticket():
numbers = list(range(0, 60)) # no need for a list comprehension to get a list from a
# range - just convert it directly
draw = [numbers.pop(numbers.index(random.choice(numbers))) + 1 for n in range(6)]
return draw
Above code is not easy to understand (as most list comprehensions), so I made an easy-to-understand-version below:
def draw_ticket():
numbers = list(range(0, 60))
draw = []
for n in range(6): # iterate 6 times
index = numbers.index(random.choice(numbers))
number = numbers.pop(index)
draw.append(number)
return draw
So that's what the above list comprehension does.
Thanks to everyone who responded, really appreciated the suggestions.
Ive kind of pieced all the answers together and come up with a version of the programme that doesn't use the function but seems to work ok:
import random
draw = list(set([(random.randint(1,60)) for i in range(1,7)]))
draw1 = list(set([(random.randint(1,60)) for i in range(1,7)]))
counter = 0
while draw[0:2] != draw1[0:2]: # using [0:2] reduces the complexity
counter += 1
draw = draw1
draw1 = list(set([(random.randint(1,60)) for i in range(1,7)]))
print(f"{counter} : Draw:{draw} - Draw1:{draw1}")
Thanks again.
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
I made a while loop for Maya python study. It works well, but it is redundant and I think there must be a way to shorten them better or make it looks good. Can you guys give me a suggestion about what I should do? Do you think using another def function would be better than this?
def addWalls(self, length, width, floorNum, bboxScale):
# count variables
count = 1
floorCount = 1
# length loop
while count < length:
# Adding floors on wall
while floorCount < floorNum:
cmds.duplicate(instanceLeaf=True)
cmds.xform(relative=True, translation=[0, 0, bboxScale[2]])
floorCount += 1
floorCount = 1
# Adding next wall
cmds.duplicate(instanceLeaf=True)
cmds.xform(relative=True, translation=[0, -bboxScale[1], -bboxScale[2] * (floorNum - 1)])
count += 1
# Final adding floors
if count == length:
while floorCount < floorNum:
cmds.duplicate(instanceLeaf=True)
cmds.xform(relative=True, translation=[0, 0, bboxScale[2]])
floorCount += 1
floorCount = 1
When I run your script it creates a grid of objects like this:
So if all it needs to do is make a grid of objects then your assumption is right, it makes no sense using a while loop. In fact it's really easy to do it with 2 for loops that represent the "wall's" width and height:
import maya.cmds as cmds
spacing = 5
width_count = 15
height_count = 15
for z in range(width_count):
for y in range(height_count):
cmds.duplicate(instanceLeaf=True)
cmds.xform(ws=True, t=[0, y * spacing, z * spacing])
It will yield the same result with a much shorter and readable script. If you want more flexibility in there it would only take simple tweaks.
I ran into a little wall here:
my point is, for a empty, to move, then add a plane on the spot, then move, then add a plane etc, and then will end up with a 100X100 plain, so i scripted:
import bge
dunWidth = 100 #meters/tiles
dunHeight = 100 #meters/tiles
b = 0
a = 0
add= bge.logic.getCurrentScene().addObject
def main():
global b, a
cont = bge.logic.getCurrentController()
dunMarker = cont.owner
#Movement Calculation: (X, Y, Z)
while b < dunWidth:
b += 1
add("FloorTile", "DunMarker",0)
dunMarker.applyMovement((1,0,0), False)
while a < dunHeight:
add("FloorTile", "DunMarker",0)
a += 1
dunMarker.applyMovement((0,1,0), False)
#dunMarker.applyMovement((0,-dunHeight,0), False)
main()
but instead, to my surprise, it First add the tile, then goes through the loop ignoring the add(), so the result is a 1x1 tile at 0x0y and the empty ends at 100x100y... how many things Im doing wrong here?Aaaand, since we are here, how would you improve the coding?(trying to learn here ;) )
pd, yeah, Roguelike 3D project
Try positioning dunMarker with worldPosition you may also want to use for loops to get a grid instead of two edges.
import bge
dunWidth = 100 #meters/tiles
dunHeight = 100 #meters/tiles
a=0
b=0
add= bge.logic.getCurrentScene().addObject
def main():
global a,b
cont = bge.logic.getCurrentController()
dunMarker = cont.owner
for b in range(0,dunWidth,2):
dunMarker.worldPosition=(b,a,0)
t=add("FloorTile", "DunMarker",0)
for a in range(0,dunHeight,2):
dunMarker.worldPosition = (b,a,0)
add("FloorTile", "DunMarker",0)
main()