2次元ランダムウォークを可視化する


はじめに

前の投稿では、1次元ランダムウオークのプログラムを書いたので、今度は2次元ランダムウォークのプログラムを書いてみました。

(x,y)にある点の次の位置を(x+1,y) (x-1,y) (x,y+1) (x,y-1)の4つとし、それぞれが、1/4の確率である場合の動きを可視化するプログラムです。

この記事の最後に、CodePenのページを埋め込んでいるので、動きを確認できます。


MyCanvasクラス

1次元ランダムウォークの時と同じように、まず、MyCanvasクラスを定義します。

class MyCanvas {

constructor (id, width, height) {
this.canvas = document.getElementById(id);
this.ctx = this.canvas.getContext('2d');
if (width)
this.ctx.canvas.width = width;
if (height)
this.ctx.canvas.height = height;
this.width = this.ctx.canvas.width;
this.height = this.ctx.canvas.height;
}

drawPoint(x1, y1, size, color = '#000') {
this.ctx.save();
this.ctx.beginPath();
this.ctx.fillStyle = color;
this.ctx.fillRect(x1, y1, size, size);
this.ctx.restore();
}

// すべてをクリア
clearAll() {
this.ctx.clearRect(0, 0, this.width, this.height);
}
}

drawPointメソッドとclearAllメソッドだけをもつ簡単なクラスです。


Walkerクラス

次は、Walkerクラスです。これは、ランダムウオークを行うこのプログラムの核となるクラスです。ひとつの点の動きを扱います。複数の点を同時に動かす場合には、このインスタンスを複数生成することになります。

walkメソッドを呼び出すたびに、ランダムに一歩だけ座標が動きます。なお、ここでは描画は行っていません。

class Walker {

constructor (size) {
this.x = 0;
this.y = 0;
this.maxHeight = 0;
this.maxWidth = 0;
this.size = size;
}

walk() {
var d = Math.floor(Math.random() * 4) + 1;
var diffx = 0, diffy = 0;
switch (d) {
case 1:
diffx = +this.size;
break;
case 2:
diffx = -this.size;
break;
case 3:
diffy = +this.size;
break;
case 4:
diffy = -this.size;
break;
}
if (0 < this.x + diffx && this.x + diffx < this.maxWidth - this.size)
this.x += diffx;
if (0 < this.y + diffy && this.y + diffy < this.maxHeight - this.size)
this.y += diffy;
return {x:this.x, y:this.y};
};
}


Prigramクラス

最後が、Walkerクラスのwalkメソッドを繰り返し呼び出し、描画を行うメイン処理を行うメインのプログラムです。

Promiseasync/awaitを使って、無限ループ風のコードを書こうとしたのですが、CodePenだと、無限ループはダメだよ、とエラーになってしまうので、setIntervalを使った従来の書き方にしました。

class Program {

constructor() {
this.height = 300;
this.width = 300;

this.canvas = new MyCanvas('mycanvas', this.width, this.height);
this.loop = true;
}

run() {
let btn = document.querySelector('#startButton');
btn.onclick = () => this.start();
let btn2 = document.querySelector('#stopButton');
btn2.onclick = () => this.stop();
}

start() {
this.canvas.clearAll();
const size = 2;
var walker = new Walker(size);
walker.x = this.width / 2;
walker.y = this.height / 2;
walker.maxWidth = this.width;
walker.maxHeight = this.height;
this.canvas.drawPoint(walker.x, walker.y, size);

this.loop = true;
let timer = setInterval(() => {
this.canvas.drawPoint(walker.x, walker.y, size, '#ccc');
walker.walk();
this.canvas.drawPoint(walker.x, walker.y, size, '#000');
if (this.loop === false)
clearInterval(timer);
}, 20);
}

stop() {
this.loop = false;
}
}

window.onload = function() {
var pgm = new Program();
pgm.run();
};


HTML

HTMLは以下の通り。

<!DOCTYPE html >

<html lang="ja">
<head>
<meta charset="utf-8">
<title>sample</title>
<style>
#mycanvas {
background-color: beige;
}
</style>
</head>
<body>
<div>
<button id="startButton" type="button">Start</button>
<button id="stopButton" type="button">Stop</button>
</div>
<div>
<canvas id="mycanvas" width="200" height="200"></canvas>
</div>
<script src="randomwalk2d.js"></script>
</body>
</html>


実行結果

スクリーンショット 2019-03-08 20.47.21.png

このプログラムですが、いいかげんな作りなので、Startボタンを複数クリックすると、クリックした数だけ点が生成され、複数の点が同時に動くようになります。

以下が、そのスクリーンショット。

スクリーンショット 2019-03-08 20.47.44.png

ちょっと、点が小さいですが、5つの黒点があるのがわかると思います。


CodePenで書いて、埋め込んでみた

CodePenのページを埋め込んでみました。Startボタンを押せば、ランダムウォークが開始されます。

眺めてるだけでも楽しいです(笑)


See the Pen
randomwalk2d
by Gushwell (@gushwell)
on CodePen.