能をつかんとする人、「よくせざらんほどは、なまじひに人に知られじ。う ちうちよく習ひ得て、さし出でたらんこそ、いと心にくからめ」と常に言ふめ れど、かく言ふ人、一芸も習ひ得ることなし。
未だ堅固かたほなるより、上手の中に交りて、毀り笑はるゝにも恥ぢず、つれ なく過ぎて嗜む人、天性、その骨なけれども、道になづまず、濫りにせずして、 年を送れば、堪能の嗜まざるよりは、終に上手の位に至り、徳たけ、人に許さ れて、双なき名を得る事なり。
天下のものの上手といへども、始めは、不堪の聞えもあり、無下の瑕瑾もあり き。されども、その人、道の掟正しく、これを重くして、放埒せざれば、世の 博士にて、万人の師となる事、諸道変るべからず。
JavaScript で格子を塗り潰す練習をしていたら、何となくライフゲームを 思い出したので作ってみました。
function LifeGame(size){
this.initialize(size);
}
LifeGame.prototype.initialize = function(size){
this.LivingColor = "#aaa";
this.DeadColor = "#eee";
this._size = size;
this._draw_ready = false;
this._cells = [];
this._next_cells = [];
this._canvas = [];
this._timer_id = null;
var x, y, a;
for(x = 0; x < size; ++x){
a = [];
for(y = 0; y < size; ++y){
a[y] = Math.floor(Math.random() * 2);
}
this._cells[x] = a;
this._next_cells[x] = [];
this._canvas[x] = [];
}
}
LifeGame.prototype._getNumberOfLivingNeighborhoods = function(x, y){
var x_next = (x == this._size - 1) ? 0 : x + 1;
var x_prev = (x == 0) ? this._size - 1 : x - 1;
var y_next = (y == this._size - 1) ? 0 : y + 1;
var y_prev = (y == 0) ? this._size - 1 : y - 1;
return this._cells[x][y_prev] +
this._cells[x][y_next] +
this._cells[x_next][y] +
this._cells[x_prev][y] +
this._cells[x_prev][y_prev] +
this._cells[x_prev][y_next] +
this._cells[x_next][y_prev] +
this._cells[x_next][y_next];
}
LifeGame.prototype.step = function(){
var x, y;
for(x = 0; x < this._size; ++x){
for(y = 0; y < this._size; ++y){
var n = this._getNumberOfLivingNeighborhoods(x, y);
switch(n){
case 3:
this._next_cells[x][y] = 1;
break;
case 2:
this._next_cells[x][y] = this._cells[x][y];
break;
default:
this._next_cells[x][y] = 0;
}
}
}
for(x = 0; x < this._size; ++x){
for(y = 0; y < this._size; ++y){
this._cells[x][y] = this._next_cells[x][y];
}
}
}
LifeGame.prototype.draw = function(){
if(!this._draw_ready){
this._drawInit();
}
for(x = 0; x < this._size; ++x){
for(y = 0; y < this._size; ++y){
this._canvas[x][y].style.backgroundColor = this._cells[x][y] ? this.LivingColor : this.DeadColor;
}
}
}
LifeGame.prototype._drawInit = function(){
var e;
var p = document.getElementById('lifegame');
for(x = 0; x < this._size; ++x){
for(y = 0; y < this._size; ++y){
e = document.createElement('div');
with(e.style){
position = 'absolute';
left = x * 20 + 'px';
top = y * 20 + 50 + 'px';
width = '20px';
height = '20px';
backgroundColor = this._cells[x][y] ? this.LivingColor : this.DeadColor;
border = "none";
}
this._canvas[x][y] = e;
p.appendChild(e);
}
}
this._draw_ready = true;
}
LifeGame.prototype.start = function(objname){
if(!this._timer_id){
this._timer_id = setInterval('(function(){ ' + objname + '.step(); ' + objname + '.draw(); })()', 300);
}
}
LifeGame.prototype.stop = function(){
if(this._timer_id){
clearInterval(this._timer_id);
}
this._timer_id = null;
}
function init(){
game = new LifeGame(30);
game.draw();
document.getElementById('start').onclick = function(){
game.start('game');
}
document.getElementById('stop').onclick = function(){
game.stop();
}
}
var game;
window.onload = init;
JavaScript で「クラス」相当のことをするにはいろいろやり方があるようですが、
ClassName.prototype.instance_method = function(){
...
}
でインスタンスメソッドを定義していくことによりできるようです。
ちょっといやなのがタイマー処理の setInterval のところで、 これをうまくクラスでラップする方法が分かりませんでした。
setInterval('function(){ this.step(); this.draw(); }', 300);
とやってもうまくいきません。
setInterval() の第1引数にはクロージャが渡せたみたいです。 よって、下のようにすることでタイマーまわりの記述がきれいになりました。
LifeGame.prototype.start = function(){
if(!this._timer_id){
var self = this;
this._timer_id = setInterval(function(){ self.step(); self.draw(); }, 300);
}
}