search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

個人項目開發示例:生命遊戲

前些天我在Live里講程序員的自我提升時,提到了做個人項目是種十分有效的方法:基於一個基礎需求進行開發,然後不停地製造新需求和特性進行迭代更新。這個過程既是對開發能力也是對產品設計能力的很好鍛煉。

當時舉了三個項目的例子。其實原本準備了四個,考慮到時間關係以及類型重複放棄了一個,即生命遊戲(John Conway's Game of Life)。不過這個項目其實是很值得一試的,我很久前做過一個,在硬碟角落裡躺了好久,翻出來給大家展開講講,也算是Live內容的一個補充吧。

先說生命遊戲本身,這東西其實很多人都很熟悉了,連LeetCode里都有它的題。給還不清楚的人做一個簡單介紹:

生命遊戲(Game of Life),或者叫它的全稱John Conway's Game of Life。是英國數學家約翰·康威在1970年代所發明的一種元胞自動機。
所謂元胞自動機其實是一種離散的狀態機,即無數個獨立的格子,每個格子處於某種狀態,然後所有格子按照預先設定好的規律進行狀態演化。格子們可以是任意維度、任意形狀、按任意規律排布的。
而生命遊戲就是最簡單的元胞自動機之一——在二維平面上的方格子(細胞),每個細胞有兩種狀態:死或活,而下一回合的狀態完全受它周圍8個細胞的狀態而定。按照以下三條規則進行演化:
1. 活細胞周圍的細胞數如果小於2個或多於3個則會死亡;(離群或過度競爭導致死亡)
2. 活細胞周圍如果有2或3個細胞可以繼續存活;(正常生存)
3. 死細胞(空格)周圍如果恰好有3個細胞則會誕生新的活細胞。(繁殖)
這三條規則簡稱B3/S23。如果調整規則對應的細胞數量,還能衍生出其他類型的自動機。

根據這個規則我們很容易就可以實現一個自由演化的簡單版本:

首先生成一個指定大小的二維數組,將數組隨機填滿0和1,然後每回合遍歷整個數組,統計每個元素周圍8個的值計算下一回合的死活狀態,並生成新的數組代替舊數組,這樣交替往複下去,每回合計算完成後將值輸出到屏幕上即可。

這就是我們為「生命遊戲」項目所開發的最初版本。這個版本用任何語言都能完成,基本邏輯也都是相通的。

但滿足於這一步還遠遠不夠。要做到自我提升,首先就是為這個版本逐步增加新的設計與需求。

我開發自己的生命遊戲項目時是2013年,當時對前端不很熟悉,但對HTML5中canvas的繪圖很感興趣,所以選擇了HTML5+javascript來實現。本文最後會放出這個版本生命遊戲的演示地址,如果對編程興趣不大或者想先有個直觀印象,拉到最底下直接訪問就行。

那麼,我就直接把自己為這個項目所做的功能特性迭代過程列出來吧。

1. 以直觀形式在網頁輸出生命遊戲二維數組的格子;

當時有幾種選擇,比如在table、div中填色,使用canvas直接操作ImageData像素,使用canvas逐個fillRect繪製矩形等方法。這些我都逐一測試過了,測試的結果是fillRect繪製矩形的性能勝出,並且考慮到將來像素尺寸的擴展性,就選擇了它。

一開始設定的細胞尺寸都是固定的。首先繪製白底,如果無生命就畫灰方塊,有生命就畫紅方塊,方塊邊緣留一個像素。這樣就有了標題圖中的效果。

2. 運行、單步、停止操作按鈕;

這個很好理解,為了便於檢查運行效果。單步也可以用於精確控制和查看細胞形狀的進化。

3. 生成隨機密度細胞的初始化模版;

分為少量隨機、中等隨機和密集隨機,其實就是給三個數字閾值,隨機大於此值則有生命,點擊初始化后繪製。

4. 調節運行速度;

原理就是修改每一幀繪製完成後到下一回合計算前的setTimout等待時間。

5. 狀態展示欄;

一共三項,分別是S當前回合數,C本回合計算時間,D本回合繪製時間。

為了實現時間統計,將計算部分和繪製部分獨立分拆成兩個函數,也是為將來的優化打下基礎。

6. 優化繪製過程;

發現計算時間基本穩定,但增大畫布尺寸和細胞數量的話,繪製時間會變得很長。

單獨優化繪製過程不太可能,需要在計算時統計所有變動的格子,繪製時只繪製這些格子而不是全部重繪,時間大大縮短。

7. 調節細胞顯示尺寸;

有了上一步的優化,在畫面上流暢繪製更多格子成為可能。這樣就可以減小尺寸,顯示更複雜更大的圖形了。

但在調節尺寸時如果直接清空畫布就不合適了,加了兩種處理方式:如果是由大變小,舊圖出現在新圖中間,周圍填滿空白;如果是由小變大,就裁切舊圖中間的一塊。

8. 增加預設模版;

這裡要插句題外話,生命遊戲遠不止是一群隨機圖形自行演化。研究者們早就發現了裡面有許多有規律的圖形:

比如靜止型的,就是每個活細胞周圍恰好都有2~3個,死細胞周圍都不是3個,所以圖形在沒有外來細胞入侵時會一直穩定下去:

還有一類是周期循環型的:

還有則是飛船型,周期變換形狀並向某方向前進:

除此之外還有槍(能夠連續發射飛船)、長者(幾個細胞就能延續幾千數萬代,構成極大一片混沌)、吞食者(平時靜止,但能吸收特定形狀的飛船並保持原狀)等等等。這些都是生命遊戲愛好者和研究者們幾十年來的結果,有的形狀多達數萬甚至數十萬個細胞,構成龐大的自動機器。想了解更多的話不妨去專門的Wiki看看:LifeWiki

扯遠了,那我們如何定義並導入模版呢?有3~4種較為通用的代碼來定義,我們選用了Wiki里常見的RLE格式,編寫了一個解析器。並在初始化選項中追加了一批比較有意思的模板,大家都可以在我的演示頁面里看到(僅限桌面端,手機版由於畫面太小顯示不全,只保留了隨機生成部分)

上面說的比較詳細,下面就只列需求特性,不再寫實現思路了,大家可以想想如果是自己的話,會怎麼實現,以及這些需求是怎麼來的。

10. 將當前全圖導出為RLE代碼;

11. 使用滑鼠左鍵和右鍵在圖上繪製及擦除細胞;

12. 允許在運行過程中也能用滑鼠繪製及擦除;

13. 預置一批可插入的形狀集合,並通過兩層下拉框進行分類;

14. 在一個小型預覽框中預覽要插入的形狀;

15. 使用旋轉及翻轉按鈕改變可插入形狀的造型,並實時生成RLE代碼;

16. 插入前滑鼠在畫面上移動顯示半透明預覽;

17. 按下Ctrl鍵時允許拖拽畫面;

18. 增加一個改變細胞顏色的按鈕;

19. 適配移動端,將原先顯示在畫面右方的工具欄一部分移到上方,一部分放到彈出窗口中。

好了,以上就是我所做的這個生命遊戲模擬器的全部功能。不是自誇,儘管是4年前做的,它實現的功能仍然是當前所有在線版模擬器中最強的。這代表它已經完美了嗎?當然沒有,無論是從界面效果還是功能和性能上仍然有無數可改進的地方。

由於是一步步迭代來的,當初我的js水平也剛入門,代碼結構和可維護性並不好,引用的還是jQuery1.9這個古老版本,要重構的話還有很大空間。

之所以沒有大改而原樣放出來,無非是想跟大家展示一下一個真正個人項目的開發思路與過程。而且生命遊戲即使不編程也是個足夠有意思的數學遊戲,網上好多DEMO,但大部分功能都很簡陋,如果玩過我的這個版本還不滿足,那就去下載Golly這種客戶端吧。裡面搞幾十萬細胞的巨型戰艦都是可以的。

演示地址:Game of life

建議使用電腦訪問,移動版在很多功能上有缺失或不好用。

代碼也開源了:github.com/atonasting/game-of-life-js

也希望大家看過之後,對我說的個人項目與自我提升有進一步了解,並且真的能嘗試通過這種方法提高自己的技術水平。當然,順便再推薦一下這場Live,雖然結束了,但我自認為很值得一聽:

程序員:如何在整個職業生涯中保持競爭力



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦