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


## はじめに

前の投稿では、[1次元ランダムウオーク](https://qiita.com/gushwell/items/59e11f0029b45515fe19)のプログラムを書いたので、今度は2次元ランダムウォークのプログラムを書いてみました。

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

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

## MyCanvasクラス

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

```js
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メソッドを呼び出すたびに、ランダムに一歩だけ座標が動きます。なお、ここでは描画は行っていません。

```js
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メソッドを繰り返し呼び出し、描画を行うメイン処理を行うメインのプログラムです。

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


```js
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は以下の通り。

```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](https://qiita-image-store.s3.amazonaws.com/0/29415/b6a645b9-533e-e6fb-32ce-76a44c7abb5f.png "スクリーンショット 2019-03-08 20.47.21.png")


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

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

![スクリーンショット 2019-03-08 20.47.44.png](https://qiita-image-store.s3.amazonaws.com/0/29415/a494452e-35fc-5be6-f4ee-90685bb8a833.png "スクリーンショット 2019-03-08 20.47.44.png")

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

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

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


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


<p class="codepen" data-height="420" data-theme-id="0" data-default-tab="js,result" data-user="gushwell" data-slug-hash="EMmomB" style="height: 420px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1em 0; padding: 1em;" data-pen-title="randomwalk2d">
  <span>See the Pen <a href="https://codepen.io/gushwell/pen/EMmomB/">
  randomwalk2d</a> by Gushwell (<a href="https://codepen.io/gushwell">@gushwell</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>