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

#define SoftName "Simple Maze"
#define Version  "0.9.8"
#define Build    "20030924"
#define Author   "MacMonster"
#define Company  "Monster Software, 1998-2003."

typedef short int sint;
typedef char *str;

void prchr(sint x, sint  y, char k);
void prstr(sint x, sint y, str s);
void prcstr(sint y, str s);
void Init();
void DrawMainScreen();
void MakeMaze();
void DrawMaze();
void DrawBlankArea(sint x, sint y, sint w, sint h);
void NumToStr(long n, str s);
void ShowResult();

sint   adjx, adjy;           // The adjustment position
sint   winw = 80, winh = 22; // The width and the height of the window
sint   q[80][22];
long   step;
time_t t1, t2;

int main()
{
    sint x, y;
    char key;

    Init();

    do
    {
        DrawMainScreen();

        do
        {
            key = getch();
            if (key == 27) { endwin(); exit(0); }
        } while (key != 10);

        MakeMaze();
        DrawMaze();
        x = 2; y = 20; step = 0; time(&t1);
        prchr(x, y, 'X');
        refresh();

        do
        {
            key = getch();
            prchr(x, y, ' ');

            if (key == 4 && q[x-1][y] >= 3) { x--; step++; }
            if (key == 5 && q[x+1][y] >= 3) { x++; step++; }
            if (key == 3 && q[x][y-1] >= 3) { y--; step++; }
            if (key == 2 && q[x][y+1] >= 3) { y++; step++; }

            prchr(x, y, 'X');
            refresh();
        } while (key != 10 && key != 27 && q[x][y] != 3);

        if (q[x][y] == 3) { time(&t2); ShowResult(); }

    } while (key != 27);

    endwin();
    exit(0);
}

void prchr(sint x, sint y, char k)
{
    move(y + adjy, x + adjx); delch(); insch(k);
}

void prstr(sint x, sint y, str s)
{
    sint i=0;
   
    while (s[i] != '\0') prchr(x+i, y, s[i++]);
}

void prcstr(sint y, str s)
{
    prstr((80-strlen(s))/2, y, s);
}

void Init()
{
    initscr(); cbreak(); noecho(); keypad(stdscr, 1); curs_set(0);
    srand((unsigned) time(NULL));
    getmaxyx(stdscr, adjy, adjx);
    adjx = (adjx - winw) / 2;
    adjy = (adjy - winh) / 2;
}

void DrawMainScreen()
{
    char temp[80]="";

    MakeMaze();
    DrawMaze();
    DrawBlankArea(20, 6, 40, 10);
    
    strcpy(temp, SoftName); strcat(temp, " Version ");
    strcat(temp, Version); prcstr(7, temp);
    
    strcpy(temp, "(Build: "); strcat(temp, Build);
    strcat(temp, ")"); prcstr(8, temp);
    
    strcpy(temp, "Press enter to start, ESC to exit."); prcstr(11, temp);
    strcpy(temp, "Written by "); strcat(temp, Author); prcstr(14, temp);

    refresh();     
}

void MakeMaze()
{
    sint i, j, m, n, r;

    for (j=0; j<22; j++) for (i=0; i<80; i++) q[i][j] = 0;
    for (i=0; i<80; i++) { q[i][0] = 1; q[i][21] = 1; }
    for (j=1; j<21; j++) { q[0][j] = 1; q[79][j] = 1; }
 
    i = 2; j = 20;
    q[i][j] = 4;

    do
    {
        m = i; n = j;
        r = rand() % 5;

        switch(r)
        {
            case 0: n++; break;
            case 1: m++; break;
            case 2: n--; break;
            case 3: m++; break;
            case 4: m++; break;
        }

        if (m < 1) m = 1; if (m > 78) m = 78;
        if (n < 1) n = 1; if (n > 20) n = 20;

        if (q[m][n] == 0)
        {
            r = 0;
            if (q[m][n-1] != 0) r++;
            if (q[m][n+1] != 0) r++;
            if (q[m-1][n] != 0) r++;
            if (q[m+1][n] != 0) r++;

            if (r < 2)
            {
                q[m][n] = 4;
                i = m; j = n;
            }
        }
    } while (m < 78);

    q[m][n] = 3;

    for (j=1; j<21; j++)
    {
        for (i=1; i<79; i++)
        {
            if (q[i][j] == 0)
            {
                r = rand() % 9;
                if (r == 1 || r == 3 || r == 5 || r == 7) q[i][j] = 2;
                else q[i][j] = 4;
            }
        }
    }
}

void DrawMaze()
{
    sint i, j;

    for (j=0; j<22; j++)
    {
        for (i=0; i<80; i++)
        {
            switch(q[i][j])
            {
                case 1: prchr(i, j, '#'); break;
                case 2: attron(A_STANDOUT); prchr(i, j, ' ');
                        attroff(A_STANDOUT); break;
                case 3: prchr(i, j, '$'); break;
                case 4: prchr(i, j, ' '); break;
            }
        }
    }

    refresh();
}

void DrawBlankArea(sint x, sint y, sint w, sint h)
{
    sint i, j;

    for (j=y; j<y+h; j++)
        for (i=x; i<x+w; i++)
            prchr(i, j, ' ');
}

void NumToStr(long n, str s)
{
    sint g=0, i;

    while ((int) (n / pow(10.0, g)) !=0) g++;
    
    for(i=0; i<g; i++)
        s[i] = ((int) (n / pow(10.0, g-i-1)) % 10) + 48;

    s[g] = '\0';
}

void ShowResult()
{
    char k, temp[10];

    DrawBlankArea(20, 6, 40, 10);
    prcstr(7, "[Game Result]");
    prstr(33, 9, "Step:"); NumToStr(step, temp); prstr(39, 9, temp);    
    prstr(33, 11, "Time:"); NumToStr(t2-t1, temp);
    strcat(temp, " seconds"); prstr(39, 11, temp);
    prcstr(14, "Press enter to clear ...");
    refresh();
    
    do { k = getch(); } while (k != 10); 
}
