Forest Fire Sim
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.