이전 기사에서 계속
C#은 새로운 왕관 전염병의 확산 속도를 시뮬레이션하기 위해 미로 맵을 사용합니다(1) - Sneko's Blog - CSDN Blog
이전 기사에서는 바이러스 소스를 중앙 셀로 설정하고 수동으로 [확산] 버튼을 클릭하여 새 크라운의 확산을 시뮬레이션했습니다. 바이러스 소스는 그리드 셀에서 임의의 것입니다.
FormNovelVirusSpread 양식은 그림과 같이 설계되었습니다.
임의의 바이러스 소스, 2초마다 자동으로 확산됩니다.
FormNovelVirusSpread 양식은 다음과 같이 수정됩니다.
using MazeDemo;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace NovelCoronavirusDemo
{
public partial class FormNovelVirusSpread : Form
{
/// <summary>
/// 是否已描绘病毒源,确保只描绘病毒源一次
/// </summary>
bool isDrawVirusSource = false;
/// <summary>
/// 病毒感染全部网格所需天数
/// </summary>
int spreadDays = 0;
MazeGridUtil mazeGridUtil = null;
/// <summary>
/// 病毒源单元格
/// </summary>
Grid gridSource = null;
public FormNovelVirusSpread()
{
InitializeComponent();
btnSpread.Enabled = false;//传播按钮永久不响应
}
/// <summary>
/// 显示文本框内容
/// </summary>
/// <param name="message"></param>
private void DisplayContent(string message)
{
this.BeginInvoke(new Action(() =>
{
if (rtxbDisplay.TextLength > 10240)
{
rtxbDisplay.Clear();
}
rtxbDisplay.AppendText(message + "\n");
rtxbDisplay.ScrollToCaret();
}));
}
/// <summary>
/// 检查输入
/// </summary>
/// <param name="txb"></param>
/// <param name="commentStr"></param>
/// <param name="count"></param>
/// <returns></returns>
private bool CheckInputCount(TextBox txb, string commentStr, out int count)
{
if (!int.TryParse(txb.Text, out count))
{
MessageBox.Show($"[{commentStr}]请输入正整数", "错误");
txb.Focus();
return false;
}
if (count <= 0 || count >= 100)
{
MessageBox.Show($"[{commentStr}]范围是【1~99】,请重新输入", "错误");
txb.Focus();
return false;
}
return true;
}
/// <summary>
/// 窗体的重绘事件,调用Invalidate()会触发重绘事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void FormNovelVirusSpread_Paint(object sender, PaintEventArgs e)
{
if (mazeGridUtil == null)
{
return;
}
int sideLength = 50;//正方形【迷宫的一个网格MazeGrid】的边长。
float fontSize = 13;//打印的起点、终点文字的字体大小
//以边长为50为例:因当前窗体的高度为606,去除窗体顶部和底部的高度【约56】,只能正常显示11行。因Panel控件的横坐标为705,因此只能显示14列。
if (mazeGridUtil.RowCount <= 11 && mazeGridUtil.ColumnCount <= 14)
{
sideLength = 50;
fontSize = 13;
}
else if (mazeGridUtil.RowCount <= 22 && mazeGridUtil.ColumnCount <= 28)
{
//如果行数在22行之内,列数在28列之内,则将网格的边长设置25
sideLength = 25;
fontSize = 8;
}
else
{
//如果行数、列数过大(行数大于22,列数大于28)。则应该将界面变大,并增加滚动条
sideLength = 25;
fontSize = 8;
if (mazeGridUtil.RowCount > 22)
{
this.Height = this.Height + (mazeGridUtil.RowCount - 22) * sideLength;
}
if (mazeGridUtil.ColumnCount > 28)
{
this.Width = this.Width + (mazeGridUtil.ColumnCount - 28) * sideLength;
//Panel操作面板要整体向右移动,即X坐标增加
panel1.Location = new Point(panel1.Location.X + (mazeGridUtil.ColumnCount - 28) * sideLength, panel1.Location.Y);
}
}
Graphics graphics = e.Graphics;
for (int i = 0; i < mazeGridUtil.RowCount; i++)
{
for (int j = 0; j < mazeGridUtil.ColumnCount; j++)
{
//注意:第一行是Y坐标没变,X坐标在变化。因此i是纵坐标 j是横坐标
Rectangle rect = new Rectangle(sideLength * j, sideLength * i, sideLength, sideLength);
graphics.DrawRectangle(new Pen(Color.Red), rect);
if (mazeGridUtil.GridArray[i, j].IsInfected)
{
graphics.FillRectangle(new SolidBrush(Color.Red), rect);//如果已感染
}
else
{
graphics.FillRectangle(Brushes.SpringGreen, rect);
}
//绘制网格的(行索引,列索引)
if (gridSource == null || gridSource.RowIndex != i || gridSource.ColumnIndex != j)
{
AddTextAlignCenter(graphics, $"({i},{j})", new Font("宋体", fontSize), rect);
}
}
}
//画横线
for (int i = 0; i < mazeGridUtil.RowCount + 1; i++)
{
graphics.DrawLine(Pens.Black, 0, sideLength * i, sideLength * mazeGridUtil.ColumnCount, sideLength * i);
}
//画纵线
for (int j = 0; j < mazeGridUtil.ColumnCount + 1; j++)
{
graphics.DrawLine(Pens.Black, sideLength * j, 0, sideLength * j, sideLength * mazeGridUtil.RowCount);
}
//设置新冠病毒源为红色,居中显示,
if (!isDrawVirusSource)
{
isDrawVirusSource = true;
DisplayContent($"新冠传播的病毒源为【({gridSource.RowIndex},{gridSource.ColumnIndex})】...");
}
Rectangle rectEnd = new Rectangle(sideLength * gridSource.ColumnIndex, sideLength * gridSource.RowIndex, sideLength, sideLength);
graphics.FillRectangle(new SolidBrush(Color.Red), rectEnd);
AddTextAlignCenter(graphics, "病毒源", new Font("宋体", fontSize), rectEnd);
}
/// <summary>
/// 将显示的文字放在矩形的中间
/// </summary>
/// <param name="graphics"></param>
/// <param name="text"></param>
/// <param name="font"></param>
/// <param name="rect"></param>
private void AddTextAlignCenter(Graphics graphics, string text, Font font, Rectangle rect)
{
SizeF sizeF = graphics.MeasureString(text, font);
float destX = rect.X + (rect.Width - sizeF.Width) / 2;
float destY = rect.Y + (rect.Height - sizeF.Height) / 2;
graphics.DrawString(text, font, Brushes.Black, destX, destY);
}
private async void btnInit_Click(object sender, EventArgs e)
{
int rowCount;//行数
int columnCount;//列数
if (!CheckInputCount(txbRowCount, "行数", out rowCount))
{
return;
}
if (!CheckInputCount(txbColumnCount, "列数", out columnCount))
{
return;
}
btnInit.Enabled = false;
isDrawVirusSource = false;
spreadDays = 0;
DisplayContent($"正在生成新冠传播网格地图【{rowCount}行{columnCount}列】,请稍候...");
mazeGridUtil = new MazeGridUtil(rowCount, columnCount);
//病毒源为 中心单元格
//gridSource = mazeGridUtil.GridArray[(mazeGridUtil.RowCount - 1) / 2, (mazeGridUtil.ColumnCount - 1) / 2];
//更新病毒源为随机单元格
Random random = new Random(Guid.NewGuid().GetHashCode());
gridSource = mazeGridUtil.GridArray[random.Next(mazeGridUtil.RowCount), random.Next(mazeGridUtil.ColumnCount)];
gridSource.IsInfected = true;
//重绘迷宫,新的开始
this.Invalidate();
System.Threading.Thread.Sleep(2000);//等待两秒后开始
await Task.Run(() =>
{
do
{
btnSpread_Click(null, null);
System.Threading.Thread.Sleep(2000);
} while (!MazeGridUtil.IsAllSpread(mazeGridUtil));
});
btnInit.Enabled = true;
}
/// <summary>
/// 新冠病毒感染附近的8个单元格
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSpread_Click(object sender, EventArgs e)
{
spreadDays++;
//获取所有已感染的并且未访问的单元格
List<Grid> mazeGrids = MazeGridUtil.GetAllInfectedAndUnvisited(mazeGridUtil);
MazeGridUtil.InfectEightGrid(mazeGrids, mazeGridUtil);
this.Invalidate();//触发paint事件
//重新获取已感染的单元格
mazeGrids = MazeGridUtil.GetAllInfectedAndUnvisited(mazeGridUtil);
DisplayContent($"第【{spreadDays}】天,新冠传播已感染网格个数【{mazeGrids.Count}】,分别为【{string.Join(";", mazeGrids.Select(grid => $"({grid.RowIndex},{grid.ColumnIndex})"))}】");
bool isAce = MazeGridUtil.IsAllSpread(mazeGridUtil);//是否全部感染
if (isAce)
{
DisplayContent($"已全部感染新冠病毒,网格地图已被病毒沦陷,所需天数【{spreadDays}】");
MessageBox.Show($"已全部感染新冠病毒,网格地图已被病毒沦陷,所需天数【{spreadDays}】", "Ace");
}
}
}
}
그림과 같이 시뮬레이션 프로그램이 실행됩니다.