6
\$\begingroup\$

The task was to find the shortest path between P and K in a maze. You can move horizontally and vertically, where # is a wall and . is free space. I've had a lot of trouble passing on the int k, which is the path length. I had to make a pair which contains a pair of x and y coordinates and int k.

Could I have done this any better without too much change in the code, especially with the pairs and the while loop? And how good is my code overall?

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int dfs(string g[30][30], pair<int, int> p)
{
    vector <pair<pair<int, int>, int> > v;
    int visited[30][30] = {0};
    int x, y, k = 0;
    pair <pair<int, int>, int> mj;
    pair <pair<int, int>, int> z;
    pair <int, int> l;
    z = make_pair(make_pair(p.first, p.second), 0);
    v.push_back(z);
    while(v.size() != 0)
    {
        mj = v.front();
        l = mj.first;
        x = l.first;
        y = l.second;
        k = mj.second;
        v.erase(v.begin());
        if(g[x][y] == "K")
            return k;
        if((g[x+1][y] == "." or g[x+1][y] == "K") and not(visited[x+1][y]))
        {
            v.push_back(make_pair(make_pair(x+1, y), k+1));
            visited[x+1][y] = 1;
        }
        if((g[x][y+1] == "." or g[x][y+1] == "K") and not(visited[x][y+1]))
        {
            v.push_back(make_pair(make_pair(x, y+1), k+1));
            visited[x][y+1] = 1;
        }
        if((g[x-1][y] == "." or g[x-1][y] == "K") and not(visited[x-1][y]))
        {
            v.push_back(make_pair(make_pair(x-1, y), k+1));
            visited[x-1][y] = 1;
        }
        if((g[x][y-1] == "." or g[x][y-1] == "K") and not(visited[x][y-1]))
        {
            v.push_back(make_pair(make_pair(x, y-1), k+1));
            visited[x][y-1] = 1;
        }
    }
}

int main()
{
    int n, m;
    pair <int, int> p;
    string s, g[30][30];
    cin >> n;
    cin >> m;
    for(int i = 0; i < n; i++)
    {
        cin >> s;
        for(int j = 0; j < s.size(); j++)
        {
            g[i][j] = s.substr(j, 1);
            if(g[i][j] == "P")
                p = make_pair(i, j);
        }
    }
    cout << dfs(g, p);
    return 0;
}

Sample input:

6
10
##########
#.P#.....#
##.#.K#..#
#..####..#
#........#
##########

Sample output:

14
\$\endgroup\$
0

2 Answers 2

4
\$\begingroup\$
  1. Please don't use using namespace std (read this).

  2. The variable names are really not helping. Why not call a maze a maze instead of g? What are mj, z, l, k? It's hard to follow the logic with names like these.

  3. Instead of working with strings like "K", "." and "P" it would be more efficient to use chars, and to give them meaningful names.

  4. Instead of int visited[30][30], bool visited[30][30] would be more natural

Here's an alternative version of your code, with some variables renamed, constants used, using char instead of string, not using namespace std:

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::pair;
using std::make_pair;
using std::vector;

const char start = 'P';
const char target = 'K';
const char empty = '.';

int dfs(char maze[30][30], pair<int, int> p)
{
    vector <pair<pair<int, int>, int> > v;
    bool visited[30][30] = {0};
    int x, y, k = 0;
    pair <pair<int, int>, int> mj;
    pair <pair<int, int>, int> z;
    pair <int, int> l;
    z = make_pair(make_pair(p.first, p.second), 0);
    v.push_back(z);
    while(v.size() != 0)
    {
        mj = v.front();
        l = mj.first;
        x = l.first;
        y = l.second;
        k = mj.second;
        v.erase(v.begin());
        if (maze[x][y] == target) {
            return k;
        }
        if((maze[x+1][y] == empty or maze[x+1][y] == target) and not(visited[x+1][y]))
        {
            v.push_back(make_pair(make_pair(x+1, y), k+1));
            visited[x+1][y] = true;
        }
        if((maze[x][y+1] == empty or maze[x][y+1] == target) and not(visited[x][y+1]))
        {
            v.push_back(make_pair(make_pair(x, y+1), k+1));
            visited[x][y+1] = true;
        }
        if((maze[x-1][y] == empty or maze[x-1][y] == target) and not(visited[x-1][y]))
        {
            v.push_back(make_pair(make_pair(x-1, y), k+1));
            visited[x-1][y] = true;
        }
        if((maze[x][y-1] == empty or maze[x][y-1] == target) and not(visited[x][y-1]))
        {
            v.push_back(make_pair(make_pair(x, y-1), k+1));
            visited[x][y-1] = true;
        }
    }
}

int main()
{
    int n, m;
    pair <int, int> p;
    char s, maze[30][30];
    cin >> n;
    cin >> m;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            cin >> s;
            maze[i][j] = s;
            if(maze[i][j] == start)
                p = make_pair(i, j);
        }
    }
    cout << dfs(maze, p) << std::endl;
    return 0;
}

Next, notice the duplication in this code:

if((maze[x+1][y] == empty or maze[x+1][y] == target) and not(visited[x+1][y]))
{
    v.push_back(make_pair(make_pair(x+1, y), k+1));
    visited[x+1][y] = true;
}
if((maze[x][y+1] == empty or maze[x][y+1] == target) and not(visited[x][y+1]))
{
    v.push_back(make_pair(make_pair(x, y+1), k+1));
    visited[x][y+1] = true;
}

The treatment for (x+1, y) is the same as for (x, y+1), and (x-1, y) and (x, y-1). Extract the duplicated parts to a helper method:

void check_maze_pos(char maze[30][30], int x, int y, int k, vector<pair<pair<int, int>, int> > &v, bool visited[30][30])
{
    if ((maze[x][y] == empty or maze[x][y] == target) and !visited[x][y])
    {
        v.push_back(make_pair(make_pair(x, y), k + 1));
        visited[x][y] = true;
    }
}

And then you can replace the 4 duplicated cases with these 4 simpler lines:

check_maze_pos(maze, x+1, y, k, v, visited);
check_maze_pos(maze, x, y+1, k, v, visited);
check_maze_pos(maze, x-1, y, k, v, visited);
check_maze_pos(maze, x, y-1, k, v, visited);

You can still improve further:

  • Eliminate the duplication of the magic number 30 in many places
  • Simplify the duplicated type definitions using typedef
\$\endgroup\$
2
  • \$\begingroup\$ Yeah, sorry for that, it's because those are the first letters of some words in my native language. Thanks for the suggestions. \$\endgroup\$
    – Tomislav
    Commented Oct 26, 2014 at 20:00
  • 2
    \$\begingroup\$ Single-letter words are poor names in general except a few special cases like i, j, k, for loop variables. \$\endgroup\$
    – janos
    Commented Oct 26, 2014 at 20:02
4
\$\begingroup\$

A few other suggestions:

You are using the and, or, not aliases to the logical operators &&, ||, !. In the C language, those are not reserved words, but macros defined in the header <iso646.h>. In C++, those are supposed to be globally visible without the need for the include file.

They might seem like a good idea at first, if you're not a big fan of punctuation, but I'll give you a couple reasons why you shouldn't use them:

  • People in general are not familiar with those aliases. I bet you a good number of C++ programmers don't even know they exits.

  • I've never actually seen them in use in any production code, so that might give you a hint about the overall view of the practice.

  • The reason these aliases where introduced, I think, was for programmers that didn't have a keyboard with the punctuation chars. But I don't think such keyboards are still being manufactured today, so that renders them obsolete for that purpose.


You should use some typedefs for the pairs. This pair<pair<int, int>, int> is not just very cumbersome and error prone to type, but also conveys little information. What is a pair of pair<in,int> and int? What's that thing supposed to be called?

Also typedef the vector<pair<pair<int, int>, int> > to some name that tells the reader the purpose of the vector.


while(v.size() != 0)

Would probably be more idiomatic if using empty() instead:

while(!v.empty())

std::pair and friends are declared in the <utility> header. So it might be a good idea to explicitly include it to make dependencies clear. At the moment, you are depending on a "residual" include probably from <vector>.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for the advice, I'm not really familiar with the typedef but I'll google it. \$\endgroup\$
    – Tomislav
    Commented Oct 27, 2014 at 14:26
  • \$\begingroup\$ @TomislavToplak - en.cppreference.com/w/cpp/language/typedef \$\endgroup\$
    – glampert
    Commented Oct 27, 2014 at 15:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.