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 _peopleToCheck; private ConcurrentBag _infectedsToCheck; private Size _terrainSize; private int _infectedProportion; private int _immuneProportion; private int _deathRate; private int _infectionRate; private int _cureTime; private Bitmap _render; private List _sainCount; private List _infectedCount; private List _immuneCount; private List _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 SainCount { get => _sainCount; set => _sainCount = value; } public List InfectedCount { get => _infectedCount; set => _infectedCount = value; } public List ImmuneCount { get => _immuneCount; set => _immuneCount = value; } public List DeadCount { get => _deadCount; set => _deadCount = value; } public int SimulationDuration { get => _simulationDuration; set => _simulationDuration = value; } public Random Rnd { get => rnd; set => rnd = value; } public ConcurrentBag PeopleToCheck { get => _peopleToCheck; set => _peopleToCheck = value; } public ConcurrentBag 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(); InfectedCount = new List(); ImmuneCount = new List(); DeadCount = new List(); 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(); InfectedsToCheck = new ConcurrentBag(); 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.SAIN: SainCount[SimulationDuration] += 1; break; case Human.INFECTED: Population[width, height].Propagate(this); InfectedCount[SimulationDuration] += 1; break; case Human.IMMUNE: ImmuneCount[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; } } }