COVID-19_Model/Covid/Form1.cs
2025-04-28 20:22:13 +03:00

320 lines
15 KiB
C#

using System;
using System.Buffers.Text;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace Covid
{
public partial class Form1 : Form
{
private Label labelRemoved; // Ìåòêà äëÿ îòîáðàæåíèÿ êîëè÷åñòâà íåéòðàëèçîâàííûõ ñëó÷àåâ
private Label labelActive; // Ìåòêà äëÿ îòîáðàæåíèÿ êîëè÷åñòâà àêòèâíûõ ñëó÷àåâ
private Label labelR; // Ìåòêà äëÿ îòîáðàæåíèÿ çíà÷åíèÿ R
private Label labelDay; // Ìåòêà äëÿ îòîáðàæåíèÿ òåêóùåãî äíÿ
public enum HealthState { Susceptible, Infected, Recovered, Dead } // Ïåðå÷èñëåíèå ñîñòîÿíèé çäîðîâüÿ
private PictureBox pictureBoxLeft; // Ëåâûé PictureBox äëÿ ãðàôèêà
private PictureBox pictureBoxRight; // Ïðàâûé PictureBox äëÿ îòîáðàæåíèÿ ëþäåé
private List<Person> population = new List<Person>(); // Ñïèñîê ëþäåé
private const int N = 500; // Óâåëè÷åíî êîëè÷åñòâî íàñåëåíèÿ
private const float InfectionRadius = 10f; // Ðàäèóñ èíôåêöèè
private const float ContagionProbability = 0.3f; // Âåðîÿòíîñòü çàðàæåíèÿ
private const int DiseaseTime = 35; // Âðåìÿ çàáîëåâàíèÿ
private const float MortalityRate = 0.12f; // Óðîâåíü ñìåðòíîñòè
private int day = 0; // Òåêóùèé äåíü
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); // Òàéìåð äëÿ îáíîâëåíèÿ ñèìóëÿöèè
private List<int> historyS = new List<int>(); // Èñòîðèÿ óÿçâèìûõ
private List<int> historyI = new List<int>(); // Èñòîðèÿ èíôèöèðîâàííûõ
private List<int> historyR = new List<int>(); // Èñòîðèÿ âûçäîðîâåâøèõ
private List<int> historyD = new List<int>(); // Èñòîðèÿ ïîãèáøèõ
private Random rand = new Random(); // Ãåíåðàòîð ñëó÷àéíûõ ÷èñåë
private int newInfectionsThisDay = 0; // Íîâûå èíôåêöèè çà äåíü
private int infectedCountPreviousDay = 0; // Êîëè÷åñòâî èíôèöèðîâàííûõ íà ïðåäûäóùèé äåíü
public class Person
{
public PointF Position; // Ïîçèöèÿ ÷åëîâåêà
public PointF Velocity; // Ñêîðîñòü äâèæåíèÿ ÷åëîâåêà
public HealthState State = HealthState.Susceptible; // Ñîñòîÿíèå çäîðîâüÿ
public int DaysInfected = 0; // Êîëè÷åñòâî äíåé, â òå÷åíèå êîòîðûõ ÷åëîâåê áûë èíôèöèðîâàí
private static Random rand = new Random(); // Ãåíåðàòîð ñëó÷àéíûõ ÷èñåë äëÿ êëàññà Person
public Person(float x, float y) // Êîíñòðóêòîð êëàññà Person
{
Position = new PointF(x, y); // Óñòàíîâêà íà÷àëüíîé ïîçèöèè
Velocity = new PointF(RandomFloat(-1, 1), RandomFloat(-1, 1)); // Óñòàíîâêà ñëó÷àéíîé ñêîðîñòè
}
private static float RandomFloat(float min, float max) // Ìåòîä äëÿ ãåíåðàöèè ñëó÷àéíîãî ÷èñëà ñ ïëàâàþùåé çàïÿòîé
{
return (float)(rand.NextDouble() * (max - min) + min);
}
public void Move(int width, int height) // Ìåòîä äëÿ ïåðåìåùåíèÿ ÷åëîâåêà
{
if (State == HealthState.Dead) return; // Åñëè ÷åëîâåê ìåðòâ, íå ïåðåìåùàåì åãî
Position = new PointF(Position.X + Velocity.X, Position.Y + Velocity.Y); // Îáíîâëåíèå ïîçèöèè
// Ïðîâåðêà ãðàíèö ýêðàíà è èçìåíåíèå íàïðàâëåíèÿ äâèæåíèÿ
if (Position.X < 0 || Position.X > width) Velocity = new PointF(-Velocity.X, Velocity.Y);
if (Position.Y < 0 || Position.Y > height) Velocity = new PointF(Velocity.X, -Velocity.Y);
}
}
public Form1()
{
InitializeComponent(); // Èíèöèàëèçàöèÿ êîìïîíåíòîâ ôîðìû
this.BackColor = Color.Black; // Óñòàíîâêà ôîíà ôîðìû
this.ForeColor = Color.White; // Óñòàíîâêà öâåòà òåêñòà
// Èíèöèàëèçàöèÿ PictureBox äëÿ ãðàôèêà
pictureBoxLeft = new PictureBox
{
Name = "pictureBoxLeft",
Width = 400,
Height = 400,
Location = new Point(10, 10),
BackColor = Color.Black
};
pictureBoxLeft.Paint += pictureBoxLeft_Paint; // Ïîäïèñêà íà ñîáûòèå îòðèñîâêè
this.Controls.Add(pictureBoxLeft); // Äîáàâëåíèå PictureBox íà ôîðìó
// Èíèöèàëèçàöèÿ PictureBox äëÿ îòîáðàæåíèÿ ëþäåé
pictureBoxRight = new PictureBox
{
Name = "pictureBoxRight",
Width = 400,
Height = 400,
Location = new Point(420, 10),
BackColor = Color.Black,
BorderStyle = BorderStyle.FixedSingle // Äîáàâëåíèå áåëîé ãðàíèöû
};
pictureBoxRight.Paint += pictureBoxRight_Paint; // Ïîäïèñêà íà ñîáûòèå îòðèñîâêè
this.Controls.Add(pictureBoxRight); // Äîáàâëåíèå PictureBox íà ôîðìó
// Ñîçäàíèå íàñåëåíèÿ
for (int i = 0; i < N; i++)
{
float x = rand.Next(pictureBoxRight.Width); // Ñëó÷àéíàÿ ïîçèöèÿ ïî X
float y = rand.Next(pictureBoxRight.Height); // Ñëó÷àéíàÿ ïîçèöèÿ ïî Y
population.Add(new Person(x, y)); // Äîáàâëåíèå íîâîãî ÷åëîâåêà â ïîïóëÿöèþ
}
// Óñòàíîâêà íà÷àëüíîãî ñîñòîÿíèÿ äëÿ íåñêîëüêèõ èíôèöèðîâàííûõ
population[0].State = HealthState.Infected;
population[1].State = HealthState.Infected;
population[2].State = HealthState.Infected;
population[3].State = HealthState.Infected;
population[4].State = HealthState.Infected;
population[5].State = HealthState.Infected;
timer.Interval = 200; // Óñòàíîâêà èíòåðâàëà òàéìåðà
timer.Tick += Timer_Tick; // Ïîäïèñêà íà ñîáûòèå òàéìåðà
timer.Start(); // Çàïóñê òàéìåðà
// Èíèöèàëèçàöèÿ ìåòîê äëÿ îòîáðàæåíèÿ ñòàòèñòèêè
labelRemoved = new Label();
labelRemoved.Name = "labelRemoved";
labelRemoved.Text = "# Íåéòðàëèçîâàííûå ñëó÷àè = 0";
labelRemoved.ForeColor = Color.LightGray;
labelRemoved.BackColor = Color.Black;
labelRemoved.AutoSize = true;
labelRemoved.Font = new Font(this.Font, FontStyle.Regular);
labelActive = new Label();
labelActive.Name = "labelActive";
labelActive.Text = "# Àêòèâíûå ñëó÷àè = 0";
labelActive.ForeColor = Color.Red;
labelActive.BackColor = Color.Black;
labelActive.AutoSize = true;
labelActive.Font = new Font(this.Font, FontStyle.Bold);
labelR = new Label();
labelR.Name = "labelR";
labelR.Text = "R = 0.00";
labelR.ForeColor = Color.White;
labelR.BackColor = Color.Black;
labelR.AutoSize = true;
labelR.Font = new Font(this.Font, FontStyle.Italic);
labelDay = new Label();
labelDay.Name = "labelDay";
labelDay.Text = "Äåíü: 0";
labelDay.ForeColor = Color.White;
labelDay.BackColor = Color.Black;
labelDay.AutoSize = true;
labelDay.Font = new Font(this.Font, FontStyle.Regular);
// Ðàñïîëîæåíèå ìåòîê ïîä PictureBox
int spacing = 5;
labelRemoved.Location = new Point(pictureBoxRight.Left, pictureBoxRight.Bottom + spacing);
labelActive.Location = new Point(labelRemoved.Right + 20, pictureBoxRight.Bottom + spacing);
labelR.Location = new Point(pictureBoxRight.Left, labelRemoved.Bottom + spacing + 5);
labelDay.Location = new Point(pictureBoxRight.Left + pictureBoxRight.Width / 2 - 30, labelRemoved.Bottom + spacing + 5);
// Äîáàâëåíèå ìåòîê íà ôîðìó
this.Controls.Add(labelRemoved);
this.Controls.Add(labelActive);
this.Controls.Add(labelR);
this.Controls.Add(labelDay);
}
private void Timer_Tick(object sender, EventArgs e) // Ìåòîä, âûçûâàåìûé ïðè êàæäîì òèêå òàéìåðà
{
day++; // Óâåëè÷åíèå äíÿ
newInfectionsThisDay = 0; // Ñáðîñ íîâûõ èíôåêöèé
// Ïåðåìåùåíèå âñåõ ëþäåé
foreach (var p in population)
p.Move(pictureBoxRight.Width, pictureBoxRight.Height);
// Îáðàáîòêà èíôèöèðîâàííûõ ëþäåé
foreach (var p in population.Where(p => p.State == HealthState.Infected).ToList())
{
p.DaysInfected++; // Óâåëè÷åíèå êîëè÷åñòâà äíåé èíôèöèðîâàíèÿ
if (p.DaysInfected >= DiseaseTime) // Ïðîâåðêà, äîñòèã ëè ÷åëîâåê ëåòàëüíîãî èñõîäà
{
if (rand.NextDouble() < MortalityRate) // Ïðîâåðêà íà ñìåðòü ÷åëîâåêà
p.State = HealthState.Dead;
else
p.State = HealthState.Recovered; // Èçìåíåíèå ñîñòîÿíèÿ íà "âûëå÷åííûé"
}
}
InfectNearby(); // Èñïîëüçóåì ìåòîä çàðàæåíèÿ ðÿäîì îêðóæàþùèõ ëþäåé
// Ïîäñ÷åò ñòàòèñòèêè
int sCount = population.Count(p => p.State == HealthState.Susceptible);
int iCount = population.Count(p => p.State == HealthState.Infected);
int rCount = population.Count(p => p.State == HealthState.Recovered);
int dCount = population.Count(p => p.State == HealthState.Dead);
// Ñîõðàíåíèå èñòîðèè
historyS.Add(sCount);
historyI.Add(iCount);
historyR.Add(rCount);
historyD.Add(dCount);
// Îáíîâëåíèå R
float R = infectedCountPreviousDay > 0 ? (float) newInfectionsThisDay / infectedCountPreviousDay : 0f;
infectedCountPreviousDay = iCount; // Îáíîâëåíèå êîëè÷åñòâà èíôèöèðîâàííûõ íà ïðåäûäóùèé äåíü
// Îáíîâëåíèå Tag äëÿ îòðèñîâêè
pictureBoxRight.Tag = new { Active = iCount, Removed = rCount + dCount, R = R, Day = day };
// Îáíîâëåíèå òåêñòà ìåòîê
labelRemoved.Text = $"# Íåéòðàëèçîâàííûå ñëó÷àè = {rCount + dCount}";
labelActive.Text = $"# Àêòèâíûå ñëó÷àè = {iCount}";
labelR.Text = $"R = {R:F2}";
labelDay.Text = $"Äåíü: {day}";
pictureBoxLeft.Invalidate(); // Ïåðåðèñîâêà ëåâîãî PictureBox
pictureBoxRight.Invalidate(); // Ïåðåðèñîâêà ïðàâîãî PictureBox
}
private void InfectNearby() // Ìåòîä äëÿ çàðàæåíèÿ ñîñåäåé
{
var infected = population.Where(p => p.State == HealthState.Infected).ToList(); // Ñïèñîê èíôèöèðîâàííûõ
var susceptible = population.Where(p => p.State == HealthState.Susceptible).ToList(); // Ñïèñîê óÿçâèìûõ
// Ïðîâåðêà ðàññòîÿíèÿ ìåæäó èíôèöèðîâàííûìè è óÿçâèìûìè
foreach (var inf in infected)
{
foreach (var sus in susceptible)
{
float dist = Distance(inf.Position, sus.Position); // Âû÷èñëåíèå ðàññòîÿíèÿ
if (dist < InfectionRadius) // Åñëè â ïðåäåëàõ ðàäèóñà èíôåêöèè
{
if (rand.NextDouble() < ContagionProbability) // Ïðîâåðêà íà çàðàæåíèå
{
sus.State = HealthState.Infected; // Èçìåíåíèå ñîñòîÿíèÿ íà èíôèöèðîâàííûé
newInfectionsThisDay++; // Óâåëè÷åíèå ñ÷åò÷èêà íîâûõ èíôåêöèé
}
}
}
}
}
private float Distance(PointF a, PointF b) // Ìåòîä äëÿ âû÷èñëåíèÿ ðàññòîÿíèÿ ìåæäó äâóìÿ òî÷êàìè
{
float dx = a.X - b.X; // Ðàçíîñòü ïî X
float dy = a.Y - b.Y; // Ðàçíîñòü ïî Y
return (float)Math.Sqrt(dx * dx + dy * dy); // Âîçâðàùåíèå ðàññòîÿíèÿ
}
private void pictureBoxLeft_Paint(object sender, PaintEventArgs e) // Ìåòîä äëÿ îòðèñîâêè ãðàôèêà
{
Graphics g = e.Graphics;
g.Clear(Color.Black); // Î÷èñòêà ôîíà
if (historyS.Count < 2) return; // Åñëè íåäîñòàòî÷íî äàííûõ äëÿ îòðèñîâêè
int w = pictureBoxLeft.Width; // Øèðèíà PictureBox
int h = pictureBoxLeft.Height; // Âûñîòà PictureBox
int maxY = population.Count; // Ìàêñèìàëüíîå çíà÷åíèå ïî Y
Pen axisPen = Pens.White; // Ïåðî äëÿ îñåé
g.DrawLine(axisPen, 40, 10, 40, h - 30); // Âåðòèêàëüíàÿ îñü
g.DrawLine(axisPen, 40, h - 30, w - 10, h - 30); // Ãîðèçîíòàëüíàÿ îñü
// Îòðèñîâêà ëèíèé ãðàôèêà äëÿ ðàçëè÷íûõ ñîñòîÿíèé
DrawGraphLine(g, historyS, maxY, Color.Cyan, 40, h - 30, w - 50, h - 40);
DrawGraphLine(g, historyI, maxY, Color.Red, 40, h - 30, w - 50, h - 40);
DrawGraphLine(g, historyR, maxY, Color.Green, 40, h - 30, w - 50, h - 40);
DrawGraphLine(g, historyD, maxY, Color.Gray, 40, h - 30, w - 50, h - 40);
// Ïîäïèñè äëÿ ãðàôèêà
g.DrawString("Óÿçâèìûå", this.Font, Brushes.Cyan, w - 130, 20);
g.DrawString("Èíôèöèðîâàííûå", this.Font, Brushes.Red, w - 130, 40);
g.DrawString("Âûëå÷åííûå", this.Font, Brushes.Green, w - 130, 60);
g.DrawString("Ïîãèáøèå", this.Font, Brushes.Gray, w - 130, 80);
}
private void DrawGraphLine(Graphics g, List<int> data, int maxY, Color color, int x0, int y0, int width, int height) // Ìåòîä äëÿ îòðèñîâêè ëèíèè ãðàôèêà
{
if (data.Count < 2) return; // Åñëè íåäîñòàòî÷íî äàííûõ äëÿ îòðèñîâêè
Pen pen = new Pen(color, 2); // Ïåðî äëÿ ëèíèè
int count = data.Count; // Êîëè÷åñòâî òî÷åê
float xStep = (float)width / (count - 1); // Øàã ïî X
PointF[] points = new PointF[count]; // Ìàññèâ òî÷åê äëÿ ëèíèè
for (int i = 0; i < count; i++)
{
float x = x0 + i * xStep; // Âû÷èñëåíèå êîîðäèíàòû X
float y = y0 - (data[i] / (float)maxY) * height; // Âû÷èñëåíèå êîîðäèíàòû Y
points[i] = new PointF(x, y); // Äîáàâëåíèå òî÷êè â ìàññèâ
}
g.DrawLines(pen, points); // Îòðèñîâêà ëèíèè
}
private void pictureBoxRight_Paint(object sender, PaintEventArgs e) // Ìåòîä äëÿ îòðèñîâêè ëþäåé
{
Graphics g = e.Graphics;
g.Clear(Color.Black); // Î÷èñòêà ôîíà
// Îòðèñîâêà êàæäîãî ÷åëîâåêà â çàâèñèìîñòè îò åãî ñîñòîÿíèÿ
foreach (var p in population)
{
Color c = p.State switch
{
HealthState.Susceptible => Color.Cyan,
HealthState.Infected => Color.Red,
HealthState.Recovered => Color.Green,
HealthState.Dead => Color.Gray,
_ => Color.White
};
g.FillEllipse(new SolidBrush(c), p.Position.X, p.Position.Y, 5, 5); // Îòðèñîâêà ÷åëîâåêà
}
}
}
}