Difficulty with C++

Status
Not open for further replies.
犬夜叉;692166 said:
Not that I like it much myself, mind you.

My class had a similar assignment. Instead of relying on Win32-only functions, we simply used a dummy getline() operation to make the next step happen upon issuing 'Enter'. This meant I could do the assignment in Linux, which I was able to do for all of my work except one where a binary input mode used a private Windows-only implementation! Functions prefixed with underscores are implementation-specifics and should not be used in non-abstracted code!

Keep in mind that C++ is not a Windows-only language. When you are looking to insert code that only works on one platform, you abstract it out so it isn't mixed in with the main code. That's just what you do. Not everyone uses Windows. Even if you don't plan on using any operating system besides Windows, at least it shows you care about people running on other OSes.

I'm still learning C++, and that program you are showing is just a pain for me. I do use printf for out, and not cout.

Don't. It's generally better to use iostream instead of cstdio unless you have a very specific reason not to use iostream. You get increased safety, automation, and C++-style error checking if you use iostream.

delete myvar;

If it's operating on an array, I'm pretty sure you need to use delete[], not delete. Otherwise you get a memory leak. Also, shouldn't

int[] myvar = new int[20];

Be this:

int *myvar = new int[20];
 
Last edited:
I must have misunderstood you guys. I added

Code:
delete[] maze;
to main(), and it caused an "assertion error". Clearly I'm out of my league here, and I'm sure I'm revealing my ignorance by saying that I attempted this, but still, I don't know how to make the program delete what it created when it's done.

Can you guys demonstrate the solution to my memory allocation problems? Here's the most recent version of my code:

Code:
#include <iostream>
#include <conio.h>
#include <Windows.h>
#define _WIN32_WINNT 0x0500
using namespace std;
enum Colors {blue=1, green, cyan, red, purple, yellow, grey, dgrey, hblue, hgreen, hred, hpurple, hyellow, hwhite};

//function prototypes
void coutc(int color, char*output);
void generate(int maze[20][20], int &i, int &j);
void initialize(int maze[20][20], int &i, int &j);
void display(int maze[20][20], int &i, int &j);
int step(int maze[20][20], int &i, int &j);

//PURPOSE: Initialize the program, call the other functions,
//and refresh the maze every tenth of a second.
int main()
{
    //specifies console dimensions to fit size of maze
    HWND console = GetConsoleWindow();
    RECT r;
    GetWindowRect(console, &r);
    MoveWindow(console, r.left, r.top, 800, 400, TRUE);

    int i, j, wait;        //counters
    int choice;            //user choice
    int maze[20][20];    //the matrix for the maze itself

    //startup message
    cout << "Welcome to Maze Navigator!" << endl;
    cout << endl;
    system("pause");

    //startup functions
    initialize(maze, i, j);
    display(maze, i, j);
    while (maze[0][10] != 2)
    {
        //one move through the maze
        step(maze, i, j);
        //delays the next move so that the individual moves can be seen
        for (wait = 0; wait < 1000000; wait++);
    }

    //displays upon maze completion
    system("cls");
    coutc(grey, "Maze Solved!");
    cout << endl;
    cout << "Do you want to run another maze? (1/0)" << endl;
    cin >> choice;

    //program exits or restarts depending on player's choice
    if (choice == 1)
    {
        system("cls");
        return main();
    }
    else
    {
        return 0;
    }
}

//PURPOSE: Output a value with a specified text color.
void coutc(int color, char* output)
{
   HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
   SetConsoleTextAttribute(handle, color);
   cout << output;
   SetConsoleTextAttribute(handle, color);
}

//PURPOSE: Generate a randomly structured maze.
void generate(int maze[20][20], int &i, int &j)
{
    int spaceFreq;
    system("cls");
    cout << "Enter a space frequency between 1 and 10: ";
    cin >> spaceFreq;
    //checks whether the chosen space frequency is valid
    if (spaceFreq < 1 || spaceFreq >10)
    {
        system("cls");
        cout << "Error: Invalid space frequency" << endl;
        system("pause");
        system("cls");
        main();
    }
    for (i = 0; i < 20; i++)
    {
        for (j = 0; j < 20; j++)
        {
            maze[i][j] = rand() & spaceFreq;
            if (maze[i][j] != 0 && maze[i][j] != 1)
            {
                maze[i][j] = 0;
            }
        }
    }
    //start point
    maze[19][9] = 2;
    //exit point
    maze[0][10] = 0;
}

//PURPOSE: Preliminarily fill in the maze with empty space before
//constructing it proper
void initialize(int maze[20][20], int &i, int &j)
{
    for(i = 0; i < 20; i++)
    {
        for(j = 0; j < 20; j++)
        {
            maze[i][j] = 0;
        }
        cout << endl;
    }
    generate(maze, i, j);
}

//PURPOSE: Display the maze on the console.
void display(int maze[20][20], int &i, int &j)
{
    system("cls");
    cout << endl << endl;;
    coutc(hgreen, "                                        $");
    cout << endl;
    coutc(green, "                  X X X X X X X X X X X   X X X X X X X X X X");
    cout << endl;
    for(i = 0; i < 20; i++)
    {
        coutc(green,"                  X");
        //specifies the characters corresponding to different components of the maze
        for(j = 0; j < 20; j++)
        {
            //empty space
            if (maze[i][j] == 0)
            {
                coutc(blue, "  ");
            }
            //wall
            else if (maze[i][j] == 1)
            {
                coutc(green, " X");
            }
            //dynamic character
            else if (maze[i][j] == 2)
            {
                coutc(hblue, " O");
            }
            //path used once
            else if (maze[i][j] == 3)
            {
                coutc(blue, " '");
            }
            //path used twice
            else if (maze[i][j] == 4)
            {
                coutc(blue, " *");
            }
            //this shouldn't ever be activated
            else
            {
                cout << "  " << maze[i][j];
            }
        }
        coutc(green," X");
        cout << endl;
    }
    coutc(green, "                  X X X X X X X X X X   X X X X X X X X X X X");
    cout << endl;
    coutc(cyan, "                                      @");
    cout << endl;
}

//PURPOSE: Make the AI move through the maze.
int step(int maze[20][20], int &i, int &j)
{
    int choice;
    for(i = 0; i < 20; i++)
    {
        for(j = 0; j < 20; j++)
        {
            if(maze[i][j] == 2)
            {
                //tries to go left
                if(maze[i][j - 1] == 0 && j != 0)
                {
                    maze[i][j - 1] = 2;
                    maze[i][j] = 3;
                }
                //tries to go up
                else if(maze[i - 1][j] == 0 && i != 0)
                {
                    maze[i - 1][j] = 2;
                    maze[i][j] = 3;
                }
                //tries to go right
                else if(maze[i][j + 1] == 0 && j != 19)
                {
                    while(maze[i][j + 1] == 0 && j != 19)
                    {
                        maze[i][j + 1] = 2;
                        maze[i][j] = 3;
                        display(maze, i, j);
                    }
                }
                //tries to go down
                else if(maze[i + 1][j] == 0 && i != 19)
                {
                    while(maze[i + 1][j] == 0 && i != 19)
                    {
                        maze[i + 1][j] = 2;
                        maze[i][j] = 3;
                        display(maze, i, j);
                    }
                }
                //checks for any paths it already crossed and reuses them if necessary
                else
                {
                    //tries to go left on a path it already crossed
                    if(maze[i][j - 1] == 3 && j != 0)
                    {
                        maze[i][j - 1] = 2;
                        maze[i][j] = 4;
                    }
                    //tries to go up on a path it already crossed
                    else if(maze[i - 1][j] == 3 && i != 0)
                    {
                        maze[i - 1][j] = 2;
                        maze[i][j] = 4;
                    }
                    //tries to go right on a path it already crossed
                    else if(maze[i][j + 1] == 3 && j != 19)
                    {
                        while(maze[i][j + 1] == 3 && j != 19)
                        {
                            maze[i][j + 1] = 2;
                            maze[i][j] = 4;
                            display(maze, i, j);
                        }
                    }
                    //tries to go down on a path it already crossed
                    else if(maze[i + 1][j] == 3 && i != 19)
                    {
                        while(maze[i + 1][j] == 3 && i != 19)
                        {
                            maze[i + 1][j] = 2;
                            maze[i][j] = 4;
                            display(maze, i, j);
                        }
                    }
                    //unable to move at all
                    else
                    {
                        system("cls");
                        coutc(grey, "Dynamic value is trapped!");
                        cout << endl;
                        cout << "Do you want to run another maze? (1/0)" << endl;
                        cin >> choice;
                        if (choice == 1)
                        {
                            system("cls");
                            return main();
                        }
                        else
                        {
                            return 0;
                        }
                    }
                }
            }
        }
    }
    display(maze, i, j);
    return 1;
}
 
You tried to dynamically delete an object that was not dynamically allocated using new.

int maze[20][20];

This was statically allocated. It will by automatically deleted once the program leaves its scope. Do not use the delete or delete[] keywords on it.

Also, you have way too many successive if/else statements. I would replace those with a switch/case statement at the very least.

Eventually, you should go the class-based route. Ideally you would make the maze into a class. This way you could simply issue a statement like Maze *myMaze = new Maze(20, 30); and then you'd get a randomly-generated maze of the specified dimensions, ready to use.

Finally, this is more proper style and documentation than anything, but documentation of functions should be done with three slashes ///. This automatically works in with documentation generation programs like Doxygen, and it has much better aesthetics than writing "PURPOSE" before every function.
 
You tried to dynamically delete an object that was not dynamically allocated using new.

int maze[20][20];

This was statically allocated. It will by automatically deleted once the program leaves its scope. Do not use the delete or delete[] keywords on it.
Answer me this, though: if the matrix is statically allocated and gets automatically deleted, then why the heck does the program keep using exactly the same maze every time I ask it to generate a new one randomly?

Also, you have way too many successive if/else statements. I would replace those with a switch/case statement at the very least.

Eventually, you should go the class-based route. Ideally you would make the maze into a class. This way you could simply issue a statement like Maze *myMaze = new Maze(20, 30); and then you'd get a randomly-generated maze of the specified dimensions, ready to use.
My professor implied that switch/case statements are rarely necessary; that's why I didn't use them. I'm so unaccustomed to them that it might be hard, but I agree it makes sense, so I'll try it.

Classes will be more of a problem, as I haven't been taught anything about classes yet. I'm well aware of how useful and powerful they are; I just lack the knowledge to create even the most basic class.

Finally, this is more proper style and documentation than anything, but documentation of functions should be done with three slashes ///. This automatically works in with documentation generation programs like Doxygen, and it has much better aesthetics than writing "PURPOSE" before every function.
The reason I did that was because I'm used to writing functions with PURPOSE, PRECONDITIONS, and POSTCONDITIONS as documentation, and I was too lazy to come up with something for the other two. I'll try your way, though.
 
Answer me this, though: if the matrix is statically allocated and gets automatically deleted, then why the heck does the program keep using exactly the same maze every time I ask it to generate a new one randomly?

This has nothing to do with memory management. It's using the same maze every time because you never initialized the random seed. Ideally, you would initialize the random seed to a value given by the system time:

Code:
#include <ctime>
#include <cstdlib>

void myInitFunction() {

    // Initializes the random number generator
    srand(time(0));
    // I'm not sure if this is needed. From my experience I've had to draw one
    //  random number for the new random seed to take effect.
    rand();

}

My professor implied that switch/case statements are rarely necessary; that's why I didn't use them. I'm so unaccustomed to them that it might be hard, but I agree it makes sense, so I'll try it.

I don't think he meant for you to use if/else statements in situations which actually call for use of switch/case. The purpose of the switch/case is to test the value of some variable against a number of possibilities, assuming that these are all integers. This is exactly what you are doing in display().

On a side note, I would avoid using magic numbers. It seems like you managed to do this successfully for the color names. Or, well, maybe you're using someone else's code. In any case, the maze grid values use magic numbers. You should probably fix that.

The reason I did that was because I'm used to writing functions with PURPOSE, PRECONDITIONS, and POSTCONDITIONS as documentation, and I was too lazy to come up with something for the other two. I'll try your way, though.

Your professor might want something different. If he doesn't care, I'd go with the triple-slashes, though that's just me. A lot of editors recognize the triple-slashes and color them appropriately in a different color than regular comments. If you want to include items such as "PURPOSE" or "PRECONDITIONS" I would at least make them lowercase and use hanging indentation. Better aesthetics. If you decide to use automatic documentation generators such as Doxygen, they may use their own syntax for the above. But I wouldn't use those if you're only going include one of them. Seems silly to me...

Edit: For multiple lines of documentation comments, use /** and **/ to mark the beginning and end of the documentation comments, respectively, rather than using /// on each and every line.
 
Last edited:
Don't. It's generally better to use iostream instead of cstdio unless you have a very specific reason not to use iostream. You get increased safety, automation, and C++-style error checking if you use iostream.

My very specific reason is that some platforms have poor C++ compilers and it auto-flushes the buffer... :)

If it's operating on an array, I'm pretty sure you need to use delete[], not delete. Otherwise you get a memory leak. Also, shouldn't

int[] myvar = new int[20];

Be this:

int *myvar = new int[20];

Probably. C++ syntax is lame. in C you'd just do:

int *myvar = malloc(sizeof(int)*20);
free(myvar);

No memorizing how many brackets to use or whatever.

Answer me this, though: if the matrix is statically allocated and gets automatically deleted, then why the heck does the program keep using exactly the same maze every time I ask it to generate a new one randomly?

Maybe your random seed is off. At the top of the function that fills in the maze, put:

srand(GetTickCount()); // Blitzzo's gonna kill me

Classes will be more of a problem, as I haven't been taught anything about classes yet.

I agree here -- learn about structs first and it'll make the jump to classes easier to understand. Not that you really even need classes, thanks to namespacing (Wizard 2 doesn't use classes at all).
 
Last edited:
My very specific reason is that some platforms have poor C++ compilers and it auto-flushes the buffer... :)

That's probably more an issue with the standard library implementation or the filesystem being used.

Probably. C++ syntax is lame.

Nah, the C++ syntax seems cleaner to me. Also, malloc() doesn't play nicely with exceptions, IIRC, if you decide to use those.
 
This has nothing to do with memory management. It's using the same maze every time because you never initialized the random seed. Ideally, you would initialize the random seed to a value given by the system time:

Code:
#include <ctime>
#include <cstdlib>

void myInitFunction() {

    // Initializes the random number generator
    srand(time(0));
    // I'm not sure if this is needed. From my experience I've had to draw one
    //  random number for the new random seed to take effect.
    rand();

}
So, all I have to do is run that before I run generate()? Or is there some more complicated thing involved?

I don't think he meant for you to use if/else statements in situations which actually call for use of switch/case. The purpose of the switch/case is to test the value of some variable against a number of possibilities, assuming that these are all integers. This is exactly what you are doing in display().
He doesn't really emphasize switch/case at all. But I think your way is probably better.

On a side note, I would avoid using magic numbers. It seems like you managed to do this successfully for the color names. Or, well, maybe you're using someone else's code. In any case, the maze grid values use magic numbers. You should probably fix that.
No, that part of the code was totally my own creation. I understand the problem with magic numbers, but I doubt it does harm in this case. I'll try an alternative set of numbers.
EDIT: Oh wait, the colors? No, that was someone else's code. Never mind.

Your professor might want something different. If he doesn't care, I'd go with the triple-slashes, though that's just me. A lot of editors recognize the triple-slashes and color them appropriately in a different color than regular comments. If you want to include items such as "PURPOSE" or "PRECONDITIONS" I would at least make them lowercase and use hanging indentation. Better aesthetics. If you decide to use automatic documentation generators such as Doxygen, they may use their own syntax for the above. But I wouldn't use those if you're only going include one of them. Seems silly to me...
FYI, my professor was the one who explicitly showed me the PURPOSE/PRECONDITIONS/POSTCONDITIONS format.

Edit: For multiple lines of documentation comments, use /** and **/ to mark the beginning and end of the documentation comments, respectively, rather than using /// on each and every line.
Why a double asterisk? One works fine. Or do you mean two asterisks for functions and one for parts within functions?

In any case, I knew that already, but I just don't see much point when there's only two or three consecutive lines. Much more and I would use block commenting.

---------- Post added 10-20-2010 at 12:09 AM ---------- Previous post was 10-19-2010 at 10:20 PM ----------

Oh hey, is there any way to adjust the probability of the outcomes for the rand() function? I want to make it so the user adjust that.

EDIT: Never mind, figured it out.
 
Last edited:
He doesn't really emphasize switch/case at all. But I think your way is probably better.

I've noticed this in my own experience with college CS professors. I think there's a wave of thinking that feels the switch() should go the way of the goto. It's all about harumphy-rumphy I'm an acadmic intellectual and envision a world of perfect theory. In reality, when you have an integer that you need to test against myriad possibilities, a switch() might even allow the compiler to optimize the code resulting in it running faster than a boatload of if/else if/else if/else if/else if/else if/else if/else if/else if/else if/else if/else if/else if/else if/else if/else if/

...and I still find good use for goto now and then...

FYI, my professor was the one who explicitly showed me the PURPOSE/PRECONDITIONS/POSTCONDITIONS format.

Has this guy ever held a real coding job? I think the purpose of having you use the PURPOSE/PRECONDITIONS/POSTCONDITIONS format is to help you understand what a function does - you start out with certain values (precondition), and the function performs some operations and leaves you with a result (postcondition). You shouldn't adopt this as a habit of doing your documentation, it's just to help you grasp the concepts.

Why a double asterisk? One works fine. Or do you mean two asterisks for functions and one for parts within functions?

The double asterisk and triple-slash are special sequences that documentation autogenerators look for. Personally, I use the John Carmack commenting style above my function headers:

//
// SetTime
//
// Given a date string, sets the current time on the PDA
//
void SetTime(char *datetime)

Some documentation generators will also pick this style up, but if you have yet to develop a habit, it's probably best to do what Blitzzo says.

And yes, just run the srand thing before your generate()
 
Last edited:
Funnily enough, I've run into several students who have coded with C++ for years prior to coming here and don't think too highly of my professor. From what I've heard, he is a brilliant guy who is involved in high-level conceptual programming research, but he tends to be decidedly out of touch with solid programming concepts--somewhat odd considering he is the chair of the whole computer science department. Incidentally, my professor has indeed done some programming jobs on the side; he was the founder of some organization (can't remember the name) that was dedicated to creating a standard, worldwide base of program functions that every programmer could draw from.

But I'm not complaining too much about it. He still knows a great deal more about C++ than I do, and as such I stand to learn plenty from him. Nevertheless, there are cases, like now, when I discover that something he taught me might not be the best way to go about things.

What is goto, by the way? I haven't been taught about that at all.
 
Last edited:
GOTO jumps execution to a predefined label. Doesn't do you much good in C, but you need loads of them to do complex tasks in languages like BASIC.
 
Nah, the C++ syntax seems cleaner to me. Also, malloc() doesn't play nicely with exceptions, IIRC, if you decide to use those.

Yes, because using '>>' makes perfect sense under normal programming methods...?
 
Yes, because using '>>' makes perfect sense under normal programming methods...?

You can do put(cout, "this or that"); instead. In the C++ standard IO library, the << operator is overloaded to mean the put function in the context of a stream, with the stream argument on the left, and the stuff to output on the right.
 
Yes, because using '>>' makes perfect sense under normal programming methods...?

I was referring to that particular example of dynamic array allocation, not C++ syntax in general. There are plenty of places where C++ has awful syntax. One example is templates.
 
Last edited:
Okay, here's an update. I've tweaked the program enormously since I last showed it to you. It's grown far more efficient, and the randomizer works properly. However, there are still some problems, and maybe some of you with more experience could work out solutions.

1. I want the AI to be able to travel over its old paths more than twice. That is, I want it to be able to keep retracing its steps as much as necessary, but when given the choice, I always want it to choose whatever path it hasn't tried yet (or whatever path it has tried the least number of times so far)
2. You guys mentioned earlier that I should avoid using magic numbers to represent the maze elements. Well, I can't think of a good way to restructure my program so that I don't do that.

I present to you two different versions of my program. If you want to help me, just use whichever program you like better. The difference between them is that in Version A, the code controlling the AI is split into nine different functions grouped under a class called "navigate", and in Version B, the code is all in a single function called "step".

Personally, I prefer Version B. I really only made Version A because I wanted to see if I was capable of using classes and objects to structure the program. Version A is about 50 or 60 lines of code longer than Version B, and as far as I know there is no difference in efficiency. But for all I know, Version A might be more conducive to any solutions you guys might have.

Version A:
Code:
#include <iostream>
#include <Windows.h>
#include <conio.h>
#include <cstdlib>
#define _WIN32_WINNT 0x0500
using namespace std;
enum Colors{blue=1, green, cyan, red, purple, yellow, grey, dgrey, hblue, hgreen, hred, hpurple, hyellow, hwhite};

//function prototypes
void coutc(int color, char*output);
void generate(int maze[20][20], int &i, int &j);
void display(int maze[20][20], int &i, int &j);
int restart();

//handles the maze AI
class navigate
{
    public:
        //master AI function
        void step(int maze[20][20], int &i, int &j);
        //movement functions
        void left(int maze[20][20], int &i, int &j);
        void up(int maze[20][20], int &i, int &j);
        void right(int maze[20][20], int &i, int &j);
        void down(int maze[20][20], int &i, int &j);
        //repeat movement functions
        void leftAgain(int maze[20][20], int &i, int &j);
        void upAgain(int maze[20][20], int &i, int &j);
        void rightAgain(int maze[20][20], int &i, int &j);
        void downAgain(int maze[20][20], int &i, int &j);
} go;    //calls movement functions from the above class

///initializes the program and calls the other functions
int main()
{
    //specifies console dimensions to fit the size of the maze
    HWND console = GetConsoleWindow();
    RECT r;
    GetWindowRect(console, &r);
    MoveWindow(console, r.left, r.top, 800, 400, TRUE);

    int i, j, wait;        //counters
    int maze[20][20];    //the matrix for the maze itself

    //initialization
    cout << "Welcome to Maze Navigator!" << endl << endl;
    system("pause");
    generate(maze, i, j);

    //loop to generate each frame of the maze
    while (maze[0][10] != 2)
    {
        //generates a frame
        go.step(maze, i, j);
        //pauses before generating the next frame
        for (wait = 0; wait < 20000000; wait++);
    }

    //displays upon maze completion
    system("cls");
    coutc(grey, "Maze Solved!");
    restart();
}

///outputs a value with a specified text color
void coutc(int color, char* output)
{
   HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
   SetConsoleTextAttribute(handle, color);
   cout << output;
   SetConsoleTextAttribute(handle, color);
}

///generates a randomly structured maze
void generate(int maze[20][20], int &i, int &j)
{
    int spaceFreq;
    srand(GetTickCount());
    system("cls");
    cout << "Enter a space frequency between 1 and 10: ";
    cin >> spaceFreq;
    spaceFreq++;
    //checks whether the chosen space frequency is valid
    if(spaceFreq < 2 || spaceFreq > 10)
    {
        system("cls");
        cout << "Error: Invalid space frequency!" << endl << endl;
        system("pause"); system("cls");
        main();
    }
    //builds a randomized maze based on the chosen space frequency
    for(i = 0; i < 20; i++)
    {
        for(j = 0; j < 20; j++)
        {
            maze[i][j] = rand() % spaceFreq + 1;
            if(maze[i][j] != 0 && maze[i][j] != 1)
            {
                maze[i][j] = 0;
            }
        }
    }
    maze[19][9] = 2;    //start point
    maze[0][10] = 0;    //exit point
}

///displays the maze on the console
void display(int maze[20][20], int &i, int &j)
{
    system("cls");
    cout << endl << endl;
    coutc(hgreen, "                                        $");
    cout << endl;
    coutc(green, "                  X X X X X X X X X X X   X X X X X X X X X X");
    cout << endl;
    for(i = 0; i < 20; i++)
    {
        coutc(green,"                  X");
        //specifies the characters corresponding to different components of the maze
        for(j = 0; j < 20; j++)
        {
            switch(maze[i][j])
            {
                case 0: coutc(blue, "  "); break;        //empty space
                case 1: coutc(green, " X"); break;        //solid wall
                case 2: coutc(hblue, " O"); break;        //navigator
                case 3: coutc(blue, " '"); break;        //path marker
                case 4: coutc(blue, " *"); break;        //repeated path marker
                default: cout << "  " << maze[i][j];    //default component
            }
        }
        coutc(green," X");
        cout << endl;
    }
    coutc(green, "                  X X X X X X X X X X   X X X X X X X X X X X");
    cout << endl;
    coutc(cyan, "                                      @");
    cout << endl;
}

///calls the functions to move the character through the maze
void navigate::step(int maze[20][20], int &i, int &j)
{
    display(maze, i, j);
    for(i = 0; i < 20; i++)
    {
        for(j = 0; j < 20; j++)
        {
            if(maze[i][j] == 2)
            {
                //tries to go left
                if(maze[i][j - 1] == 0 && j != 0)
                {
                    go.left(maze, i, j);
                }
                //tries to go up
                else if(maze[i - 1][j] == 0 && i != 0)
                {
                    go.up(maze, i, j);
                }
                //tries to go right
                else if(maze[i][j + 1] == 0 && j != 19)
                {
                    go.right(maze, i, j);
                }
                //tries to go down
                else if(maze[i + 1][j] == 0 && i != 19)
                {
                    go.down(maze, i, j);
                }
                //checks for any paths it already crossed and reuses them if necessary
                else
                {
                    //tries to go left on a path it already crossed
                    if(maze[i][j - 1] == 3 && j != 0)
                    {
                        go.leftAgain(maze, i, j);
                    }
                    //tries to go up on a path it already crossed
                    else if(maze[i - 1][j] == 3 && i != 0)
                    {
                        go.upAgain(maze, i, j);
                    }
                    //tries to go right on a path it already crossed
                    else if(maze[i][j + 1] == 3 && j != 19)
                    {
                        go.rightAgain(maze, i, j);
                    }
                    //tries to go down on a path it already crossed
                    else if(maze[i + 1][j] == 3 && i != 19)
                    {
                        go.downAgain(maze, i, j);
                    }
                    //stops the loop if the AI traps itself
                    else
                    {
                        system("cls");
                        coutc(grey, "The navigator is trapped!");
                        restart();
                        break;
                    }
                }
            }
        }
    }
}

///moves left
void navigate::left(int maze[20][20], int &i, int &j)
{
    while(maze[i][j - 1] == 0 && j != 0)
    {
        maze[i][j - 1] = 2;
        maze[i][j] = 3;
        display(maze, i, j);
    }
}

///moves up
void navigate::up(int maze[20][20], int &i, int &j)
{
    while(maze[i - 1][j] == 0 && i != 0)
    {
        maze[i - 1][j] = 2;
        maze[i][j] = 3;
        display(maze, i, j);
    }
}

///moves right
void navigate::right(int maze[20][20], int &i, int &j)
{
    while(maze[i][j + 1] == 0 && j != 19)
    {
        maze[i][j + 1] = 2;
        maze[i][j] = 3;
        display(maze, i, j);
    }
}

///moves down
void navigate::down(int maze[20][20], int &i, int &j)
{
    while(maze[i + 1][j] == 0 && i != 19)
    {
        maze[i + 1][j] = 2;
        maze[i][j] = 3;
        display(maze, i, j);
    }
}

///moves left onto a space it has already crossed
void navigate::leftAgain(int maze[20][20], int &i, int &j)
{
    while(maze[i][j - 1] == 3 && j != 0)
    {
        maze[i][j - 1] = 2;
        maze[i][j] = 4;
        display(maze, i, j);
    }
}

///moves up onto a space it has already crossed
void navigate::upAgain(int maze[20][20], int &i, int &j)
{
    while(maze[i - 1][j] == 3 && i != 0)
    {
        maze[i - 1][j] = 2;
        maze[i][j] = 4;
        display(maze, i, j);
    }
}

///moves right onto a space it has already crossed
void navigate::rightAgain(int maze[20][20], int &i, int &j)
{
    while(maze[i][j + 1] == 3 && j != 19)
    {
        maze[i][j + 1] = 2;
        maze[i][j] = 4;
        display(maze, i, j);
    }
}

///moves down onto a space it has already crossed
void navigate::downAgain(int maze[20][20], int &i, int &j)
{
    while(maze[i + 1][j] == 3 && i != 19)
    {
        maze[i + 1][j] = 2;
        maze[i][j] = 4;
        display(maze, i, j);
    }
}

///exits or restarts program depending on user input
int restart()
{
    int choice;
    cout << endl << "Do you want to run another maze? (1/0)" << endl;
    cin >> choice;
    switch(choice)
    {
        case 0: return 0; break;
        case 1: system("cls"); return main(); break;
        default: return 0;
    }
}
Version B:
Code:
#include <iostream>
#include <Windows.h>
#include <conio.h>
#include <cstdlib>
#define _WIN32_WINNT 0x0500
using namespace std;
enum Colors{blue=1, green, cyan, red, purple, yellow, grey, dgrey, hblue, hgreen, hred, hpurple, hyellow, hwhite};

//function prototypes
void coutc(int color, char*output);
void generate(int maze[20][20], int &i, int &j);
void display(int maze[20][20], int &i, int &j);
void step(int maze[20][20], int &i, int &j);
int restart();

///initializes the program and calls the other functions
int main()
{
    //specifies console dimensions to fit the size of the maze
    HWND console = GetConsoleWindow();
    RECT r;
    GetWindowRect(console, &r);
    MoveWindow(console, r.left, r.top, 800, 400, TRUE);

    int i, j, wait;        //counters
    int maze[20][20];    //the matrix for the maze itself

    //initialization
    cout << "Welcome to Maze Navigator!" << endl << endl;
    system("pause");
    generate(maze, i, j);

    //loop to generate each frame of the maze
    while (maze[0][10] != 2)
    {
        //generates a frame
        step(maze, i, j);
        //pauses before generating the next frame
        for (wait = 0; wait < 20000000; wait++);
    }

    //displays upon maze completion
    system("cls");
    coutc(grey, "Maze Solved!");
    restart();
}

///outputs a value with a specified text color
void coutc(int color, char* output)
{
   HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
   SetConsoleTextAttribute(handle, color);
   cout << output;
   SetConsoleTextAttribute(handle, color);
}

///generates a randomly structured maze
void generate(int maze[20][20], int &i, int &j)
{
    int spaceFreq;
    srand(GetTickCount());
    system("cls");
    cout << "Enter a space frequency between 1 and 10: ";
    cin >> spaceFreq;
    spaceFreq++;
    //checks whether the chosen space frequency is valid
    if(spaceFreq < 2 || spaceFreq > 10)
    {
        system("cls");
        cout << "Error: Invalid space frequency!" << endl << endl;
        system("pause"); system("cls");
        main();
    }
    //builds a randomized maze based on the chosen space frequency
    for(i = 0; i < 20; i++)
    {
        for(j = 0; j < 20; j++)
        {
            maze[i][j] = rand() % spaceFreq + 1;
            if(maze[i][j] != 0 && maze[i][j] != 1)
            {
                maze[i][j] = 0;
            }
        }
    }
    maze[19][9] = 2;    //start point
    maze[0][10] = 0;    //exit point
}

///displays the maze on the console
void display(int maze[20][20], int &i, int &j)
{
    system("cls");
    cout << endl << endl;
    coutc(hgreen, "                                        $");
    cout << endl;
    coutc(green, "                  X X X X X X X X X X X   X X X X X X X X X X");
    cout << endl;
    for(i = 0; i < 20; i++)
    {
        coutc(green,"                  X");
        //specifies the characters corresponding to different components of the maze
        for(j = 0; j < 20; j++)
        {
            switch(maze[i][j])
            {
                case 0: coutc(blue, "  "); break;        //empty space
                case 1: coutc(green, " X"); break;        //solid wall
                case 2: coutc(hblue, " O"); break;        //navigator
                case 3: coutc(blue, " '"); break;        //path marker
                case 4: coutc(blue, " *"); break;        //repeated path marker
                default: cout << "  " << maze[i][j];    //default component
            }
        }
        coutc(green," X");
        cout << endl;
    }
    coutc(green, "                  X X X X X X X X X X   X X X X X X X X X X X");
    cout << endl;
    coutc(cyan, "                                      @");
    cout << endl;
}

///AI to make the character move through the maze
void step(int maze[20][20], int &i, int &j)
{
    display(maze, i, j);
    for(i = 0; i < 20; i++)
    {
        for(j = 0; j < 20; j++)
        {
            if(maze[i][j] == 2)
            {
                //tries to go left
                if(maze[i][j - 1] == 0 && j != 0)
                {
                    while(maze[i][j - 1] == 0 && j != 0)
                    {
                        maze[i][j - 1] = 2;
                        maze[i][j] = 3;
                        display(maze, i, j);
                    }
                }
                //tries to go up
                else if(maze[i - 1][j] == 0 && i != 0)
                {
                    while(maze[i - 1][j] == 0 && i != 0)
                    {
                        maze[i - 1][j] = 2;
                        maze[i][j] = 3;
                        display(maze, i, j);
                    }
                }
                //tries to go right
                else if(maze[i][j + 1] == 0 && j != 19)
                {
                    while(maze[i][j + 1] == 0 && j != 19)
                    {
                        maze[i][j + 1] = 2;
                        maze[i][j] = 3;
                        display(maze, i, j);
                    }
                }
                //tries to go down
                else if(maze[i + 1][j] == 0 && i != 19)
                {
                    while(maze[i + 1][j] == 0 && i != 19)
                    {
                        maze[i + 1][j] = 2;
                        maze[i][j] = 3;
                        display(maze, i, j);
                    }
                }
                //checks for any paths it already crossed and reuses them if necessary
                else
                {
                    //tries to go left on a path it already crossed
                    if(maze[i][j - 1] == 3 && j != 0)
                    {
                        while(maze[i][j - 1] == 3 && j != 0)
                        {
                            maze[i][j - 1] = 2;
                            maze[i][j] = 4;
                            display(maze, i, j);
                        }
                    }
                    //tries to go up on a path it already crossed
                    else if(maze[i - 1][j] == 3 && i != 0)
                    {
                        while(maze[i - 1][j] == 3 && i != 0)
                        {
                            maze[i - 1][j] = 2;
                            maze[i][j] = 4;
                            display(maze, i, j);
                        }
                    }
                    //tries to go right on a path it already crossed
                    else if(maze[i][j + 1] == 3 && j != 19)
                    {
                        while(maze[i][j + 1] == 3 && j != 19)
                        {
                            maze[i][j + 1] = 2;
                            maze[i][j] = 4;
                            display(maze, i, j);
                        }
                    }
                    //tries to go down on a path it already crossed
                    else if(maze[i + 1][j] == 3 && i != 19)
                    {
                        while(maze[i + 1][j] == 3 && i != 19)
                        {
                            maze[i + 1][j] = 2;
                            maze[i][j] = 4;
                            display(maze, i, j);
                        }
                    }
                    //stops the loop if the AI traps itself
                    else
                    {
                        system("cls");
                        coutc(grey, "The navigator is trapped!");
                        restart();
                        break;
                    }
                }
            }
        }
    }
}

///program exits or restarts depending on user input
int restart()
{
    int choice;
    cout << endl << "Do you want to run another maze? (1/0)" << endl;
    cin >> choice;
    switch(choice)
    {
        case 0: return 0; break;
        case 1: system("cls"); return main(); break;
        default: return 0;
    }
}
 
Yeah, I don't think you've quite grasped the concept of classes yet.

You need to think of classes as objects. Nagivation isn't an object, it's an action. The point of the class should be to hold the maze as a private data member, so that you aren't passing in the maze every time you do an operation on it.

Speaking of passing data, you are passing the entire contents of the maze for every operation you make. This is very slow because it has to copy the entire data each time, rather than simply referencing the data. Also, I'm a bit amazed that the program is working as well as it is when you pass the array as a copy like that. Any operations you perform on the array will only happen inside the function, on the array copy that the function is working with, rather than the array you intend to work with.

Please, just pass by reference or pointer.
 
Don't arrays automatically pass by reference?

Also, I kind of thought that passing the entire contents of the maze each time was the only way for the program to figure out which values it has to modify. Plus, I view the slowness as a feature rather than a drawback, seeing as how the progress of the navigator would be hard to follow if the program did things quickly.

If you can show me a better way to code this, I'm eager to see it; that's the whole reason I brought this endeavor to your attention. I have but a small fraction of your understanding of C++, and I've already pushed my own skills to the limit.
 
Don't arrays automatically pass by reference?

Maybe in Python they do (actually, called lists in Python IIRC), but I'm unaware of C++ performing automatic passes by reference, unless it's something I don't know about.

Also, I kind of thought that passing the entire contents of the maze each time was the only way for the program to figure out which values it has to modify.

Passing the entire contents is not what you want, for several reasons. First of all, it's totally pointless in this instance, because you can access the array's contents by pointer or reference just as well as you could if you were to pass the entire thing in. Second of all, passing the entire thing doesn't do what you want anyway. It makes a copy of the array, so you're not actually doing operations on the original array, but rather, what got passed into the function. In order for the changes by the function to be reflected, you would have to use a return value. This is extremely slow and sloppy, and I highly discourage you from doing it. If you need to pass a 2D array, do it as a pointer to a pointer to an int (int **) since arrays are actually pointers in disguise. And since that datatype does not describe the size of the 2D array, you must also pass the width and height manually as ints.

I really would recommend you use classes, or at least structs, since this would greatly simplify things. You wouldn't need to pass the same five variables every single function call you make. You'd just need to pass a pointer to some struct that includes the maze, its dimensions, and position in the maze.

Actually, yes. I think structs are the recommended course of action here. The basics are pretty simple, and it would make your life easier. Well, if it's okay with your instructor, anyway.

Plus, I view the slowness as a feature rather than a drawback, seeing as how the progress of the navigator would be hard to follow if the program did things quickly.

Not an excuse to use inefficient methods. If it's throwing off the timing of your program, fix your timing code.
 
Status
Not open for further replies.

Who is viewing this thread (Total: 0, Members: 0, Guests: 0)

Back
Top