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];
}
}
}