1

I'm having a problem correctly printing out a 2D array for the traveling salesman problem. I'm getting my input from a text file using input redirection. The file contains cities and arcs with the distance between the cities. Here is a small example.

c 1
c 2
a 1 2 1400

After setting up my array and plotting the distance between the cities I use a nested for loop to print out the array but it looks like this.

    0       1       2       3       4       5

    1       0       1400    1800    4000    3500

    2       1       0       0       3400    3600

    3       1800    1200    0       2300    0

    4       4000    3400    2300    0       2100

    5       3500    3600    0       2100    0

EDIT: I want to make it appear like this

    0       1       2       3       4       5

    1       0       1400    1800    4000    3500

    2       1400    0       1200    3400    3600

    3       1800    1200    0       2300    2700

    4       4000    3400    2300    0       2100

    5       3500    3600    2700    2100    0

I tried manipulating the for loop different ways but I can't seem to figure out where my problem is in the loop or if it's somewhere else in my code.

// Sets up the array
int CityArray [6][6] = { {0, 0, 0, 0, 0, 0},
                         {0, 0, 0, 0, 0, 0},
                         {0, 0, 0, 0, 0, 0},
                         {0, 0, 0, 0, 0, 0},
                         {0, 0, 0, 0, 0, 0},
                         {0, 0, 0, 0, 0, 0}
                       };

int main(void) // Takes in a variable number of arguments
{
    // Sets a string input for the city
    char Cbuffer[32];
    char *b = Cbuffer;
    size_t cbufsize = 32;
    size_t cinput;

    // Other vairables
    int x = 1; // used to go through the array
    int n1, n2, n3, n4, cost; // variables to store the value pulled the cost from the arc

    // Reads in the city and sets the prices for each arc
    while((cinput = getline(&b, &cbufsize, stdin)) != -1)
    {
        if (Cbuffer[0] == 'c')
        {
            // Stores the last element as a digit to CityArray
            if (Cbuffer[2] >= '0' && Cbuffer[2] <= '9')
            {
                CityArray[x][0] = Cbuffer[2] - '0';
                int z = CityArray[x][0];
                // Flips it
                CityArray[0][x] = Cbuffer[2] - '0';
                z = CityArray[0][x];
                // printf("CityArray[%d] is '%d' \n", x, z);
                x++;
            }
        }
        else if (Cbuffer[0] == 'a')
        {
            int y = 1;
            // I know this looks ugly but it's the only way I could think of getting the prices
            if ((Cbuffer[6] >= '0' && Cbuffer[6] <= '9') && (Cbuffer[7] >= '0' && Cbuffer[7] <= '9') &&
                    (Cbuffer[8] >= '0' && Cbuffer[8] <= '9') && (Cbuffer[9] >= '0' && Cbuffer[9] <= '9'))
            {
                for (x = 1; x < 6; x++)
                {
                    for (y; y < 6; y++)
                    {   // converts the char to a int
                        n1 = CityArray[x][6] = Cbuffer[6] - '0';
                        n2 = CityArray[x][7] = Cbuffer[7] - '0';
                        n3 = CityArray[x][8] = Cbuffer[8] - '0';
                        n4 = CityArray[x][9] = Cbuffer[9] - '0';
                    }
                } // sets all converted ints to = cost
                cost = (n1 * 1000) + (n2 * 100) + (n3 * 10) + (n4 * 1);
                x++;
            }
            // Checks where the arc is located and plots the distance of the trip
            if (Cbuffer[2] == '1')
            {
                if (Cbuffer[4] == '2')
                {
                    CityArray[1][2] = cost;
                    CityArray[2][1] = cost;
                }
                else if (Cbuffer[4] == '3')
                {
                    CityArray[1][3] = cost;
                    CityArray[3][1] = cost;
                }
                else if (Cbuffer[4] == '4')
                {
                    CityArray[1][4] = cost;
                    CityArray[4][1] = cost;
                }
                else if (Cbuffer[4] == '5')
                {
                    CityArray[1][5] = cost;
                    CityArray[5][1] = cost;
                }
            }
            else if (Cbuffer[2] == '2')
            {
                if (Cbuffer[4] == '3')
                {
                    CityArray[2][3] = cost;
                    CityArray[3][2] = cost;
                }
                else if (Cbuffer[4] == '4')
                {
                    CityArray[2][4] = cost;
                    CityArray[4][2] = cost;
                }
                else if (Cbuffer[4] == '5')
                {
                    CityArray[2][5] = cost;
                    CityArray[5][2] = cost;
                }
            }
            else if (Cbuffer[2] == '3')
            {
                if (Cbuffer[4] == '4')
                {
                    CityArray[3][4] = cost;
                    CityArray[4][3] = cost;
                }
            else if (Cbuffer[4] == '5')
            {
                    CityArray[4][5] = cost;
                    CityArray[5][4] = cost;
                }
            }
            else if (Cbuffer[2] == '4')
            {
                if (Cbuffer[4] == '5')
                {
                    CityArray[4][5] = cost;
                    CityArray[5][4] = cost;
                }
            }
        }   
    }

    // Prints the array
    int i, j;
    printf("\n\nThe cost list is:\n\n");
    for(i = 0; i < 6;i ++)
    {
        printf("\n\n");
        for(j = 0; j < 6; j++)
        {
            printf("\t%d", CityArray[i][j]);
        }
        printf("\n");
    }

    return 0;
}
Cheezdue
  • 19
  • 6
  • "but it looks like this." - can you also explain how you expected it to look – M.M Apr 01 '16 at 04:23
  • Will do let me edit my question! – Cheezdue Apr 01 '16 at 04:25
  • 1
    If the buffer is not big enough, then `getline` will `free` the existing buffer and allocate a new one, returning that. So you should not pass `Cbuffer` as it is an error to free that; and you should not test `Cbuffer[0] == 'c'` etc. because the line may have been reallocated. Instead you could set `b = NULL` to start, and use `b[0]` etc. (Or to avoid a lot of typing, get rid of `b` and use `char *Cbuffer = NULL;`). [Probably not related to your problem since your file lines are all shorter than 32 , but it's a ticking timebomb] – M.M Apr 01 '16 at 04:25
  • Thanks for the advice, didn't realize that could lead to issues if I was dealing lines larger than 32. – Cheezdue Apr 01 '16 at 04:31

1 Answers1

1

Your problem is here:

            for (x = 1; x < 6; x++)
            {
                for (y; y < 6; y++)
                {   // converts the char to a int
                    n1 = CityArray[x][6] = Cbuffer[6] - '0';
                    n2 = CityArray[x][7] = Cbuffer[7] - '0';
                    n3 = CityArray[x][8] = Cbuffer[8] - '0';
                    n4 = CityArray[x][9] = Cbuffer[9] - '0';
                }
            } // sets all converted ints to = cost
            cost = (n1 * 1000) + (n2 * 100) + (n3 * 10) + (n4 * 1);
            x++;

First, you don't need loops here; looping here means just that you will do the conversion several times. (It's worse, actually: Because you don't initialise y you may not do the conversion at all. If you activate warnings, you'll get someting along the lines of "statement with no effect" for the standaone y.)

Second, you store the converted digits in CityArray[x][6 ... 9], but indices of 6 and beyond are out of bounds. That's undefined behaviour. In practice, you overwrite the data for the next city.

Third, you shouldn't use x as a looping variable and as a variable to hold the numer of cities. The loop will overwrite the data. (But that problem goes away when you remove the loops.)

Just do:

            n1 = Cbuffer[6] - '0';
            n2 = Cbuffer[7] - '0';
            n3 = Cbuffer[8] - '0';
            n4 = Cbuffer[9] - '0';

            cost = (n1 * 1000) + (n2 * 100) + (n3 * 10) + (n4 * 1);

The code still has many problems. In particular, the parsing of the cities and distances is very restricted. What happens if the cost for a city isn't a four digit number? And what happens if the first city has a greater number than the second city?

You can also use the conversion from ASCII to a single-digit integer for the cities:

        int from = Cbuffer[2] - '0';
        int dest = Cbuffer[4] - '0';

        CityArray[from][dest] = cost;
        CityArray[dest][from] = cost;

This will get rid of a lot of code. Instead of hard-coding all possibilities, your energy is better spent in writing meaningful error messages, for example if a city's id is out of bounds.

You should also consider using the standard approaches to parsing the input. getline in combination with scanf may be a good approach.

Edit: Below is an example implementation of the input. It can take at most 10 cities, identified by a single character which may be a digit. It doesn't have any restrictions of the exact format of the c and a lines and also keeps track of the number of actual cities in the variable ncitiy. It accepts blank lines and lines starting with a # as non-commands.

Despite the heavy error checking, this program is a bit shorter than yours. Here goes:

#define _GNU_SOURCE

#include <stdlib.h>
#include <stdio.h>

#define MAX 10


int find(int array[], int n, int x)
{
    for (int i = 0; i < n; i++) {
        if (array[i] == x) return i;
    }

    return -1;
}

int main(void)
{
    int cost[10][10] = {{0}};       // cost matrix
    int id[MAX];                    // city id; can be any character
    int ncity = 0;                  // number of cities

    char *line = NULL;
    size_t nline = 0;
    int error = 0;

    while (getline(&line, &nline, stdin) != -1) {
        char c1, c2;
        int c;

        if (sscanf(line, " c %c", &c1) == 1) {
            if (find(id, ncity, c1) != -1) {
                fprintf(stderr, "Duplicate city id %c.\n", c1);
                error = 1;
                break;
            } else if (ncity >= MAX) {
                fprintf(stderr, "Maximum number of cities exceeded\n");
                error = 1;
                break;
            } else {
                id[ncity++] = c1;
            }
            continue;
        }

        if (sscanf(line, " a %c %c %d\n", &c1, &c2, &c) == 3) {
            int from = find(id, ncity, c1);
            int dest = find(id, ncity, c2);

            if (from < 0) {
                fprintf(stderr, "Unknown city id %c.\n", c1);
                error = 1;
                break;
            }

            if (dest < 0) {
                fprintf(stderr, "Unknown city id %c.\n", c2);
                error = 1;
                break;
            }

            cost[from][dest] = c;
            cost[dest][from] = c;

            continue;
        }

        if (sscanf(line, " %c", &c1) == 1 && c1 != '#') {
            fprintf(stderr, "Unknown command: %s", line);
            error = 1;
            break;
        }
    }

    free(line);

    if (error) {
        fprintf(stderr, "Errors in input. Aborting.\n");
        exit(1);
    }

    printf("%8s", "");
    for (int j = 0; j < ncity; j++) {
        printf("%8c", id[j]);
    }
    puts("");

    for(int i = 0; i < ncity; i ++)
    {
        printf("%8c", id[i]);
        for (int j = 0; j < ncity; j++) {
            printf("%8d", cost[i][j]);
        }
        puts("");
    }
    puts("");

    return 0;
}
M Oehm
  • 28,726
  • 3
  • 31
  • 42
  • Thanks, your suggestion worked! Originally I was thinking my problem lied in that section of my code but I failed to realize why it was causing my problem. I knew my method of reading in cities is very restricted but I couldn't figure out a better method. The code you provided is a great method to read in the cities! – Cheezdue Apr 01 '16 at 13:57