/**********************************
    3Dimensional maze
	1991.04.31- 05.04
	 programming  H.Asano
***********************************/

#include <graph.h>
#include <time.h>

#define locate(x,y)   printf("\x1b[%d;%dH",(x)+1,(y)+1)
#define line(x1,y1,x2,y2)  { _moveto((x1),(y1)); _lineto((x2),(y2)); }

#define MX    21
#define MY    37
#define KABE   0         /* 壁   */
#define MICHI  1         /* 道   */
#define EXIT   2         /* 出口 */
#define HINTMAX 6        /* 最大ヒント回数 */
#define ON     1
#define OFF    0
#define REV   "\x1b[7m"
#define RES   "\x1b[m"

static int map[MX][MY]; /* 迷路の配置を記憶 */

static int houkou     ; /* 方向  0:上  1:右  2:下  3:左 */

			/*上 右 下 左  */
static int dx[4]      = {-1, 0, 1, 0};   /* 迷路のx方向変位 */
static int dy[4]      = { 0, 1, 0,-1};   /* 迷路のy方向変位 */
static int dlrx[2][4] = { 0,-1, 0, 1,    /* 左隣のx座標変位 */
			  0, 1, 0,-1};   /* 右隣のx座標変位 */
static int dlry[2][4] = {-1, 0, 1, 0,    /* 左隣のy座標変位 */
			  1, 0,-1, 0};   /* 右隣のy座標変位 */

static int nowx,nowy;   /* 現在の位置 */
static char c;          /* 入力キーコード */
static int prtsu = 0;   /* ヒントの出力回数 */
static int goalflg  ;
static time_t starttime;
static time_t keikatime;
static time_t now;
static int mm,ss;

static int fwd1x,fwd1y,fwd2x,fwd2y,fwd3x,fwd3y,
	   l0x,l0y,l1x,l1y,l2x,l2y,
	   r0x,r0y,r1x,r1y,r2x,r2y ;

void main(void)
{
  void init(void);
  void graph(void);
  void prt_maze(void);
  void term(void);

     init();
     
     graph();
     while(map[nowx][nowy] != EXIT) { /* 出口 まで */
       c = getch();
       switch(c) {
	 case 0x1b :    /* ESCキーが押されたら処理中断 */
	   _setvideomode(_DEFAULTMODE);
	   printf(RES);
	   exit(0);
	 case 0x1D :    /* ←  : 左を向く */
	   houkou = (houkou+3)%4;
	   break;
	 case 0x1C :    /* →  : 右を向く */
	   houkou = (houkou+1)%4;
	   break;
	 case 0x0d :    /* 実行キー */
	   if(!map[nowx+dx[houkou]][nowy+dy[houkou]]) {  /* 壁で進めない */
	     printf("\a"); /* beep音 */
	     break;
	   }
	   nowx += dx[houkou];    /* 前進 */
	   nowy += dy[houkou];
	   break;
	 case 'h' :    /* h,Hキー (Help) : 地図を出力 */
	 case 'H' :
	   prt_maze();
           break;
         default :
           continue;
       }
       graph();  
     }

     term();
}

void init(void)
{
  void make_maze(int,int);
  int  get_rand(int);

     _setvideomode(_FMRESSCOLOR); /* ｸﾞﾗﾌｨｯｸ & TEXT ﾓｰﾄﾞ */
     _setvieworg(50,50);          /* 原点を移動          */
     _displaycursor(_GCURSOROFF); /* カーソル OFF        */
     srand(time(&now));           /* 乱数列 初期化       */
     make_maze(1,1);              /* 迷路の作成          */
     map[MX-1][MY-2] = EXIT;      /* 出口                */
     do {                         /* 最初の位置          */
       nowx = get_rand(MX-1);
       nowy = get_rand(MY-1);
     } while(!map[nowx][nowy]) ;
     houkou    = get_rand(4);     /* 最初の方向          */
     starttime = time(&now);      /* 始まりの時間を記録  */

}

void make_maze(int x,int y)
{

  int i;
  int xx,yy;
  int sum=0;
  int work[] = {0,0,0,0};

     for(;;) {
       work[i=get_rand(4)]++;
       xx = x+dx[i]*2;
       yy = y+dy[i]*2;
       if((0<=xx) && (xx<=MX-1) && (0<=yy) && (yy<=MY-1) && (!map[xx][yy]))
	  break ;        /* 迷路内で道が出来ていない場所ならOK */
       else              /* 行けない場所だったらワークに記憶   */
	 if((work[i]==1) && (++sum==4)) return; /* 行き止まりならリターン */
     }
     map[x][y] = MICHI;
     map[x+dx[i]][y+dy[i]] = MICHI;
     map[xx][yy] = MICHI;

     if((xx != MX-2) || (yy != MY-2))  make_maze(xx,yy);
     make_maze(x,y);
     return;
}

void prt_maze()
{
  int x,y;
  long w;

     if(prtsu >= HINTMAX) return; /* 最大ヒント回数以上はリターン */
     prtsu++;
     _clearscreen(_GCLEARGRAPH);  /* グラフィックのクリア */
     locate(3,0);
     for(x=0;x<MX;x++) {
       for(y=0;y<MY;y++) {
	 if(x==nowx && y==nowy) {
	   printf(REV);
	   switch(houkou) {
	     case  0 :
	       printf("\x1b[36m↑\x1b[m");
	       break;
	     case  1 :
	       printf("\x1b[36m→\x1b[m");
	       break;
	     case  2 :
	       printf("\x1b[36m↓\x1b[m");
	       break;
	     case 3 :
	       printf("\x1b[36m←\x1b[m");
	       break;
	   }
	 }
	 else if(!map[x][y]) {  /* 壁 */
	   printf(REV);
	   printf("  ");
	 }
	 else {                 /* 道,出口 */
	   printf(RES);
	   printf("  ");
	 }
       }
       printf(RES);
       printf("\n");
     }
     for(w=0;w<400000;w++) x=y; /* 時間かせぎ */
     _clearscreen(_GCLEARSCREEN);
}

void term(void)
{
     _setvideomode(_DEFAULTMODE);
     keikatime = time(&now) - starttime;
     mm = keikatime / 60;
     ss = keikatime % 60;
     printf(RES);
     locate(2,5);
     printf("おめでとうございます｡\n");
     printf("      通過時間は %d分%d秒 でした。\n\n",mm,ss);
}

int get_rand(int max)   /* max未満の乱数を返します */
{
  int temp;

     do {
       temp = rand() / (32767/max);
     } while(temp >= max);
     return(temp);
}

void graph(void)
{
  void graphk0(void),graphk1(void),graphk2(void),
       graphm0(void),graphm1(void),graphm2(void),
       clear(void)  ;

     if(goalflg) {  /* 画面にゴール印が表示されていたらクリア */
       clear();
       goalflg=OFF;
     }
     _clearscreen(_GCLEARGRAPH);
     locate(0,5);
     printf(" ** 3 Dimensional Maze  [V1.0] **   Copyright (c) 1991 H.Asano\n");
     keikatime   = time(&now) - starttime;
     mm = keikatime / 60;
     ss = keikatime % 60;
     printf("\n         経過時間 = %2d分%2d秒",mm,ss);
     printf("    残りヒント回数 = %d\n" ,HINTMAX-prtsu);
     _rectangle(_GBORDER,0,0,500,300);    /* 枠を描く     */

     fwd1x = nowx  + dx[houkou];
     fwd1y = nowy  + dy[houkou];
     fwd2x = fwd1x + dx[houkou];
     fwd2y = fwd1y + dy[houkou];
     fwd3x = fwd2x + dx[houkou];
     fwd3y = fwd2y + dy[houkou];
     l0x   = nowx  + dlrx[0][houkou];
     l0y   = nowy  + dlry[0][houkou];
     r0x   = nowx  + dlrx[1][houkou];
     r0y   = nowy  + dlry[1][houkou];
     l1x   = fwd1x + dlrx[0][houkou];
     l1y   = fwd1y + dlry[0][houkou];
     r1x   = fwd1x + dlrx[1][houkou];
     r1y   = fwd1y + dlry[1][houkou];
     l2x   = fwd2x + dlrx[0][houkou];
     l2y   = fwd2y + dlry[0][houkou];
     r2x   = fwd2x + dlrx[1][houkou];
     r2y   = fwd2y + dlry[1][houkou];

     if((map[fwd1x][fwd1y]==KABE) || (map[fwd1x][fwd1y]==EXIT)) {
       graphk0();
     }
     else if((map[fwd2x][fwd2y]==KABE) || (map[fwd2x][fwd2y]==EXIT)) {
       graphk1();
       graphm0();
     }
     else if((map[fwd3x][fwd3y]==KABE) || (map[fwd3x][fwd3y]==EXIT)) {
       graphk2();
       graphm1();
       graphm0();
     }
     else {
       graphm2();
       graphm1();
       graphm0();
    }
}

void graphk0(void)
{
     line( 60, 36,440, 36);
     line( 60,264,440,264);

     if(map[l0x][l0y]) {     /* 左側が道 */
       line(  0, 36, 60, 36);
       line(  0,264, 60,264);
     }
     else {                  /* 左側が壁 */
       line(  0,  0, 60, 36);
       line( 60, 36, 60,264);
       line( 60,264,  0,300);
     }
     if(map[r0x][r0y]) {     /* 右側が道 */
       line(440, 36,500, 36);
       line(440,264,500,264);
     }
     else {                  /* 右側が壁 */
       line(500,  0,440, 36);
       line(440, 36,440,264);
       line(440,264,500,300);
     }
     if(map[fwd1x][fwd1y]==EXIT) {
       goalflg = ON ;
       printf("\x1b[7m");
       locate( 9,25); printf("                         ");
       locate(10,25); printf("                         ");
       locate(11,25); printf("                         ");
       locate(12,25); printf("         G O A L         ");
       locate(13,25); printf("                         ");
       locate(14,25); printf("                         ");
       locate(15,25); printf("                         \x1b[m");
     }
}

void graphm0(void)
{
      if(map[l0x][l0y]) {
	line(  0, 36, 60, 36);
	line( 60, 36, 60,264);
	line( 60,264,  0,264);
      }
      else {
	line(  0,  0, 60, 36);
	line( 60,264,  0,300);
      }
      if(map[r0x][r0y]) {
	line(500, 36,440, 36);
	line(440, 36,440,264);
	line(440,264,500,264);
      }
      else {
	line(500,  0,440, 36);
	line(440,264,500,300);
      }
}

void graphk1(void)
{
     line(150, 90,350, 90);
     line(150,214,350,214);

     if(map[l1x][l1y]) {
       line( 60, 90,150, 90);
       line( 60,214,150,214);
       line( 60, 36, 60,264);
     }
     else {
       line( 60, 36,150, 90);
       line(150, 90,150,214);
       line(150,214, 60,264);
     }
     if(map[r1x][r1y]) {
       line(440, 90,350, 90);
       line(440, 36,440,264);
       line(440,214,350,214);
     }
     else {
       line(440, 36,350, 90);
       line(350, 90,350,214);
       line(350,214,440,264);
     }
     if(map[fwd2x][fwd2y]==EXIT) {
       goalflg = ON;
       printf("\x1b[7m");
       locate(11,30); printf("               ");
       locate(12,30); printf("    G O A L    ");
       locate(13,30); printf("               \x1b[m");
     }
}

void graphm1(void)
{
     if(map[l1x][l1y]) {
       line( 60, 36, 60,264);
       line( 60, 90,150, 90);
       line(150, 90,150,214);
       line(150,214, 60,214);
     }
     else {
       line( 60, 36,150, 90);
       line( 60,264,150,214);
     }
     if(map[r1x][r1y]) {
       line(440, 36,440,264);
       line(440, 90,350, 90);
       line(350, 90,350,214);
       line(350,214,440,214);
     }
     else {
       line(440, 36,350, 90);
       line(440,264,350,214);
     }
}

void graphk2(void)
{
     line(210,126,290,126);
     line(210,176,290,176);

     if(map[l2x][l2y]) {
       line(150, 90,150,214);
       line(150,126,210,126);
       line(150,176,210,176);
     }
     else {
       line(150, 90,210,126);
       line(210,126,210,176);
       line(210,176,150,214);
     }
     if(map[r2x][r2y]) {
       line(350, 90,350,214);
       line(350,126,290,126);
       line(350,176,290,176);
     }
     else {
       line(350, 90,290,126);
       line(290,126,290,176);
       line(290,176,350,214);
     }
     if(map[fwd3x][fwd3y]==EXIT) {
       goalflg = ON;
       locate(12,34);
       printf("\x1b[7mG O A L\x1b[m");
     }
}

void graphm2(void)
{
     line(210,126,290,176);
     line(290,126,210,176);

     if(map[l2x][l2y]) {
       line(150, 90,150,214);
       line(150,126,210,126);
       line(210,126,210,176);
       line(210,176,150,176);
     }
     else {
       line(150, 90,210,126);
       line(210,176,150,214);
     }
     if(map[r2x][r2y]) {
       line(350, 90,350,214);
       line(350,126,290,126);
       line(290,126,290,176);
       line(290,176,350,176);
     }
     else {
       line(350, 90,290,126);
       line(290,176,350,214);
     }
}

void clear(void)
{
       printf(RES);
       locate( 9,25); printf("                         ");
       locate(10,25); printf("                         ");
       locate(11,25); printf("                         ");
       locate(12,25); printf("                         ");
       locate(13,25); printf("                         ");
       locate(14,25); printf("                         ");
       locate(15,25); printf("                         ");
}
