LoginSignup
1
0

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-03-09

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

基本的なあみだ制作

けっか.JPG

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

tree = 5;
treeHeight = 15;

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

Math.floor(Math.random() * 2);

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

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

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++;
                }
            }
        }

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

あみだのルートを辿る赤いラインを入れたい

辿るときの参考.JPG

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

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

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

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];   //ここで2重配列を作る

            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++){  //同じ座標を同じ列に2つ置く(右回り用と左回り用)
                        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

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

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);
        }

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


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

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>
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];//ここで2重配列を作る

            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++){  //同じ座標を同じ列に2つ置く(右回り用と左回り用)
                        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で装飾したり、あみだの本数を選択式にして自在に長さ変られるようにしたり、変形させたりさまざまなあみだくじを作ってみてください。暇つぶしやプログラミングの練習、夏休みの自由研究等にどうぞ。
いまいちピンとこない箇所があれば質問大歓迎です(分かる範囲でお答え致します)


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

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0