-1

noob here.

I'm working on a program to track a "surveillance balloon" for a school project, it's basically glorified battleship. I need to make a 2D array to represent a grid. The grid needs to be a square anywhere between 10x10 and 20x20. I'm using a constant to determine the size of the grid called ARRAY_SIZE*.* My code works all right when ARRAY_SIZE is set to 10, but whenever I go any higher I get issues with the array having missing values, or characters that aren't supposed to be there.

#include <iostream>
using namespace std;

const int ARRAY_SIZE = 10; //The array will consist of this constant squared

//Function to print the map

void printMap(int size, char Bmap[ARRAY_SIZE-1][ARRAY_SIZE-1]) {
    cout << size << "x" << size << "map" << endl;
    for (int z = 0; z <= size; z++) {
        if (z == 0) {
            cout << "    ";
        }
        if (z < 10) {
            cout << z << "  ";
        } else {
            cout << z << " ";
        }
    }
    cout << endl;
    cout << endl;
    for (int row = 0; row <= size; row++){
        if (row < 10) {
            cout << row << "   ";
            for (int col = 0; col <= size; col++) {
               cout << Bmap[row][col] << "  "; 
            }
            cout << endl;
        } 
        else {
            cout << row << "  ";
            for (int col2 = 0; col2 <= size; col2++) {
               cout << Bmap[row][col2] << "  "; 
            }
            cout << endl;
        }
    }
} 


//Main Function


int main() {
    bool balloonFound = false;

    int endGame = false;

    int queryInput = 0;

    char map[ARRAY_SIZE-1][ARRAY_SIZE-1] = {'$'};

    //VARIABLE DECLARATIONS ^^^
    int Ycord = 0;
    int Xcord = 0;
    for (Xcord = 0; Xcord <= (ARRAY_SIZE-1); Xcord++) {
        for (Ycord = 0; Ycord <= (ARRAY_SIZE-1); Ycord++) {
            map[Xcord][Ycord] = { '$' };
        }
        Ycord = 0;
    }
    Xcord = 0;
    while (endGame != true) {
        cout << "What would you like to do?:" << endl;
        cout << "1. Display Map" << endl;
        cout << "2. Guess Location of Balloon" << endl;
        cout << "3. Exit" << endl;
        cin >> queryInput;
        if (queryInput == 1) {
            printMap(ARRAY_SIZE-1, map);

        } else if (queryInput == 2) {
            int userX = 0;
            int userY = 0;
            cout << "Enter the X coordinate (0 - " << ARRAY_SIZE-1 << ") :" << endl; 
            cin >> userX;
            cout << "Enter the X coordinate (0 - " << ARRAY_SIZE-1 << ") :" << endl; 
            cin >> userY;
            map[userX][userY] = 'G';
        } else if (queryInput == 3) {
            endGame = 1;
        cout << "Thank you for using the Surveillance Balloon Tracker" << endl;
        } else {
        cout << queryInput << " is not a valid choice" << endl;
            }
        }
}

This is the part that doesn't appear to be working properly

for (Xcord = 0; Xcord <= (ARRAY_SIZE-1); Xcord++) {
        for (Ycord = 0; Ycord <= (ARRAY_SIZE-1); Ycord++) {
            map[Xcord][Ycord] = { '$' };
        }
        Ycord = 0;
    }
    Xcord = 0;

This works all right, when I enter "1" to print the array my output looks like

    0  1  2  3  4  5  6  7  8  9

0   $  $  $  $  $  $  $  $  $  $
1   $  $  $  $  $  $  $  $  $  $
2   $  $  $  $  $  $  $  $  $  $
3   $  $  $  $  $  $  $  $  $  $
4   $  $  $  $  $  $  $  $  $  $
5   $  $  $  $  $  $  $  $  $  $
6   $  $  $  $  $  $  $  $  $  $
7   $  $  $  $  $  $  $  $  $  $
8   $  $  $  $  $  $  $  $  $  $
9   $  $  $  $  $  $  $  $  $  $

But when ARRAY_SIZE is set to 20 the output looks like this

    0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19

0   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
1   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
2   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
3   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  
4   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
5   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
6   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
7   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
8   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
9   $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  
10  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
11  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
12  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
13  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
14  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
15  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
16  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  
17  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
18  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $  $
19  $  $  $          $  $  $  $

I'm just not quite sure why this is happening, any insight would be appreciated.

  • 3
    For *any* array of `N` elements, the valid indexes are in the range of `0` to `N - 1`. So if the number of elements is `ARRAY_SIZE-1` (it's the `N`) then the indexes go from `0` to `(ARRAY_SIZE-1) - 1`. So what happens here is that you have *undefined behavior* because you go out of bounds of your arrays. The actual value of `ARRAY_SIZE` is irrelevant, you *always* have the UB with the code you show. – Some programmer dude Feb 27 '23 at 18:47
  • I'm curious how you decided that you need a constant `ARRAY_SIZE` (that's good) but then decided that in your code you really needed to subtract one from it `ARRAY_SIZE-1` (that's bad). If you have a variable that holds your array size, then just use it, don't subtract one from it. Some strange logic is making your code more complicated than it needs to be. – john Feb 27 '23 at 18:56
  • If `ARRAY_SIZE` (and not `ARRAY_SIZE-1`) really is your array size, then your loops should be written like this `for (Xcord = 0; Xcord < ARRAY_SIZE; Xcord++) {`, not a `-1` anywhere. – john Feb 27 '23 at 18:59
  • 1
    Side note: Dynamic 2D arrays usually suck (trade ease-of-use for performance *and* ease-of-management). Instead consider having a [1D array in a wrapper](https://isocpp.org/wiki/faq/operator-overloading#matrix-subscript-op) that makes it look 2D. This can be [ludicrously easy](https://stackoverflow.com/a/2076668/4581301) when backed with a `std::vector`, and [if you're careful](https://stackoverflow.com/a/36123944/4581301) you can use operator `[]`. – user4581301 Feb 27 '23 at 19:07
  • @Someprogrammerdude Thank you for the explanation, I was able to fix it. – Aidan Ibrahim Feb 27 '23 at 19:13
  • @user4581301 I'd love too, but the rubric explicitly requires the use of a 2D array. – Aidan Ibrahim Feb 27 '23 at 19:14
  • what john said and then your loops are `Xcord = 0; Xcord <= (ARRAY_SIZE-1); Xcord++` which would be ok if `ARRAY_SIZE` would be the size, but it is not and `map[Xcord][Ycord]` is out of bounds. – 463035818_is_not_an_ai Feb 27 '23 at 19:17
  • On another note, if you don't know the actual size at compile-time, only at run-time, then use a `std::vector` instead of a plain array. Unlike an array, `std::vector` is actually dynamic. – Some programmer dude Feb 27 '23 at 19:21

2 Answers2

0

The issue was with my array bounds. I was using ARRAY_SIZE-1 as part of the loops I was using to fill the array with characters, I have fixed it and will attach the corrected code here. Credit to Someprogrammerdude and John

#include <iostream>
using namespace std;

const int ARRAY_SIZE = 15; //The array will consist of this constant squared

//Function to print the map

void printMap(int size, char Bmap[ARRAY_SIZE][ARRAY_SIZE]) {
    cout << size << "x" << size << "map" << endl;
    for (int z = 0; z <= size; z++) {
        if (z == 0) {
            cout << "    ";
        }
        if (z < 10) {
            cout << z << "  ";
        } else {
            cout << z << " ";
        }
    }
    cout << endl;
    cout << endl;
    for (int row = 0; row <= size; row++){
        if (row < 10) {
            cout << row << "   ";
            for (int col = 0; col <= size; col++) {
               cout << Bmap[row][col] << "  "; 
            }
            cout << endl;
        } 
        else {
            cout << row << "  ";
            for (int col2 = 0; col2 <= size; col2++) {
               cout << Bmap[row][col2] << "  "; 
            }
            cout << endl;
        }
    }
} 


//Main Function


int main() {
    bool balloonFound = false;

    int endGame = false;

    int queryInput = 0;
    int dimension = ARRAY_SIZE-1;

    char map[ARRAY_SIZE][ARRAY_SIZE] = {'$'};

    //VARIABLE DECLARATIONS ^^^
    int Ycord = 0;
    int Xcord = 0;
    for (Xcord = 0; Xcord <= (dimension); Xcord++) {
        for (Ycord = 0; Ycord <= (dimension); Ycord++) {
            map[Xcord][Ycord] = { '$' };
        }
        Ycord = 0;
    }
    Xcord = 0;

    while (endGame != true) {
        cout << "What would you like to do?:" << endl;
        cout << "1. Display Map" << endl;
        cout << "2. Guess Location of Balloon" << endl;
        cout << "3. Exit" << endl;
        cin >> queryInput;
        if (queryInput == 1) {
            printMap(dimension, map);

        } else if (queryInput == 2) {
            int userX = 0;
            int userY = 0;
            cout << "Enter the X coordinate (0 - " << dimension << ") :" << endl; 
            cin >> userX;
            cout << "Enter the X coordinate (0 - " << dimension << ") :" << endl; 
            cin >> userY;
            map[userX][userY] = 'G';
        } else if (queryInput == 3) {
            endGame = 1;
        cout << "Thank you for using the Surveillance Balloon Tracker" << endl;
        } else {
        cout << queryInput << " is not a valid choice" << endl;
            }
        }
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • usually a loop goes like this `for (int i = 0; i < SIZE; ++i)` or simply `for (const auto& elem : container)`. You still have `ARRAY_SIZE` and `dimension` which is `ARRAY_SIZE-1`. You do not need this, it merely add uncessesary complexity – 463035818_is_not_an_ai Feb 28 '23 at 08:56
0

You have several off-by-one errors. Also, if you need a dynamic array (size) for the game it is better to use a dynamic array. For dynamic arrays, use special libraries for that, such as Boost.MultiArray or Multi (disclosure, I am the author).

If you do that you will not need to pass c-arrays (char[N][M]) as function arguments and no more need for global constants.

To answer the question title concretelly "How to fill 2D array of varying sizes in C++"?

The answer is it depends what array type do you use at the end.

If you use c-arrays:

char map[SIZE][SIZE]; std::fill_n(&map[0][0], SIZE*SIZE, '$');

If you use Boost.MultiArray (see full code):

boost::multi_array<char, 2> map(boost::extents[SIZE][SIZE]); std::fill_n(map.data(), map.num_elements(), '$');

If you use Multi (see full code):

multi::array<char, 2>({SIZE, SIZE}, '$');

Inserted in your original code, the answer looks like this: (I simplified your code minimally to show the changes, For a live demo see here: https://godbolt.org/z/bGWscx7rW)

#include <multi/array.hpp>  // from https://gitlab.com/correaa/boost-multi
// #include <boost/multi_array.hpp>  // from Boost

#include <iostream>
#include <sstream>

using namespace std;

using char2d = boost::multi::array<char, 2>;
// using char2d = boost::multi_array<char, 2>;

//Function to print the map

void printMap(char2d const& Bmap) {
    cout << Bmap.size() << "x" << Bmap[0].size() << " map" << endl;
    for (int z = 0; z < Bmap.size(); z++) {
        if (z == 0) {
            cout << "    ";
        }
        if (z < 10) {
            cout << z << "  ";
        } else {
            cout << z << " ";
        }
    }
    cout << endl;
    cout << endl;
    for (int row = 0; row < Bmap.size(); row++){
        if (row < 10) {
            cout << row << "   ";
            for (int col = 0; col < Bmap[0].size() ; col++) {
               cout << Bmap[row][col] << "  "; 
            }
            cout << endl;
        } 
        else {
            cout << row << "  ";
            for (int col2 = 0; col2 < Bmap[0].size(); col2++) {
               cout << Bmap[row][col2] << "  "; 
            }
            cout << endl;
        }
    }
} 

//Main Function

int main() {
    const int ARRAY_SIZE = 5; //The array will consist of this constant squared

    char2d map({ARRAY_SIZE, ARRAY_SIZE}, '$');
    // char2d map(boost::extents[ARRAY_SIZE][ARRAY_SIZE]);
    // std::fill_n(map.data(), map.num_elements(), '$');

    std::istringstream in{"1\n2\n3\n2\n1"};  // emulate user input
    auto& cin = in;

    bool balloonFound = false;
    int endGame = false;
    while (endGame != true) {
        cout << "What would you like to do?:" << endl;
        cout << "1. Display Map" << endl;
        cout << "2. Guess Location of Balloon" << endl;
        cout << "3. Exit" << endl;
        int queryInput = 0;
        cin >> queryInput;
        if (queryInput == 1) {
            printMap(map);
        } else if (queryInput == 2) {
            int userX = 0;
            int userY = 0;
            cout << "Enter the X coordinate (0 - " << map.size() - 1 << ") :" << endl; 
            cin >> userX;
            cout << "Enter the X coordinate (0 - " << map[0].size() - 1 << ") :" << endl; 
            cin >> userY;
            map[userX][userY] = 'G';
        } else if (queryInput == 3) {
            endGame = 1;
        cout << "Thank you for using the Surveillance Balloon Tracker" << endl;
        } else {
        cout << queryInput << " is not a valid choice" << endl;
            }
        }
}
alfC
  • 14,261
  • 4
  • 67
  • 118