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 population = new List(); // Список людей 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 historyS = new List(); // История уязвимых private List historyI = new List(); // История инфицированных private List historyR = new List(); // История выздоровевших private List historyD = new List(); // История погибших 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 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); // Отрисовка человека } } } }