/*
**********************************************************************
**
**  MiniDD1.cpp
**
**  Author:  Bary W Pollack
**
**  A simple interactive "Miniature Dungeons & Dragons" game
**
**  This program plays Mini D&D on a rectangular board.  A single
**  cell containing "Gold" is randomly placed on the board.
**  The player's job is to find the Gold by exploring the board,
**  one cell at a time, moving North, South, East, West.
**
**  Modified:  05/01/99  -  created
**
**********************************************************************
*/

#include <iostream.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>       // for seeding the random number generator

//  Random() and Randomize() have been "borrowed" from stdlib.h
//  since some C++ IDE's don't contain one or the other routine.
//
//      Random(num) returns a random int in the range:  0 - (num-1)
//      Randomize seeds the pseudo-random number generator

#define Random(num) (int)(((long)rand()*(num))/(RAND_MAX+1))

void Randomize (void) { srand ((unsigned) time (NULL)); }

/*
**********************************************************************
**  Global declarations: constants and types
**********************************************************************
*/

#ifndef WIN32                       // true for MSVC; false for Borland
enum bool { false, true };          // setup the Boolean type
#endif

const int BRD_HEIGHT = 4;           // height of the board
const int BRD_WIDTH  = 5;           // width of the board

enum CELL                           // contents of the board
{                                   //   (for display purposes)
    EMPTY   = '_',                  // empty cell - unexplored
    PASSED  = 'X',                  // passed thru this cell
    ME      = '?'                   // indicates your current position
};

struct Coord
{
    int  y;                         // y coordinate 1..BRD_HEIGHT
    int  x;                         // x coordinate 1..BRD_WIDTH
};

/*
**********************************************************************
**  Class declarations for Gold
**********************************************************************
**  Gold is a cell that is placed on the board in a fixed random 
**  location when the constructor is called.
**
**  IsAt is the Gold's way to tell you if it is located at (y,x).
**
**  Display is a routine used during development that allows you to
**  see the (private) Gold data.
**********************************************************************
*/

class Gold
{
  public:
    Gold (void);
    bool IsAt (const Coord aCoord) const;
    void Display (void) const;

  private:
    Coord   goldCoords;
};

/*
**********************************************************************
**  Class declarations for MiniDDGame
**********************************************************************
**  MiniDDGame is a game played on a rectangular board that contains
**  a hidden pot of Gold.  The player must find the Gold.
**
**  Play currently plays a single game.  Play is responsible for 
**  acquiring movement instructions from the user; updating the board,
**  tracking the player's path; etc.
**
**  DisplayState is used by Play to display the current board state.
**********************************************************************
*/

class MiniDDGame
{
  public:
    MiniDDGame (void);
    void Play (void);
    void EnterMove (const Coord aCoord);
    void DisplayState (const char *sMsg) const;

  private:
    bool    ValidMove (const Coord aCoord) const;

    CELL    board[BRD_HEIGHT+1][BRD_WIDTH+1];
    Gold    theGold;
    Coord   myCoords;
};

/*
**********************************************************************
**  Implementation of class Gold
**********************************************************************
**  Constructor - randomly places the Gold on the board.
**                The Gold's position is HIDDEN from the game)
**********************************************************************
*/

Gold::Gold (void)
{
    goldCoords.y = Random (BRD_HEIGHT) + 1;
    goldCoords.x = Random (BRD_WIDTH)  + 1;
}

/*
**********************************************************************
**  IsAt - returns true if (y,x) is where the Gold is located
**********************************************************************
*/

bool Gold::IsAt (const Coord aCoord) const
{
    bool bIsAt = (aCoord.y == goldCoords.y && aCoord.x == goldCoords.x);

    return bIsAt;
}

/*
**********************************************************************
**  This debug routine was used during development so that we can see
**  where the Gold actually is located.
**********************************************************************
*/

void Gold::Display (void) const
{
    cout << "Gold: (" << goldCoords.y << "," << goldCoords.x << ")" << endl;
}

/*
**********************************************************************
**  Implementation of class MiniDDGame
**********************************************************************
**  The constructor sets up the game.  This includes initialization
**  of the entire board as well as other game-related data such as 
**  the player's initial location, etc.
**********************************************************************
*/

MiniDDGame::MiniDDGame (void)
{
    for (int j = 1; j <= BRD_HEIGHT; j++)
        for (int i = 1; i <= BRD_WIDTH; i++)
            board [j][i] = EMPTY;

    myCoords.y = myCoords.x = 1;
    EnterMove (myCoords);
}

/*
**********************************************************************
**  Play implements one "game:"  repeatedly asking for movement 
**  instructions, updating the board, tracking the player's path, 
**  redisplaying the board, etc., until the Gold is discovered.
**
**  Coordinate convention:  (y x), with y increasing downwards and
**                                      x increasing to the right
**
**                             x -->
**
**                         1 2 3 4 5 6 7
**                      1  X X . . . . .
**                  y   2  . X . . . . .
**                  |   3  . X X X ? * .
**                  V   4  . . . . . . .
**
**  Note:  the 0-row and 0-column of the board (array) are "ignored"
**         so that we can use "natural" values for indices.
**
**********************************************************************
*/

void MiniDDGame::Play (void)
{
    bool bAborted = false;

    while (! bAborted && ! theGold.IsAt (myCoords))
    {
        DisplayState ("The current situation:");

        // There is a reasonable argument to make the acquisition of the
        // user's input process into a separate routine.  And, in general,
        // you probably should.  But, in this program there's so little to
        // do that there isn't much of an advantage to do so.  So I didn't.

        bool  bMoveIsOK = false;
        do
        {
            Coord aCoord = myCoords;

            char  cDirection;
            cout << "Direction to move (N,S,E,W): ";
            cin >> cDirection;

            switch (toupper (cDirection))
            {
                case '.':   // fall thru to 'Q'
                case 'Q':   bAborted = bMoveIsOK = true;    break;
                case 'N':   --aCoord.y;                     break;
                case 'S':   ++aCoord.y;                     break;
                case 'E':   ++aCoord.x;                     break;
                case 'W':   --aCoord.x;                     break;
                default:    aCoord.y = 0;                   break;
            }
            
            if (ValidMove (aCoord))
            {
                bMoveIsOK = true;
                EnterMove (aCoord);
            }
            else
                cout << endl << "Invalid move" << endl << endl;

        } while (! bMoveIsOK);

    }

    if (! bAborted)
        DisplayState ("You found the GOLD!");
}

/*
**********************************************************************
**  Enters the current move; updates myCoords
**********************************************************************
*/

void MiniDDGame::EnterMove (const Coord aCoord)
{
    board [myCoords.y][myCoords.x] = PASSED;
    myCoords = aCoord;
    board [aCoord.y][aCoord.x] = ME;
}

/*
**********************************************************************
**  Display the state of the game as a 2-D tableau
**********************************************************************
*/

void MiniDDGame::DisplayState (const char *sMsg) const
{
    int  j, i;

    cout << endl << endl << sMsg << endl << endl << ' ';

    for (i = 1; i <= BRD_WIDTH; i++)
        cout << ' ' << i;
    cout << endl;

    for (j = 1; j <= BRD_HEIGHT; j++)
    {
        cout << j << ' ' << char (board [j][1]);
        for (i = 2; i <= BRD_WIDTH; i++)
            cout << '|' << char (board [j][i]);
        cout << endl;
    }

    // theGold.Display ();          //FOO - for development only...
    cout << endl << endl;
}

/*
**********************************************************************
**  ValidMove is true if the move is legal; it is false otherwise
**********************************************************************
*/

bool MiniDDGame::ValidMove (const Coord aCoord) const
{
    CELL myLocation = board [aCoord.y][aCoord.x];

    bool bValidMove = (1 <= aCoord.y && aCoord.y <= BRD_HEIGHT)
                   && (1 <= aCoord.x && aCoord.x <= BRD_WIDTH)
                   && (myLocation == EMPTY  ||
                       myLocation == PASSED ||
                       myLocation == ME);

    return bValidMove;
}

/*
**********************************************************************
**  The main routine plays multiple games of Gold.
**********************************************************************
*/

int main (void)
{
    Randomize ();

    cout << "* * M I N I - D U N G E O N S - A N D - D R A G O N S * *" << endl;

    char ch;
    do              // play one game
    {
        MiniDDGame game;
        game.Play ();

        do          // ask user about playing again;
        {           // again, this could be a separate routine...
            cout << "Game Over!" << endl << endl
                 << "Would you like to play again (y/n)? ";
            cin >> ch;
            ch = char (tolower (ch));
        } while (ch != 'y' && ch != 'n');

    } while (ch == 'y');

    cout << endl << "Thanks for playing..." << endl << endl;

    return 0;
}

/*
**********************************************************************
**  The output...
**********************************************************************

* * M I N I - D U N G E O N S - A N D - D R A G O N S * *

The current situation:

  1 2 3 4 5
1 ?|_|_|_|_
2 _|_|_|_|_
3 _|_|_|_|_
4 _|_|_|_|_

Direction to move (N,S,E,W): e

The current situation:

  1 2 3 4 5
1 X|?|_|_|_
2 _|_|_|_|_
3 _|_|_|_|_
4 _|_|_|_|_

Direction to move (N,S,E,W): e

The current situation:

  1 2 3 4 5
1 X|X|?|_|_
2 _|_|_|_|_
3 _|_|_|_|_
4 _|_|_|_|_

Direction to move (N,S,E,W): e

The current situation:

  1 2 3 4 5
1 X|X|X|?|_
2 _|_|_|_|_
3 _|_|_|_|_
4 _|_|_|_|_

Direction to move (N,S,E,W): e

The current situation:

  1 2 3 4 5
1 X|X|X|X|?
2 _|_|_|_|_
3 _|_|_|_|_
4 _|_|_|_|_

Direction to move (N,S,E,W): s

You found the GOLD!

  1 2 3 4 5
1 X|X|X|X|X
2 _|_|_|_|?
3 _|_|_|_|_
4 _|_|_|_|_

Game Over!

Would you like to play again (y/n)? y

The current situation:

  1 2 3 4 5
1 ?|_|_|_|_
2 _|_|_|_|_
3 _|_|_|_|_
4 _|_|_|_|_

Direction to move (N,S,E,W): s

The current situation:

  1 2 3 4 5
1 X|_|_|_|_
2 ?|_|_|_|_
3 _|_|_|_|_
4 _|_|_|_|_

Direction to move (N,S,E,W): s

The current situation:

  1 2 3 4 5
1 X|_|_|_|_
2 X|_|_|_|_
3 ?|_|_|_|_
4 _|_|_|_|_

Direction to move (N,S,E,W): s

The current situation:

  1 2 3 4 5
1 X|_|_|_|_
2 X|_|_|_|_
3 X|_|_|_|_
4 ?|_|_|_|_

Direction to move (N,S,E,W): e

The current situation:

  1 2 3 4 5
1 X|_|_|_|_
2 X|_|_|_|_
3 X|_|_|_|_
4 X|?|_|_|_

Direction to move (N,S,E,W): e

The current situation:

  1 2 3 4 5
1 X|_|_|_|_
2 X|_|_|_|_
3 X|_|_|_|_
4 X|X|?|_|_

Direction to move (N,S,E,W): e

The current situation:

  1 2 3 4 5
1 X|_|_|_|_
2 X|_|_|_|_
3 X|_|_|_|_
4 X|X|X|?|_

Direction to move (N,S,E,W): e

You found the GOLD!

  1 2 3 4 5
1 X|_|_|_|_
2 X|_|_|_|_
3 X|_|_|_|_
4 X|X|X|X|?

Game Over!

Would you like to play again (y/n)? n

Thanks for playing...

******/
