I am making an asteroid generator that creates a 2D array of bool
s.
The generator takes an int
parameter, size
, that should determine how many True
cells will be in the 2D array.
How can I guarantee that there aren't holes in the output array and that the right amount of cells are True
?
I saw the question Randomly Generating Clusters in a 2d Array, but I can't think of a way to apply it to my use case since I need to know the amount of tiles that must be generated.
In the code below, I randomly place tiles and then use cellular automata to smooth and make sure there are no holes, but keeping the right number of True
cells is the problem, especially since taking out random True
cells to meet the correct size would likely create holes.
def create_shape(size, seed):
# init rng with seed
rng = random.Random(seed)
# initial grid values empty and full mass left
# make the grid size by size so any shape could fit
grid = [[False for x in range(size)] for y in range(size)]
mass_remaining = size
# guarantee the center is something
center = size // 2
grid[center][center] = True
mass_remaining -= 1 # remember to reduce available mass
# generate random values
for x in range(size):
for y in range(size):
# skip the already filled in center
if x == y == center:
continue
# assign random value
value = bool(rng.randint(0, 1))
grid[y][x] = value
# remember to reduce mass
if value:
mass_remaining -= 1
# smoothen things out with cellular automata neighbor checking
for x in range(size):
for y in range(size):
# skip the center
if x == y == center:
continue
# get neighbors
# set neighbors is the count of neighbors set to True
set_neighbors = 0
for i in range(-1, 2):
for j in range(-1, 2):
# skip counting self
if i == j == 0:
continue
nx, ny = x + i, y + j
if 0 <= nx < size and 0 <= ny < size:
# only get in-range cells
if grid[ny][nx]:
set_neighbors += 1
# more than 3 -> become True, less than 3 -> become False
if set_neighbors > 3:
grid[y][x] = True
mass_remaining -= 1
elif set_neighbors < 3:
grid[y][x] = False
mass_remaining += 1
else:
# otherwise leave it the same
pass
# find out how well the mass is staying "in-budget"
print(mass_remaining)
return grid
The function often print
s out a whole range of different remaining mass quantities, for example, having -14
in "debt" or having 42
extra. I would expect the output to be 0
if the function worked properly.
For example, output like this ...
create_shape(8) ->
[ 0, 0, 0, 0, 0, 0,
0, 1, 1, 0, 0, 0,
0, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 0,
0, 1, 1, 1, 1, 0 ]
... is solid, but has too many set tiles.