2016年7月19日火曜日

RWIで迷路を作る2



以下記事では、棒倒し法という方法を使って迷路を作りました。

RWIで迷路を作る
http://sas-boubi.blogspot.jp/2015/08/rwi.html


今回は穴掘り法という方法を使って迷路を描画してみます。
それと、もうちょっと見た目を迷路っぽく改良してみます。



迷路作成の初期値を設定

%let n    = 37;
%let init = 1234;

n    ・・・ 迷路のマスの数。(必ず奇数を設定する事)
init  ・・・ 迷路の壁と床を決めるために生成する乱数のシード値

初期値を変えるたびに異なる迷路を作ることが出来ます。



迷路の生成と描画

SAS9.4より前のバージョンでは、 format_cellメソッドの「style_attr」を「overrides」に置き換える。

※ SAS9.2および9.3ではRWIが評価版のため、ERRORやWARNINGが出ます。
また出力される迷路も9.4と多少異なります。あくまでこんな雰囲気の迷路が出来るという参考程度としてください。

ods noresults;
ods html path="★出力先のパスを指定" file="maze2.html";



*--- 穴掘り法による迷路の生成 ----------------------------------------------;
data _NULL_;


  * 乱数のシード ;
  call streaminit(&init);

  * 迷路のブロックを入れておく箱を用意 ;
  dcl hash hs(ordered:'yes');
  hs.definekey("x","y");
  hs.definedata("block");
  hs.definedone();

  * 始点候補のブロックを入れておく箱を用意 ;
  dcl hash st(ordered:'yes');
  st.definekey("no");
  st.definedata("x","y");
  st.definedone();


  * ブロックを作って箱に放り込む ;
  do y2=1 to &n;
      do x2=1 to &n;
   
         * 外側を道ブロック,それ以外壁ブロックを詰める(1:道ブロック 2:壁ブロック) ;
         if x2 in (1,&n) or y2 in (1,&n) then BLOCK=1;
         else BLOCK=2;

         * ブロックを放り込む ;
         hs.add(key:x2, key:y2, data:block);
       
         * 始点候補のブロックを放り込む ;
         if BLOCK=2 and mod(x2*y2,2)=1 then do;
            no+1;
            st.add(key:no, data:x2, data:y2);
         end;
      end;
  end;



  do i=1 by 1 while (1);

      * 始点候補更新 ;
      if i>1 then do;
          no=0;
          st.clear();
          do y2=1 to &n;
          do x2=1 to &n;
       
               * 始点候補のブロックを放り込む ;
               if x2^in (1,&n) and y2^in (1,&n) and mod(x2*y2,2)=1 then do;
                    hs.find(key:x2, key:y2);
                    if block=1 then do;
                        * 上下左右いずれか2マス先が壁ブロックだったら始点候補にする ;
                        block_num=0;
                        hs.find(key:x2-2, key:y2  ); block_num+(block=2);
                        hs.find(key:x2+2, key:y2  ); block_num+(block=2);
                        hs.find(key:x2,   key:y2-2); block_num+(block=2);
                        hs.find(key:x2,   key:y2+2); block_num+(block=2);
                        if block_num>0 then do;
                              no+1;
                              st.add(key:no,data:x2,data:y2);
                        end;
                    end;
               end;
       
          end;
          end;
      end;

      if st.num_items=0 then leave;


      * 始点をランダムに決めてブロックの位置を取得 ;
      no = ceil(rand('uniform') * st.num_items);
      st.find(key:no);
      x2=x; y2=y;

      * 進めなくなるまで道ブロックを詰めこんでいく ;
      do while (1);
   
          * 進めるか確認 ;
          block_num=0;
          hs.find(key:x2-2, key:y2  ); block_num+(block=2);
          hs.find(key:x2+2, key:y2  ); block_num+(block=2);
          hs.find(key:x2,   key:y2-2); block_num+(block=2);
          hs.find(key:x2,   key:y2+2); block_num+(block=2);
          if block_num=0 then leave;

          * 進行方向を決める ;
          RAND = ceil(rand('uniform')*4);


          * 進行方向の2マス先が壁なら掘る ;
          if RAND=1 then do; x_move=0;  y_move=-1; end;  * 上 ;
          if RAND=2 then do; x_move=0;  y_move=1;  end;  * 下 ;
          if RAND=3 then do; x_move=1;  y_move=0;  end;  * 右 ;
          if RAND=4 then do; x_move=-1; y_move=0;  end;  * 左 ;

          if i=1 then hs.replace(key:x2, key:y2, data:1) ;
          hs.find( key:x2+x_move*2 , key:y2+y_move*2);
          if block=1 then continue;

          hs.replace(key:x2+x_move  , key:y2+y_move  , data:1) ;
          hs.replace(key:x2+x_move*2, key:y2+y_move*2, data:1) ;

          * 2マス先に移動 ;
          x2=x2+x_move*2;
          y2=y2+y_move*2;

      end;
  end;



  * ブロックを箱から出して迷路組み立て(迷路の描画) ;
  length WID HEI $10.;
  dcl odsout ob();
  ob.table_start();

  do y=1 to &n;
      ob.row_start();

      do x=1 to &n;

         hs.find(key:x, key:y);
       
         wid="7mm";
         hei="7mm";
         if (x=2 and y=3) or (x=&n-1 and y=&n-2) then do;
            block=1;
         end;
         if mod(x,2)=0 then wid="1mm";
         if mod(y,2)=0 then hei="1mm";

         *** 壁と床を描画 ;
          if (x=3 and y=3) or (x=&n-2 and y=&n-2) then do;
             block=1;
          end;
          ob.format_cell(style_attr: " bordercolor="|| choosec(block,"white","black") ||
                                     " background=" || choosec(block,"white","black") ||
                                     " height="|| hei || " width=" || wid);
      end;

      ob.row_end();
  end;

  ob.table_end();

  stop;
run;


ods html close;
ods html;
ods results;



プログラム書いてる時は、楽しくていっきに書き上げたけど、長いですね。。

0 件のコメント:

コメントを投稿