2.LifeGame生命游戲

這個(gè)東西以前在看知乎的時(shí)候就看到過挣磨,感覺挺好玩的。最近又看到了荤懂,細(xì)細(xì)看了一下原理,恍然大悟這不就是一個(gè)空域?yàn)V波么塘砸?寫一個(gè)應(yīng)該很好玩吧节仿?于是就動(dòng)手了,為了顯示方便用的Opencv的Mat數(shù)據(jù)結(jié)構(gòu)來存取數(shù)據(jù)和顯示掉蔬。寫了一下午差不多就可以了廊宪,后面再加了些配置文件的接口,并給了一些配置文件女轿,這里記錄一下箭启。

程序:https://github.com/zhxing001/LifeGame

1.生命游戲

生命游戲也叫康威游戲,是一種細(xì)胞自動(dòng)機(jī)蛉迹,最初是由數(shù)學(xué)家約翰·何頓·康威在1970年發(fā)明的傅寡。

這個(gè)游戲是一個(gè)零玩家游戲,整個(gè)游戲會(huì)根據(jù)定義的規(guī)則自動(dòng)執(zhí)行下去。

生命游戲的游戲場(chǎng)地是一個(gè)二維的棋盤荐操,每一個(gè)位置叫做一個(gè)細(xì)胞芜抒,有, 兩種狀態(tài),如果相鄰方格活著的細(xì)胞數(shù)量過多托启,這個(gè)細(xì)胞會(huì)因?yàn)?code>資源匱乏而死亡宅倒,相反,如果因?yàn)橹車募?xì)胞過少屯耸,這個(gè)細(xì)胞會(huì)因?yàn)樘聠味廊ス涨ā?shí)際中,這種規(guī)則是可以自定義的疗绣。有一點(diǎn)要注意:棋牌上的所有細(xì)胞同時(shí)刷新狀態(tài)线召。一個(gè)細(xì)胞生死變化不立即影響其他細(xì)胞,在這種規(guī)則下持痰,雜亂無序的的細(xì)胞會(huì)逐漸演化出各種精致灶搜,有型的結(jié)構(gòu)。

有個(gè)軟件工窍,內(nèi)置了各種規(guī)則以及初始狀態(tài)割卖,也不大,可以下載下來玩一下:golly主頁患雏,主頁上的動(dòng)圖感受一下鹏溯,這是一種比較復(fù)雜的初始狀態(tài)了。還有一個(gè)網(wǎng)址可以在線玩:https://playgameoflife.com/

ticker.gif

我采取的是最原始的規(guī)則:(一個(gè)點(diǎn)周圍的8個(gè)點(diǎn)為8鄰域)

  • 1. 如果一個(gè)細(xì)胞周圍有3個(gè)細(xì)胞為生(一個(gè)細(xì)胞周圍共有8個(gè)細(xì)胞)淹仑,則該細(xì)胞為生(即該細(xì)胞若原先為死丙挽,則轉(zhuǎn)為生,若原先為生匀借,則保持不變) 颜阐。
  • 2. 如果一個(gè)細(xì)胞周圍有2個(gè)細(xì)胞為生,則該細(xì)胞的生死狀態(tài)保持不變吓肋;
  • 3. 在其它情況下凳怨,該細(xì)胞為死(即該細(xì)胞若原先為生,則轉(zhuǎn)為死是鬼,若原先為死肤舞,則保持不變)

利用這個(gè)規(guī)則讓其自動(dòng)演化就可以了:

2. 常見種子。

  • 滑翔機(jī)均蜜。
    可以向右下方滑動(dòng):


  • 滑翔者李剖。



    每四個(gè)回合會(huì)向右走一格。

  • 脈沖星囤耳。
    周期為3篙顺,不斷閃爍偶芍。



    結(jié)果:


  • 滑翔者槍



    這個(gè)玩意可以不斷的發(fā)射滑翔者。
    再有其他的復(fù)雜的圖像就只能自己去發(fā)掘了慰安,還有一種方法就是隨機(jī)初始化種子:

  • 隨機(jī)初始化種子腋寨。
    就是隨機(jī)讓一部分的細(xì)胞存活,然后執(zhí)行游戲規(guī)則化焕,有可能會(huì)產(chǎn)生出比較穩(wěn)定的狀態(tài)萄窜,當(dāng)然這個(gè)也是有研究的,結(jié)果就發(fā)現(xiàn)隨機(jī)激活37.5%的種子的時(shí)候產(chǎn)生比較穩(wěn)定圖案的概率比較大撒桨。這個(gè)我在代碼里也給了查刻,可以設(shè)置。

3. 實(shí)現(xiàn)過程凤类。

其實(shí)主要的代碼比較簡(jiǎn)單穗泵,就是空域?yàn)V波的錨點(diǎn)如何根據(jù)周圍的點(diǎn)來決定自己的狀態(tài):

  • 游戲規(guī)則實(shí)現(xiàn):
void lifeGame(Mat &init_image, int loop_num, bool writeImg,int ms)
{
    int rows = init_image.rows;
    int cols = init_image.cols;
    namedWindow("source", WINDOW_NORMAL);
    imshow("source", init_image);

    //k是迭代次數(shù)
    namedWindow("LIFE_GAME", 2);
    for (int k = 0; k < loop_num; k++)
    {
        cout << k << endl;
        Mat tmp = Mat::zeros(rows, cols, CV_8UC1);
        uchar x1, x2, x3,
              x4,       x6,
              x7, x8, x9;

        for (int i = 1; i < rows - 1; i++)
        {
            int count = 0;
            for (int j = 1; j < cols - 1; j++)
            {
                x1 = init_image.at<uchar>(i - 1, j - 1);
                x2 = init_image.at<uchar>(i - 1, j);
                x3 = init_image.at<uchar>(i - 1, j + 1);
                x4 = init_image.at<uchar>(i, j - 1);
                x6 = init_image.at<uchar>(i, j + 1);
                x7 = init_image.at<uchar>(i + 1, j - 1);
                x8 = init_image.at<uchar>(i + 1, j);
                x9 = init_image.at<uchar>(i + 1, j + 1);
                count = x1 + x2 + x3
                    + x4 + x6
                    + x7 + x8 + x9;
                //生命游戲的核心代碼,三個(gè)if代表三個(gè)規(guī)則
                if (count == 255 * 3)
                    tmp.at<uchar>(i, j) = 255;
                else if (count == 255 * 2)
                    tmp.at<uchar>(i, j) = init_image.at<uchar>(i, j);
                else
                    tmp.at<uchar>(i, j) = 0;         //這一句也是可以不要的谜疤,因?yàn)楸旧砭褪?
            }
        }
        tmp.copyTo(init_image);
        tmp.release();
        imshow("LIFE_GAME", init_image);
        if (writeImg)
            imwrite("res//" + to_string(k) + ".jpg", init_image);
        waitKey(ms);
    }

這樣的話生成的畫布是固定大小的佃延,自己設(shè)置,有的平移類的種子出了邊界就不會(huì)再回來了夷磕,在此基礎(chǔ)上又想了一種辦法:把左右兩邊相連履肃,上下相連,這樣就可以變向的實(shí)現(xiàn)畫布放大(當(dāng)然這不是理想的解法)坐桩,另外一點(diǎn)畫布也是可以設(shè)置大一點(diǎn)的尺棋,因?yàn)樗惴ê?jiǎn)單,用C++寫出來效率還是很高的绵跷,2000*2000的圖像還是可以實(shí)現(xiàn)勉強(qiáng)實(shí)時(shí)的膘螟。

  • 配置文件讀取:
    配置文件以txt文件形式存儲(chǔ)碾局,然后讀入荆残,只存儲(chǔ)活著點(diǎn)的坐標(biāo),每一行的第一個(gè)數(shù)表示該行的行坐標(biāo)净当,后面是列坐標(biāo)脊阴,比如:
1 5
2 4 5 6
3 3 4 5 6 7
4 2 3 4 5 6 7 8
5 1 2 3 4 5 6 7 8 9
6 2 3 4 5 6 7 8
7 3 4 5 6 7
8 4 5 6
9 5

對(duì)應(yīng)的圖片張這樣:


把所有的點(diǎn)移動(dòng)到左上角來定位坐標(biāo),坐標(biāo)初始位置從1開始蚯瞧。

解析的方法也比較簡(jiǎn)單,獲取每一行的數(shù)字使用getline函數(shù)品擎,每一行獲取數(shù)字的時(shí)候使用istringstream,具體:

void getInt(string &s, vector<Point2d> &res,int &cmax,int &rmax)  //從一行中解析出整數(shù),并記錄最大行數(shù)
{
    istringstream iss(s);
    
    int num;
    int cnt=0;       //讀第一個(gè)數(shù)的標(biāo)志
    int line;        //行數(shù)
    int colmax = 0;
    while (iss >> num)
    {
        if (cnt == 0)     //每一行的第一個(gè)數(shù)是行號(hào)
        {
            line = num;
            rmax = line;       //記錄行號(hào)
            cnt++;
        }
        else             //重構(gòu)坐標(biāo)存入res中
        {
            res.push_back(Point2d(line, num));
            if (num > colmax)
            {
                colmax = num;
            }
        }
    }
    cmax = colmax;
}
//從txt中提取坐標(biāo)點(diǎn)埋合,并記錄最大的行和列
void getPos(string &file, vector<Point2d> &CfgMat, int &rmax,int &cmax)
{
    ifstream cfg(file);
    string s;
    int _cmax = 0;
    int _rmax = 0;
    while (getline(cfg, s))
    {
        cout << s << endl;
        getInt(s, CfgMat,_cmax,_rmax);
        if (_cmax > cmax)
            cmax = _cmax;
        if (_rmax > rmax)
            rmax = _rmax;
    } 
}

重構(gòu)棋盤矩陣的時(shí)候會(huì)把棋牌擴(kuò)大(根據(jù)記錄的種子的最大行和列自定義行和列的放大系數(shù))。
其他的就沒什么了萄传,在cfg文件里我存了幾個(gè)比較經(jīng)典的初始種子甚颂,可以讀取來顯示蜜猾。

4. 效果展示。

  • X型種子振诬。

  • 滑翔者槍
    這里有點(diǎn)小蹭睡,程序里是可以調(diào)整顯示大小的:

test.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赶么,隨后出現(xiàn)的幾起案子肩豁,更是在濱河造成了極大的恐慌,老刑警劉巖辫呻,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件清钥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡放闺,警方通過查閱死者的電腦和手機(jī)祟昭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怖侦,“玉大人篡悟,你說我怎么就攤上這事∝仪蓿” “怎么了搬葬?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長旗吁。 經(jīng)常有香客問我踩萎,道長,這世上最難降的妖魔是什么很钓? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任香府,我火速辦了婚禮,結(jié)果婚禮上码倦,老公的妹妹穿的比我還像新娘企孩。我一直安慰自己,他們只是感情好袁稽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布勿璃。 她就那樣靜靜地躺著,像睡著了一般推汽。 火紅的嫁衣襯著肌膚如雪补疑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天歹撒,我揣著相機(jī)與錄音莲组,去河邊找鬼。 笑死暖夭,一個(gè)胖子當(dāng)著我的面吹牛锹杈,可吹牛的內(nèi)容都是我干的撵孤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼竭望,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼邪码!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咬清,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤闭专,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后枫振,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喻圃,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年粪滤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了斧拍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杖小,死狀恐怖肆汹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情予权,我是刑警寧澤昂勉,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站扫腺,受9級(jí)特大地震影響岗照,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笆环,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一攒至、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧躁劣,春花似錦迫吐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鳖擒,卻和暖如春溉浙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蒋荚。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國打工放航, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人圆裕。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓广鳍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吓妆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赊时,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354