Forest Fire Sim

Tags: short, bext, simulation

This simulation shows a forest whose trees are constantly growing and then being burned down. On each step of the simulation, there is a 1 percent chance that a blank space grows into a tree and a 1 percent chance that a tree is struck by lightning and burns. Fires will spread to adjacent trees, so a densely packed forest is more likely to suffer a larger fire than a sparsely packed one. This simulation was inspired by Nicky Case’s Emoji Sim at http://ncase.me/simulating/model/.

forest_fire_sim.py
  1"""Forest Fire Sim, by Al Sweigart al@inventwithpython.com
  2A simulation of wildfires spreading in a forest. Press Ctrl-C to stop.
  3Inspired by Nicky Case's Emoji Sim http://ncase.me/simulating/model/
  4This code is available at https://nostarch.com/big-book-small-python-programming
  5Tags: short, bext, simulation"""
  6
  7import random, sys, time
  8
  9try:
 10    import bext
 11except ImportError:
 12    print('This program requires the bext module, which you')
 13    print('can install by following the instructions at')
 14    print('https://pypi.org/project/Bext/')
 15    sys.exit()
 16
 17# Set up the constants:
 18WIDTH = 79
 19HEIGHT = 22
 20
 21TREE = 'A'
 22FIRE = 'W'
 23EMPTY = ' '
 24
 25# (!) Try changing these settings to anything between 0.0 and 1.0:
 26INITIAL_TREE_DENSITY = 0.20  # Amount of forest that starts with trees.
 27GROW_CHANCE = 0.9  # Chance a blank space turns into a tree.
 28FIRE_CHANCE = 0.0001  # Chance a tree is hit by lightning & burns.
 29
 30# (!) Try setting the pause length to 1.0 or 0.0:
 31PAUSE_LENGTH = 0.5
 32
 33
 34def main():
 35    forest = createNewForest()
 36    bext.clear()
 37
 38    while True:  # Main program loop.
 39        displayForest(forest)
 40
 41        # Run a single simulation step:
 42        nextForest = {'width': forest['width'],
 43                      'height': forest['height']}
 44
 45        for x in range(forest['width']):
 46            for y in range(forest['height']):
 47                if (x, y) in nextForest:
 48                    # If we've already set nextForest[(x, y)] on a
 49                    # previous iteration, just do nothing here:
 50                    continue
 51
 52                if ((forest[(x, y)] == EMPTY)
 53                    and (random.random() <= GROW_CHANCE)):
 54                    # Grow a tree in this empty space.
 55                    nextForest[(x, y)] = TREE
 56                elif ((forest[(x, y)] == TREE)
 57                    and (random.random() <= FIRE_CHANCE)):
 58                    # Lightning sets this tree on fire.
 59                    nextForest[(x, y)] = FIRE
 60                elif forest[(x, y)] == FIRE:
 61                    # This tree is currently burning.
 62                    # Loop through all the neighboring spaces:
 63                    for ix in range(-1, 2):
 64                        for iy in range(-1, 2):
 65                            # Fire spreads to neighboring trees:
 66                            if forest.get((x + ix, y + iy)) == TREE:
 67                                nextForest[(x + ix, y + iy)] = FIRE
 68                    # The tree has burned down now, so erase it:
 69                    nextForest[(x, y)] = EMPTY
 70                else:
 71                    # Just copy the existing object:
 72                    nextForest[(x, y)] = forest[(x, y)]
 73        forest = nextForest
 74
 75        time.sleep(PAUSE_LENGTH)
 76
 77
 78def createNewForest():
 79    """Returns a dictionary for a new forest data structure."""
 80    forest = {'width': WIDTH, 'height': HEIGHT}
 81    for x in range(WIDTH):
 82        for y in range(HEIGHT):
 83            if (random.random() * 100) <= INITIAL_TREE_DENSITY:
 84                forest[(x, y)] = TREE  # Start as a tree.
 85            else:
 86                forest[(x, y)] = EMPTY  # Start as an empty space.
 87    return forest
 88
 89
 90def displayForest(forest):
 91    """Display the forest data structure on the screen."""
 92    bext.goto(0, 0)
 93    for y in range(forest['height']):
 94        for x in range(forest['width']):
 95            if forest[(x, y)] == TREE:
 96                bext.fg('green')
 97                print(TREE, end='')
 98            elif forest[(x, y)] == FIRE:
 99                bext.fg('red')
100                print(FIRE, end='')
101            elif forest[(x, y)] == EMPTY:
102                print(EMPTY, end='')
103        print()
104    bext.fg('reset')  # Use the default font color.
105    print('Grow chance: {}%  '.format(GROW_CHANCE * 100), end='')
106    print('Lightning chance: {}%  '.format(FIRE_CHANCE * 100), end='')
107    print('Press Ctrl-C to quit.')
108
109
110# If this program was run (instead of imported), run the game:
111if __name__ == '__main__':
112    try:
113        main()
114    except KeyboardInterrupt:
115        sys.exit()  # When Ctrl-C is pressed, end the program.

https://inventwithpython.com/bigbookpython/project29.html