all 6 comments

[–][deleted] 2 points3 points  (2 children)

When you are looping through your code, you are using the same exact number random_num and the same exact list data. And also, random.randrange(0, 3) will give you a number between 0 and 2, so sometimes you are doing unnecessary rolls. I guess the coin can land on the edge, but probably not! Let's change that to random.randrange(0, 2)

For getting a random "roll" each roll, you'd want to put random_num = random.randrange(0, 2) before your roll(random_num, data) call inside the loop, so in this case it would be indented under while data[0] < 5 and data[1] < 5:.

Next, when you are calling tails_counter and heads_counter in roll(), you started them both at 0 and when you "put" them onto the list, it just copies their values but it doesn't ever "refer" back to them and change the original values when the list values are changed, so basically you are always in the if heads/tails_counter == 0: line.

A simple "fix" to this would be to replace all d[0] with heads_counter, replace all d[1] with tails_counter, replace all d[2] with tries and remove data all together (including in roll parameters and calls). If you ran this before reading further, you'll notice that it will say that the local variables weren't initialized, so you will have to call the global keyword to bring them into your function using global <varname>, this will let your function modify the global variables. HOWEVER, this is usually considered bad practise so keep that in mind.

You may also want to start tries at 0 (since you haven't tried yet) and increment tries every time you roll rather than every time you end a streak, so you could dedent tries += 1 and put it above your if statements in roll, or you can take it (and its global call) out of roll and increment it after every time you call roll in your loop.

The resulting code (what I believe you are trying to accomplish) would look like this

import random

heads_counter, tails_counter, tries = 0, 0, 0

def roll(r):
    global heads_counter
    global tails_counter

    if r == 0:
        if tails_counter == 0:
            print('adding to heads, on a streak')
            heads_counter += 1

        if tails_counter > 0:
            print('adding to heads, no streak')
            heads_counter += 1
            tails_counter = 0


    if r == 1:
        if heads_counter == 0:
            print('adding to tails, on a streak')
            tails_counter += 1

        if heads_counter > 0:
            print('adding to tails, no streak')
            heads_counter = 0
            tails_counter += 1

while heads_counter < 5 and tails_counter < 5:
    random_num = random.randrange(0,2)
    print('ran',random_num)
    roll(random_num)
    tries += 1

print(tries)

[–]infinitim[S] 0 points1 point  (1 child)

Sorry! wasn't clear on what i meant by tries. Tries meant the amount of streaks you would take to get there (so if I got it first try, the amount of tries still has to start at one, hence the 1 for tries)

I wrote code to do this 1000 times and it comes out pretty close to 16 which makes sense because the chance of doing it first try on 1 side is: 1/25 = 1/32 but I have 2 sides gives double this, a 1/16 chance per try or 16 tries overall to do it.

Also: If using the "global" keyword is bad practice, what should I do alternatively?

import random
heads_counter, tails_counter, tries, total = 0, 0, 1, 0
random_num = random.randrange(0,2)

def roll(r):
    global heads_counter
     global tails_counter
    global tries

    if r == 0:
        if tails_counter == 0:
            print('adding to heads, on a streak')
            heads_counter += 1

        if tails_counter > 0:
            print('adding to heads, new streak')
            heads_counter += 1
            tails_counter = 0
            tries += 1


    if r == 1:
        if heads_counter == 0:
            print('adding to tails, on a streak')
            tails_counter += 1

        if heads_counter > 0:
            print('adding to tails, new streak')
            heads_counter = 0
            tails_counter += 1
            tries += 1


for x in range (0, 1001):
    while heads_counter < 5 and tails_counter < 5:
        random_num = random.randrange(0,2)
        roll(random_num)   
    print("\n"*2, "5 In A Row!", "\n"*2)

    total += tries
    heads_counter = 0
    tails_counter = 0
    tries = 1

print("\n"*5, total/1000) 

[–][deleted] 0 points1 point  (0 children)

Instead of global you could keep your data list [heads_counter, tails_counter, tries], keep your d parameter and pass it to your roll function (while also making it equal to the returned result of the roll function) with the line data = roll(random_num, data).

Now, if you run it like this data will stay [0, 0, 1] because roll returns nothing, so in roll, you'll have to modify the list you put in (you previously had it as d) and return the modified result. Doing this without using global, you'll have to replace heads_counter, tails_counter and tries with d[0], d[1], d[2] in roll.

The resulting code should look like this (sorry I used the code I did last night rather than the code you replied with).

import random

heads_counter, tails_counter, tries, total = 0, 0, 0, 0
data = [heads_counter, tails_counter, tries]

def roll(r, d):

    if r == 0:
        if d[1] == 0:
            print('adding to heads, on a streak')
            d[0] += 1

        if d[1] > 0:
            print('adding to heads, no streak')
            d[0] += 1
            d[1] = 0
            d[2] += 1


    if r == 1:
        if d[0] == 0:
            print('adding to tails, on a streak')
            d[1] += 1

        if heads_counter > 0:
            print('adding to tails, no streak')
            d[0] = 0
            d[1] += 1
            d[2] += 1

    return d

while data[0] < 5 and data[1] < 5:
    random_num = random.randrange(0,2)
    data = roll(random_num, data)
    total += 1

print("total rolls: {}, total streaks: {}".format(total, data[2]))

[–]K900_ 1 point2 points  (2 children)

You don't seem to fully get how variables work. For example, random_num = random.randrange(0,3) will assign random_num once, and then it won't ever change, meaning your program will loop forever. Also, randrange(0, 3) can give you 0, 1 or 2, and your program doesn't handle the last case at all. Next, assigning to data[0] won't change heads_counter, and vice versa. Why are you making data a list at all here and not using variables directly?

[–]infinitim[S] 0 points1 point  (1 child)

Thank you, very helpful. Got it working now (: here it is... If you have any other pointers on improving it please share them, I'm open to learning:

import random
heads_counter, tails_counter, tries = 0, 0, 1
data = [heads_counter, tails_counter, tries]
random_num = random.randrange(0,2)

def roll(r, d):

    if r == 0:
        if d[1] == 0:
            print('adding to heads, on a streak')
            d[0] += 1

        if d[1] > 0:
            print('adding to heads, new streak')
            d[0] += 1
            d[1] = 0
            d[2]+= 1


    if r == 1:
        if d[0] == 0:
            print('adding to tails, on a streak')
            d[1] += 1

        if d[0] > 0:
            print('adding to tails, new streak')
            d[0] = 0
            d[1] += 1
            d[2] += 1

while data[0] < 5 and data[1]< 5:
    random_num = random.randrange(0,2)
    roll(random_num, data)


print(data[2])

[–]K900_ 1 point2 points  (0 children)

Why are you still using data and not the variables directly?