今天发奇想,想试试康威生命游戏。规则非常简单:
每个细胞有两种状态 - 存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动。(如图,黑色为存活,白色为死亡)
当前细胞为存活状态时,当周围低于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的十字,每次死循环之后按空格暂停,在中间继续画十字,可以得到非常有趣的图案。尽管尝试吧。。