Я пишу 2D платформер, где мне нужны комнаты с (максимум 4) дверями. Я пишу это на Java, но язык не имеет значения.
Каждая комната может иметь 4 двери, сверху, снизу и по бокам. Я называю их NORTH
, SOUTH
, EAST
и WEST
. Когда я строю комнату, я даю ей только целое число, где каждый бит целого числа представляет дверь.
Например, если мне нужна комната с 3 дверями (одна на севере, одна на востоке и одна на западе), я даю комнате номер: 11 (1011 в двоичном формате).
По этой причине каждая дверь имеет целое число, идентифицирующее ее.
NORTH = 8;//1000
SOUTH = 4;//0100
EAST = 2;//0010
WEST = 1;//0001
Если я создаю комнату, я даю ей комбинацию этих идентификаторов.
Например: указанная ранее комната получит идентификатор
doorStock = NORTH | EAST | WEST;
Я храню эти двери в простом массиве:
Door doors[] = new Door[4];
Моя проблема: мне нужна функция, которая может сопоставлять идентификаторы с правильным индексом в массиве. Мне не всегда нужны 4 двери.
То, что я сделал на первый взгляд, кажется самым простым: массив дверей всегда будет состоять из 4 элементов, а индексы, которые я бы не использовал, просто останутся нулевыми.
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;
}
Чтобы быть в безопасности, мне нужно было определить, действительно ли дверь, которую я запрашиваю, существует в комнате.
private boolean doorExists(int doorID){
return (doorID & doorStock) != 0
}
Таким образом, функция запроса выглядела так:
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;
}
Который работал. НО! Таким образом, массив может тратить место на неиспользуемые элементы. Кроме того, класс Door
потенциально может быть любого размера, что увеличивает потери памяти.
Не говоря уже о том, что мне может понадобиться больше «слотов» для дверей (например, если я попытаюсь реализовать это в 3D), поэтому я решил попробовать и сделать размер массива дверей в зависимости от идентификатора дверей:
Door doors = new Door[Integer.bitCount(doorStock)];
Что очень быстро дало ошибку IndexOutOfBounds
. Меня это не удивило, потому что массив дверей мог быть любого размера от 0 до 4, поэтому мне понадобился новый метод хеширования.
Я придумал две хеш-таблицы, одну для индексов массива:
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*/
};
и один, который помогает в отображении предыдущей таблицы:
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*/
};
поэтому моя текущая функция отображения выглядит так:
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;
}
Что тоже, кажется, работает нормально, но я чувствую, что есть более простое решение этой проблемы или решение с менее расточительными хеш-таблицами. Я чувствую, что это не так асимптотически гибко, как должно быть, или я слишком усложняю вещи. Что было бы лучшим методом?
Спасибо за ваше время!
0000
— недопустимое значение. - person Grim   schedule 08.10.2013