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

320 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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); // Отрисовка человека
}
}
}
}