【100sites #011】LifeGame,生命遊戲模擬無限的可能性

Published on
Currently displaying Chinese version content. This article doesn't have a English version yet. Please stay tuned!

LifeGame,生命遊戲模擬無限的可能性

點我玩生命遊戲線上 demo

點我查看 Github 原始碼

  • ENTER:暫停
  • 滑鼠左鍵:放置或刪除細胞

螢幕快照 2016-03-27 上午12.35.39.png

什麼是生命遊戲?

生命遊戲,是由英國數學家John Horton Conway研發的一套細胞自動機模型,地圖中每個細胞會根據周圍 8 格細胞的狀態來決定自身的存亡,詳細規則如下:(摘錄自維基百科

  1. 當前細胞為存活狀態時,當周圍低於 2 個(不包含 2 個)存活細胞時,該細胞變成死亡狀態。(模擬生命數量稀少)
  2. 當前細胞為存活狀態時,當周圍有 2 個或 3 個存活細胞時,該細胞保持原樣。
  3. 當前細胞為存活狀態時,當周圍有 3 個以上(不包含 3 個)的存活細胞時,該細胞變成死亡狀態。(模擬生命數量過多)
  4. 當前細胞為死亡狀態時,當周圍有 3 個存活細胞時,該細胞變成存活狀態。 (模擬繁殖)

僅僅這 4 條規則,就可以建構出一個複雜而美妙的生物遊戲世界。我們可以找到一些特殊形狀的生物單位,他們會表現出一些有趣的行為模式。

三種經典的特殊生物單位: 螢幕快照 2016-03-28 上午11.36.59.png

  • 左邊的 Blinker(信號燈)會以特定週期變化
  • 中間的 Spaceship(太空船)會往右下角移動(你也可以設計出往其他方向移動的船)
  • 右邊的 Beehive(蜂窩)則會保持靜止

今天的 LifeGame

而在今天的 LifeGame 裡頭,我已經為你在左上角放置一個 Blinker 了,剩下的空間,就交給你來創造無限的可能性了!

今天的程式碼:

<!DOCTYPE html> <html> <head> <meta charset-"UTF-8"> <title>LifeGame</title> <link rel="stylesheet" type="text/css" href="style.css" /> <script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.23/p5.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.23/addons/p5.dom.min.js"></script> <script src="lifegame.js"></script> </head> <body></body> </html>
* { margin: 0; padding: 0; } body { overflow: hidden; }
var SIZE = 20 var oldMap = [], newMap = [], num_x = 0, num_y = 0, paused = false, frameCount = 0 var pauseButton function setup() { createCanvas(windowWidth, windowHeight) frameRate(30) // init map num_x = windowWidth / SIZE num_y = windowHeight / SIZE for (var i = 0; i < num_x; ++i) { oldMap.push([]) newMap.push([]) for (var j = 0; j < num_y; ++j) { oldMap[i].push(false) newMap[i].push(false) } } loadDefaultMap() // init pause button pauseButton = createButton('Pause') pauseButton.size(80, 30) pauseButton.position(windowWidth / 2 - 40, windowHeight - 40) pauseButton.mousePressed(togglePauseSimulate) } function draw() { // fresh map every 5 frames if (!paused) { if (frameCount % 5 == 0) { freshMap() } } // increase frame count ++frameCount if (frameCount >= 30) { frameCount = 0 } drawMap() } // default map: a blink unit at top left corner function loadDefaultMap() { oldMap[1][1] = true oldMap[1][2] = true oldMap[1][3] = true } function drawMap() { background(0) // draw grid stroke(30) for (var i = 0; i < num_x; ++i) { line(i * SIZE, 0, i * SIZE, windowHeight) } for (var i = 0; i < num_y; ++i) { line(0, i * SIZE, windowWidth, i * SIZE) } fill(255) // draw cells for (var i = 0; i < num_x; ++i) { for (var j = 0; j < num_y; ++j) { if (oldMap[i][j]) { rect(i * SIZE, j * SIZE, SIZE, SIZE) } } } // draw mouse cell fill(color('rgba(100,100,100,0.5)')) var mouseCellX = int(mouseX / SIZE) var mouseCellY = int(mouseY / SIZE) rect(mouseCellX * SIZE, mouseCellY * SIZE, SIZE, SIZE) } // press ENTER to pause simulate function keyPressed() { if (keyCode == ENTER) { togglePauseSimulate() } } // press mouse to add or remove cell function mousePressed() { var mouseCellX = int(mouseX / SIZE) var mouseCellY = int(mouseY / SIZE) oldMap[mouseCellX][mouseCellY] = !oldMap[mouseCellX][mouseCellY] } function togglePauseSimulate() { if (paused) { paused = false } else { paused = true } } // count how many neighbors there are of a cell function neighbors(xpos, ypos) { var total = 0 // four corners if (xpos != 0 && ypos != 0) { if (oldMap[xpos - 1][ypos - 1]) { ++total } } if (xpos != 0 && ypos != num_y - 1) { if (oldMap[xpos - 1][ypos + 1]) { ++total } } if (xpos != num_x - 1 && ypos != 0) { if (oldMap[xpos + 1][ypos - 1]) { ++total } } if (xpos != num_x - 1 && ypos != num_y - 1) { if (oldMap[xpos + 1][ypos + 1]) { ++total } } // left and right if (xpos != 0) { if (oldMap[xpos - 1][ypos]) { ++total } } if (xpos != num_x - 1) { if (oldMap[xpos + 1][ypos]) { ++total } } if (ypos != 0) { if (oldMap[xpos][ypos - 1]) { ++total } } if (ypos != num_y - 1) { if (oldMap[xpos][ypos + 1]) { ++total } } return total } // calculate the next map according to the Life Game rule function freshMap() { for (var i = 0; i < num_x; ++i) { for (var j = 0; j < num_y; ++j) { var neighbor_count = neighbors(i, j) if (neighbor_count <= 1 || neighbor_count >= 4) { newMap[i][j] = false } else if (neighbor_count === 2) { newMap[i][j] = oldMap[i][j] } else { // neighbor_count === 3 newMap[i][j] = true } } } for (var i = 0; i < num_x; ++i) { for (var j = 0; j < num_y; ++j) { oldMap[i][j] = newMap[i][j] } } }