Skip to content

Instantly share code, notes, and snippets.

@st98
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save st98/9581797 to your computer and use it in GitHub Desktop.
Save st98/9581797 to your computer and use it in GitHub Desktop.
ワイヤワールドというセルオートマトンを実装してみました。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Wireworld</title>
<style>
body {
font-family: Helvetica, Arial, sans-serif;
}
canvas {
display: block;
}
</style>
</head>
<body>
<h1>Wireworld</h1>
<script src="wireworld.js"></script>
<script src="main.js"></script>
</body>
</html>
(function main() {
var width = 25;
var height = 25;
var scale = 20;
var canvas = document.createElement('canvas');
canvas.width = width * scale;
canvas.height = height * scale;
var context = canvas.getContext('2d');
var world = new Wireworld({
width: width,
height: height
});
var colors = {
empty: 'rgb(0, 0, 0)',
head: 'rgb(255, 255, 255)',
tail: 'rgb(0, 128, 255)',
conductor: 'rgb(255, 196, 0)'
};
var isRunning = false;
var isDragging = false;
var state = world.states.conductor;
var mousedown = function (e) {
var rect = e.target.getBoundingClientRect();
var x = Math.floor((e.clientX - rect.left) / scale);
var y = Math.floor((e.clientY - rect.top) / scale);
world.field[x + y * width] = state;
isDragging = true;
};
var mouseup = function () {
isDragging = false;
};
var mousemove = function (e) {
var rect = e.target.getBoundingClientRect();
var x = Math.floor((e.clientX - rect.left) / scale);
var y = Math.floor((e.clientY - rect.top) / scale);
if (isDragging) {
world.field[x + y * width] = state;
}
};
canvas.addEventListener('mousedown', mousedown, false);
canvas.addEventListener('mouseup', mouseup, false);
canvas.addEventListener('mousemove', mousemove, false);
canvas.addEventListener('mouseout', mouseup, false);
var draw = function () {
var x, y;
context.clearRect(0, 0, width * scale, height * scale);
for (x = 0; x < width; x++) {
for (y = 0; y < height; y++) {
switch (world.field[x + y * width]) {
case world.states.empty:
context.fillStyle = colors.empty;
break;
case world.states.head:
context.fillStyle = colors.head;
break;
case world.states.tail:
context.fillStyle = colors.tail;
break;
case world.states.conductor:
context.fillStyle = colors.conductor;
break;
}
context.fillRect(x * scale, y * scale, scale, scale);
context.fillStyle = 'rgb(16, 16, 16)';
context.fillRect(x * scale, y * scale + scale - 1, scale, 1);
context.fillRect(x * scale + scale - 1, y * scale, 1, scale);
}
}
};
var render = function () {
if (isRunning) {
world.update();
}
draw();
};
var fps = 30;
var interval = 1000 / fps;
setInterval(render, interval);
var makeButton = function (text) {
var txt = document.createTextNode(text);
var button = document.createElement('button');
button.appendChild(txt);
return button;
};
var buttonEmpty = makeButton('empty');
var buttonHead = makeButton('head');
var buttonTail = makeButton('tail');
var buttonConductor = makeButton('conductor');
var buttonToggle = makeButton('start/stop');
var buttonReset = makeButton('reset');
buttonToggle.addEventListener('click', function () {
isRunning = !isRunning;
});
buttonReset.addEventListener('click', function () {
world.reset();
});
buttonEmpty.addEventListener('click', function () {
state = world.states.empty;
});
buttonHead.addEventListener('click', function () {
state = world.states.head;
});
buttonTail.addEventListener('click', function () {
state = world.states.tail;
});
buttonConductor.addEventListener('click', function () {
state = world.states.conductor;
});
document.body.appendChild(buttonToggle);
document.body.appendChild(buttonReset);
document.body.appendChild(buttonEmpty);
document.body.appendChild(buttonHead);
document.body.appendChild(buttonTail);
document.body.appendChild(buttonConductor);
document.body.appendChild(canvas);
}).call(this);
var Wireworld;
(function () {
Wireworld = function Wireworld(options) {
if (!(this instanceof Wireworld)) {
return new Wireworld(options);
}
this.width = options.width || 100;
this.height = options.height || 100;
this.reset();
};
Wireworld.prototype.states = {
empty: 0,
head: 1,
tail: 2,
conductor: 3
};
Wireworld.prototype.reset = function () {
var x, y;
this.generation = 0;
this.field = [];
for (x = 0; x < this.width; x++) {
for (y = 0; y < this.height; y++) {
this.field[x + y * this.width] = this.states.empty;
}
}
};
Wireworld.prototype.update = function () {
var x, y, state;
var neighbors;
var newField = [];
for (x = 0; x < this.width; x++) {
for (y = 0; y < this.height; y++) {
state = this.field[x + y * this.width];
if (state == this.states.head) {
newField[x + y * this.width] = this.states.tail;
} else if (state == this.states.tail) {
newField[x + y * this.width] = this.states.conductor;
} else if (state == this.states.conductor) {
neighbors = this.neighbors(x, y);
if (neighbors == 1 || neighbors == 2) {
newField[x + y * this.width] = this.states.head;
} else {
newField[x + y * this.width] = state;
}
} else {
newField[x + y * this.width] = state;
}
}
}
this.generation++;
this.field = newField;
};
Wireworld.prototype.neighbors = function (x, y) {
var offsetX, offsetY;
var count = 0;
for (offsetX = -1; offsetX <= 1; offsetX++) {
for (offsetY = -1; offsetY <= 1; offsetY++) {
if ((x + offsetX) > -1 && (y + offsetY) > -1 && (x + offsetX) < this.width && (y + offsetY) < this.height) {
count += this.field[(x + offsetX) + (y + offsetY) * this.width] == this.states.head;
}
}
}
return count;
};
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment