I want to make a maze in C++, however I keep running into a problem with the right outer wall. I was wondering if you guys know a way so I can make the outer wall. Ive been trying to work from up to down using \n, but when \n is used the next symbol just goes to the left wall. Thanks in advance!
#include <iostream>
#include <vector>
#include <stack>
/* class Maze {
public:
void makeMaze();
void Print() const;
private:
std::vector <std::vector <char>> Maze;
}; */
int main(int argc, char* argv[]) {
const int WIDTH = 4;
const int HEIGHT = 4;
/* std::string seedValue = " ";
HEIGHT = atoi(argv[1]);
WIDTH = atoi(argv[2]);
if (argc > 3) {
seedValue = argv[3];
} */
// row, column
std::vector <std::vector <std::string>> Maze (WIDTH + 1, std::vector<std::string> (HEIGHT + 1));
// Roof
for (int column = 0; column < WIDTH; column++) {
Maze[0][column] = "+---";
}
// Left Wall
for (int row = 1; row < HEIGHT + 1; row++) {
Maze[row][0] = "|\n+";
}
// Floor
for (int i = 1; i < WIDTH + 1; i++) {
Maze[HEIGHT][i] = "---+";
}
// Right Wall
// FIXME
// Print Maze
for (int i = 0; i < Maze.size(); i++) {
for (int j = 0; j < Maze.at(0).size(); j++) {
std::cout << Maze[i][j];
}
std::cout << std::endl;
}
}
You may want to treat your printable maze as a matrix of chars instead of strings:
You can consider each cell of the maze border having a horizontal fill +--- and a vertical fill +|, and a cell_width and cell_height.
Your maze would be then defined as a matrix of chars sized maze_height * cell_height + 1 and maze_width * cell_width + 1. That extra one is needed for the right and bottom borders.
In order to fill the border, you can define two helper functions, fill_horizontal_border_cell and fill_vertical_border_cell. These functions just copy the contents of the strings horizontal_border_fill and vertical_border_fill respectively to the maze matrix.
Finally, you'd need to separately fill the bottom left border corner.
All this code should be properly encapsulated into classes (e.g. MazeView for the printable maze, MazeBorderView for the printable maze border, and so on).
[Demo]
#include <iostream> // cout
#include <string>
#include <vector>
int main(int argc, char* argv[]) {
// Border
const std::string horizontal_border_fill{"+---"};
const std::string vertical_border_fill{"+|"};
auto cell_width{horizontal_border_fill.size()};
auto cell_height{vertical_border_fill.size()};
// Maze
const size_t maze_width = 6;
const size_t maze_height = 5;
std::vector<std::vector<char>> maze(maze_height * cell_height + 1,
std::vector<char>(maze_width * cell_width + 1, ' ')); // + 1 for the right and bottom borders
// Fill border
auto fill_horizontal_border_cell = [&maze, &horizontal_border_fill, &cell_width, &cell_height](size_t row, size_t col) {
row *= cell_height;
col *= cell_width;
for (auto& c : horizontal_border_fill) { maze[row][col++] = c; }
};
auto fill_border_vertical_cell = [&maze, &vertical_border_fill, &cell_width, &cell_height](size_t row, size_t col) {
row *= cell_height;
col *= cell_width;
for (auto& c : vertical_border_fill) { maze[row++][col] = c; }
};
for (size_t col{0}; col < maze_width; ++col) { // horizontal borders
fill_horizontal_border_cell(0, col); // top
fill_horizontal_border_cell(maze_height, col); // bottom
}
for (size_t row{0}; row < maze_height; ++row) { // vertical borders
fill_border_vertical_cell(row, 0); // top
fill_border_vertical_cell(row, maze_width); // bottom
}
maze[maze_height * cell_height][maze_width * cell_width] = horizontal_border_fill[0]; // bottom left border corner
// Print maze
for (size_t row{0}; row < maze.size(); ++row) {
for (size_t col{0}; col < maze[0].size(); ++col) {
std::cout << maze[row][col];
}
std::cout << "\n";
}
}
// Outputs:
//
// +---+---+---+---+---+---+
// | |
// + +
// | |
// + +
// | |
// + +
// | |
// + +
// | |
// +---+---+---+---+---+---+
You can keep your current implementation for the most part. The only issue is \n. Do not have any of those, they are not needed and will only give you problems. Your std::cout << std::endl; makes a new line after each row, that's all you need. So, do not include \n in neither the left, nor the right wall
To also "create" the right wall, you can copy what you did for the left wall, but with some minor adjustments:
// Right wall
for (int row = 1; row < HEIGHT + 1; row++) {
Maze[row][WIDTH] = "|";
}
Also, what are the pluses for? (+)
You don't have to merge the strings together in order to make it work. Your std::cout << Maze[i][j]; simply adds each string to end of the previous, until you send the endl to make a new line.
So, I would say get rid of the pluses, (unless it is for styling).
The left wall should look like this:
// Left Wall
for (int row = 1; row < HEIGHT + 1; row++) {
Maze[row][0] = "|";
}
Related
I am given a square matrix size n and characters that the matrix contains.
For example:
3
one
two
row
I have to rotate the matrix by 45 degrees.
| | |o| | |
|o|n|e| | |t| |n| |
|t|w|o| -> |r| |w| |e|
|r|o|w| | |o| |o| |
| | |w| | |
I get
| | |o| | |
|o|n|e| | |t| |n| |
|t|w|o| -> |r| |w| |e|
|r|o|w| | |o|w|o| |
| | | | | |
This is due to rounding values.
I wrote the code. My solution is very limited, I represent it only to show the Idea and my mistakes.
The main thing I don't understand is how to store characters in array so that they have right place (coordinates x, y) after rounding floating point values to integer values. Is there a proper way of doing it?
Are there any advices or observations?
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cmath>
struct Coordinates
{
int x, y;
};
int main()
{
int n;
std::ifstream fin("U3.txt");
fin >> n;
fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::vector<std::vector<char>> matrix;
for (int i = 0; i < n; i++)
{
std::string temp_s;
std::vector<char> temp_v(n);
std::getline(fin, temp_s, '\n');
for (int j = 0; j < n; j++)
{
temp_v[j] = temp_s[j];
}
matrix.push_back(temp_v);
}
fin.close();
std::vector<Coordinates> cord(n * n); // Store coordinates after rotation
int index = 0;
for (int y = 0; y < n; y++)
{
for (int x = 0; x < n; x++)
{
// Multiplying two matrices
/*
[ cos(45) -sin(45) ] [ x ]
[ sin(45) con(45) ] [ y ]
=
[ sqrt(2)/2 -sqrt(2)/2 ] [ x ]
[ sqrt(2)/2 sqrt(2)/2 ] [ y ]
=
[ sqrt(2)/2 * (x - y) ]
[ sqrt(2)/2 * (x + y) ]
*/
double new_x = (std::sqrt(2) / 2 * (x - y));
double new_y = (std::sqrt(2) / 2 * (x + y));
// Trying to round value to int because the index of array is int
cord[index].x = (new_x >= 0.0 ? std::ceil(new_x) : std::floor(new_x));
cord[index].y = (new_y >= 0.0 ? std::ceil(new_y) : std::floor(new_y));
index++;
}
}
/*
Finding the min_x and min_y to know how much should I add to
the new x and y coordinates to keep the coordinates positive,
as I have to store them in array.
*/
int min_x = std::numeric_limits<int>::max();
int min_y = std::numeric_limits<int>::max();
for (int i = 0; i < n * n; i++)
{
if (min_x > cord[i].x) { min_x = cord[i].x; }
if (min_y > cord[i].y) { min_y = cord[i].y; }
}
// If there are no negative coordinates then there is nothing to add
// So I initialize min_x and min_y to 0
if (min_x >= 0) { min_x = 0; }
if (min_y >= 0) { min_y = 0; }
std::vector<std::vector<char>> new_matrix(10, std::vector<char>(10, ' '));
int row = 0, column = 0;
for (int i = 0; i < cord.size(); i++)
{
new_matrix[cord[i].y + min_y * (-1)][cord[i].x + min_x * (-1)] = matrix[row][column];
if ((i + 1) % n == 0) { row++; column = 0; continue; }
column++;
}
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
std::cout << new_matrix[i][j];
}
std::cout << std::endl;
}
return 0;
}
Hm, in my understanding, your algorithm using a rotation matrix will not work. You will always have problems with rounding. And with the correct placement of the values at the correct position in the destination matrix.
And it is even not needed, because the indices for the 2 dimensional can be calculated really simply, without any float operation.
To find a better approach, let us first analyse the possible data:
As you can see. The "next value" of an original row, has to be placed always one to the right and one down. So, for n=3. Print 1, goto right, go down, print 2, goto right, go down. And so on.
If a new source row starts, then the destination start row and start column, are the above value, but then one left and one down. And then again the same as above. Right, down, right, down. And so on and so on.
So, very simple.
Then, the size of the new matrix is always 2*n-1, because we will always have a space between 2 characters.
The destination start row is of course also 0 and the destination start column is simply n-1.
All indices are of course 0 based.
With that approach, no shifting is necessary. Target values can be placed at the correct coordinates immediately.
The solution is then straightforward:
#include <string>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <iterator>
const std::string sourceFileName{ "U3.txt" };
int main() {
// Open the source File and check, if it could be opened
if (std::ifstream sourceFileStream{ sourceFileName }; sourceFileStream) {
// Read the size of the matrix (Must be > 0). Eat trailing white spaces
if (size_t matrixSize{}; (sourceFileStream >> matrixSize >> std::ws) and (matrixSize > 0)) {
// Read the complete source file
std::vector sourceRow(std::istream_iterator<std::string>(sourceFileStream), {});
// Define a 2 dim vector for target. Size is 2*n-1
std::vector<std::vector<char>> destination(matrixSize*2-1, std::vector<char>(matrixSize*2-1, ' '));
// Set start indices for destination. Please note: XStart, or Column start is always matrixSize-1
size_t startOffsetIndexDestinationColumn{ matrixSize - 1 };
size_t startOffsetIndexDestinationRow{};
// Iterate over all source rows
for (size_t row{}; (row < matrixSize) and (row < sourceRow.size()); ++row) {
// Calculate offset for coordinates in destination table
size_t offsetRow{ startOffsetIndexDestinationRow };
size_t offsetColumn{ startOffsetIndexDestinationColumn - row};
// Iterate over source columns in rows and assign value to calculated destination coordinates
for (size_t column{}; (column < matrixSize) and (column < sourceRow[row].size()); ++column) {
// Assign value
destination[row + offsetRow++][column + offsetColumn] = sourceRow[row][column];
}
}
// Show result to user. For each row in the destination vector
for (const auto& row : destination) {
// And for each column in this row
for (const char c : row) std::cout << c;
// Next row
std::cout << '\n';
}
}
else std::cerr << "\nError: Wrong dimension in source file ' " << sourceFileName << "'\n";
}
else std::cerr << "\nError: Could not open source file '" << sourceFileName << "'\n";
return 0;
}
So, selecting the proper algorithm will save you a lot of headaches.
I'm building Space Invaders in C++ (using the MBed platform) for a microcontroller. I've used a 2D Vector of object pointers to organise the invaders.
The movement algorithm is below, and runs in the main while loop for the game. Basically, I get the highest/lowest x and y values of invaders in the vector, and use those to set bounds based on screensize (the HEIGHT variable);
I also get the first invader's position, velocity, and width, which I apply changes to based on the bounds above.
Then I iterate through the whole vector again and apply all those changes. It sort of works – the invaders move – but the bounds don't seem to take effect, and so they fly off screen. I feel like I'm missing something really dumb, thanks in advance!
void Army::move_army() {
int maxy = HEIGHT - 20;
int Ymost = 0; // BOTTOM
int Yleast = 100; // TOP
int Xmost = 0; // LEFT
int Xleast = 100; // RIGHT
int first_row = _rows;
int first_column = _columns;
int firstWidth = 0;
Vector2D firstPos;
Vector2D firstVel;
for (int i = 0; i < _rows; i++) {
for (int n = 0; n < _columns; n++) {
bool state = invaders[i][n]->get_death();
if (!state) {
if (i < first_row && n < first_column) {
firstPos = invaders[i][n]->get_pos();
firstVel = invaders[i][n]->get_velocity();
firstWidth = invaders[i][n]->get_width();
}
Vector2D pos = invaders[i][n]->get_pos();
if (pos.y > Ymost) {Ymost = pos.y;} // BOTTOM
else if (pos.y < Yleast) {Yleast = pos.y;} // TOP
else if (pos.x > Xmost) {Xmost = pos.x;} // LEFT
else if (pos.x < Xleast) {Xleast = pos.x;} // RIGHT
}
}
}
firstVel.y = 0;
if (Xmost >= (WIDTH - 8) || Xleast <= 2) {
firstVel.x = -firstVel.x;
firstPos.y += _inc;
// reverse x velocity
// increment y position
}
else if (Ymost > maxy) {
_inc = -_inc;
// reverse increment
}
else if (Yleast < 2) {
_inc = -_inc;
// reverse increment
}
for (int i = 0; i < _rows; i++) {
int setx = firstPos.x;
if (i > 0) {firstPos.y += 9;}
for (int n = 0; n < _columns; n++) {
invaders[i][n]->set_velocity(firstVel);
invaders[i][n]->set_pos(setx,firstPos.y);
setx += firstWidth + 2;
}
}
It looks like you have your assignment cases reversed. Assignment always goes: right <- left, so in the first case you're changing the YMost value, not pos.y. It looks like if you swap those four assignments in your bounds checking it should work. Good luck!
I am new with OpenCV. I am working on Visual Studio 2017 and use the plugin Image Watch to see Mat file of openCV.
What I've done:
I have to read a binary file to get 1000 images (256*320 pixels uint16 so 2 octets by pixel) in an array of double. After this, I wanted to see with Image Watch my data to be sure all is okay. So I convert the first image into a uchar on 8 bit to visualise it. I add my code (most part don't read it, just go to the end) :
#include "stdafx.h"
#include <iostream>
#include "stdio.h"
#include <fstream>
#include <stdint.h>
#include "windows.h"
#include <opencv2/core/core.hpp> // cv::Mat
#include <math.h>
#include <vector>
using namespace std;
using namespace cv;
template<class T>
T my_ntoh_little(unsigned char* buf) {
const auto s = sizeof(T);
T value = 0;
for (unsigned i = 0; i < s; i++)
value |= buf[i] << CHAR_BIT * i;
return value;
}
int main()
{
ifstream is("Filename", ifstream::binary);
if (is) {
// Reading size of the file and initialising variables
is.seekg(0, is.end);
int length = is.tellg();
int main_header_size = 3000;
int frame_header_size = 1000;
int width = 320, height = 256, count_frames = 1000;
int buffer_image = width * height * 2;
unsigned char *data_char = new unsigned char[length]; // Variable which will contains all the data
// Initializing 3D array for stocking all images
double ***data;
data = new double**[count_frames];
for (unsigned i = 0; i < count_frames; i++) {
data[i] = new double*[height];
for (unsigned j = 0; j < height; j++)
data[i][j] = new double[width];
}
// Reading the file once
is.seekg(0, is.beg);
is.read(reinterpret_cast<char*>(data_char), length);
// Convert pixel by pixel uchar into uint16 (using pointer on data_char)
int indice, minid = 65536.0, maxid = 0.0;
for (unsigned count = 0; count < count_frames; count++) {
// Initialize pointer address
indice = main_header_size + count * (frame_header_size + buffer_image) + frame_header_size;
for (unsigned i = 0; i < height; i++) {
for (unsigned j = 0; j < width; j++) {
data[count][i][j] = my_ntoh_little<uint16_t>(data_char + indice);
// Search for min/max for normalize after
if (data[count][i][j] < minid and count == 0)
minid = data[count][i][j];
if (data[count][i][j] > maxid and count == 0)
maxid = data[count][i][j];
// Updating pointer to next pixel
indice += 2;
}
}
}
// Get back first image, normalize between 0-255, cast into uchar to the future Mat object
uchar *dataImRGB = new uchar[width * height * 3];
int image_display = 900;
int pixel_norm;
for (unsigned i = 0; i < height; i++) {
for (unsigned j = 0; j < width; j++) {
pixel_norm = round((data[image_display][i][j] - double(minid)) / double(maxid - minid) * 255);
dataImRGB[i * 320 * 3 + 3 * j] = static_cast<uchar>(pixel_norm);
dataImRGB[i * 320 * 3 + 3 * j + 1] = static_cast<uchar>(pixel_norm);
dataImRGB[i * 320 * 3 + 3 * j + 2] = static_cast<uchar>(pixel_norm);
}
}
// Create Mat object (it is imageRGB8 I can see on Image watch)
Mat imageRGB8 = Mat(width, height, CV_8UC3, dataImRGB);
// Creating a list of Map and add first Mat
vector<Mat> listImages;
listImages.push_back(imageRGB8);
// -----------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------
// Future : directly keep the uchar read in the original file and import it on a Mat object
// But how to get the pixel at (0,0) of the first Mat on the vector ?
// -----------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------
// De-Allocate memory to prevent memory leak
for (int i = 0; i < count_frames; ++i) {
for (int j = 0; j < height; ++j)
delete[] data[i][j];
delete[] data[i];
}
delete[] data;
}
return 0;
}
Where I am stuck:
I don't know how to work with this vector, how to manipulate the data. For example, if i want to do the mean of all images, so the mean of all Mat objects in the vector, how to do this ? Or just how to get the first pixel of the third image in the vector ? These examples have for aim to explain me the slicing with such type of data because I know how it works with vector of double, but not with openCv object.
Thank you in advance for any help/advice.
Assuming that you have got all of your images properly packed into your image list you can do the following:
This will get the mean of all images in your list:
cv::Scalar meansum(0.0f,0.0f,0.0f);
size_t length = listImages.size();
for (size_t i = 0; i < length; i++){
//mu == mean of current image
cv::Scalar mu = cv::mean(listImages[i]);
meansum += mu;
}
float means[3] = { meansum[0] / length, meansum[1] / length, meansum[2] / length };
std::cout << "Means " << means[0] << " " << means[1] << " " << means[2] << std::endl;
To get the first pixel in your third image you can use the at() method or a row pointer. (Row pointers are faster, but don't have any guards against accessing out of bounds memory locations.)
Mat third_image = list_images[2];
//using at()
uchar first_pixel_blue_value = third_image.at<uchar>(0,0,0);
std::cout<<(int)first_pixel_blue_value<<std::endl;
//using row pointer
uchar* row = third_image.ptr<uchar>(0); //pointer to row 0
std::cout<<"blue: " <<(int)row[0];
std::cout<<" green: "<<(int)row[1];
std::cout<<" red: " <<(int)row[2];
More info can be found here:
https://docs.opencv.org/3.1.0/d2/de8/group__core__array.html (under functions)
and here:
https://docs.opencv.org/trunk/d3/d63/classcv_1_1Mat.html
I would like to create a function which initializes a vector or array of size width * height, but which also creates a border around these values.
The values around the outside also need to be initialized to a different value from the ones in the center.
The objects I am storing do not have a default constructor, so I cannot rely on that for initialization.
This is the code I have so far, but it feels like there should be a simpler or more idiomatic way of doing this.
I can use any features up to and including C++1z.
#include <iostream>
#include <vector>
void fill_values(const unsigned width, const unsigned height, std::vector<int> &values) {
for(unsigned y=0; y<height+2; ++y) {
for(unsigned x=0; x<width+2; ++x) {
if(x==0 || x==width+1 || y==0 || y==height+1) {
values.push_back(1);
} else {
values.push_back(0);
}
}
}
}
int main(int argc, char *argv[]) {
const unsigned width = 4;
const unsigned height = 3;
std::vector<int> values;
fill_values(width, height, values);
for(unsigned y=0; y<height+2; ++y) {
for(unsigned x=0; x<width+2; ++x) {
std::cout << values[y * (width+2) + x];
}
std::cout << '\n';
}
return 0;
}
Output : -
111111
100001
100001
100001
111111
Honestly, your code is fine. I pretty easily understood what it does.
But in the spirit of proposing alternate complex implementations, I'd propose the following. A different way to fill the matrix is to add a full row of 1s, then height rows of 1000...001, then another full row of 1s. We can make that a bit more explicit. Also, would suggest returning a vector instead of filling it:
std::vector<int> fill_values(const unsigned width, const unsigned height) {
std::vector<int> m;
m.reserve((width + 2) * (height + 2));
// add row of 1s
m.insert(m.end(), width + 2, 1);
// add height middle rows
for (int i = 0; i < height; ++i) {
m.push_back(1);
m.insert(m.end(), width, 0);
m.push_back(1);
}
// and a final row of 1s
m.insert(m.end(), width + 2, 1);
return m;
}
As #Fedorico said in the comments, using a vector of vectors is a better representation for your values variable. Rather than pass the values by reference as a parameter, it would be better to depend on copy elision for the return value. I also found it easier to just use the set height and width to be the total number of rows and cols in the data so that there's no need to add two.
The following code depends on c++11 or newer:
#include <iostream>
#include <vector>
using namespace std;
// Fills the 2D matrix with 1s on the border and 0s in the middle.
vector<vector<int>> generate_matrix(int rows, int cols);
void print_matrix(const vector<vector<int>>& matrix);
int main()
{
// Don't sync I/O with C stdio.
ios_base::sync_with_stdio(false);
// Height and Width of the entire 2D matrix.
const int rows = 6;
const int cols = 5;
vector<vector<int>> matrix = generate_matrix(rows, cols);
print_matrix(matrix);
return 0;
}
vector<vector<int>> generate_matrix(int rows, int cols)
{
// fill a rows x cols 2D vector with 0s.
vector<vector<int>> matrix(rows, vector<int>(cols, 0));
// fill in 1s on top and bottom rows.
if (rows > 0)
{
for (int i = 0; i < cols; ++i)
{
matrix[0][i] = 1;
matrix[rows-1][i] = 1;
}
}
// fill in 1s on the left and right columns.
if (cols > 0)
{
for (int i = 0; i < rows; ++i)
{
matrix[i][0] = 1;
matrix[i][cols-1] = 1;
}
}
return matrix;
}
void print_matrix(const vector<vector<int>>& matrix)
{
// Use a reference for the row iterator to prevent a vector copy.
for (auto& row : matrix)
{
for (auto element : row)
{
cout << element;
}
cout << '\n';
}
}
Not a great difference, but you can use std::generate_n() (starting from c++11) with a lambda function.
The following is a full working example
#include <vector>
#include <iostream>
#include <algorithm>
int main ()
{
constexpr std::size_t width { 4U };
constexpr std::size_t height { 3U };
constexpr std::size_t w2 { width + 2U };
constexpr std::size_t h2 { height + 2U };
std::vector<int> values;
values.resize ( w2 * h2 );
std::generate_n(values.begin(), w2 * h2, [=]() -> int
{
static std::size_t i = -1;
++i;
return ( 0U == i / w2 ) || ( h2 - 1U == i / w2 )
|| ( 0U == i % w2 ) || ( w2 - 1U == i % w2 );
});
for(unsigned y=0; y<height+2; ++y) {
for(unsigned x=0; x<width+2; ++x) {
std::cout << values[y * (width+2) + x] << ' ';
}
std::cout << '\n';
}
return 0;
}
If width and heigth are known at compile time, you can initialize the std::vector (or the std::array?) with initializer list, using a little template work (give me some time and I'll show an example).
I am working with a dynamic square 2D array that I sometimes need to enlarge for my needs. The enlarging part consist in adding a new case on each border of the array, like this:
To achieve this, I first copy the content of my actual 2D array in a temporary other 2D array of the same size. Then I create the new 2D array with the good size, and copy the original content of the array in the middle of the new one.
Is there any quick way to copy the content of the old array in the middle of my new array? The only way I have found so far is only by using two for sections:
for(int i = 1; i < arraySize-1; i++)
{
for(int j = 1; j < arraySize-1; j++)
{
array[i][j] = oldArray[i-1][j-1];
}
}
But I'm wondering if there is no quicker way to achieve this. I thought about using std::fill, but I don't see how it would be possible to use it in this particular case.
My EnlargeArray function:
template< typename T >
void MyClass<T>::EnlargeArray()
{
const int oldArraySize = tabSize;
// Create temporary array
T** oldArray = new T*[oldArraySize];
for(int i = 0; i < oldArraySize; i++)
{
oldArray[i] = new T[oldArraySize];
}
// Copy old array content in the temporary array
for(int i = 0; i < arraySize; i++)
{
for(int j = 0; j < arraySize; j++)
{
oldArray[i][j] = array[i][j];
}
}
tabSize+=2;
const int newArraySize = arraySize;
// Enlarge the array
array= new T*[newArraySize];
for(int i = 0; i < newArraySize; i++)
{
array[i] = new T[newArraySize] {0};
}
// Copy of the old array in the center of the new array
for(int i = 1; i < arraySize-1; i++)
{
for(int j = 1; j < arraySize-1; j++)
{
array[i][j] = oldArray[i-1][j-1];
}
}
for(int i = 0; i < oldArraySize; i++)
{
delete [] oldArray[i];
}
delete [] oldArray;
}
Is there any quick way to copy the content of the old array in the middle of my new array?
(Assuming the question is "can I do better than a 2D for-loop?".)
Short answer: no - if your array has R rows and C columns you will have to iterate over all of them, performing R*C operations.
std::fill and similar algorithms still have to go through every element internally.
Alternative answer: if your array is huge and you make sure to avoid
false sharing, splitting the copy operation in multiple threads that deal with a independent subset of the array could be beneficial (this depends on many factors and on the hardware - research/experimentation/profiling would be required).
First, you can use std::make_unique<T[]> to manage the lifetime of your arrays. You can make your array contiguous if you allocate a single array of size row_count * col_count and perform some simple arithmetic to convert (col, row) pairs into array indices. Then, assuming row-major order:
Use std::fill to fill the first and last rows with zeros.
Use std::copy to copy the old rows into the middle of the middle rows.
Fill the cells at the start and end of the middle rows with zero using simple assignment.
Do not enlarge the array. Keep it as it is and allocate new memory only for the borders. Then, in the public interface of your class, adapt the calculation of the offets.
To the client of the class, it will appear as if the array had been enlarged, when in fact it wasn't really touched by the supposed enlargement. The drawback is that the storage for the array contents is no longer contiguous.
Here is a toy example, using std::vector because I cannot see any reason to use new[] and delete[]:
#include <vector>
#include <iostream>
#include <cassert>
template <class T>
class MyClass
{
public:
MyClass(int width, int height) :
inner_data(width * height),
border_data(),
width(width),
height(height)
{
}
void Enlarge()
{
assert(border_data.empty()); // enlarge only once
border_data.resize((width + 2) * 2 + (height * 2));
width += 2;
height += 2;
}
int Width() const
{
return width;
}
int Height() const
{
return height;
}
T& operator()(int x, int y)
{
assert(x >= 0);
assert(y >= 0);
assert(x < width);
assert(y < height);
if (border_data.empty())
{
return inner_data[y * width + x];
}
else
{
if (y == 0)
{
return border_data[x]; // top border
}
else if (y == height - 1)
{
return border_data[width + x]; // bottom border
}
else if (x == 0)
{
return border_data[width + height + y]; // left border
}
else if (x == width - 1)
{
return border_data[width * 2 + height * 2 + y]; // right border
}
else
{
return inner_data[(y - 1) * (width - 2) + (x - 1)]; // inner matrix
}
}
}
private:
std::vector<T> inner_data;
std::vector<T> border_data;
int width;
int height;
};
int main()
{
MyClass<int> test(2, 2);
test(0, 0) = 10;
test(1, 0) = 20;
test(0, 1) = 30;
test(1, 1) = 40;
for (auto y = 0; y < test.Height(); ++y)
{
for (auto x = 0; x < test.Width(); ++x)
{
std::cout << test(x, y) << '\t';
}
std::cout << '\n';
}
std::cout << '\n';
test.Enlarge();
test(2, 0) = 50;
test(1, 1) += 1;
test(3, 3) = 60;
for (auto y = 0; y < test.Height(); ++y)
{
for (auto x = 0; x < test.Width(); ++x)
{
std::cout << test(x, y) << '\t';
}
std::cout << '\n';
}
}
Output:
10 20
30 40
0 0 50 0
0 11 20 0
0 30 40 0
0 0 0 60
The key point is that the physical representation of the enlarged "array" no longer matches the logical one.