0

Let's say I have a 2D array that represents a board game. I have a list of "values" (keys) (1, 2, 3 ... 12) that represent positions in the array that are relative to the current position.

For example, in array[1][1], the key 1 represents the position left array[1][0] while the key 2 might represent the position left and above it array [0][0].

Is there any way to store these two data (I'd like to avoid a bunch of if-statements every time I use the values) in a HashMap? Or, in any data structure? Is this the right time to create an enum?

I tried this, which clearly doesn't work.

int row = 3;
int col = 5;
HashMap<Integer,String> markMap = new HashMap<>();
markMap.put(1,"col-1");
String location = markMap.get(1);
grid[row][(int)location] = 500;
Michael
  • 776
  • 4
  • 19

3 Answers3

2

Take advantage of OOP and make an object! Store an array of "delta" location objects which would be a pair of delta-x, delta-y that stores the relative location of the current index.

In your example,

int row = 1;
int col = 1;
// one left
array[row][col] = new DeltaLocation(-1,0); // (col, row) or (x, y) 

int relativeCol = col + array[row][col].getDeltaX();

You could either place these into a Hashmap, or implement the DeltaLocation object to hold a value as well. Up to you.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • This solution is actually similar to using enums. The advantage with this one is that you can dynamically create more delta-x and delta-y at runtime if necessary. Enums are fine too if you're okay with fixing your deltas statically. – mkzh Jul 18 '16 at 00:24
  • @mkzh, There doesn't need to be N^2 enums though. What if we are looking more than one position in any direction? What if the delta position goes outside the array? – OneCricketeer Jul 18 '16 at 00:28
  • enums **are** actual objects – Hovercraft Full Of Eels Jul 18 '16 at 00:29
  • ^ Any method an object has an enum can have as well. Enums in java are basically a generalization of the static singleton pattern for multiple instances. – mkzh Jul 18 '16 at 00:31
1

There are many solutions that would work. One that comes to mind is storing the row offset and the column offset in two different maps. For instance

int row = 3
int col = 5
HashMap<Integer, Integer> rowOffset = new HashMap<>();
HashMap<Integer, Integer> colOffset = new HashMap<>();
rowOffset.put(1, 0)
colOffset.put(1, -1)
grid[row + rowOffset.get(1)][col + colOffset.get(1)] = 500

It would probably be cleaner to make an object that stores both the row and column offsets, but this should give you the idea.

Evan VanderZee
  • 797
  • 6
  • 10
  • I like the idea of two HashMaps, but you said it'd be cleaner to make an object with both offsets. By that, are you referring to the objects/enums that the others proposed? – Michael Jul 18 '16 at 01:48
  • The other answers had not been added at the time that I posted my answer, so I was not referring to them. The objects that you get from the Direction enum, which have both a row and column offset in the object, are like what I had in mind, though I was thinking along the lines of putting objects like the Direction object in a HashMap with an int key. The enum gives you a different way to access them, so the HashMap isn't needed. The tradeoff is that you have to know ahead of time all the pairs of offsets you need to use. – Evan VanderZee Jul 18 '16 at 17:24
1

Great suggestions so far. Building off of everyone else, you can also create an enum of offsets

enum Direction {
    LEFT(-1, 0),
    UPPERLEFT(-1, -1),
    DOWN(0, - 1),
    ...;

    public final int xoffset;
    pubiic final int yoffset;

    Direction(int xoffset, int yoffset) {
        this.xoffset = xoffset;
        this.yoffset = yoffset;
    }

    public static GridObject getRelativeItem(GridObject[][] grid, int x, int y, Direction dir) {
        return grid[x + dir.xoffset][y + dir.yoffset];
    }

    public static void setRelativeItem(GridObject[][] grid, int x, int y, Direction dir, GridObject newValue) {
        grid[x + dir.xoffset][y + dir.yoffset] = newValue;
    } 
}

If you stick with this design, you can access grid items by calling (if you wanted to access the left of (1, 1)

Direction.getRelativeItem(grid, 1, 1, LEFT)

To set, you can likewise call this to set value:

Direction.setRelativeItem(grid, 1, 1, LEFT, myValue)

Though this is awkward and admittedly reeks of poor abstraction. Alternatively you can define getters for the offsets (add instance methods xoffset and yoffset that just return the private variable values). Then you would have static objects LEFT, UPPERLEFT, DOWN much like cricket_007's solution. In this case if you wanted to get a value, you can call

grid[x + LEFT.xoffset()][y + LEFT.yoffset()]

to set

grid[x + LEFT.xoffset()][y + LEFT.yoffset()] = myValue;

By definition, you cannot instantiate an enum yourself. Enums are initialized by the JVM, and there are only a fixed number of them (in this case LEFT, UPPERLEFT, DOWN...).

Community
  • 1
  • 1
mkzh
  • 196
  • 1
  • 8
  • I think I understand the enum. Can you show a quick few lines on how I would construct/refer to/use the enum in my main class? For example, if I want to change the value of a specific location. – Michael Jul 18 '16 at 01:43
  • I updated my answer with a little more details. I would personally add getters (it looks a little bit better, but still isn't great). Note that you are allowed to perform operations in the array accessor operator, but casting does not evaluate an expression. I noticed in your own answer you have `grid[row][(int)location] = 500`. The reason this won't work is because at Runtime Java will try to cast the string "col - 1" to an int, which it does not know how to do (only simple numbers are handled). Unfortunately you have to write out the expression in the array access yourself at some point. – mkzh Jul 18 '16 at 04:16