def diamond_square(
    shape: (int, int),
    min_height: [float or int],
    max_height: [float or int],
    roughness: [float or int],
    random_seed=None,
    uint16=True,
    minus_mean=True
):
    """Runs a diamond square algorithm and returns an array (or list) with the landscape

        An important difference (possibly) between this, and other implementations of the
    diamond square algorithm is how I use the roughness parameter. For each "perturbation"
    I pull a random number from a uniform distribution between min_height and max_height.
    I then take the weighted average between that value, and the average value of the
    "neighbors", whether those be in the diamond or in the square step, as normal. The
    weights used for the weighted sum are (roughness) and (1-roughness) for the random
    number and the average, respectively, where roughness is a float that always falls
    between 0 and 1.
        The roughness value used in each iteration is based on the roughness parameter
    passed in, and is computed as follows:

        this_iteration_roughness = roughness**iteration_number

    where the first iteration has iteration_number = 0. The first roughness value
    actually used (in the very first diamond and square step) is roughness**0 = 1. Thus,
    the values for those first diamond and square step entries will be entirely random.
    This effectively means that I am seeding with A 3x3 grid of random values, rather
    than with just the four corners.
        As the process continues, the weight placed on the random number draw falls from
    the original value of 1, to roughness**1, to roughness**2, and so on, ultimately
    approaching 0. This means that the values of new cells will slowly shift from being
    purely random, to pure averages.


    OTHER NOTES:
    Internally, all heights are between 0 and 1, and are rescaled at the end.


    PARAMETERS
    ----------
    :param shape
        tuple of ints, (int, int): the shape of the resulting landscape

    :param min_height
        Int or Float: The minimum height allowed on the landscape

    :param max_height
        Int or Float: The maximum height allowed on the landscape

    :param roughness
        Float with value between 0 and 1, reflecting how bumpy the landscape should be.
        Values near 1 will result in landscapes that are extremely rough, and have almost no
        cell-to-cell smoothness. Values near zero will result in landscapes that are almost
        perfectly smooth.

        Values above 1.0 will be interpreted as 1.0
        Values below 0.0 will be interpreted as 0.0

    :param random_seed
        Any value. Defaults to None. If a value is given, the algorithm will use it to seed the random
        number generator, ensuring replicability.

    :param as_ndarray
        Bool: whether the landscape should be returned as a numpy array. If set
        to False, the method will return list of lists.

    :returns [list] or nd_array
    """

    # sanitize inputs
    if roughness > 1:
        roughness = 1.0
    if roughness < 0:
        roughness = 0.0

    working_shape, iterations = _get_working_shape_and_iterations(shape)

    # create the array
    diamond_square_array = np.full(working_shape, -1, dtype='float')

    # seed the random number generator
    random.seed(random_seed)

    # seed the corners
    diamond_square_array[0, 0] = random.uniform(0, 1)
    diamond_square_array[working_shape[0] - 1, 0] = random.uniform(0, 1)
    diamond_square_array[0, working_shape[1] - 1] = random.uniform(0, 1)
    diamond_square_array[working_shape[0] - 1, working_shape[1] - 1] = random.uniform(0, 1)

    # do the algorithm
    for i in range(iterations):
        r = math.pow(roughness, i)

        step_size = math.floor((working_shape[0] - 1) / math.pow(2, i))

        _diamond_step(diamond_square_array, step_size, r)
        _square_step(diamond_square_array, step_size, r)

    # rescale the array to fit the min and max heights specified
    diamond_square_array = min_height + (diamond_square_array * (max_height - min_height))

    # trim array, if needed
    final_array = diamond_square_array[:shape[0], :shape[1]]

    if minus_mean:
        final_array -= np.mean(final_array)

    if uint16:
        final_array = final_array.astype(np.uint16)

    return final_array
Runs a diamond square algorithm and returns an array (or list) with the landscape

        An important difference (possibly) between this, and other implementations of the
    diamond square algorithm is how I use the roughness parameter. For each "perturbation"
    I pull a random number from a uniform distribution between min_height and max_height.
    I then take the weighted average between that value, and the average value of the
    "neighbors", whether those be in the diamond or in the square step, as normal. The
    weights used for the weighted sum are (roughness) and (1-roughness) for the random
    number and the average, respectively, where roughness is a float that always falls
    between 0 and 1.
        The roughness value used in each iteration is based on the roughness parameter
    passed in, and is computed as follows:

        this_iteration_roughness = roughness**iteration_number

    where the first iteration has iteration_number = 0. The first roughness value
    actually used (in the very first diamond and square step) is roughness**0 = 1. Thus,
    the values for those first diamond and square step entries will be entirely random.
    This effectively means that I am seeding with A 3x3 grid of random values, rather
    than with just the four corners.
        As the process continues, the weight placed on the random number draw falls from
    the original value of 1, to roughness**1, to roughness**2, and so on, ultimately
    approaching 0. This means that the values of new cells will slowly shift from being
    purely random, to pure averages.


    OTHER NOTES:
    Internally, all heights are between 0 and 1, and are rescaled at the end.


    PARAMETERS
    ----------
    :param shape
        tuple of ints, (int, int): the shape of the resulting landscape

    :param min_height
        Int or Float: The minimum height allowed on the landscape

    :param max_height
        Int or Float: The maximum height allowed on the landscape

    :param roughness
        Float with value between 0 and 1, reflecting how bumpy the landscape should be.
        Values near 1 will result in landscapes that are extremely rough, and have almost no
        cell-to-cell smoothness. Values near zero will result in landscapes that are almost
        perfectly smooth.

        Values above 1.0 will be interpreted as 1.0
        Values below 0.0 will be interpreted as 0.0

    :param random_seed
        Any value. Defaults to None. If a value is given, the algorithm will use it to seed the random
        number generator, ensuring replicability.

    :param as_ndarray
        Bool: whether the landscape should be returned as a numpy array. If set
        to False, the method will return list of lists.

    :returns [list] or nd_array
    
metadrive/third_party/diamond_square/diamond_square.py