本文实现的生命游戏机(元胞自动机)有如下规则:
- 在100x100的方格中,蓝色代表存活,白色代表无;
- 每个T时间后根据规则修改每个元胞的状态;
- 对一个活的元胞,如果它的邻居中有两个或三个元胞是活的,那么该元胞将继续生存下去,否则死亡。
- 繁衍:对一个空的元胞,如果它的邻居中有且仅有3个存活的元胞,则变成存活
一次随机中,有三个种子存活。左上的不断扩张,中间的达到平衡,不增加也不减少。右下的则是不断循环。效果图如下:
一段时间后,扩张的也停下来了。
wpf 界面代码:
<Window x:Class="CellularAutomata.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CellularAutomata"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<UniformGrid Name="grid0">
</UniformGrid>
</Window>
C# 后端实现代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace CellularAutomata
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
static SolidColorBrush LiveColor = Brushes.Blue;
static SolidColorBrush DeadColor = Brushes.White;
const int ColCount = 100;
const int RowCount = 100;
const int MinNum_Live = 2;
const int MaxNum_Live = 6;
const int MinNum_Procreation = 3;
/// <summary>
/// 空白出自然产生的概率
/// </summary>
const double Probability_Birth = 1E-3;
static readonly int[][] AroundIdxList = new int[][]
{
new int[]{ -1,-1},
new int[]{ -1,0},
new int[]{ -1,1},
new int[]{ 0,-1},
//new int[]{ 0,0},
new int[]{ 0,1},
new int[]{ 1,-1},
new int[]{ 1,0},
new int[]{ 1,1},
};
int[,] list;
Border[,] ctrllist;
DispatcherTimer timer;
int showTurn = 0;
Random random = new Random(DateTime.Now.Millisecond);
public MainWindow()
{
InitializeComponent();
this.grid0.Width = 400;
this.grid0.Columns = 1;
ctrllist = new Border[ColCount, RowCount];
list = new int[ColCount, RowCount];
for (int i = 0; i < ColCount; i++)
{
UniformGrid ugrid = new UniformGrid();
ugrid.Rows = 1;
for (int j = 0; j < ColCount; j++)
{
Border b = new Border();
b.Width = 5;
b.Height = 5;
ugrid.Children.Add(b);
ctrllist[i, j] = b;
}
this.grid0.Children.Add(ugrid);
}
// 洒下N个种子
int seedCount = (int)Math.Ceiling((double)ColCount / 20);
for (int i = 0; i < seedCount; i++)
{
int r = random.Next(0, RowCount - 1);
int c = random.Next(0, ColCount - 1);
for (int j = 0; j < 3; j++)
{
int randomNum = random.Next(-3, 3);
if (r + randomNum >= RowCount)
break;
list[r + randomNum, c] = 1;
}
for (int j = 0; j < 3; j++)
{
int randomNum = random.Next(-3, 3);
if (c + randomNum >= ColCount)
break;
list[r, c + randomNum] = 1;
}
}
UpdateCtrl();
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 1, 10);
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
//1 计算上一轮存活格是否继续存活,以及繁衍
int[,] templist = new int[ColCount, RowCount];
for (int i = 0; i < ColCount; i++)
{
for (int j = 0; j < RowCount; j++)
{
if (list[i, j] == 1)
{
// 是否继续存活
templist[i, j] = IsLived(i, j) ? 1 : 0;
}
else
{
// 繁衍
templist[i, j] = IsProcreation(i, j) ? 1 : 0;
}
}
}
2 计算是否有新的诞生
//for (int i = 0; i < ColCount; i++)
//{
// for (int j = 0; j < RowCount; j++)
// {
// if (templist[i, j] == 0)
// {
// if (IsBirth())
// {
// templist[i, j] = 1;
// }
// else
// {
// templist[i, j] = 0;
// }
// }
// }
//}
list = templist;
// 每演变N轮更新一次界面
//if (showTurn++ >= 10)
{
showTurn = 0;
UpdateCtrl();
}
}
public bool IsLived(int colIdx, int rowIdx)
{
int num_Lived = GetAroundNum_lived(colIdx, rowIdx);
if (num_Lived < MinNum_Live || num_Lived > MaxNum_Live)
return false;
else
return true;
}
/// <summary>
/// 是否产生新的存活格
/// </summary>
public bool IsProcreation(int colIdx, int rowIdx)
{
int num_Lived = GetAroundNum_lived(colIdx, rowIdx);
return num_Lived == MinNum_Procreation;
}
public bool IsBirth()
{
int max = (int)(1 / Probability_Birth);
return random.Next(1, max) == 1;
}
public int GetAroundNum_lived(int colIdx, int rowIdx)
{
int num = 0;
foreach (var item in AroundIdxList)
{
int tempCIdx = colIdx + item[0];
if (tempCIdx < 0 || tempCIdx >= RowCount)
{
continue;
}
int tempRIdx = rowIdx + item[1];
if (tempRIdx < 0 || tempRIdx >= ColCount)
{
continue;
}
if (list[tempCIdx, tempRIdx] == 1)
{
num++;
}
}
return num;
}
public void UpdateCtrl()
{
for (int i = 0; i < ColCount; i++)
{
for (int j = 0; j < ColCount; j++)
{
if (list[i, j] == 0)
{
ctrllist[i, j].Background = DeadColor;
}
else
{
ctrllist[i, j].Background = LiveColor;
}
}
}
}
}
}