22.11.20225 min

Redakcja Bulldogjob

Małe projekty w Pythonie: pożar lasu

Sprawdź, jak wykorzystać podstawy Pythona w praktyce na podstawie jednego z 81 łatwych projektów, które pozwolą Ci przećwiczyć praktyczne kodowanie. Przeczytaj przykład o pożarze lasu.

Małe projekty w Pythonie: pożar lasu

Niniejszy artykuł pochodzi z książki pt. „Wielka księga małych projektów w Pythonie. 81 łatwych praktycznych programów" Al Sweigart (Helion 2022).


Pożar lasu

Ta symulacja pokazuje las, w którym drzewa ciągle rosną, a potem płoną. W każdym kroku symulacji istnieje 1 procent prawdopodobieństwa, że na pustym polu urośnie drzewo, i 1 procent, że drzewo zostanie trafione przez błyskawicę i spłonie.

Ogień rozprzestrzeni się na sąsiednie drzewa, więc w przypadku gęstego lasu istnieje większe prawdopodobieństwo, że wybuchnie większy pożar, niż w przypadku lasu, gdzie drzewa rosną w większych odstępach od siebie.

Inspiracją tej symulacji był program Nicky’ego Case’a, opublikowany na stronie https://ncase.me/simulating/ model/.

Rysunek: Symulacja pożaru lasu, w której zielone litery A to drzewa, a czerwone litery W to płomienie


Jak to działa?

Ta symulacja jest przykładem emergencji — interakcji między prostymi częściami systemu, w wyniku której tworzą się bardziej złożone wzorce. Na pustych miejscach rosną drzewa, pioruny powodują, że drzewa zaczynają płonąć i ogień zamienia drzewa z powrotem w puste miejsce, po czym rozprzestrzenia się na sąsiednie drzewa.

Przez dostosowanie prędkości wzrostu i częstotliwości uderzeń pioruna możesz sprawić, by las pokazywał inne zjawisko. Na przykład małe prawdopodobieństwo uderzenia pioruna i wysokie tempo wzrostu drzew powodują duże, ciągłe pożary lasu, gdyż drzewa znajdują się blisko siebie, a na miejscu spalonych drzew szybko pojawiają się nowe.

Niskie tempo wzrostu i wysokie prawdopodobieństwo uderzenia pioruna skutkują kilkoma małymi pożarami, które szybko gasną, ponieważ w pobliżu nie ma innych drzew. Nie programujemy takiego zachowania, to wynika naturalnie ze stworzonego przez nas systemu.

001: """Symulacja pożaru lasu, autor: Al Sweigart, al@inventwithpython.com
002: Symulacja pożaru rozprzestrzeniającego się w lesie. Naciśnij Ctrl+C, by zatrzymać program.
003: Zainspirowana programem Nicky'ego Case'a ze strony http://ncase.me/simulating/model/.
004: Kod pobrany ze strony https://ftp.helion.pl/przyklady/wiksma.zip.
005: Etykiety: krótki, bext, symulacja"""
006: 
007: import random, sys, time
008: 
009: try:
010:     import bext
011: except ImportError:
012:     print('Ten program wymaga modułu bext,')
013:     print('który możesz zainstalować według instrukcji ze strony')
014:     print('https://pypi.org/project/Bext/.')
015:     sys.exit()
016: 
017: # Deklaracja stałych:
018: WIDTH = 79
019: HEIGHT = 22
020: 
021: TREE = 'A'
022: FIRE = 'W'
023: EMPTY = ' '
024: 
025: # (!) Spróbuj zmienić te ustawienia na dowolną wartość między 0.0 a 1.0:
026: INITIAL_TREE_DENSITY = 0.20  # Liczba drzew na początku programu.
027: GROW_CHANCE = 0.01  # Szansa na wyrośnięcie drzewa w pustym miejscu.
028: FIRE_CHANCE = 0.01  # Szansa, że drzewo zostanie uderzone przez piorun i spłonie.
029: 
030: # (!) Spróbuj ustawić długość pauzy na 1.0 lub 0.0:
031: PAUSE_LENGTH = 0.5
032: 
033: 
034: def main():
035:     forest = createNewForest()
036:     bext.clear()
037: 
038:     while True:  # Główna pętla programu.
039:         displayForest(forest)
040: 
041:         # Uruchom pojedynczy krok symulacji:
042:         nextForest = {'width': forest['width'],
043:                       'height': forest['height']}
044: 
045:         for x in range(forest['width']):
046:             for y in range(forest['height']):
047:                 if (x, y) in nextForest:
048:                     # Jeśli ustawiłeś już nextForest[(x, y)]
049:                     # w poprzednim obiegu pętli, to nic nie rób:
050:                     continue
051: 
052:                 if ((forest[(x, y)] == EMPTY)
053:                     and (random.random() <= GROW_CHANCE)):
054:                     # Zasadź drzewo w pustym miejscu:
055:                     nextForest[(x, y)] = TREE
056:                 elif ((forest[(x, y)] == TREE)
057:                     and (random.random() <= FIRE_CHANCE)):
058:                     # Piorun podpala drzewo:
059:                     nextForest[(x, y)] = FIRE
060:                 elif forest[(x, y)] == FIRE:
061:                     # To drzewo cały czas płonie.
062:                     # Przejdź przez wszystkie sąsiednie miejsca:
063:                     for ix in range(-1, 2):
064:                         for iy in range(-1, 2):
065:                             # Pożar rozprzestrzenia się na sąsiednie drzewa:
066:                             if forest.get((x + ix, y + iy)) == TREE:
067:                                 nextForest[(x + ix, y + iy)] = FIRE
068:                     # Drzewo spłonęło, więc je usuń:
069:                     nextForest[(x, y)] = EMPTY
070:                 else:
071:                     # Po prostu skopiuj istniejący obiekt:
072:                     nextForest[(x, y)] = forest[(x, y)]
073:         forest = nextForest
074: 
075:         time.sleep(PAUSE_LENGTH)
076: 
077: 
078: def createNewForest():
079:     """Zwraca słownik dla nowego lasu."""
080:     forest = {'width': WIDTH, 'height': HEIGHT}
081:     for x in range(WIDTH):
082:         for y in range(HEIGHT):
083:             if (random.random() * 100) <= INITIAL_TREE_DENSITY:
084:                 forest[(x, y)] = TREE  # Zacznij od drzewa.
085:             else:
086:                 forest[(x, y)] = EMPTY  # Zacznij od pustego miejsca.
087:     return forest
088: 
089: 
090: def displayForest(forest):
091:     """Wyświetl las na ekranie."""
092:     bext.goto(0, 0)
093:     for y in range(forest['height']):
094:         for x in range(forest['width']):
095:             if forest[(x, y)] == TREE:
096:                 bext.fg('green')
097:                 print(TREE, end='')
098:             elif forest[(x, y)] == FIRE:
099:                 bext.fg('red')
100:                 print(FIRE, end='')
101:             elif forest[(x, y)] == EMPTY:
102:                 print(EMPTY, end='')
103:         print()
104:     bext.fg('reset')  # Użyj domyślnego koloru czcionki.
105:     print('Szansa na drzewo: {}%  '.format(GROW_CHANCE * 100), end='')
106:     print('Szansa na piorun: {}%  '.format(FIRE_CHANCE * 100), end='')
107:     print('Naciśnij Ctrl+C, by zatrzymać program.')
108: 
109: 
110: # Jeśli program został uruchomiony (a nie zaimportowany), rozpocznij grę:
111: if __name__ == '__main__':
112:     try:
113:         main()
114:     except KeyboardInterrupt:
115:         sys.exit()  # Po naciśnięciu Ctrl+C zakończ program. 


Po przepisaniu kodu źródłowego i uruchomieniu go kilka razy spróbuj poeksperymentować. Komentarze opatrzone znakiem (!) są sugestiami zmian, jakie możesz wprowadzić.


Możesz również samodzielnie postarać się wykonać następujące zadania:

  • Dodaj losowo tworzone jeziora i rzeki, które działają jak blokady dla ognia.
  • Dodaj prawdopodobieństwo, że drzewo zapłonie od sąsiedniego.
  • Dodaj różnego rodzaju drzewa z różną predyspozycją do zapalania się.
  • Dodaj różne stadia palenia się drzew, tak by symulacja pożaru miała kilka kroków, zanim drzewo spłonie.


Odkrywanie programu

Postaraj się znaleźć odpowiedzi na poniżej podane pytania. Spróbuj wprowadzić zmiany w kodzie i uruchomić ponownie program, by zobaczyć, jaki efekt to przyniosło.

  1. Co się stanie, gdy zamienisz bext.fg('green') w linii 96. na bext.fg('random')?
  2. Co się stanie, gdy zamienisz EMPTY = ' ' w linii 23. na EMPTY = '.'?
  3. Co się stanie, gdy zamienisz forest.get((x + ix, y + iy)) == TREE w linii 66. na forest.get((x + ix, y + iy)) == EMPTY?
  4. Co się stanie, gdy zamienisz nextForest[(x, y)] = EMPTY w linii 69. na nextForest[(x, y)] = FIRE?
  5. Co się stanie, gdy zamienisz forest[(x, y)] = EMPTY w linii 86. na forest[(x, y)] = TREE?


Artykuł stanowi fragment książki pt. „Wielka księga małych projektów w Pythonie. 81 łatwych praktycznych programów” Ala Sweigarta (Helion 2022)

<p>Loading...</p>