fast-abelian-sandpile/sandpile_recursive.py

54 lines
1.3 KiB
Python

"""This one is "recursive" but only to the left, where the linear scan
won't go until the next run. Those bonus fires can push grains to the
next line where they're picked again by the linear scan."""
import sys
def topple(terrain, x, y):
div, terrain[x][y] = divmod(terrain[x][y], 4)
terrain[x - 1][y] += div
if terrain[x-1][y] >= 4:
topple(terrain, x - 1, y)
terrain[x + 1][y] += div
terrain[x][y - 1] += div
terrain[x][y + 1] += div
def apply_gravity(terrain):
"""
$ python -m pyperf timeit --fast -s 'from examples.sandpile3 import main' 'main(10_000, False)'
...........
Mean +- std dev: 2.02 sec +- 0.04 sec
"""
width = len(terrain)
while True:
did_someting = False
for x in range(width):
for y in range(width):
if terrain[x][y] >= 4:
topple(terrain, x, y)
did_someting = True
if not did_someting:
return
def main(height, show=True):
width = int(height ** .5) + 1
terrain = [[0] * width for _ in range(width)]
terrain[width // 2][width // 2] = height
apply_gravity(terrain)
if show:
import numpy as np
np.set_printoptions(threshold=sys.maxsize, linewidth=999)
print(np.array(terrain))
if __name__ == "__main__":
main(int(sys.argv[1]))