Skip to content

Instantly share code, notes, and snippets.

@shigehiro-yanbe
Last active April 24, 2017 05:36
Show Gist options
  • Save shigehiro-yanbe/9f51972c78c19f623c296a36cc87547e to your computer and use it in GitHub Desktop.
Save shigehiro-yanbe/9f51972c78c19f623c296a36cc87547e to your computer and use it in GitHub Desktop.
ライフゲーム
var PIXEL_SIZE = 8;
var ModelEvent = {
Update: 0,
Play: 1,
Stop: 2,
SizeChange: 3,
}
//=============================================================================
var Controller = (function(){
var Controller = function() {
this.width = 32;
this.height = 32;
this.model = new Model(this.width, this.height);
this.view = new View(this.model, this);
this.model.Start();
}
var p = Controller.prototype;
p.Stop = function() { this.model.Stop(); }
p.Play = function() { this.model.Start(); }
p.Clear = function() { this.model.Clear(); }
p.SetRandom = function() { this.model.SetRandom(); }
p.SizeChange = function(width,height) { this.model.SizeChange(width,height); }
p.MouseDown = function(e) { this.model.MouseDown(e); }
p.MouseUp = function(e) { this.model.MouseUp(e); }
p.MouseMove = function(e) { this.model.MouseMove(e); }
return Controller;
})();
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>lifegame</title>
<script src="constants.js"></script>
<script src="matrix.js"></script>
<script src="lifegame.js"></script>
<script src="mousehandler.js"></script>
<script src="model.js"></script>
<script src="view.js"></script>
<script src="controller.js"></script>
</head>
<body onload="new Controller();">
<form>
<input id="stop" type="button" value="停止">
<input id="play" type="button" value="再生">
<input id="clear" type="button" value="クリア">
<input id="random" type="button" value="ランダム">
幅:
<input id="width" type="text" size="5">
高さ:
<input id="height" type="text" size="5">
<input id="size" type="button" value="サイズ変更">
</form>
<canvas id="canvas" style="border: solid thin #000000">
</body>
</html>
//=============================================================================
// ライフゲームの状態クラス
var LifeGame = (function(){
// コンストラクタ
var LifeGame = function(width, height) {
this.width = width;
this.height = height;
this.front = new Matrix(width+2, height+2);
this.back = new Matrix(width+2, height+2);
this.SetRandom();
}
var p = LifeGame.prototype;
// ランダムに初期状態を設定する
p.SetRandom = function() {
for (var y = 1; y <= this.height; ++y) {
for (var x = 1; x <= this.width; ++x) {
this.front.set(x, y, Math.random() <= 0.25);
}
}
}
// クリア
p.Clear = function() {
this.front.Clear();
}
// 一世代進める
p.NextGeneration = function() {
for (var y = 1; y <= this.height; ++y) {
for (var x = 1; x <= this.width; ++x) {
this.back.set(x, y, this.front.nextState(x,y));
}
}
this.swap();
}
// フロントバッファとバックバッファを入れ替える
p.swap = function() {
var tmp = this.front;
this.front = this.back;
this.back = tmp;
}
// 指定セルの状態を返す
p.Get = function(x, y) {
return this.front.get(x+1,y+1);
}
// 指定セルの状態を設定する
p.Set = function(x,y,onoff) {
this.front.set(x+1,y+1,onoff);
}
return LifeGame;
})();
// 一世代分の状態を保持する二次元配列クラス
var Matrix = (function(){
// コンストラクタ
var Matrix = function(width, height) {
this.width = width;
this.height = height;
this.matrix = this.createMatrix(width, height);
}
var p = Matrix.prototype;
// 二次元配列を作る
p.createMatrix = function(width, height) {
var matrix = new Array(height);
for (var h = 0; h < height; ++h) {
matrix[h] = new Array(width);
for (var w = 0; w < width; ++w) {
matrix[h][w] = false;
}
}
return matrix;
}
// 全クリア
p.Clear = function() {
for (var y = 0; y < this.height; ++y) {
for (var x = 0; x < this.width; ++x) {
this.matrix[y][x] = false;
}
}
}
// 指定のマスの周囲のtrueの数を返す
p.count = function(x, y) {
var c = 0;
var xl = x - 1;
var xr = x + 1;
var yu = y - 1;
var yd = y + 1;
if (this.matrix[yu][xl]) ++c;
if (this.matrix[yu][x ]) ++c;
if (this.matrix[yu][xr]) ++c;
if (this.matrix[y ][xl]) ++c;
if (this.matrix[y ][xr]) ++c;
if (this.matrix[yd][xl]) ++c;
if (this.matrix[yd][x ]) ++c;
if (this.matrix[yd][xr]) ++c;
return c;
}
// 指定のマスの次世代の状態を返す
p.nextState = function(x, y) {
switch (this.count(x,y)) {
case 2: return this.get(x, y);
case 3: return true;
default: return false;
}
}
// 指定のマスの状態を返す
p.get = function(x, y) {
return this.matrix[y][x];
}
// 指定のマスの状態を設定する
p.set = function(x, y, onoff) {
this.matrix[y][x] = onoff;
}
return Matrix;
})();
//=============================================================================
var Model = (function(){
var Model = function() {
this.Width = 32;
this.Height = 32;
this.lifegame = new LifeGame(this.Width, this.Height);
this.isPlay = true;
this.listener = null;
this.mouseHandler = new MouseHandler_PlayMode();
}
var p = Model.prototype;
p.Start = function() {
this.isPlay = true;
this.update();
this.notifyEvent( ModelEvent.Play );
this.mouseHandler = new MouseHandler_PlayMode();
}
p.Stop = function() {
this.isPlay = false;
this.notifyEvent( ModelEvent.Stop );
this.mouseHandler = new MouseHandler_EditMode(this);
}
p.Clear = function() {
this.lifegame.Clear();
this.notifyEvent( ModelEvent.Update );
this.Stop();
}
p.SetRandom = function() {
this.lifegame.SetRandom();
this.notifyEvent( ModelEvent.Update );
this.Stop();
}
p.SizeChange = function(width,height) {
this.Width = width;
this.Height = height;
this.lifegame = new LifeGame(width,height);
this.notifyEvent( ModelEvent.SizeChange );
this.Stop();
}
p.update = function() {
if (this.isPlay) {
this.lifegame.NextGeneration();
this.notifyEvent( ModelEvent.Update );
var self = this;
setTimeout( function(){ self.update(); }, 500 );
}
}
p.notifyEvent = function(event) {
if (this.listener) {
this.listener(event);
}
}
p.Get = function(x,y) {
return this.lifegame.Get(x,y);
}
p.Set = function(x,y,onoff) {
this.lifegame.Set(x,y,onoff);
}
p.RegisterEventListener = function(listener) {
this.listener = listener;
}
p.MouseDown = function(e) {
this.mouseHandler.MouseDown(e);
}
p.MouseUp = function(e) {
this.mouseHandler.MouseUp(e);
}
p.MouseMove = function(e) {
this.mouseHandler.MouseMove(e);
}
return Model;
})();
//=============================================================================
var MouseHandler_PlayMode = (function(){
var MouseHandler_PlayMode = function() {
}
var p = MouseHandler_PlayMode.prototype;
p.MouseDown = function(e) {}
p.MouseUp = function(e) {}
p.MouseMove = function(e) {}
return MouseHandler_PlayMode;
})();
//=============================================================================
var MouseHandler_EditMode = (function(){
var MouseHandler_EditMode = function(model) {
this.model = model;
this.isMouseDown = false;
this.isDrag = false;
this.firstPos = undefined;
this.prevPos = undefined;
}
var p = MouseHandler_EditMode.prototype;
p.MouseDown = function(e) {
this.isMouseDown = true;
this.isDrag = false;
var pos = this.toPoint(e);
this.firstPos = this.prevPos = pos;
this.model.Set(pos.x, pos.y, !this.model.Get(pos.x,pos.y));
this.model.notifyEvent( ModelEvent.Update );
}
p.MouseUp = function(e) {
this.isMouseDown = false;
}
p.MouseMove = function(e) {
if (!this.isMouseDown) { return; }
var pos = this.toPoint(e);
if ((pos.x == this.prevPos.x) && (pos.y == this.prevPos.y)) { return; }
if (!this.isDrag) {
this.model.Set(this.firstPos.x, this.firstPos.y, true);
this.isDrag = true;
}
this.model.Set(pos.x, pos.y, true);
this.model.notifyEvent( ModelEvent.Update );
this.prevPos = pos;
}
p.toPoint = function(e) {
return {
x:this.toCell( e.offsetX ),
y:this.toCell( e.offsetY ),
};
}
p.toCell = function(offset) {
return Math.floor( offset / PIXEL_SIZE );
}
return MouseHandler_EditMode;
})();
//=============================================================================
var View = (function(){
var View = function(model, controller) {
this.model = model;
this.controller = controller;
this.canvas = document.getElementById('canvas');
this.initElements();
this.registerEvent();
}
var p = View.prototype;
p.initElements = function() {
this.canvas.width = this.model.Width * PIXEL_SIZE;
this.canvas.height = this.model.Height * PIXEL_SIZE;
document.getElementById('width') .value = this.model.Width;
document.getElementById('height').value = this.model.Height;
}
p.registerEvent = function() {
var self = this;
this.model.RegisterEventListener( function(event){ self.onModelEvent(event); } );
function registerClickEvent(id,handler)
{
document.getElementById(id).addEventListener
('click', function(){ handler();}, false );
}
registerClickEvent('stop', function(){ self.onClick_Stop(); });
registerClickEvent('play', function(){ self.onClick_Play(); });
registerClickEvent('clear', function(){ self.onClick_Clear(); });
registerClickEvent('random', function(){ self.onClick_Random(); });
registerClickEvent('size', function(){ self.onClick_SizeChange(); });
var canvas = document.getElementById('canvas');
canvas.addEventListener('mousedown', function(e){ self.onMouseDown(e); }, false);
canvas.addEventListener('mouseup', function(e){ self.onMouseUp(e); }, false);
canvas.addEventListener('mousemove', function(e){ self.onMouseMove(e); }, false);
}
p.onClick_Stop = function() {
this.controller.Stop();
}
p.onClick_Play = function() {
this.controller.Play();
}
p.onClick_Clear = function() {
this.controller.Clear();
}
p.onClick_Random = function() {
this.controller.SetRandom();
}
p.onClick_SizeChange = function() {
var width = document.getElementById('width') .value;
var height = document.getElementById('height').value;
this.controller.SizeChange(width, height);
}
p.onMouseDown = function(e) {
this.controller.MouseDown(e);
}
p.onMouseUp = function(e) {
this.controller.MouseUp(e);
}
p.onMouseMove = function(e) {
this.controller.MouseMove(e);
}
p.onSizeChange = function() {
this.initElements();
this.render();
}
p.onModelEvent = function(event) {
switch (event) {
case ModelEvent.Update: this.render(); break;
case ModelEvent.Play: this.setActive_SizeFields(false); break;
case ModelEvent.Stop: this.setActive_SizeFields(true); break;
case ModelEvent.SizeChange: this.onSizeChange(); break;
default:
alert("");
throw null;
}
}
p.setActive_SizeFields = function(onoff) {
var disabled = !onoff;
document.getElementById('width' ).disabled = disabled;
document.getElementById('height').disabled = disabled;
document.getElementById('size' ).disabled = disabled;
}
p.render = function() {
var ctx = this.canvas.getContext('2d');
ctx.fillStyle = "rgb(255,255,255)";
ctx.fillRect(0, 0, (this.model.Width * PIXEL_SIZE), (this.model.Height * PIXEL_SIZE));
ctx.fillStyle = "rgb(0,0,0)";
for (var y = 0; y < this.model.Height; ++y) {
for (var x = 0; x < this.model.Width; ++x) {
if (this.model.Get(x,y)) {
ctx.fillRect(x*PIXEL_SIZE, y*PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE);
}
}
}
}
return View;
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment