Generate a random grid

Write a function that returns a grid of random numbers as a nested list, with rows inner lists of columns values each. Every value lies from low through high. When number_type is "int", generate integers with random.randint; when it is "float", generate decimals with random.uniform.

Do not call random.seed inside the function. The caller seeds the generator, so the same function can give a reproducible grid or a fresh one. Start from the code and tests in Listing 1.

Listing 1: Starter code and tests for generate_random_grid (Question 1).
import random


def generate_random_grid(
    rows: int, columns: int, low: int, high: int, number_type: str = "int"
) -> list[list[float]]:
    # Add code here!
    pass


# Shape: 2 rows of 3 values each
grid = generate_random_grid(2, 3, 1, 10)
assert isinstance(grid, list), f"expected a list, got {grid!r}"
assert len(grid) == 2, f"expected 2 rows, got {len(grid)}"
assert all(len(row) == 3 for row in grid), f"expected 3 values per row, got {grid!r}"

# Integers stay from 1 through 10
grid = generate_random_grid(4, 5, 1, 10)
values = [value for row in grid for value in row]
assert all(isinstance(value, int) for value in values), f"expected integers, got {values!r}"
assert min(values) >= 1 and max(values) <= 10, f"value outside 1 to 10 in {values!r}"

# Floats stay from 0 through 1
grid = generate_random_grid(3, 3, 0, 1, "float")
values = [value for row in grid for value in row]
assert all(isinstance(value, float) for value in values), f"expected floats, got {values!r}"
assert min(values) >= 0 and max(values) <= 1, f"value outside 0 to 1 in {values!r}"

# The same seed reproduces the same grid
random.seed(42)
first = generate_random_grid(3, 4, 1, 100)
random.seed(42)
second = generate_random_grid(3, 4, 1, 100)
assert first == second, f"seed 42 gave {first!r} and then {second!r}"

# A different seed gives a different grid
random.seed(7)
third = generate_random_grid(3, 4, 1, 100)
assert third != first, f"seeds 42 and 7 gave the same grid {first!r}"

# Your case: which grid size or bounds are most likely to break it?
# grid = generate_random_grid(___, ___, ___, ___)
# assert ___, f"unexpected grid {grid!r}"