【100sites #011】LifeGame,生命遊戲模擬無限的可能性
- Published on
LifeGame,生命遊戲模擬無限的可能性
- ENTER:暫停
- 滑鼠左鍵:放置或刪除細胞
什麼是生命遊戲?
生命遊戲,是由英國數學家John Horton Conway研發的一套細胞自動機模型,地圖中每個細胞會根據周圍 8 格細胞的狀態來決定自身的存亡,詳細規則如下:(摘錄自維基百科)
- 當前細胞為存活狀態時,當周圍低於 2 個(不包含 2 個)存活細胞時,該細胞變成死亡狀態。(模擬生命數量稀少)
- 當前細胞為存活狀態時,當周圍有 2 個或 3 個存活細胞時,該細胞保持原樣。
- 當前細胞為存活狀態時,當周圍有 3 個以上(不包含 3 個)的存活細胞時,該細胞變成死亡狀態。(模擬生命數量過多)
- 當前細胞為死亡狀態時,當周圍有 3 個存活細胞時,該細胞變成存活狀態。 (模擬繁殖)
僅僅這 4 條規則,就可以建構出一個複雜而美妙的生物遊戲世界。我們可以找到一些特殊形狀的生物單位,他們會表現出一些有趣的行為模式。
三種經典的特殊生物單位:
- 左邊的 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]
}
}
}