Javascript初心者がcanvasでランダムあみだくじを作った結果

初心者の入門向け＆私的忘備録です
記事作成＆markdown形式は初めてなのでお見苦しい所、分かり辛い箇所等ありますが、お許しください。


##基本的なあみだ制作
![けっか.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431947/3c9b92cb-f379-6266-71a3-ea157a363b7e.jpeg)



まず最初に、あみだの縦棒の本数(x)を５、縦の長さ(y)を15としてみます。

>tree = 5;
>treeHeight = 15;

ブラウザをリロードするたびにあみだのルートが変わるランダム要素を付け加えるために、Math.randomを採用します。
	
>Math.floor(Math.random() * 2);

これで、0の場合縦棒のみ。1の場合縦棒＋横棒が描画されるようになります。

準備が整いました。描画していきます
流れとしてはまずは横一列に５本分描画してから、改行して次の列も描画するという流れを１５回繰り返します。
コードにすると下記のような感じですね。x軸の間隔は50,ｙ軸の間隔は30。

```javascript:main.js
		//getContext等canvas使う際の決り文句は省略

		const tree = 5;
		const treeHeight = 15;

		function random(){
			return Math.floor(Math.random() * 2);
		}

		ctx.clearRect(0,0,canvas.width,	canvas.height);
		ctx.strokeStyle = 'black';
      
		for (let y = 1; y <= treeHeight; y++){
			for (let x = 1; x <= tree; x++){
				if (x === tree || random() === 0 || y === treeHeight){
					ctx.beginPath();
					ctx.moveTo( 50 * x ,y * 30 );
					ctx.lineTo( 50 * x ,((y + 1) * 30));
					ctx.stroke();
				} else {
					ctx.beginPath();
					ctx.moveTo( 50 * x, y * 30 );
					ctx.lineTo( 50 * x,((y + 1) * 30));
					ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
					ctx.moveTo( 50 * (x + 1), y * 30 );
					ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
					ctx.stroke();
					x++;
				}
			}
		}
```




１列ごとに、Math.randomで判定して０なら縦棒のみ、１なら縦棒＆横棒+隣の縦棒（"｜＿｜"といった感じ）と、描画していきます。それと同時にあみだの右端、底辺は横棒を回避するように。
これで簡単なランダムなあみだの完成です。




##あみだのルートを辿る赤いラインを入れたい
![辿るときの参考.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431947/9e9112d0-3d64-c4ce-a902-4aa019fae82a.jpeg)


下の２行を使ってmath.Randomで1になったときのx,yの座標を記録していきます。

>redLine = [ ];(この配列に座標を格納します)
>redPoint;

先程のあみだのコードにredLineの座標を収集するコードを追加していきます。

```javascript:main.js
		ctx.clearRect(0,0,canvas.width,	canvas.height);
		ctx.strokeStyle = 'black';

		const redLine = [];
		let redPoint;

		for (let y = 1; y <= treeHeight; y++){
			redPoint = 0;
			redLine[y - 1] = [redPoint];   //ここで２重配列を作る

			for (let x = 1; x <= tree; x++){
				if (x === tree || random() === 0 || y === treeHeight){
					redLine[y - 1][redPoint] = [0,0];
					redPoint++;

					ctx.beginPath();
					ctx.moveTo( 50 * x ,y * 30 );
					ctx.lineTo( 50 * x ,((y + 1) * 30));
					ctx.stroke();
				} else {
					for (let t = 0; t < 2; t++){  //同じ座標を同じ列に２つ置く(右回り用と左回り用)
						redLine[y - 1][redPoint]= [x, x + 1];
						redPoint++;
					}

					ctx.beginPath();
					ctx.moveTo( 50 * x, y * 30 );
					ctx.lineTo( 50 * x,((y + 1) * 30));
					ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
					ctx.moveTo( 50 * (x + 1), y * 30 );
					ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
					ctx.stroke();
					x++;
				}
			}
		}
```


この状態でredLine配列に格納される数値は
![redLine配列.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431947/f648d129-7ec4-0d85-8d12-9892cc577d6e.jpeg)

という感じに。[y座標[横棒開始のx座標,横棒終端のx座標]]です。
全分岐点の横棒の有無、座標が格納されたので後はこれを使って先程のあみだのコードの直下に追加します。

```javascript:main.js
		const undo = ctx.getImageData(0,0,canvas.width,canvas.height); //赤い線のない画像を保存しておく

		ctx.strokeStyle = '#e65353';
		ctx.lineWidth = 3;

		for (let x = 1; x <= tree; x++){
			input = document.createElement('input');
			input.type = "submit";
			input.value = "たどる"

			input.addEventListener('click', () =>{
				ctx.putImageData(undo,0,0);      //保存掛けた画像を置いてあみだの赤い線を上書きする
				r = x;    	        //x座標を一時保存

				for(let y = 1; y <= treeHeight; y++){
						ctx.moveTo( 50 * x, y * 30 );
					if(redLine[y - 1][x - 1][0] === x){  //配列の0番目の数値が現在地点だったら右に移動
						ctx.beginPath();
						ctx.moveTo( 50 * x, y * 30 );
						ctx.lineTo( 50 * x,((y + 1) * 30));
						ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
						ctx.stroke();
						x++;
					} else if(redLine[y - 1][x - 1][1] === x){  //配列の1番目の数値が現在地点だったら左に移動
						ctx.beginPath();
						ctx.moveTo( 50 * x, y * 30 );
						ctx.lineTo( 50 * x,((y + 1) * 30));
						ctx.lineTo( 50 * (x - 1),((y + 1) * 30));
						ctx.stroke();
						x--;
					} else{
						ctx.beginPath();
						ctx.moveTo( 50 * x ,y * 30 );
						ctx.lineTo( 50 * x ,((y + 1) * 30));
						ctx.stroke();
					}
				}
				x = r;   //あっちこっちに移動したxの座標を初期化する
			});
			document.body.appendChild(input);
		}
```


という具合にです、無事にあみだを辿るコードを追加することが出来ました。

---
最後にコードの全体図を載せておきます

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<title>あみだくじ試作</title>
</head>

<body>
	<canvas width="330" height="530" id="canvas" style="background: #ddd;">
		canvas not supported.....
	</canvas>
	<script type="text/javascript" src="main.js"></script>
</body>
</html>
```

```javascript:main.js
{
	'use strict'
	function makeAmida() {
		const canvas = document.getElementById('canvas');
		if (!canvas.getContext || !canvas){
			return;
		}
		const ctx = canvas.getContext('2d');
		const tree = 5;
		const treeHeight = 15;

		function random() {
			return Math.floor(Math.random() * 2);
		}

		ctx.clearRect(0, 0, canvas.width, canvas.height);
		ctx.strokeStyle = 'black';

		const redLine = [];//あみだを辿るときに使う座標を格納するコンテナ
		let redPoint;

		for (let y = 1; y <= treeHeight; y++){
			redPoint = 0;
			redLine[y - 1] = [redPoint];//ここで２重配列を作る

			for (let x = 1; x <= tree; x++){
				if (x === tree || random() === 0 || y === treeHeight){
					redLine[y - 1][redPoint] = [0, 0];
					redPoint++;

					ctx.beginPath();
					ctx.moveTo( 50 * x ,y * 30 );
					ctx.lineTo( 50 * x ,((y + 1) * 30));
					ctx.stroke();
				} else {
					for (let t = 0; t < 2; t++){  //同じ座標を同じ列に２つ置く(右回り用と左回り用)
						redLine[y - 1][redPoint]= [x, x + 1];
						redPoint++;
					}
					ctx.beginPath();
					ctx.moveTo( 50 * x, y * 30 );
					ctx.lineTo( 50 * x,((y + 1) * 30));
					ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
					ctx.moveTo( 50 * (x + 1), y * 30 );
					ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
					ctx.stroke();
					x++;
				}
			}
		}
		const undo = ctx.getImageData(0, 0, canvas.width, canvas.height); //赤い線のない画像を保存しておく

		ctx.strokeStyle = '#e65353';
		ctx.lineWidth = 3;

		for (let x = 1; x <= tree; x++){
			input = document.createElement('input');
			input.type = "submit";
			input.value = "たどる"
			
			input.addEventListener('click', () =>{
				ctx.putImageData(undo, 0, 0);			  		 //保存掛けた画像を置いてあみだの赤い線を上書きする
				r = x;															  	//x座標を一時保存

				for(let y = 1; y <= treeHeight; y++){
						ctx.moveTo( 50 * x, y * 30 );
					if(redLine[y - 1][x - 1][0] === x){ //redLine配列の情報:[y座標[横棒始点のx, 横棒終点のx]]
						ctx.beginPath();
						ctx.moveTo( 50 * x, y * 30 );
						ctx.lineTo( 50 * x,((y + 1) * 30));
						ctx.lineTo( 50 * (x + 1),((y + 1) * 30));
						ctx.stroke();
						x++;
					} else if(redLine[y - 1][x - 1][1] === x){
						ctx.beginPath();
						ctx.moveTo( 50 * x, y * 30 );
						ctx.lineTo( 50 * x,((y + 1) * 30));
						ctx.lineTo( 50 * (x - 1),((y + 1) * 30));
						ctx.stroke();
						x--;
					} else{
						ctx.beginPath();
						ctx.moveTo( 50 * x ,y * 30 );
						ctx.lineTo( 50 * x ,((y + 1) * 30));
						ctx.stroke();
					}
				}
				x = r; //あっちこっちに移動したxの座標を初期化する
			});
			document.body.appendChild(input);
		}
	}
	makeAmida();
}
```







初心者でも意外と簡単に作れるので、CSSで装飾したり、あみだの本数を選択式にして自在に長さ変られるようにしたり、変形させたりさまざまなあみだくじを作ってみてください。暇つぶしやプログラミングの練習、夏休みの自由研究等にどうぞ。
いまいちピンとこない箇所があれば質問大歓迎です（分かる範囲でお答え致します）




---
ちなみに先日、上記の２つを応用してこんな物を作りました
[あみだくじ](https://usukonsome.github.io/makeamida/)
(github: https://github.com/usukonsome/makeamida) 
コードレビューお待ちしております（小声）