I write a 2D platformer game where I need rooms with (maximum 4) doors. I write it in Java, but the language is irrelevant.
Each room can have 4 doors, on the top, bottom and sides. I call them NORTH
, SOUTH
, EAST
and WEST
. When I'm building a room, I only give it an integer, where each bit in the integer is representing a door.
For example if I want a room with 3 doors (one on the North, one on the East, and on on the west) i give the room the number: 11(1011 in binary).
For this reason each door has an integer, identifying it.
NORTH = 8;//1000
SOUTH = 4;//0100
EAST = 2;//0010
WEST = 1;//0001
If I generate a room, I give it the combination of these identifiers.
For example: the previously mentioned room would get the identifier
doorStock = NORTH | EAST | WEST;
I store these doors in a simple array:
Door doors[] = new Door[4];
My problem is: I need a function which can map the identifiers to the correct index in the array. I don't always need 4 doors.
What I did at first seems the simplest: the doors array would always have 4 elements, and the indices I would not use would simply remain nulls.
public Door getDoor(int doorID){
switch(doorID){
case NORTH:{
return doors[0];
}
case SOUTH:{
return doors[1];
}
case EAST:{
return doors[2];
}
case WEST:{
return doors[3];
}
}
return null;
}
In order to be safe I needed to to determine if the door I'm requesting actually exists in the room.
private boolean doorExists(int doorID){
return (doorID & doorStock) != 0
}
So with this, the query function looked like this:
public Door getDoor(int doorID){
switch(doorID){
case NORTH:{
if(doorExists(NORTH))return doors[0];
else return null;
}
case SOUTH:{
if(doorExists(NORTH))return doors[1];
else return null;
}
case EAST:{
if(doorExists(NORTH))return doors[2];
else return null;
}
case WEST:{
if(doorExists(NORTH))return doors[3];
else return null;
}
}
return null;
}
Which was working. BUT! This way the array could waste space with the unused elements. Plus the class Door
could potentially be any size, increasing the memory waste.
Not to mention I could need more "slots" for doors( for example, If I try to implement this in 3D), so I decided to try and make the doors array's size depending on the identifier for the doors:
Door doors = new Door[Integer.bitCount(doorStock)];
Which gave an IndexOutOfBounds
error real quick. I wasn't surprised, because the doors array could be any size from 0 to 4, so I needed a new hashing method.
What I came up with are two hash tables, one for the array indices:
private final int[][] doorhash = {
/* NORTH SOUTH EAST WEST doorStock*/
{ -1, -1, -1, -1} /*0000*/,
{ -1, -1, -1, 0} /*0001*/,
{ -1, -1, 0, -1} /*0010*/,
{ -1, -1, 0, 1} /*0011*/,
{ -1, 0, -1, -1} /*0100*/,
{ -1, 0, -1, 1} /*0101*/,
{ -1, 0, 1, -1} /*0110*/,
{ -1, 0, 1, 2} /*0111*/,
{ 0, -1, -1, -1} /*1000*/,
{ 0, -1, -1, 1} /*1001*/,
{ 0, -1, 1, -1} /*1010*/,
{ 0, -1, 1, 2} /*1011*/,
{ 0, 1, -1, -1} /*1100*/,
{ 0, 1, -1, 2} /*1101*/,
{ 0, 1, 2, -1} /*1110*/,
{ 0, 1, 2, 3} /*1111*/
};
and one, which helps helps in the mapping of the previous table:
private final int[] directionHash = {
-1, /*0000*/
3, /*0001 - WEST*/
2, /*0010 - EAST*/
-1, /*0011*/
1, /*0100 - SOUTH*/
-1, /*0101*/
-1, /*0110*/
-1, /*0111*/
0, /*1000 - NORTH*/
};
so my current mapping function looks like this:
public Door getDoor(int doorID){
switch(doorID){
case NORTH:{
if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[NORTH]]];
else return null;
}
case SOUTH:{
if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[SOUTH]]];
else return null;
}
case EAST:{
if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[EAST]]];
else return null;
}
case WEST:{
if(doorExists(NORTH))return doors[doorhash[doorStock][directionHash[WEST]]];
else return null;
}
}
return null;
}
Which also seems to work fine, but I feel there's a simpler solution to this problem, or one with less wasteful hash tables. I feel this isn't as asymptotically flexible as it should be, or I am over-complicating things. What would be a better method?
Thank you for your time!
0000
is an illegal value. – Mangum