(C) Copyright by IOI日本委員会 1994 All rights reserved.


問題1 解答・解説

 以下に示すプログラムは解答の一例である。空欄1と空欄2を併せて、手続き (プロシージャ、関数) wall() を完成させたものを示した。
 空欄2には、一区間分の壁を描く手続きを書けばよい(空欄1には、変数 l と ll の宣言を書くが、BASIC では不要)。まず、 m の値(0〜3) により、次の壁の終点の座標 (xs[k], ys[k]) (BASIC では (xs(k), ys(k)))を計算する。これが switch文(SELECT CASE文、case文) の中身である。 m の値に上下左右どれを対応させてもかまわない。 座標 (xs[k], ys[k]) が現在書いている折れ線上にあったら何もせず ループを先に進める。これが for文の中身である。この中で goto文を使って いるのを嫌う人もあろうが、他の制御構造で書いても汚なさは大差ないと思う。 座標 (xs[k], ys[k]) が現在書いている折れ線以外の壁にぶつかって いたら、最後の壁を書いた後にこの手続き wall を終了する。そうでない時は、 手続き line(mline)で実際に一区間の壁を書きループを先に進める。 なお、line(mline)を呼び出す直前にループの終了条件を変数 ll に セーブしているが、line(mline)の中で map の内容が書き換えられる ので、これは必要である。
スタックがあふれない限り、この方法で作成された迷路には入口から出口に 抜ける通路は一つしかない。

BASIC の場合、本質的なことではないが、次の2点に注意したい。
 (1)問題文にある DECLARE文は、なくても差し支えないが、 プロシージャ名もこのように明示的に宣言しておくことを薦める。
 (2)シングルモジュールプログラムを仮定しているので、問題文では DEFINT文は冒頭に1つだけ書かれているだけである。この DEFINT文は なくてもよいが、その場合、一般に(浮動小数点演算用のコプロセッサー がない限り)実行速度は落ちる。整数を扱う場合は(4バイトで表現できない くらい大きい数を扱わないのであれば)、INTEGER型あるいは LONG型変数を 用いるべきである。
 以上の2点は問題2〜4についても同様である。


C プログラム

void wall(int x, int y)  /* 迷路の壁を作成する */
{
   int k,l,ll,m;
   int xs[StackSize],ys[StackSize]; /* 迷路の壁の角を記憶しておくスタック */

   if (map[x][y]=='*') return;
   for (xs[k=0]=x, ys[k]=y, m=rand()%4; ;) {
      LOOP_TOP:
      if (++k >= StackSize) return;
      if (rand()%10000 <= misc) m=rand()%4;
      switch (m) {
         case 0: xs[k]=xs[k-1]+wx; ys[k]=ys[k-1];    break;
         case 1: xs[k]=xs[k-1];    ys[k]=ys[k-1]+wy; break;
         case 2: xs[k]=xs[k-1]-wx; ys[k]=ys[k-1];    break;
         case 3: xs[k]=xs[k-1];    ys[k]=ys[k-1]-wy; break;
      }
      for (l=0; l < k; l++) if (xs[l]==xs[k] && ys[l]==ys[k]) goto LOOP_TOP;
      ll=(map[xs[k]][ys[k]]=='*');
      line(xs[k-1],ys[k-1],xs[k],ys[k]);
      if (ll) return;
   }
}


Quick BASICプログラム

SUB wall(x, y)  ' 迷路の壁を作成する
   CONST StackSize=1000
   DIM xs(StackSize),ys(StackSize) ' 迷路の壁の角を記憶しておくスタック

   IF map(x,y)=1 THEN EXIT SUB
   k=0: xs(k)=x: ys(k)=y: m=INT(RND*4)
   DO
      LOOPTOP:
      k=k+1
      IF k >= StackSize THEN EXIT SUB
      IF INT(RND*10000) <= misc THEN m=INT(RND*4)
      SELECT CASE m
         CASE 0: xs(k)=xs(k-1)+wx: ys(k)=ys(k-1)
         CASE 1: xs(k)=xs(k-1):    ys(k)=ys(k-1)+wy
         CASE 2: xs(k)=xs(k-1)-wx: ys(k)=ys(k-1)
         CASE 3: xs(k)=xs(k-1):    ys(k)=ys(k-1)-wy
      END SELECT
      FOR l=0 TO k-1
         IF xs(l)=xs(k) AND ys(l)=ys(k) THEN goto LOOPTOP
      NEXT l
      ll=(map(xs(k),ys(k))=1)
      CALL mline(xs(k-1),ys(k-1),xs(k),ys(k))
   LOOP UNTIL ll
END SUB


Turbo Pascalプログラム

procedure wall(x, y : integer);  { 迷路の壁を作成する }
   label LOOP_TOP;
   var k,l,m : integer;
       ll    : boolean;
       xs,ys : array[1..StackSize] of integer;
                      { 迷路の壁の角を記憶しておくスタック }
begin
   if map[x,y] <> '*' then begin
      k:=1; xs[1]:=x; ys[1]:=y; m:=random(4);
      repeat
         LOOP_TOP:
         k:=k+1;
         if k > StackSize then goto exit;
         if random(10000) <= misc then m:=random(4);
         case m of
            0: begin xs[k]:=xs[k-1]+wx; ys[k]:=ys[k-1];    end;
            1: begin xs[k]:=xs[k-1];    ys[k]:=ys[k-1]+wy; end;
            2: begin xs[k]:=xs[k-1]-wx; ys[k]:=ys[k-1];    end;
            3: begin xs[k]:=xs[k-1];    ys[k]:=ys[k-1]-wy; end;
         end;
         for l:=1 to k-1 do begin
            if (xs[l]=xs[k]) and (ys[l]=ys[k]) then goto LOOP_TOP;
         end;
         ll:=(map[xs[k],ys[k]]='*');
         line(xs[k-1],ys[k-1],xs[k],ys[k]);
      until ll;
   end;
end;