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


問題1

10点

 乱数で下の図のような迷路を描くプログラムを作りたい。 迷路は 79×23 の配列 map 内に以下のようなアルゴリズムで作る。 最初、左上と右下に入口と出口の穴を残して長方形の外枠を描く。 次に、乱数で長方形内の10点程度を選び、その点から他の壁にぶつかるまで ランダムな折れ線を描く(この作業は wall() で行なわれる)。 最後に迷路内の各点について可能ならば左上から右下に向かって順に、再び上の 手続き wall() を用いてランダムな折れ線で壁を埋めていく。
 以下の C, Quick BASIC, Pascal のプログラムリストは、このようにして 迷路を作成するプログラムであるが、手続き(関数)wall() の本質的な 部分が空欄になっている。 入口から出口への道があるような迷路を作成できる原理をよく理解した上で、 この空欄を埋めよ。 この空欄には上述のように、点 (x,y) からすでに描かれている壁にぶつかるまで ランダムに折れ線を描く手続きが入る。 ただし、折れ線を描いている途中で自分自身にぶつかってしまったら、 直前の一区間だけ壁を描かないで先に進むことにする。さもないと、 迷路内に到達できない領域ができてしまうからである。
 参考までに述べると、変数 m には空欄2の直前で0以上3以下の値が 与えられて、それぞれの値は次に描く1区間分の壁の方向(上下右左)を表す。

 入口
 *  ****************************************************************************
 *                       *     *        *        *                 *  *  *     *
 *******  ****************  *  ****  *  ****  *  *  ****************  *  ****  *
 *                       *  *  *     *  *  *  *                    *           *
 *******  *******************  *******  *  *  ****  **********************  *  *
 *           *           *  *                 *        *           *        *  *
 *  *  ****  *******  ****  ****  *******************  *  **********  *  *******
 *  *  *                 *        *  *  *     *                 *  *  *        *
 *  *******  *  **********  *******  *  ****  *  *  *******  *  *  *  *  *******
 *  *        *           *     *  *     *  *     *     *     *     *  *        *
 *  *  *  ****  *******  *  ****  ****  *  *  **********************  *  *  *  *
 *  *  *  *           *  *     *  *     *        *           *        *  *  *  *
 *  ****  *************  *  *  *  *  **********  ****  *  *  ****  ****  ****  *
 *  *     *     *     *  *  *           *        *     *  *  *        *  *     *
 ****  *  *  *  ****  *  *  *******  *************  *  ****  *  *******  *  *  *
 *  *  *  *  *        *  *  *                    *  *  *              *  *  *  *
 *  ****  *******  *  **********  *  ****************  *  *  *  *  ****  *  *  *
 *     *  *        *  *     *     *     *        *     *  *  *  *     *  *  *  *
 ****  *  *  ****  *  ****  *  *******  *  ****  *************************  *  *
 *           *  *  *  *     *  *     *        *              *     *        *  *
 *  ****  *  *  *  *  ****  *  ****  *******  *  *  *  ****  *  *  ****  *  *  *
 *     *  *  *     *                 *        *  *  *     *     *        *  *  *
 ****************************************************************************  *


C プログラム

#include < stdio.h >
#include < stdlib.h >
#include < time.h >
#include < math.h >

void wall(int, int);
void line(int, int, int, int);

char map[79][23];  /* 迷路: * が壁、スペースが通路 */
int wx,wy;         /* 横・縦の迷路の通路の幅 */
int misc;          /* 壁の角の個数を調整するための定数 */

void main()
{
   int x,y,p;
   int lx,ly;      /* 迷路全体の横・縦の大きさ */
   int nx,ny;      /* 迷路の横・縦のますの個数 */

   nx=26; ny=11; wx=78/nx; lx=wx*nx; wy=22/ny; ly=wy*ny;
   for (x=0; x <= lx; x++) for (y=0; y <= ly; y++) map[x][y]=' ';
   misc=10000/sqrt(sqrt((double)(nx+ny))); srand((unsigned int)time(NULL));

   line(wx,0,lx,0); line(lx,0,lx,ly); line(0,0,0,ly); line(0,ly,lx-wx,ly);
   for (p=0; p< 10; p++) wall(wx*(rand()%nx), wy*(rand()%ny));
   for (x=wx; x < lx; x+=wx) for (y=wy; y < ly; y+=wy) wall(x,y);
   for (y=0; y <= ly; y++) {
      for (x=0; x <= lx; x++) printf("%c",map[x][y]);
      printf("\n");
   }
   exit(0);
}

#define StackSize 1000

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

   空欄1 (下の空欄2内で必要な変数等があったらここで宣言せよ)

   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;

      空欄2 (10〜20行程度のプログラム)

   }
}

void line(int x1, int y1, int x2, int y2)  /* (x1,y1)と(x2,y2)を結ぶ壁を描く */
{
   int i,w;
   if (x1 > x2) {w=x1; x1=x2; x2=w;}
   if (y1 > y2) {w=y1; y1=y2; y2=w;}
   if (x1==x2) for (i=y1; i <= y2; i++) map[x1][i]='*';
   if (y1==y2) for (i=x1; i <= x2; i++) map[i][y1]='*';
   return;
}


Quick BASIC プログラム

DEFINT A-z
OPTION BASE 0
DECLARE SUB wall(x,y)
DECLARE SUB mline(x1,y1,x2,y2)
DIM SHARED map(78,22)  ' 迷路: 1が壁、0が通路
DIM SHARED wx,wy       ' 横・縦の迷路の通路の幅
DIM SHARED misc        ' 壁の角の個数を調整するための定数


   nx=26: ny=11        ' 迷路の横・縦のますの個数
   wx=78/nx: wy=22/ny
   lx=wx*nx: ly=wy*ny  ' 迷路全体の横・縦の大きさ
   FOR x=0 TO lx: FOR y=0 TO ly: map(x,y)=0: NEXT y: NEXT x
   misc=10000/SQR(SQR(nx+ny)): RANDOMIZE TIMER

   CALL mline(wx,0,lx,0): CALL mline(lx,0,lx,ly)
   CALL mline(0,0,0,ly):  CALL mline(0,ly,lx-wx,ly)
   FOR p=0 TO 9: CALL wall(wx*INT(RND*nx), wy*INT(RND*ny)): NEXT p
   x=wx
   WHILE x < lx
      y=wy
      WHILE y < ly
         CALL wall(x,y)
         y=y+wy
      WEND
      x=x+wx
   WEND
   FOR y=0 TO ly
      FOR x=0 TO lx
         IF map(x,y)=1 THEN PRINT "*"; ELSE PRINT " ";
      NEXT x
      PRINT
   NEXT y
END

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

   空欄1 (下の空欄2内で必要な変数等があったらここで宣言せよ)

   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)

      空欄2 (10〜20行程度のプログラム)

END SUB

SUB mline(x1,y1,x2,y2)   ' (x1,y1)と(x2,y2)を結ぶ壁を描く
   IF x1 > x2 THEN SWAP x1,x2
   IF y1 > y2 THEN SWAP y1,y2
   IF x1=x2 THEN FOR i=y1 TO y2: map(x1,i)=1: NEXT i
   IF y1=y2 THEN FOR i=x1 TO x2: map(i,y1)=1: NEXT i
END SUB


Turbo Pascal プログラム

program maze;
   const StackSize=1000;
   var   map : array[0..78,0..22] of char;  { 迷路: * が壁、スペースが通路 }
         wx,wy,                   { 横・縦の迷路の通路の幅 }
         misc : integer;          { 壁の角の個数を調整するための定数 }

   procedure line(x1,y1,x2,y2 : integer); { (x1,y1)と(x2,y2)を結ぶ壁を描く }
      var i,w : integer;
   begin
      if x1 > x2 then begin w:=x1; x1:=x2; x2:=w; end;
      if y1 > y2 then begin w:=y1; y1:=y2; y2:=w; end;
      if x1=x2 then begin for i:=y1 to y2 do map[x1,i]:='*'; end;
      if y1=y2 then begin for i:=x1 to x2 do map[i,y1]:='*'; end;
   end;

   procedure wall(x, y : integer);  { 迷路の壁を作成する }
      label LOOP_TOP;
      var k,m   : integer;
          xs,ys : array[1..StackSize] of integer;
                         { 迷路の壁の角を記憶しておくスタック }

      空欄1 (下の空欄2内で必要な変数等があったらここで宣言せよ)

   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 exit;
            if random(10000) <= misc then m:=random(4);

            空欄2 (10〜20行程度のプログラム)

      end;
   end;

   var x,y,p,
       lx,ly,            { 迷路全体の横・縦の大きさ }
       nx,ny : integer;  { 迷路の横・縦のますの個数 }

begin
   nx:=26; ny:=11; wx:=78 div nx; lx:=wx*nx; wy:=22 div ny; ly:=wy*ny;
   for x:=0 to lx do begin
      for y:=0 to ly do map[x,y]:=' ';
   end;
   misc:=trunc(10000.0/sqrt(sqrt(nx+ny))); randomize;

   line(wx,0,lx,0); line(lx,0,lx,ly); line(0,0,0,ly); line(0,ly,lx-wx,ly);
   for p:=0 to 9 do wall(wx*random(nx), wy*random(ny));
   x:=wx;
   while x< lx do begin
      y:=wy;
      while y < ly do begin wall(x,y); y:=y+wy; end;
      x:=x+wx;
   end;
   for y:=0 to ly do begin
      for x:=0 to lx do write(map[x,y]);
      writeln;
   end;
end.