Probability calculator project, random module error

I’m getting an error when I run the test module. The traceback shows the error is coming from a method in the random module. I don’t understand what is going wrong. It looks like at some point randrange receives an empty range, but I have no idea how or why. Below I post the traceback and my experiment function.

======================================================================
ERROR: test_prob_experiment (test_module.UnitTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/denny/Sync/freeCodeCamp/scientific_computing_with_python/probability-calculator/test_module.py", line 29, in test_prob_experiment
    probability = prob_calculator.experiment(hat=hat, expected_balls={"yellow":2,"blue":3,"test":1}, num_balls_drawn=20, num_experiments=100)
  File "/home/denny/Sync/freeCodeCamp/scientific_computing_with_python/probability-calculator/prob_calculator.py", line 42, in experiment
    balls = hat_copy.draw(num_balls_drawn)
  File "/home/denny/Sync/freeCodeCamp/scientific_computing_with_python/probability-calculator/prob_calculator.py", line 23, in draw
    idx = random.randint(0, (num_balls_in_hat - 1))
  File "/usr/lib/python3.10/random.py", line 370, in randint
    return self.randrange(a, b+1)
  File "/usr/lib/python3.10/random.py", line 353, in randrange
    raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))
ValueError: empty range for randrange() (0, 0, 0)

----------------------------------------------------------------------
Ran 3 tests in 0.008s

FAILED (errors=1)

My experiment function:

def experiment(hat: Hat, expected_balls={}, num_balls_drawn=0, num_experiments=0) -> float:
    M = 0

    # convert expected balls from a dict to a list
    expected_balls_list = []
    for ball, quantity in expected_balls.items():
        expected_balls_list += [ball] * quantity

    for i in range(num_experiments):
        hat_copy = copy.deepcopy(hat)
        balls = hat_copy.draw(num_balls_drawn)
        
        ebl_in_bl = True
        for b in expected_balls_list:
            if b in balls:
                balls.remove(b)
            else:
                ebl_in_bl = False
                break

        if ebl_in_bl:
            M += 1

    return (M / num_experiments)

How your draw method looks like? Looking just at the error message, perhaps there’s situation when only one ball is left in the hat, what would result in passing two 0s to the randint.

My draw method:

    def draw(self, num_balls: int) -> list[str]:
        balls = []
        num_balls_in_hat = len(self.contents)
        for i in range(num_balls):
            idx = random.randint(0, (num_balls_in_hat - 1))
            try:
                balls.append(self.contents.pop(idx))
                num_balls_in_hat = len(self.contents)
            except:
                continue

        return balls

I was a bit wrong, error happens when there’s 0 balls in hat. this makes call random.randint(0, -1) which results with exception.

I believe it is expected, that once all balls are drawn from the hat, they are supposed to be returned to it.

Thank you, that helped lead me to a solution. i just moved the troublesome line into the try block.

    def draw(self, num_balls: int) -> list[str]:
        """Remove balls from hat at random.
        Randomly remove elements from the instance's `contents` list.
        Returns a list of balls.
        If the number of balls to draw exceeds the number in the hat, all of the
        balls in the hat are returned."""
        # contents = copy.copy(self.contents)
        balls = []
        num_balls_in_hat = len(self.contents)
        for i in range(num_balls):
            try:
                idx = random.randint(0, (num_balls_in_hat - 1))
                balls.append(self.contents.pop(idx))
                num_balls_in_hat = len(self.contents)
            except:
                continue

        return balls

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.