1

So I wanted to create a game with a square world.

When starting the game, the user should be able to specify the size of the world.

The world is saved as a two-dimensional array of shorts.

const short gameSize = 4;
short world[gameSize][gameSize];

The short gameSize must be const, otherwise, I can't put gameSize as the size of world[][].

However, this won't allow me to set gameSize to the player-wished size.

I thought of something simple like

short gameSize = 0;
cout << "World size?" << endl;
cin >> gameSize;
short world[gameSize][gameSize];

As stated, this won't work.

In the game, it won't be possible to change the value of gameSize later on.

How could I make this happen?

8
  • Does this answer your question? How to create a dynamic array of integers Commented Jun 3, 2021 at 13:11
  • 1
    Does this answer your question? How do I declare a 2d array in C++ using new? Commented Jun 3, 2021 at 13:11
  • 2
    @Edex, while the linked question is technically the correct answer to your question, it's almost certainly not what you want to do in your specific circumstance. Commented Jun 3, 2021 at 13:23
  • 2
    @yaodav Almost all answers to that question are terrible. It’s a very bad canonical duplicate. In fact, that question should probably be nuked from orbit. Commented Jun 3, 2021 at 13:25
  • 2
    @Frank The original question is indeed fine, as a theoretical C++ language question. The problem with that question is that, based on its views and activity, it has become the canonical link target for all kinds of questions, and that’s actively harmful. Commented Jun 3, 2021 at 13:39

1 Answer 1

6
  1. Don't use new as the linked questions/answers would lead you to do. It's unnecessary in your case and will increase the risk of potential bugs for no good reason.
  2. Use std::vector<short> to manage an indexable chunk of memory storing the world values.
  3. Convert 2D coordinates into an index as needed.
  4. (optional) Encapsulate it all in a class so it's hidden
#include <vector>
#include <cassert>
#include <iostream>

class GameWorld {
  std::size_t world_size_;
  std::vector<short> data_;

public:
  GameWorld(std::size_t size) 
    : world_size_(size) 
    , data_(size * size) {}

  short& operator()(std::size_t x, std::size_t y) {
    assert(x < world_size_ && y < world_size_);

    return _data[y + x * world_size_];
  }

  const short& operator()(std::size_t x, std::size_t y) const {
    assert(x < world_size_ && y < world_size_);

    return _data[y + x * world_size_];
  }
};


int main() {
  short gameSize = 0;
  std::cout << "World size?" << std::endl;
  std::cin >> gameSize;
  GameWorld world(gameSize);

  // Set the value of [0, 0] to 4
  world(0, 0) = 4;
}

There's a few things happening here:

  1. Using vector<short> will give you a dynamic size as well as memory safety. i.e. Things will get cleaned up "automatically" as appropriate.
  2. You might be tempted to use vector<vector<short>>, so that world[x][y] "just works". But that's not great because the memory will be all over the place, packing it tightly into a one-dimensional array gives you a bunch of performance benefits in general.
  3. You might "think" that calling a function and doing math on indices is expensive, but it's actually the exact same thing the compiler does when you call world[x][y] in your previous code, so no additional cost is incurred here.
  4. The assert()s will catch potential bugs when building in debug mode, and disappear entirely in release/NDEBUG mode. So you have an additional "free" safety net .

Bonus: If you want to start dipping your toes into template programming, this is a great starting point to learn. Templating GameWorld so that it can be a world of float, int, or some struct Cell instead of always being a world of short is useful, easy to do, and not disruptive to the rest of the code.

Sign up to request clarification or add additional context in comments.

20 Comments

Re (1): OP does not mention new. It’s extremely unfortunate that the chosen duplicate does.
@KonradRudolph The question was marked as a duplicate of something specific to new. I felt like specifying that this is a bad idea in OP's case is warranted. so I updated the answer with step 1.
In your answer, you wrote: "packing it tightly into a one-dimensional array gives you a bunch of performance benefits" -- That will mainly increase performance when accessing one dimension contiguously, but not so much when accessing the other dimension contiguously. Therefore, the game Factorio instead divides the game world into "chunks" of 32*32 tiles (total 1024 tiles), which are each stored in memory contiguously, so that CPU cache performance is good for both dimensions.
@Lundin Personally, while you are technically correct, I think overfocusing on the allocation cost for a class that is clearly meant to ever be allocated once in a blue moon is grasping a straws, especially when your alternative only works for "small" allocations when used on the stack. The removed indirection benefit is much more real.
@Frank Good point. My own attempt was looking at the codegen for a loop, and the codegen in that case is identical since the accessor inside the loop is inlined and thus doesn’t need the added indirection (except once, at the very beginning).
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.