Files
propagation-remastered/VsProject/PropagationRemasteredBeta/Terrain.cs
T

285 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections.Concurrent;
namespace PropagationRemasteredBeta
{
public class Terrain
{
const int DEFAULT_SIZE = 200;
const int DEFAULT_INFECTED_PROPORTION = 10;
const int DEFAULT_IMMUNE_PROPORTION = 10;
const int DEFAULT_DEATH_RATE = 5;
const int DEFAULT_INFECTION_RATE = 20;
const int DEFAULT_CURE_TIME = 25;
//Only here for performance purposes
public SolidBrush SAIN_COLOR = new SolidBrush(Color.Green);
public SolidBrush INFECTED_COLOR = new SolidBrush(Color.Red);
public SolidBrush IMMUNE_COLOR = new SolidBrush(Color.Blue);
public SolidBrush DEATH_COLOR = new SolidBrush(Color.Black);
private Human[,] _population;
private ConcurrentBag<Human> _peopleToCheck;
private ConcurrentBag<Human> _infectedsToCheck;
private Size _terrainSize;
private int _infectedProportion;
private int _immuneProportion;
private int _deathRate;
private int _infectionRate;
private int _cureTime;
private Bitmap _render;
private List<int> _sainCount;
private List<int> _infectedCount;
private List<int> _immuneCount;
private List<int> _deadCount;
private int _simulationDuration;
/*Used for debugging*/
public int DRAWED_ELEMENTS;
private Random rnd;
public Human[,] Population { get => _population; set => _population = value; }
public Size TerrainSize { get => _terrainSize; private set => _terrainSize = value; }
public int InfectedProportion { get => _infectedProportion; private set => _infectedProportion = value; }
public int ImmuneProportion { get => _immuneProportion; private set => _immuneProportion = value; }
public int DeathRate { get => _deathRate; private set => _deathRate = value; }
public int InfectionRate { get => _infectionRate; private set => _infectionRate = value; }
public int CureTime { get => _cureTime; set => _cureTime = value; }
public Bitmap Render { get => _render; set => _render = value; }
public List<int> SainCount { get => _sainCount; set => _sainCount = value; }
public List<int> InfectedCount { get => _infectedCount; set => _infectedCount = value; }
public List<int> ImmuneCount { get => _immuneCount; set => _immuneCount = value; }
public List<int> DeadCount { get => _deadCount; set => _deadCount = value; }
public int SimulationDuration { get => _simulationDuration; set => _simulationDuration = value; }
public Random Rnd { get => rnd; set => rnd = value; }
public ConcurrentBag<Human> PeopleToCheck { get => _peopleToCheck; set => _peopleToCheck = value; }
public ConcurrentBag<Human> InfectedsToCheck { get => _infectedsToCheck; set => _infectedsToCheck = value; }
public Terrain(Size terrainSize, int infectedProportion, int resistantProportion, int deathRate, int infectionRate, int cureTime)
{
Population = new Human[terrainSize.Width, terrainSize.Height];
TerrainSize = terrainSize;
InfectedProportion = infectedProportion;
ImmuneProportion = resistantProportion;
DeathRate = deathRate;
InfectionRate = infectionRate;
CureTime = cureTime;
Rnd = new Random();
//all of this is for stats purposes
SainCount = new List<int>();
InfectedCount = new List<int>();
ImmuneCount = new List<int>();
DeadCount = new List<int>();
SimulationDuration = -1;
}
public Terrain() : this(new Size(DEFAULT_SIZE, DEFAULT_SIZE), DEFAULT_INFECTED_PROPORTION, DEFAULT_IMMUNE_PROPORTION, DEFAULT_DEATH_RATE, DEFAULT_INFECTION_RATE, DEFAULT_CURE_TIME)
{
//empty
}
public void Generate()
{
if (Population[0,0] != null)
{
Population = new Human[TerrainSize.Width, TerrainSize.Height];
}
//That is the basic method to scan trough the entire field
for (int width = 0; width < TerrainSize.Width; width += 1)
{
for (int height = 0; height < TerrainSize.Height; height += 1)
{
int state;
state = Human.SAIN;
if (Rnd.Next(0, 100) < InfectedProportion)
{
state = Human.INFECTED;
}
else
{
if (Rnd.Next(0, 100) < ImmuneProportion)
{
state = Human.IMMUNE;
}
}
Human tmp = new Human(new Point(width, height), state,SAIN_COLOR,INFECTED_COLOR,IMMUNE_COLOR,DEATH_COLOR);
Population[width, height] = tmp;
}
}
}
public void GenerateOneForAll()
{
if (Population[0, 0] != null)
{
Population = new Human[TerrainSize.Width, TerrainSize.Height];
}
for (int width = 0; width < TerrainSize.Width; width += 1)
{
for (int height = 0; height < TerrainSize.Height; height += 1)
{
int state;
state = Human.SAIN;
if (Rnd.Next(0, 100) < ImmuneProportion)
{
state = Human.IMMUNE;
}
Human tmp = new Human(new Point(width, height), state, SAIN_COLOR, INFECTED_COLOR, IMMUNE_COLOR, DEATH_COLOR);
Population[width, height] = tmp;
}
}
//we generate a single infected in a random location
int x = Rnd.Next(0, TerrainSize.Width);
int y = Rnd.Next(0, TerrainSize.Height);
Population[x, y].State = Human.INFECTED;
}
public void Refresh()
{
SimulationDuration++;
SainCount.Add(0);
ImmuneCount.Add(0);
InfectedCount.Add(0);
DeadCount.Add(0);
PeopleToCheck = new ConcurrentBag<Human>();
InfectedsToCheck = new ConcurrentBag<Human>();
Parallel.For(0, TerrainSize.Width, width =>
//for (int width = 0; width < TerrainSize.Width; width += 1)
{
Parallel.For(0, TerrainSize.Height, height =>
//for (int height = 0; height < TerrainSize.Height; height += 1)
{
//we also send the random so if one day we want to add a seed feature it will also affect the transmission
switch (Population[width, height].State)
{
case Human.INFECTED:
Population[width, height].Propagate(this);
break;
default:
//nothing to do for now
break;
}
//only for stats purposes
switch (Population[width, height].State)
{
case Human.SAIN:
SainCount[SimulationDuration] += 1;
break;
case Human.IMMUNE:
ImmuneCount[SimulationDuration] += 1;
break;
case Human.INFECTED:
InfectedCount[SimulationDuration] += 1;
break;
case Human.DEAD:
DeadCount[SimulationDuration] += 1;
break;
}
});
});
//Parallel.ForEach(PeopleToCheck, target =>
foreach (Human target in PeopleToCheck)
{
Population[target.Position.X, target.Position.Y] = target.CheckInfection(InfectionRate, Rnd);
}//);
foreach (Human target in InfectedsToCheck)
{
Population[target.Position.X, target.Position.Y] = target.CheckDeath(DeathRate,CureTime,Rnd);
}
}
public Bitmap Display()
{
DRAWED_ELEMENTS = 0;
Render = new Bitmap(TerrainSize.Width, TerrainSize.Height);
using (Graphics gr = Graphics.FromImage(Render))
{
//this is an attempt to save time, we display the dominant color in the all bitmap and only change the others.
int dominantState = Human.SAIN;
//we check that it is not the first frame and by default we expect the first frame to be dominantly sain people
if (InfectedCount.Count() > 0)
{
int infecteds = InfectedCount[InfectedCount.Count - 1];
int sain = SainCount[SainCount.Count - 1];
int immune = ImmuneCount[ImmuneCount.Count - 1];
int deaths = DeadCount[DeadCount.Count - 1];
if (infecteds > sain && infecteds > immune && infecteds > deaths)
{
//the dominant color is the infected one
dominantState = Human.INFECTED;
}
if (immune > sain && immune > infecteds && immune > deaths)
{
//the dominant color is the infected one
dominantState = Human.IMMUNE;
}
if (deaths > sain && deaths > infecteds && deaths > immune)
{
//the dominant color is the infected one
dominantState = Human.DEAD;
}
}
//as Color object cannot be a constant we cannot use Human.SAIN_COLOR we need to use a fully constructed human
Human sample = new Human(new Point(-1, -1), Human.DEAD);
switch (dominantState)
{
case Human.SAIN:
sample.State = Human.SAIN;
break;
case Human.IMMUNE:
sample.State = Human.IMMUNE;
break;
case Human.INFECTED:
sample.State = Human.INFECTED;
break;
case Human.DEAD:
sample.State = Human.DEAD;
break;
default:
sample.State = Human.SAIN;
break;
}
gr.FillRectangle(sample.Sprite, new Rectangle(new Point(0, 0), new Size(Render.Width, Render.Height)));
for (int width = 0; width < TerrainSize.Width; width += 1)
{
for (int height = 0; height < TerrainSize.Height; height += 1)
{
Human individual = Population[width, height];
if (individual.State != dominantState)
{
SolidBrush c = individual.Sprite;
gr.FillRectangle(c, new Rectangle(individual.Position, new Size(1,1)));
DRAWED_ELEMENTS++;
}
}
}
}
return Render;
}
}
}