Task: Forest fire



C#: using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Threading;
using System.Windows.Forms;

namespace ForestFire
{
    class Program : Form
    {
        private static readonly Random rand = new Random();
        private Bitmap img;

        public Program(int w, int h, int f, int p)
        {
            Size = new Size(w, h);
            StartPosition = FormStartPosition.CenterScreen;

            Thread t = new Thread(() => fire(f, p));
            t.Start();

            FormClosing += (object sender, FormClosingEventArgs e) => { t.Abort(); t = null; };
        }

        private void fire(int f, int p)
        {
            int clientWidth = ClientRectangle.Width;
            int clientHeight = ClientRectangle.Height;
            int cellSize = 10;

            img = new Bitmap(clientWidth, clientHeight);
            Graphics g = Graphics.FromImage(img);

            CellState[,] state = InitializeForestFire(clientWidth, clientHeight);

            uint generation = 0;

            do
            {
                g.FillRectangle(Brushes.White, 0, 0, img.Width, img.Height);
                state = StepForestFire(state, f, p);

                for (int y = 0; y < clientHeight - cellSize; y += cellSize)
                {
                    for (int x = 0; x < clientWidth - cellSize; x += cellSize)
                    {
                        switch (state[y, x])
                        {
                            case CellState.Empty:
                                break;
                            case CellState.Tree:
                                g.FillRectangle(Brushes.DarkGreen, x, y, cellSize, cellSize);
                                break;
                            case CellState.Burning:
                                g.FillRectangle(Brushes.DarkRed, x, y, cellSize, cellSize);
                                break;
                        }
                    }
                }

                Thread.Sleep(500);

                Invoke((MethodInvoker)Refresh);

            } while (generation < uint.MaxValue);

            g.Dispose();
        }

        private CellState[,] InitializeForestFire(int width, int height)
        {
            
            var state = new CellState[height, width];
            state.Initialize();
            return state;
        }

        private enum CellState : byte
        {
            Empty = 0,
            Tree = 1,
            Burning = 2
        }

        private CellState[,] StepForestFire(CellState[,] state, int f, int p)
        {
            
            var newState = (CellState[,])state.Clone();

            int numRows = state.GetLength(0);
            int numCols = state.GetLength(1);

            for (int r = 1; r < numRows - 1; r++)
            {
                for (int c = 1; c < numCols - 1; c++)
                {
                    
                    switch (state[r, c])
                    {
                        case CellState.Empty:
                            if (rand.Next(0, p) == 0)
                                newState[r, c] = CellState.Tree;
                            break;

                        case CellState.Tree:
                            if (NeighborHasState(state, r, c, CellState.Burning) || rand.Next(0, f) == 0)
                                newState[r, c] = CellState.Burning;
                            break;

                        case CellState.Burning:
                            newState[r, c] = CellState.Empty;
                            break;
                    }
                }
            }

            return newState;
        }

        private bool NeighborHasState(CellState[,] state, int x, int y, CellState value)
        {
            
            for (int r = -1; r <= 1; r++)
            {
                for (int c = -1; c <= 1; c++)
                {
                    if (r == 0 && c == 0)
                        continue;

                    if (state[x + r, y + c] == value)
                        return true;
                }
            }

            return false;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            e.Graphics.DrawImage(img, 0, 0);
        }

        [STAThread]
        static void Main(string[] args)
        {
            Application.Run(new Program(w: 500, h: 500, f: 2, p: 5));
        }
    }
}




PHP: <?php

define('WIDTH',      10);
define('HEIGHT',     10);

define('GEN_CNT',    10);
define('PAUSE',  250000);

define('TREE_PROB',  50);
define('GROW_PROB',   5);
define('FIRE_PROB',   1);

define('BARE',      ' ');
define('TREE',      'A');
define('BURN',      '/');


$forest = makeNewForest();

for ($i = 0; $i < GEN_CNT; $i++) {
    displayForest($forest, $i);
    $forest = getNextForest($forest);
}

displayForest($forest, 'done');
exit;


function makeNewForest() {
    return mapForest([
        'func' => function(){
            return isProb(TREE_PROB) ? TREE : BARE;
        }
    ]);
}


function displayForest($forest, $generationNum) {
    system("clear");
    echo PHP_EOL . "Generation: $generationNum" . PHP_EOL;
    mapForest(['forest' => $forest, 'func' => function($f, $x, $y){
            echo $f[$y][$x] . ($x == WIDTH - 1 ? PHP_EOL : '');
        }
    ]);
    echo PHP_EOL;
    usleep(PAUSE);
}


function getNextForest($oldForest) {
    return mapForest(['forest' => $oldForest, 'func' => function($f, $x, $y){
            switch ($f[$y][$x]) {
                case BURN:
                    return BARE;
                case BARE:
                    return isProb(GROW_PROB) ? TREE : BARE;
                case TREE:
                    $caughtFire = isProb(FIRE_PROB);
                    $ablaze = $caughtFire ? true : getNumBurningNeighbors($f, $x, $y) > 0;
                return $ablaze ? BURN : TREE;
            }
        }
    ]);
}


function getNumBurningNeighbors($forest, $x, $y) {
    $burningNeighbors = mapForest([
        'forest' => $forest, 
        'x1' => $x - 1, 'x2' => $x + 2,
        'y1' => $y - 1, 'y2' => $y + 2,
        'default' => 0,
        'func' => function($f, $x, $y){
            return $f[$y][$x] == BURN ? 1 : 0;
        }
    ]);
    
    $numOnFire = 0;
    foreach ($burningNeighbors as $row) {
        $numOnFire += array_sum($row);
    }
    return $numOnFire;
}


function mapForest($params) {
    $p = array_merge([
        'forest' => [], 
        'func' => function(){echo "default\n";},
        'x1' => 0,
        'x2' => WIDTH,
        'y1' => 0,
        'y2' => HEIGHT,
        'default' => BARE
    ], $params);
    
    $newForest = [];
    for ($y = $p['y1']; $y < $p['y2']; $y++) {
        $newRow = [];
        for ($x = $p['x1']; $x < $p['x2']; $x++) {
            $inBounds = ($x >= 0 && $x < WIDTH && $y >= 0 && $y < HEIGHT);
            $newRow[] = ($inBounds ? $p['func']($p['forest'], $x, $y) : $p['default']);
        }
        $newForest[] = $newRow;
    }
    return $newForest;
}


function isProb($prob) {
    return rand(0, 100) < $prob;
}