1

I have a grid of squares. Each square is either black or white. The grid ranges from X:-10 to 10 and Y:-10 to 10. I want to represent the grid with a 2D array of bools - black is true and white is false. Array indexes are only positive though- so if I want to create the grid with an array it would be bool array [21][21]. This does the job, but it gets confusing when I am trying to access an element. For example, if I want to access the coordinate "0,3" on the grid, my array index would be [11][14]. This works, but its really messy.

Is there any "cleaner" way I could get the index to correspond with the coordinate?

2
  • 1
    If grid ranges from -10 to 10 on each axis, shouldn't your array be a grid of 21 by 21? Commented Feb 22, 2012 at 3:18
  • @AndréCaron Your right. I fixed it. Commented Feb 22, 2012 at 3:19

6 Answers 6

6

You can encapsulate the logic into a class that offers the interface you need. To make it a bit generic, you can consider that the type to be stored and the dimensions could vary:

 template <typename T, int DimX, int DimY>
 class offset_array2d
 {
    T data[ DimX*DimY ];
    static const int offset_x = DimX / 2;
    static const int offset_y = DimY / 2;
 public:
    offset_array2d() : data() {}
    T& operator()( int x, int y ) {
       return data[ (x+offset_x) + (y+offset_y)*DimY ];
    }
    T const & operator()( int x, int y ) const {
       return data[ (x+offset_x) + (y+offset_y)*DimY ];
    }
 };

The implementation probably needs some details, but the general idea is there. There should be error reporting and many more things... the dimensions could be made a runtime property (rather than template argument) but that requires dynamic allocation, a proper destructor and copy constructor... I don't really want to go into all that for just the idea.

The other end of the spectrum is user code, that would be quite simple now:

 int main() {
    offset_array2d<bool,21,21> board;
    for ( int i = -10; i < 11; ++i )
      board( i, i ) = true;          // write the diagonal
 }
Sign up to request clarification or add additional context in comments.

3 Comments

Regarding the constructor: offset_array2d(): data() {} ought to do the job... and I would fathom it should be + instead of - in -offset_x.
@MatthieuM.: You are right on both accounts, thanks for pointing out the errors.
@DavidRodríguez-dribeas: My pleasure :) For once I notice them instead of making them!
3

You could simply access your array through a function, which will calculate the correct offset in the array (add 10 to x and y):

bool grid[21][21];

bool getSquareColour(size_t x, size_t y)
{
   // add bounds checking here
   return grid[x+10][y+10];
}

The same goes for setting the squares. I would wrap all of this into a Grid class.

You might also want to use std::vector<bool> as opposed to bool[], which will store each bool as individual bits and gives you the extra (possibly unneeded) functionality of the std::vector class.

6 Comments

To cover the domain -10 .. +10 you need to set your array to [21][21].
@BicycleDude: Woops, copy+pasted the OP's (now editted) declaration of array and didn't even think to check it, thanks.
@AusCBloke bool grid[21][21] is still smaller than the implementations of std::vector it could be replaced by. Not to even mention the data. Why add the complexity!?
@Daniel: It was just a suggestion for anything else he might do in the future, I'm aware that it's completely unnecessary for such a small thing like this.
Handy trick: change the return type to bool & and then you don't need a setter: getSquareColour(5, 5) = false would work.
|
2

I'm pretty sure this invokes undefined behavior. I'm also pretty sure this works on every architecture I care about.

#include <cassert>


int main () {
   bool grid_[21][21];
   bool (*grid)[21];

   grid = (bool (*)[21])(&grid_[10][10]);
   assert(&grid_[0][0] == &grid[-10][-10]);
   assert(&grid_[0][20] == &grid[-10][10]);
   assert(&grid_[20][20] == &grid[10][10]);
}

Comments

2

You should make a helper function

#define OFFSET 10

void place_elem(int x, int y, bool color){

     //put bounds checks here
     a[OFFSET+x][OFFSET+y] = color;

}

So

place_elem(0, -3, true) == (a[10][7] = true)

If you are worried about the overhead of making a function call for every change to the array, then you could use a macro instead:

#define PLACE_ELEM(x, y, c) (a[OFFSET+x][OFFSET+y] = c)

But, DON'T do this unless you fully understand the safety issues with using macros. Also, if you are using C99 or C++ you can use inline methods/functions. This will do the same thing, but without the hazards.

Also, an enum might be better than a bool

Think:

enum Color {BLACK, WHITE};

2 Comments

Not that it matters in this case, but doesn't an enum typically take up the size of an int? Therefore increasing his size requirements by sizeof(int)? Could be important be in a bigger grid.
You are correct, but sometimes the clarity is worth the trade off
1

Just to provide an alternate answer that's simple to use (but a little hard to implement and maintain), create a C++ class that hides the complexity with a getter and setter. You can also consider overloading operators. Since the field is binary, I've elected to pack the data using bitwise operations:

class SimpleArray
{
public:
  SimpleArray()
  {
    memset(data, 0, sizeof(data));
  }

  void set(int x, int y, bool value)
  {
    if (x >= -10 && x <= 10 && y >= -10 && y <= 10)
    {
      if (value)
      {
          data[y + 10] |= (1 << (x + 10));
      }
      else
      {
          data[y + 10] &= ~(1 << (x + 10));
      }
    }
  }

  bool get(int x, int y)
  {
    if (x >= -10 && x <= 10 && y >= -10 && y <= 10)
    {
      return (data[y + 10] & (1 << (x + 10))) != 0;
    }
    return false;
  }

  private:
    unsigned int data[21];
};

Comments

0

Instead of incrementing/decrementing your indexes I would create a map of ints (map) where you would assign a specific index to your specific value.

You would then pass the value of the map for your square value (e.g. you pass the result of the map in your square tables for key value -10 => 0 (index)).

You have an example of maps here: http://www.yolinux.com/TUTORIALS/CppStlMultiMap.html

Sure maps use more memory than a simple function but its better for re-usability.

Comments

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.