162 lines
5.1 KiB
C#
162 lines
5.1 KiB
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)
|
|
{
|
|
// Create our state array, initialize all indices as Empty, and return it.
|
|
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)
|
|
{
|
|
/* Clone our old state, so we can write to our new state
|
|
* without changing any values in the old state. */
|
|
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++)
|
|
{
|
|
/*
|
|
* Check the current cell.
|
|
*
|
|
* If it's empty, give it a 1/p chance of becoming a tree.
|
|
*
|
|
* If it's a tree, check to see if any neighbors are burning.
|
|
* If so, set the cell's state to burning, otherwise give it
|
|
* a 1/f chance of combusting.
|
|
*
|
|
* If it's burning, set it to empty.
|
|
*/
|
|
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)
|
|
{
|
|
// Check each cell within a 1 cell radius for the specified 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));
|
|
}
|
|
}
|
|
}
|