今天发奇想,想试试康威生命游戏。规则非常简单:
每个细胞有两种状态 - 存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动。(如图,黑色为存活,白色为死亡)
当前细胞为存活状态时,当周围低于2个(不包含2个)存活细胞时, 该细胞变成死亡状态。(模拟生命数量稀少)
当前细胞为存活状态时,当周围有2个或3个存活细胞时, 该细胞保持原样。
当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。 (模拟繁殖)
用C#实现起来也是非常顺滑,只在那个嵌套for循环的地方为了避免O(n*8)复杂度过大做了一个catch处理。一个winform就可以了:
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 using System.Linq;
7 using System.Text;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
10
11 namespace Cell
12 {
13 public partial class Form1 : Form
14 {
15 public Form1()
16 {
17 InitializeComponent();
18 this.Width = m_kXCount * m_kGridSize + m_kGridSize;
19 this.Height = m_kYCount * m_kGridSize + m_kGridSize;
20 DoubleBuffered = true;
21 StartPosition = FormStartPosition.CenterScreen;
22 BackColor = Color.White;
23 initGrids();
24 initTimer();
25 }
26
27 //网格对象
28 class Grid
29 {
30 public Rectangle rect;
31 public bool alive;
32 public bool next;
33 public Point pos;//topleft
34 }
35
36 const int m_kGridSize = 20;
37 const int m_kXCount = 100;
38 const int m_kYCount = 60;
39 const int m_kAntCount = 1;
40
41 Grid[,] m_allGrids = new Grid[m_kXCount, m_kYCount];
42 void initGrids()
43 {
44 for (int x = 0; x < m_kXCount; x++)
45 {
46 for (int y = 0; y < m_kYCount; y++)
47 {
48 m_allGrids[x, y] = new Grid
49 {
50 alive = false,
51 pos = new Point(x, y),
52 rect = new Rectangle(x * m_kGridSize, y * m_kGridSize, m_kGridSize, m_kGridSize),
53 };
54 }
55 }
56 }
57
58 //细胞
59 List<Grid> m_allCells = new List<Grid>();
60
61 //定时器
62 Timer m_timer = new Timer();
63 void initTimer()
64 {
65 m_timer.Interval = 100;//蚂蚁移动速度
66 m_timer.Tick += onTimerTick;
67 m_timer.Enabled = false;
68 }
69
70 Size[] m_8offset = new Size[8]
71 {
72 new Size(-1,-1),
73 new Size(0,-1),
74 new Size(1,-1),
75 new Size(-1,0),
76 new Size(1,0),
77 new Size(-1,1),
78 new Size(0,1),
79 new Size(1,1)
80 };
81
82 private void onTimerTick(object sender, EventArgs e)
83 {
84 //每个细胞有两种状态 - 存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动。(如图,黑色为存活,白色为死亡)
85 //当前细胞为存活状态时,当周围低于2个(不包含2个)存活细胞时, 该细胞变成死亡状态。(模拟生命数量稀少)
86 //当前细胞为存活状态时,当周围有2个或3个存活细胞时, 该细胞保持原样。
87 //当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成死亡状态。(模拟生命数量过多)
88 //当前细胞为死亡状态时,当周围有3个存活细胞时,该细胞变成存活状态。 (模拟繁殖)
89
90 List<Grid> newCells = new List<Grid>();
91 List<Grid> checkedCell = new List<Grid>();
92 foreach(var cell in m_allCells)
93 {
94 int cnt = 0;
95 foreach(var s in m_8offset)
96 {
97 var p = cell.pos + s;
98 if (p.X < 0) p.X = m_kXCount - 1;
99 if (p.Y < 0) p.Y = m_kYCount - 1;
100 if (p.X == m_kXCount) p.X = 0;
101 if (p.Y == m_kYCount) p.Y = 0;
102
103 var _cell = m_allGrids[p.X, p.Y];
104 if (_cell.alive)
105 {
106 cnt++;
107 }
108 else
109 {
110 if (checkedCell.Contains(_cell))
111 continue;
112
113 checkedCell.Add(_cell);
114
115 //死亡状态的细胞
116 int _cnt = 0;
117 foreach(var _s in m_8offset)
118 {
119 var _p = _cell.pos + _s;
120 if (_p.X < 0) _p.X = m_kXCount - 1;
121 if (_p.Y < 0) _p.Y = m_kYCount - 1;
122 if (_p.X == m_kXCount) _p.X = 0;
123 if (_p.Y == m_kYCount) _p.Y = 0;
124
125 var __cell = m_allGrids[_p.X, _p.Y];
126 if (__cell.alive)
127 {
128 _cnt++;
129 }
130 }
131 if (_cnt == 3)
132 {
133 _cell.next = true;
134 newCells.Add(_cell);
135 }
136 }
137 }
138 if (cnt < 2 || cnt > 3)
139 cell.next = false;
140 else
141 cell.next = true;
142 }
143 foreach(var cell in m_allCells)
144 {
145 cell.alive = cell.next;
146 }
147 foreach(var cell in newCells)
148 {
149 cell.alive = cell.next;
150 }
151 m_allCells.RemoveAll(c => !c.alive);
152 m_allCells.AddRange(newCells);
153 Invalidate();
154 }
155
156 //鼠标选择活细胞
157 protected override void OnMouseClick(MouseEventArgs e)
158 {
159 base.OnMouseClick(e);
160 if (m_isRunning) return;
161 //计算位置
162 int x = e.X / m_kGridSize;
163 int y = e.Y / m_kGridSize;
164 var grid = m_allGrids[x, y];
165 grid.alive = true;
166 m_allCells.Add(grid);
167
168 Invalidate();
169 }
170
171 bool m_isRunning = false;
172 //键盘控制启动暂停
173 protected override void OnKeyDown(KeyEventArgs e)
174 {
175 base.OnKeyDown(e);
176 if (e.KeyCode == Keys.Space)
177 {
178 m_isRunning = !m_isRunning;
179 m_timer.Enabled = !m_timer.Enabled;
180 }
181 }
182
183 Pen m_gridPen = new Pen(Color.Black, 1);
184 protected override void OnPaint(PaintEventArgs e)
185 {
186 base.OnPaint(e);
187 var g = e.Graphics;
188
189 //绘制网格
190 for (int x = 0; x < m_kXCount; x++)
191 {
192 for (int y = 0; y < m_kYCount; y++)
193 {
194 var grid = m_allGrids[x, y];
195 if (grid.alive)
196 {
197 g.FillRectangle(Brushes.Brown, grid.rect);
198 }
199 g.DrawRectangle(m_gridPen, grid.rect);
200 }
201 }
202 }
203
204 }
205 }
定时器默认是关闭的,用鼠标点选一个初始形状作为种子,按空格键开始/暂停。我尝试了一个3*3的十字,每次死循环之后按空格暂停,在中间继续画十字,可以得到非常有趣的图案。尽管尝试吧。。