Пошук напрямку подорожі у світі із загорнутими краями


20

Мені потрібно знайти найкоротший напрямок відстані від однієї точки мого 2D світу до іншої точки, де краї загорнуті (як астероїди тощо). Я знаю, як знайти найкоротшу відстань, але намагаюся знайти напрямок.

Найкоротша відстань задається:

int rows = MapY;
int cols = MapX;

int d1 = abs(S.Y - T.Y);
int d2 = abs(S.X - T.X);
int dr = min(d1, rows-d1);
int dc = min(d2, cols-d2);

double dist = sqrt((double)(dr*dr + dc*dc));

Приклад світу

                   :         
                   :  T    
                   :         
    :--------------:---------
    :              :
    :           S  :
    :              :
    :              :
    :  T           :
    :              :
    :--------------:

На діаграмі краї показані за допомогою: і -. Я також показав завершене повторення світу вгорі праворуч. Я хочу знайти напрямок у градусах від S до T. Отже, найкоротша відстань - до правого верхнього повтору Т., але як я обчислюю напрям, відмічений від S до повторного T у верхньому правому куті?

Я знаю позиції і S, і T, але, мабуть, мені потрібно знайти положення повторного T, однак там більше 1.

Система координат світу починається з 0,0 у верхньому лівому куті, а 0 градусів у напрямку може починатися на Заході.

Здається, це не повинно бути надто важким, але я не зміг розробити рішення. Я сподіваюся, що сомон може допомогти? Будь-які веб-сайти будуть вдячні.


Які координати для Т вгорі праворуч?

Я ніколи не бачив гру з діагональним обгортанням. Зазвичай у вас є по одному обгортку для кожного напрямку (N, E, S, W).

5
Будь-яка гра, яка має горизонтальне та вертикальне обгортання, за замовчуванням має діагональне обгортання.

Подумайте про кожну координату як про життя по колу і вигадайте коротшу з двох можливих відстаней для кожної координати окремо.
Керрек СБ

1
@crazy: Знайдіть "торус" у Вікіпедії ...
Керрек СБ

Відповіді:


8

Вам доведеться трохи налаштувати свій алгоритм, щоб обчислити кут - на даний момент ви записуєте лише абсолютну різницю в положенні, але вам потрібна відносна різниця (тобто може бути додатною чи негативною залежно від позиціонування).

int dx = T.X - S.X; // difference in position
int dy = T.Y - S.Y;

if (dx > MapX / 2) // if distance is bigger than half map width, then looping must be closer
    dx = (dx - MapX) * -1; // reduce distance by map width, reverse 
else if (dx < -MapX / 2) // handle the case that dx is negative
    dx = (dx + MapX) * -1;

//Do the same for dy
if (dy > MapY / 2)
    dy = (dy - MapY) * -1;
else if (dy < -MapY / 2)
    dy = (dy + MapY) * -1;

double dist = sqrt(dy*dy+dx*dx); // same as before
double angle = atan2(dy,dx) * 180 / PI; // provides angle in degrees

1
Вам потрібна деяка робота над знаками dx і dy, оскільки код, як є, зламається, якщо TX менше SX або TY менше XY. Крім того, що це найкраще рішення IMHO.
Скотт Чемберлен

Саме тоді я це виправлю.

1
Я все-таки отримав кілька помилок щодо того, якими будуть знаки dx та dy, коли все буде сказано і зроблено, майте на увазі, якщо я редагую?
Скотт Чемберлен

Чому це прийнята відповідь ?? Це навіть не працює . Припустимо, MapXце 100, T.Xце 90 і S.X10. dxмає бути чітко 20, але цей алгоритм поверне 30!
sam hocevar

Цього - це те, що відбувається, коли вам не потрібно мати можливість тестувати код перед його публікацією. Виправимо. Якщо хтось виявить іншу помилку з цим, я, ймовірно, просто видалюю її, перш ніж занадто багато людей потраплять в оману.
Toomai

11

У такому світі існує нескінченна кількість шляхів від S до T. Позначимо координати T по (Tx, Ty), координати S по (Sx, Sy)і розмір світу по (Wx, Wy). Обернені координати T - це (Tx + i * Wx, Ty + j * Wy), де iі jє цілі числа, тобто елементи множини {..., -2, -1, 0, 1, 2, ...}. Вектори, що з'єднують S до T, є (Dx, Dy) := (Tx + i * Wx - Sx, Ty + j * Wy - Sy). Для даної (i, j)пари відстань - це довжина вектора sqrt(Dx * Dx + Dy * Dy), а напрямок у радіанах - atan(Dy / Dx). Найкоротший шлях є одним з 9 шляхів, де iі jзнаходяться в {-1, 0, 1}: введіть тут опис зображення

Значення iта jзначення для найкоротшого шляху можна визначити безпосередньо:

int i = Sx - Tx > Wx / 2 ? 1 : Sx - Tx < -Wx / 2 ? -1 : 0;
int j = Sy - Ty > Wy / 2 ? 1 : Sy - Ty < -Wy / 2 ? -1 : 0;

Дякую, @IlmariKaronen, @SamHocevar та @romkyns за допомогу!


1
Можна зробити і краще: якщо abs(Tx-Sx) < Wx/2, тоді i=0оптимально; в іншому випадку оптимальним вибором є i=-1або i=1, залежно від ознаки Tx-Sx. Те саме стосується Ty-Syі j.
Ільмарі Каронен

1
Ця відповідь неймовірно складна для такої простої проблеми. Немає необхідності використовувати лінійний пошук, коли мінімальне значення можна обчислити безпосередньо.
sam hocevar

Приємна картинка, але запропонований алгоритм не заслуговує жодного з результатів цієї відповіді.
RomanSt

5

Обчисліть один можливий вектор напрямку, навіть якщо він не найкоротший, оберніть його координату X так, щоб він знаходився в [-MapX/2,MapX/2]діапазоні, і той самий для Y:

int DirX = (T.X - S.X + 3 * MapX / 2) % MapX) - MapX / 2;
int DirY = (T.Y - S.Y + 3 * MapY / 2) % MapY) - MapY / 2;

Це воно! Ви також отримуєте відстань без додаткових розрахунків:

double dist = sqrt((double)(DirX*DirX + DirY*DirY));

Спасибі! Версія GLSL:vec2 toroidalNearestWay (vec2 from, vec2 to, vec2 mapSize) { return (mod((to - from + 3.0 * mapSize / 2.0), mapSize)) - mapSize / 2.0; }
1j01

0

Я думаю, існує кілька способів зробити це. Ось 2, про які я можу придумати голову:

№1: Обробляти корпуси вручну

Це може статися рівно 10 випадків:

  • Це в тій же плитці, що і S
  • Він є в будь-якому з 8 навколишніх плиток
  • Його взагалі не знайдено.

Однак для кожної оточуючої плитки вони є перестановками різних обчислень для компонента відстані X або Y. Оскільки це обмежена кількість випадків, ви можете просто зафіксувати, як їх обчислити, і знайти найкоротшу відстань між усіма ними.

Ось ілюстрація 2 випадків пошуку dx. Випадок 1, де Tзнаходиться в тій же плитці S, що і dx, просто S.x - T.x. Для плиток праворуч dxбуде обчислюватися як TileWidth - S.x + T.x.

               :         
               :  T    
               :         
:--------------:---------
:              :
:           S  :
:  |--------|--:--|
:dx=(S.x-T.x) dx=(TileWidth-S.x+T.x)
:  T           :
:              :
:--------------:

Як невелика оптимізація, знайдіть мінімальну відстань, перш ніж взяти квадратний корінь. Тоді ви заощадите до 7 sqrtдзвінків.

№ 2: Анотація координат

Якщо вам потрібно зробити щось більш просторово "текуче", наприклад алгоритм пошуку шляху, просто абстрагуйте координати, щоб ваш алгоритм пошуку шляху навіть не усвідомлював, що світ складається з повторюваних плиток. Алгоритм пошуку шляху може теоретично йти нескінченно в будь-якому напрямку (гаразд, ви будете обмежені числовими межами, але ви отримаєте точку).

Для простого розрахунку відстані, не турбуйте це робити.


Розумна ідея про порівняння величини відстані у квадраті перед тим, як приймати sqrt!
Скотт Чемберлен

О, я бачу, @Kol має аналогічну відповідь з більш математичним поясненням, дякую, це дає мені з чим працювати

Порівнювати відстань у квадраті може бути розумнішим, ніж брати sqrt, але використовувати відстань на Манхеттені ще розумніше, оскільки воно зовсім не потребує множення.
Сем Хочевар

0

Не турбуйтеся з "9 напрямками". Причина полягає в тому, що серед цих 9 випадків є 5 вироджених: "пряма північ", "проточний захід", "прямий південь", "прямий схід" та "однаковий". Наприклад, пряма північ є виродженою, оскільки це являє собою випадок, коли північний захід і північний схід з'єднуються і дають однаковий результат.

Таким чином, у вас є 4 напрямки для обчислення, і ви можете просто вибрати мінімум.


Я не думаю, що це правильно, або я вас зовсім неправильно зрозумів. Одне з двох.

-1

Дякую за всі відповіді, врешті-решт я використав Toomai під редакцією Скотта Чемберлена. Я теж вніс декілька змін через те, що моя система координат починається з у верхньому лівому куті і збільшується при русі вниз (в основному перевернуто порівняно зі звичайними графіками координат для у).

Я публікував у випадку, якщо хтось інший знайде цю сторінку та має ту саму перевернуту систему y.

  int dx = T.X - S.X; // difference in position
int dy = S.Y - T.Y;

if (dx > MapX / 2) // if distance is bigger than half map width, then looping must be closer
    dx = (dx - (MapX / 2)) * -1; // reduce distance by half map width, reverse 
else if (dx < -MapX / 2) // handle the case that dx is negative
    dx = (dx + (MapX / 2)) * -1;

//Do the same for dy
if (dy > MapY / 2)
    dy = (MapY - dy)) * -1;
else if (dy < -MapY / 2)
    dy = (dy + MapY);

double angle = atan2(dy,dx) * 180 / PI; // provides angle in degrees

angle = 180 - angle; //convert to 360 deg

Цей код трохи кращий, ніж Toomai, але також не працює.
sam hocevar

1
Також вам потрібно зрозуміти, чому вам довелося робити ці зміни. Це не тому, що ваша система координат починається yвгорі. Це тому, що бажана поведінка повинна ніби обертати координати на межі світу, тоді як код, який ви повторно використовували, відображав координати на кожному кордоні.
sam hocevar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.