今天發奇想,想試試康威生命游戲。規則非常簡單:
每個細胞有兩種狀態 - 存活或死亡,每個細胞與以自身為中心的周圍八格細胞產生互動。(如圖,黑色為存活,白色為死亡)
當前細胞為存活狀態時,當周圍低於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的十字,每次死循環之后按空格暫停,在中間繼續畫十字,可以得到非常有趣的圖案。盡管嘗試吧。。