凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

JavaScriptであみだくじを自動生成して自動トレース

文字を使用してあみだくじを自動生成するコードを書いてみました.
サンプル: amida.html 直
実行時の画像:

余談ですが,漢字だと「阿弥陀籤」と書いて,昔は放射状に描かれていたことから,それが後光に似ている言われたのが名前の由来だそうです(Wikipedia).

あみだくじを作成する部分

// あみだくじを作成するよ!
function makeAmida(num, len){

 // ①〜⑳まで
 if(num > 20) alert("20人以下でお願いします.")

 // 縦の長さ
 var amida = new Array(len);
 for(i=0; i<len; i++) { // 縦のループ

  // 横の長さ
  amida[i] = new Array(2*num-1);
  for(j=0; j<2*num-1; j++) { // 横のループ

   // 横の位置:偶数番目
   if (j%2) {
    if(amida[i][j-1] == "┣") {
     amida[i][j] = "━"; // 左が┣だったら━
    } else {
     amida[i][j] = " "; // それ以外(┫┃)だったら空白
    }
   }

   // 横の位置:奇数番目
   else {
    if(amida[i][j-1] == "━") {
     amida[i][j] = "┫"; // 左が━だったら┫
    }
    else {
     if(j==2*(num-1)) {
      amida[i][j] = "┃"; // 右端は┃のみ
     } else {
      // その他の位置では1/2で┃か┣ (**ランダムなアミダを作る箇所**)
      amida[i][j] = (Math.floor(Math.random()*2)) ? "┃" : "┣";
     }
    }
   }

  } // for i
 } // for j

 return amida; // 作成したアミダを返す
}

偶数番目/奇数番目でありえる記号をamidaに代入していきます.例えば,偶数番目なら「━」か空白「 」し有り得ず,奇数番目なら「┫」か「┃」か「┣」しか有り得ないわけです.左から順に描いていくので,左が「┣」だったら絶対に「━」を取る,とか左が「━」だったら絶対に「┫」を取る,とかを考えながら場合わけをします.「┃」か「┣」の部分だけ1/2でどちらかを選択するようにコードを書けば,ランダムなアミダが生成できます.

アミダを描く

// 作成したアミダを描くよ!
function writeAmida(matrix) {
 // ○付番号
 var nums = new Array("①","②","③","④","⑤","⑥","⑦","⑧","⑨","⑩","⑪","⑫","⑬","⑭","⑮","⑯","⑰","⑱","⑲","⑳");

 // 頭の番号
 for(i=0; i<(matrix[0].length-1)/2; i++) {
  document.write('<span class="amida" id="top_num' + i + '">');
  document.write(nums[i]);
  document.write(' </span>');
 }
 document.write("<br />");

 // アミダくじを描く
 for(i=0; i<matrix.length; i++) {
  // 横
  for(j=0; j<matrix[0].length; j++) {
   document.write('<span class="amida" id="amida' + i + "l" + j + '">');
   document.write(matrix[i][j]);
   document.write('</span>');
  }
  document.write("<br />");
 }

 // 下の番号
 for(i=0; i<(matrix[0].length-1)/2; i++) {
  document.write('<span class="amida" id="bottom_num' + i + '">');
  document.write(nums[i]);
  document.write(' </span>');
 }

}

得られたアミダを描きます.後でトレース出来るように適当なidを各要素に持たせておきます.

アミダをトレースする

// アミダをトレースするよ!
// matrix: アミダ
// i,  j  : 現在位置(i: 縦 j: 横)
// i2, j2 : 1個前の位置
// 再帰回数
function traceAmida(matrix,i,i2,j,j2,step) {
 // 前のポジションを記憶
 pre_i = i; pre_j = j;

 // 一番初めの時
 if(step==0) {
  i = i2 = 0; pre_i = -1;
  j = j2 = 0; pre_j = 0;

  // 最初の開始位置はランダム(偶数位置)
  j = 2*Math.floor(Math.random()*(matrix[0].length+1)/2);
 }
 // 通常時
 else {
  // 現在位置の記号で場合わけ
  if(matrix[i][j] == "━") {
   j += (j - j2); // そのままの速度で横方向へ移動
  } 
  else if (matrix[i][j] == "┫") {
   if (i-i2) { // 下向きに速度を持っていたら
    j--; // 左へ移動
   } else { // 横向きだったら
    i++; // 下へ移動
   }
  }
  else if(matrix[i][j] == "┣") {
   if (i-i2) { // 下向きに速度を持っていたら
    j++; // 右へ移動
   } else { // 横向きだったら
    i++; // 下へ移動
   }
  }
  else { // ┃であった場合
   i++; // 下へ移動
  }
 }

 // 最終段に達していないとき(道中の時)
 if (!(i==matrix.length)) {
  if (step == 0) { // 開始箇所は黄色にする
   document.getElementById("top_num" + (j/2)).style.color = "#ff0";
   document.getElementById("amida" + i + "l" + j).style.color = "#ff0";
  } else { // 通常時は赤色でトレース
   document.getElementById("amida" + i + "l" + j).style.color = "#f00";
  }

  // 50ms後に再度実行
  step++;
  setTimeout(function(){ traceAmida(matrix, i, pre_i, j, pre_j, step) }, 50);
 }
 // 最終段の場合
 else {
  document.getElementById("bottom_num" + (j/2)).style.color = "#00f";
 }
}

前の位置を記憶して,現在位置と前の位置から,現在左に進んでいるか,右に進んでいるか,それとも下に進んでいるかを判断して,妥当な方向にiとjを進める,ということをしています.

通しで実行

// アミダくじを実行
function amida(num, len){
 var amida = makeAmida(num, len);
 writeAmida(amida);
 traceAmida(amida,0,0,0,0,0);
}

// 人数を求めるプロンプトを出し,アミダを実行
function askNum() {
 var num = prompt("人数を入力してください.決定するとスタートします.","5");
 amida(num, 30)
}

askNumを実行すれば動作します.

おわりに

シンプルに書くにはどうすればいいんですかね?個人的に速度で調べる部分がちょっと微妙な感じがするのですが,巧い実装方法があったら教えてください.