国家开始发布疫情放开政策,本人于2022-12-21开始感染并发高烧。
最近才康复。有感于此
我们用初始感染源来影响九宫网格来查看新冠的传播速度
小游戏规则如下:
一个感染源 可以传播附近相邻的8个网格【类似于扫雷】,假如每个感染源一天只能感染相邻的8个网格,问:一个感染源多久可以感染所有N*M网格
初始所有单元格都是绿色的,初始当前红色的单元格可以感染8个最近的单元格【九宫格】,
并将其他8个单元格设置为红色。
迷宫的单元格没有障碍和阻塞
病毒传染源居于中心将达到最快速度,
新建Windows窗体应用程序NovelCoronavirusDemo,
将默认的Form1重命名为FormNovelVirusSpread,窗体FormNovelVirusSpread设计如图:
为窗体绑定Paint事件FormNovelVirusSpread_Paint,
按钮Init绑定Click事件btnInit_Click,按钮Spread绑定Click事件btnSpread_Click
窗体设计代码如下【FormNovelVirusSpread.Designer.cs】:
namespace NovelCoronavirusDemo
{
partial class FormNovelVirusSpread
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.rtxbDisplay = new System.Windows.Forms.RichTextBox();
this.btnSpread = new System.Windows.Forms.Button();
this.btnInit = new System.Windows.Forms.Button();
this.txbColumnCount = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.txbRowCount = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
this.panel1.Controls.Add(this.rtxbDisplay);
this.panel1.Controls.Add(this.btnSpread);
this.panel1.Controls.Add(this.btnInit);
this.panel1.Controls.Add(this.txbColumnCount);
this.panel1.Controls.Add(this.label2);
this.panel1.Controls.Add(this.txbRowCount);
this.panel1.Controls.Add(this.label1);
this.panel1.Location = new System.Drawing.Point(652, 12);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(320, 618);
this.panel1.TabIndex = 11;
//
// rtxbDisplay
//
this.rtxbDisplay.Location = new System.Drawing.Point(3, 88);
this.rtxbDisplay.Name = "rtxbDisplay";
this.rtxbDisplay.ReadOnly = true;
this.rtxbDisplay.Size = new System.Drawing.Size(311, 527);
this.rtxbDisplay.TabIndex = 9;
this.rtxbDisplay.Text = "";
//
// btnSpread
//
this.btnSpread.Font = new System.Drawing.Font("宋体", 13F, System.Drawing.FontStyle.Bold);
this.btnSpread.Location = new System.Drawing.Point(209, 3);
this.btnSpread.Name = "btnSpread";
this.btnSpread.Size = new System.Drawing.Size(80, 80);
this.btnSpread.TabIndex = 0;
this.btnSpread.Text = "Spread";
this.btnSpread.UseVisualStyleBackColor = true;
this.btnSpread.Click += new System.EventHandler(this.btnSpread_Click);
//
// btnInit
//
this.btnInit.Font = new System.Drawing.Font("宋体", 13F, System.Drawing.FontStyle.Bold);
this.btnInit.Location = new System.Drawing.Point(113, 3);
this.btnInit.Name = "btnInit";
this.btnInit.Size = new System.Drawing.Size(80, 80);
this.btnInit.TabIndex = 8;
this.btnInit.Text = "Init";
this.btnInit.UseVisualStyleBackColor = true;
this.btnInit.Click += new System.EventHandler(this.btnInit_Click);
//
// txbColumnCount
//
this.txbColumnCount.Font = new System.Drawing.Font("宋体", 13F, System.Drawing.FontStyle.Bold);
this.txbColumnCount.Location = new System.Drawing.Point(57, 42);
this.txbColumnCount.Name = "txbColumnCount";
this.txbColumnCount.Size = new System.Drawing.Size(38, 27);
this.txbColumnCount.TabIndex = 7;
this.txbColumnCount.Text = "6";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Font = new System.Drawing.Font("宋体", 13F, System.Drawing.FontStyle.Bold);
this.label2.Location = new System.Drawing.Point(7, 45);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(46, 18);
this.label2.TabIndex = 6;
this.label2.Text = "列数";
//
// txbRowCount
//
this.txbRowCount.Font = new System.Drawing.Font("宋体", 13F, System.Drawing.FontStyle.Bold);
this.txbRowCount.Location = new System.Drawing.Point(57, 9);
this.txbRowCount.Name = "txbRowCount";
this.txbRowCount.Size = new System.Drawing.Size(38, 27);
this.txbRowCount.TabIndex = 5;
this.txbRowCount.Text = "5";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("宋体", 13F, System.Drawing.FontStyle.Bold);
this.label1.Location = new System.Drawing.Point(7, 12);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(46, 18);
this.label1.TabIndex = 4;
this.label1.Text = "行数";
//
// FormNovelVirusSpread
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(984, 642);
this.Controls.Add(this.panel1);
this.Name = "FormNovelVirusSpread";
this.Text = "新冠病毒传播演示";
this.Paint += new System.Windows.Forms.PaintEventHandler(this.FormNovelVirusSpread_Paint);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.RichTextBox rtxbDisplay;
private System.Windows.Forms.Button btnSpread;
private System.Windows.Forms.Button btnInit;
private System.Windows.Forms.TextBox txbColumnCount;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox txbRowCount;
private System.Windows.Forms.Label label1;
}
}
新建网格对象实体类Grid,Grid类的源代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NovelCoronavirusDemo
{
/// <summary>
/// 一个单元格,由行索引,列索引,是否已被感染等属性组成
/// </summary>
public class Grid
{
/// <summary>
/// 网格所在的行的索引
/// </summary>
public int RowIndex { get; set; }
/// <summary>
/// 网格所在的列的索引
/// </summary>
public int ColumnIndex { get; set; }
/// <summary>
/// 是否已被感染新冠
/// </summary>
public bool IsInfected { get; set; }
/// <summary>
/// 是否已访问
/// </summary>
public bool IsVisited { get; set; }
/// <summary>
/// 打印该网格对象
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"{{Row={RowIndex},Column={ColumnIndex},IsInfected={IsInfected}}}";
}
}
}
新建操作类MazeGridUtil,MazeGridUtil类的源代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NovelCoronavirusDemo
{
/// <summary>
/// 网格地图可以认为是一个N*M的行列式,也可以认为是一个二维数组,每个元素都是一个单元格Grid
/// </summary>
public class MazeGridUtil
{
/// <summary>
/// 行数
/// </summary>
public int RowCount { get; set; }
/// <summary>
/// 列数
/// </summary>
public int ColumnCount { get; set; }
/// <summary>
/// 网格行列式
/// </summary>
public Grid[,] GridArray { get; set; }
/// <summary>
/// 初始化网格地图:需指定行数和列数
/// </summary>
/// <param name="rowCount">行数</param>
/// <param name="columnCount">列数</param>
public MazeGridUtil(int rowCount, int columnCount)
{
RowCount = rowCount;
ColumnCount = columnCount;
GridArray = new Grid[RowCount, ColumnCount];
for (int i = 0; i < RowCount; i++)
{
for (int j = 0; j < ColumnCount; j++)
{
GridArray[i, j] = new Grid()
{
RowIndex = i,
ColumnIndex = j,
IsInfected = false,
IsVisited = false
};
}
}
}
/// <summary>
/// 获取所有已感染的并且未访问的单元格
/// </summary>
/// <returns></returns>
public static List<Grid> GetAllInfectedAndUnvisited(MazeGridUtil mazeGridUtil)
{
List<Grid> mazeGrids = new List<Grid>();
for (int i = 0; i < mazeGridUtil.RowCount; i++)
{
for (int j = 0; j < mazeGridUtil.ColumnCount; j++)
{
if (mazeGridUtil.GridArray[i, j].IsInfected && !mazeGridUtil.GridArray[i, j].IsVisited)
{
//考虑到可能会重复,这里过滤掉重复的
if (!mazeGrids.Contains(mazeGridUtil.GridArray[i, j]))
{
mazeGrids.Add(mazeGridUtil.GridArray[i, j]);
}
}
}
}
return mazeGrids;
}
/// <summary>
/// 将当前已感染的单元格的相邻的8个九宫格设置为已感染新冠状态
/// </summary>
/// <param name="mazeGrids">所有已感染的并且未访问的单元格</param>
/// <param name="mazeGridUtil"></param>
public static void InfectEightGrid(List<Grid> mazeGrids, MazeGridUtil mazeGridUtil)
{
for (int index = 0; index < mazeGrids.Count; index++)
{
int i = mazeGrids[index].RowIndex;//i对应行Row
int j = mazeGrids[index].ColumnIndex;//j对应列Column
mazeGridUtil.GridArray[i, j].IsVisited = true;
//将当前已感染的单元格的相邻的8个九宫格设置为感染
if (i - 1 >= 0 && j - 1 >= 0)
{
if (!mazeGridUtil.GridArray[i - 1, j - 1].IsInfected)
{
mazeGridUtil.GridArray[i - 1, j - 1].IsInfected = true;
}
}
if (i - 1 >= 0)
{
if (!mazeGridUtil.GridArray[i - 1, j].IsInfected)
{
mazeGridUtil.GridArray[i - 1, j].IsInfected = true;
}
}
if (j + 1 < mazeGridUtil.ColumnCount && i - 1 >= 0)
{
if (!mazeGridUtil.GridArray[i - 1, j + 1].IsInfected)
{
mazeGridUtil.GridArray[i - 1, j + 1].IsInfected = true;
}
}
if (j - 1 >= 0)
{
if (!mazeGridUtil.GridArray[i, j - 1].IsInfected)
{
mazeGridUtil.GridArray[i, j - 1].IsInfected = true;
}
}
if (j + 1 < mazeGridUtil.ColumnCount)
{
if (!mazeGridUtil.GridArray[i, j + 1].IsInfected)
{
mazeGridUtil.GridArray[i, j + 1].IsInfected = true;
}
}
if (j - 1 >= 0 && i + 1 < mazeGridUtil.RowCount)
{
if (!mazeGridUtil.GridArray[i + 1, j - 1].IsInfected)
{
mazeGridUtil.GridArray[i + 1, j - 1].IsInfected = true;
}
}
if (i + 1 < mazeGridUtil.RowCount)
{
if (!mazeGridUtil.GridArray[i + 1, j].IsInfected)
{
mazeGridUtil.GridArray[i + 1, j].IsInfected = true;
}
}
if (i + 1 < mazeGridUtil.RowCount && j + 1 < mazeGridUtil.ColumnCount)
{
if (!mazeGridUtil.GridArray[i + 1, j + 1].IsInfected)
{
mazeGridUtil.GridArray[i + 1, j + 1].IsInfected = true;
}
}
}
}
/// <summary>
/// 是否已全部感染
/// </summary>
/// <returns></returns>
public static bool IsAllSpread(MazeGridUtil mazeGridUtil)
{
bool isAce = true;//是否全部感染
for (int i = 0; i < mazeGridUtil.RowCount; i++)
{
bool existOK = false;//是否存在未感染
for (int j = 0; j < mazeGridUtil.ColumnCount; j++)
{
if (!mazeGridUtil.GridArray[i, j].IsInfected)
{
isAce = false;
existOK = true;
break;
}
}
if (existOK)
{
break;
}
}
return isAce;
}
}
}
窗体FormNovelVirusSpread.cs源代码如下:
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;
public FormNovelVirusSpread()
{
InitializeComponent();
}
/// <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);
}
//绘制网格的(行索引,列索引)
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;
//设置新冠病毒源为红色,居中显示
Rectangle rectEnd = new Rectangle(sideLength * ((mazeGridUtil.ColumnCount - 1) / 2), sideLength * ((mazeGridUtil.RowCount - 1) / 2), sideLength, sideLength);
graphics.FillRectangle(new SolidBrush(Color.Red), rectEnd);
AddTextAlignCenter(graphics, "病毒源", new Font("宋体", fontSize), rectEnd);
mazeGridUtil.GridArray[(mazeGridUtil.RowCount - 1) / 2, (mazeGridUtil.ColumnCount - 1) / 2].IsInfected = true;
DisplayContent($"新冠传播的病毒源为【({(mazeGridUtil.RowCount - 1) / 2},{(mazeGridUtil.ColumnCount - 1) / 2})】...");
}
}
/// <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 void btnInit_Click(object sender, EventArgs e)
{
int rowCount;//行数
int columnCount;//列数
if (!CheckInputCount(txbRowCount, "行数", out rowCount))
{
return;
}
if (!CheckInputCount(txbColumnCount, "列数", out columnCount))
{
return;
}
isDrawVirusSource = false;
spreadDays = 0;
DisplayContent($"正在生成新冠传播网格地图【{rowCount}行{columnCount}列】,请稍候...");
mazeGridUtil = new MazeGridUtil(rowCount, columnCount);
//重绘迷宫,新的开始
this.Invalidate();
}
/// <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");
}
}
}
}
运行测试如图: