2d Array Help

Pages: 12
If I have a dynamic 2D array, and I want elements to change depending on a certain element's value in the array, how do I code that?

For example:

Say the text file that I read in says the array is 4x4 and holds the following elements:

[1][1][1][1]
[1][1][1][1]
[1][1][1][1]
[1][0][0][1]

But I want it to look like this instead: 1's around the 0's in the array, 2's around the 1's in the array, 3's around the 2's in the array etc.

[3][3][3][3]
[2][2][2][2]
[1][1][1][1]
[1][0][0][1]

How do I code this??

My code currently fills the entire array with the same value except if it sees a 0 in an element, then it is unchanged.
You say you have a "dynamic 2D array". But how exactly do you store the array?

If you want to have a N×M array, you can allocate a single block of memory and "emulate" the 2D array:
1
2
3
4
5
6
7
8
9
10
#define INDEX(x, y) (((x) * (M)) + (y))
int *array = new int[N * M];

for (size_t x = 0; x < N; ++x)
{
    for (size_t y = 0; y < M; ++y)
    {
        array[INDEX(x, y)] = /*...*/
    }
}

...or you can create an array of pointers to separate memory blocks for each "row":
1
2
3
4
5
6
7
8
9
10
11
12
13
int **array = new int*[N];
for (size_t x = 0; x < N; ++x)
{
    array[x] = new int[M];
}

for (size_t x = 0; x < N; ++x)
{
    for (size_t y = 0; y < M; ++y)
    {
        array[x][y] = /*...*/
    }
}
Last edited on
@a456df
Create a queue and put the positions of the 0s in it.

Then, repeatedly:
- pop the front off the queue;
- assign all the adjacent positions of your popped position that have not yet been assigned to a value 1 more than that of your popped position, and add them to the queue.

Continue until the queue is exhausted. Your array should then be filled with “distance contours” around the 0s.


Probably.


Your description of your problem is anything but clear and I suspect that “dynamic array” is a bit of a red herring.
Last edited on
Thanks for your response! I'm having a lot of difficulty getting this code right. I'm not sure if my use of "dynamic" is correct.

Currently have the array allocate the number of rows and columns like this. I can't figure out how to change the individual elements according to how far they are from A spaces:

/**
*Initialize Amount Values
*/
void TestInput::inputAmount()
{
// Allocate the number of required rows (pointers)
Amount = new int*[ height() ];

// Allocate each row
for( int i = 0; i < height(); i++ ) {
Amount[i] = new int[ width() ];

//Set each cell as uninitialized
for( int j = 0; j < width(); j++ ) {
Amount[i][j] = A_UNINIT;

// Set Barrier Amount
if( toupper(testArr[i][j]) == 'X' ) {
Amount[i][j] = A_WALL;
}
else if( toupper(testArr[i][j]) == 'A' ) {
// Set A space amount
Amount[i][j] = A_FINISH;
}
}
}
/**Assign Amount to blanks in text here**/
}
> I can't figure out how to change the individual elements according to how far they are from A spaces:
¿what's an «A space»?

1
2
3
4
for row in matrix.rows:
   for cell in row.cells:
      distance = get_distance(A_space, cell.coordinates)
      cell.set_value(distance)
@lastchance , I haven't learned queues yet but in some searches I think this would be viable potentially. How would I put the 0 positions into the queue if they are notated by an "A" in the text file my program reads in? I'm quite lost.
@lastchance , because the position would change every time depending on which file the user chooses.
When posting code, please use code tags so the code is readable:


[code]
formatted code goes here
[/code]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
*Initialize Amount Values
*/
	void TestInput::inputAmount() {
	// Allocate the number of required rows (pointers)
		Amount = new int* [height()];

		// Allocate each row
		for (int i = 0; i < height(); i++) {
			Amount[i] = new int[width()];

			//Set each cell as uninitialized
			for (int j = 0; j < width(); j++) {
				Amount[i][j] = A_UNINIT;

				// Set Barrier Amount
				if (toupper(testArr[i][j]) == 'X') {
					Amount[i][j] = A_WALL;
				} else if (toupper(testArr[i][j]) == 'A') {
				// Set A space amount
					Amount[i][j] = A_FINISH;
				}
			}
		}
		/**Assign Amount to blanks in text here**/
	}
}



Or for Amount, just use std::vector<std::vector<int>>

1
2
3
using Matrix = std::vector<std::vector<int>>;

Matrix Amount(height(), std::vector<int>(width(), 0));


Then just use Amount as a 2-d array using [][] et al. No need for memory allocation/free with new/delete etc and Amount can now be easily passed to functions etc using type Matrix (and by ref or const ref).
Last edited on
As some test code, consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <vector>
#include <cctype>

enum elems { A_UNINIT, A_WALL, A_FINISH };

using MatrixC = std::vector<elems>;
using TestC = std::vector<unsigned char>;
using Matrix = std::vector<MatrixC>;
using Test = std::vector<TestC>;

struct TestInput {
	TestInput(size_t h_, size_t w_) : h(h_), w(w_), Amount(Matrix(h_, MatrixC(w_, A_UNINIT))) {}
	void inputAmount();
	size_t height() const { return h; }
	size_t width() const { return w; }

	Matrix Amount;
	Test testArr;        // To be initialised
	const size_t h {};
	const size_t w {};
};

void TestInput::inputAmount() {
	for (size_t i {}; i < height(); ++i)
		for (size_t j {}; j < width(); ++j)
			if (toupper(testArr[i][j]) == 'X')
				Amount[i][j] = A_WALL;
			else if (toupper(testArr[i][j]) == 'A')
				Amount[i][j] = A_FINISH;
}

Last edited on
I want elements to change depending on a certain element's value in the array

Not clear enough. Given this matrix, which elements are those "certain elements"?
[a][b][c][d][e][f][g]
[h][i][j][k][l][m][n]
[o][p][q][r][s][t][u]
[v][w][x][y][z][å][€]

Furthermore, for each element that is not "certain element", do you want to modify them or set their value based on their position/distance from the "certain elements"?

You have said that you read matrix from input. In all your examples you care only about the elements that have value 0. The other elements you just overwrite?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <queue>
#include <utility>
using namespace std;

//----------------------------------------------------------

vector<string> readInput( istream &in )
{
   vector<string> result;
   for ( string line; getline( in, line ); ) result.push_back( line );
   return result;
}

//----------------------------------------------------------

void drawSolution( const vector< vector<int> > &F, int W = 1 )
{
   for ( auto &row : F )
   {
      for ( int e : row )
      {
         if ( e < 0 ) cout << setw( W ) << '.';
         else         cout << setw( W ) << e;
      }
      cout << '\n';
   }
}

//----------------------------------------------------------

vector< vector<int> > fill( const vector<string> &board )
{
   const vector< pair<int,int> > jp = { {-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1} };

   int rows = board.size(), cols = board[0].size();
   queue< pair<int,int> > Q;
   vector< vector<int> > F( rows, vector<int>( cols, -99 ) );

   // Set starting points (as 0) and barriers (as -1)
   for ( int i = 0; i < rows; i++ )
   {
      for ( int j = 0; j < cols; j++ )
      {
         if ( board[i][j] == 'A' )
         {
            F[i][j] = 0;
            Q.push( { i, j } );
         }
         else if ( board[i][j] == 'X' )
         {
            F[i][j] = -1;
         }
      }
   }

   // Check adjacent points to current front-of-queue
   while( !Q.empty() )
   {
      auto pr = Q.front();   Q.pop();
      int i = pr.first, j = pr.second;
      int nextValue = F[i][j] + 1;
      for ( int n = 0; n < jp.size(); n++ )
      {
         int ii = i + jp[n].first, jj = j + jp[n].second;                // adjacent point
         if ( ii < 0 || ii >= rows || jj < 0 || jj >= cols ) continue;   // if outside domain then ignore
         if ( F[ii][jj] < -1 )                                           // not yet set
         {
            F[ii][jj] = nextValue;                                       // set or improve value
            Q.push( { ii, jj } );                                        // add to current front line
         }
      }
   }

   return F;
}

//----------------------------------------------------------

int main()
{
// ifstream in( "input.txt" );              // for real
   istringstream in( "..............\n"     // for demo
                     "..............\n"
                     "..............\n"
                     "..............\n"
                     ".......X......\n"
                     ".......X......\n"
                     "...A...X......\n"
                     "...A...X......\n"
                     ".......X......\n"
                     ".......X......\n" );

   vector<string> board = readInput( in );
   vector< vector<int> > F = fill( board );
   drawSolution( F, 3 );
}


  6  6  6  6  6  6  6  6  6  6  7  8  9 10
  5  5  5  5  5  5  5  5  5  6  7  8  9 10
  4  4  4  4  4  4  4  4  5  6  7  8  9 10
  3  3  3  3  3  3  3  4  5  6  7  8  9 10
  3  2  2  2  2  2  3  .  5  6  7  8  9 10
  3  2  1  1  1  2  3  .  6  6  7  8  9 10
  3  2  1  0  1  2  3  .  7  7  7  8  9 10
  3  2  1  0  1  2  3  .  8  8  8  8  9 10
  3  2  1  1  1  2  3  .  9  9  9  9  9 10
  3  2  2  2  2  2  3  . 10 10 10 10 10 10

Last edited on
HUGE help! I'm testing several suggestions and will update asap.
After testing several suggestions, @lastchance this code is working perfectly.
Is there such a thing as a class for a vector?

I'm having trouble with this portion in my program.
1
2
3
4
5
6
7
vector< vector<int> > fill( const vector<string> &board )
{
   const vector< pair<int,int> > jp = { {-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1} };

   int rows = board.size(), cols = board[0].size();
   queue< pair<int,int> > Q;
   vector< vector<int> > F( rows, vector<int>( cols, -99 ) );


a456df wrote:
Is there such a thing as a class for a vector?

Yes, it's a (template) class defined by the c++ standard:
https://cplusplus.com/reference/vector/vector/

It's probably the most straightforward way of defining a dynamic array in C++.



a456df wrote:
I'm having trouble with this portion in my program.

You will have to be more explicit - it has a lot of different things in.
a456df wrote:
Is there such a thing as a class for a vector?

Another look at std::vector, this one a bit more technical:

https://en.cppreference.com/w/cpp/container/vector

A lot of C++ features are template classes, from std::string to all the C++ containers and a lot more.

There are 4 parts of the C++ standard library (stdlib) that are template classes:

Algorithms
Containers
Functions
Iterators

https://www.geeksforgeeks.org/the-c-standard-template-library-stl/

"A lot of people" say the Standard Template Library (STL) is the C++ stdlib.... No, it ain't.

http://kera.name/articles/2010/08/it-is-not-called-the-stl-mmkay/

The STL inspired the template classes that became part of the C++ stdlib. Most knowledgeable people know what you mean if you call the C++ stdlib "The STL," though it isn't accurate. Beginners should be encouraged to call what's in C++ "the C++ stdlib."

Kinda like where the C++ standard doesn't use the "Plain Old Data (POD)" terminology any longer, but most people informally still use it since it succinctly describes a key part of C++. Trivial data types.

If cppreference and the pre-C++20 standard uses POD who are we to argue otherwise?
https://en.cppreference.com/w/cpp/named_req/PODType

Since C++20: https://en.cppreference.com/w/cpp/named_req/TrivialType

Regarding using std::vector as a 2D container......

I would only use a 2D std::vector if I were to add or delete a row and add or delete columns to one or more rows. If you are dealing with a fixed size 2d container consider using std::array instead.

OR

consider using std::vector/std::array in a simulated 2D arrangement. M height (rows) by N length (columns), sized on creation as (M * N) for both, only a std::vector can be resized after creation.
@lastchance, all of this has been SO helpful.

That portion of code I posted above works seamlessly in main but when I've attempted to merge it into my existing class in a void member function, I end up with an error message on this line
vector< vector<int> > fill( const vector<string> &board )
that says vector was not declared in this scope.

In trying to follow this thread: https://stackoverflow.com/questions/8553464/vector-as-a-class-member. Could this mean I need to put this vector into the original class constructor and not use it inside of a void function?

Most of what I had built to this point doesn't seem to function well with the other aspects for the game. If I could get it all built into main I would be ecstatic at this point.

I need to set the user and computer player's start position in the element value farthest from the 0 elements. (This position is notated by the 10's in your example output)

6 6 6 6 6 6 6 6 6 6 7 8 9 10
5 5 5 5 5 5 5 5 5 6 7 8 9 10
4 4 4 4 4 4 4 4 5 6 7 8 9 10
3 3 3 3 3 3 3 4 5 6 7 8 9 10
3 2 2 2 2 2 3 . 5 6 7 8 9 10
3 2 1 1 1 2 3 . 6 6 7 8 9 10
3 2 1 0 1 2 3 . 7 7 7 8 9 10
3 2 1 0 1 2 3 . 8 8 8 8 9 10
3 2 1 1 1 2 3 . 9 9 9 9 9 10
3 2 2 2 2 2 3 . 10 10 10 10 10 10
but of course it won't always be 10 as it changes depending on the course the player chooses to use for their game.

I have another function that displays the game board as blank spaces for the real game mode except for the "deep ocean" notated by the .'s above and the Land spaces notated by the 0's above that each player will be attempting to reach first to win the game. I'm also going to create a bool function to check if the path is clear and will return false when there is an obstacle (so no one can go through the "deep ocean" spots.) Each player can only move up to 5 spaces up/down or left/right at a time.

I have only ever coded basic loops - if statements etc and am LOVING learning how to do more!
With regard to your “vector is not declared ..” error, you just need
#include <vector>
at the top of each code unit that uses them (or header file if you are using them for declarations). Then you need either “using namespace std;” before use, or put std:: in front of every occurrence of “vector”. I’m not going to join the flame wars about that, or this thread will go off on an undesirable tangent.

You should NOT be following that StackOverflow post: it has nothing to do with your original problem.

It is best if you show your code that has the error; it is not easy to debug code that we cannot see, and there is the risk of an XY problem.




If I could get all built into main

DON’T do that. Break your code into short functions doing specific things.
Last edited on
I'm having trouble building this into a correct template class structure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//----------------------------------------------------------

vector< vector<int> > fill( const vector<string> &board )
{
   const vector< pair<int,int> > jp = { {-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1} };

   int rows = board.size(), cols = board[0].size();
   queue< pair<int,int> > Q;
   vector< vector<int> > F( rows, vector<int>( cols, -99 ) );

   // Set starting points (as 0) and barriers (as -1)
   for ( int i = 0; i < rows; i++ )
   {
      for ( int j = 0; j < cols; j++ )
      {
         if ( board[i][j] == 'A' )
         {
            F[i][j] = 0;
            Q.push( { i, j } );
         }
         else if ( board[i][j] == 'X' )
         {
            F[i][j] = -1;
         }
      }
   }

   // Check adjacent points to current front-of-queue
   while( !Q.empty() )
   {
      auto pr = Q.front();   Q.pop();
      int i = pr.first, j = pr.second;
      int nextValue = F[i][j] + 1;
      for ( int n = 0; n < jp.size(); n++ )
      {
         int ii = i + jp[n].first, jj = j + jp[n].second;                // adjacent point
         if ( ii < 0 || ii >= rows || jj < 0 || jj >= cols ) continue;   // if outside domain then ignore
         if ( F[ii][jj] < -1 )                                           // not yet set
         {
            F[ii][jj] = nextValue;                                       // set or improve value
            Q.push( { ii, jj } );                                        // add to current front line
         }
      }
   }

   return F;
}

//---------------------------------------------------------- 


How would I code this to be able to call it using "this->" or is that possible here?
Currently getting an error "invalid use of tempalte-name 'std::vector' without an argument list.
Currently getting an error "invalid use of tempalte-name 'std::vector' without an argument list.

M'ok, so which line(s) of code does the error point to?

We aren't mind readers, nor are we looking at the monitor as you try to compile. You have to be our eyes and ears and show/tell us what you see.

Gotta "Lucy 'splain" in more detail, please.
Pages: 12