Sarılı kenarları olan bir dünyada seyahat yönünü bulma


20

2B dünyamdaki bir noktadan kenarların sarıldığı başka bir noktaya (asteroitler vb.) En kısa mesafe yönünü bulmam gerekiyor. En kısa mesafeyi nasıl bulacağımı biliyorum ama hangi yönde olduğunu bulmakta zorlanıyorum.

En kısa mesafe şu şekilde verilir:

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));

Dünya örneği

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

Diyagramda kenarlar: ve - ile gösterilmiştir. Ben de sağ üstte dünyanın sarılmış bir tekrarı gösterdim. Yönü S'den T'ye derece olarak bulmak istiyorum. Öyleyse en kısa mesafe T'nin sağ üst tekrarıdır, ancak S'den sağ üstte tekrarlanan T'ye kadar derecedeki yönü nasıl hesaplayabilirim?

Hem S hem de T'nin pozisyonlarını biliyorum ama sanırım tekrarlanan T'nin pozisyonunu bulmam gerekiyor, ancak 1'den fazla var.

Dünyanın koordinatlar sistemi sol üstte 0,0 ile başlar ve yön için 0 derece Batı'da başlayabilir.

Bu çok zor olmamalı gibi görünüyor ama bir çözüm bulamadım. Umarım birisi yardımcı olabilir mi? Herhangi bir web sitesi takdir edilecektir.


Sağ üstteki T için koordinatlar nelerdir?

Diyagonal ambalajlı bir oyun görmedim. Genellikle her yön için bir sargı vardır (N, E, S, W).

5
Hem yatay hem de dikey kaydırmaya sahip herhangi bir oyun varsayılan olarak çapraz kaydırmaya sahiptir.

Her koordinatı bir çember üzerinde yaşayan olarak düşünün ve her koordinat için iki olası mesafenin daha kısa olduğunu ayrı ayrı anlayın.
Kerrek SB

1
@crazy: Wikipedia'da "torus" u ara ...
Kerrek SB

Yanıtlar:


8

Açıyı hesaplamak için algoritmanızı biraz değiştirmelisiniz - şu anda sadece pozisyondaki mutlak farkı kaydediyorsunuz, ancak göreceli farka ihtiyacınız var (yani konumlandırmaya bağlı olarak pozitif veya negatif olabilir).

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
Eğer TX SX'den küçükse veya TY XY'den küçükse kod kırılacağı için dx ve dy işaretleri üzerinde biraz çalışmaya ihtiyacınız var. Bunun dışında en iyi çözüm IMHO.
Scott Chamberlain

Tam o zaman bunu düzeltirim.

1
Hala söylendiğinde ve yapıldığında dx ve dy'nin işareti ne olacağına dair birkaç hata var, eğer düzenlersem?
Scott Chamberlain

Bu neden kabul edilen cevaptır? Çalışmıyor bile . Diyelim ki MapX100, T.X90 ve S.X10. dxAçıkça 20 olmalı, ancak bu algoritma 30 döndürecek!
sam hocevar

Ugh, kodu göndermeden önce test etme fırsatınız olmadığında olan şey budur. Düzeltecek. Birisi bununla ilgili başka bir hata bulursa, muhtemelen çok fazla insan yanıltıcı olmadan önce silerim.
Toomai

11

Böyle bir dünyada S'den T'ye sonsuz sayıda yol vardır. T'nin (Tx, Ty)koordinatlarını, S'nin koordinatlarını (Sx, Sy)ve dünyanın büyüklüğünü gösterelim (Wx, Wy). T sarılmış koordinatlar (Tx + i * Wx, Ty + j * Wy), burada ive jolduğunu, kümenin elemanları tamsayılardır {..., -2, -1, 0, 1, 2, ...}. S'yi T'ye bağlayan vektörler (Dx, Dy) := (Tx + i * Wx - Sx, Ty + j * Wy - Sy). Belirli bir (i, j)çift ​​için mesafe vektörün uzunluğu sqrt(Dx * Dx + Dy * Dy)ve radyan cinsinden yönüdür atan(Dy / Dx). En kısa yol 9 yolları biridir ive jvardır {-1, 0, 1}: resim açıklamasını buraya girin

En kısa yol için ive jdeğerleri doğrudan belirlenebilir:

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;

Yardımınız için teşekkür ederim, @IlmariKaronen, @SamHocevar ve @romkyns!


1
Bundan daha iyisini yapabilirsiniz: eğer abs(Tx-Sx) < Wx/2, o i=0zaman optimal; aksi takdirde en uygun seçim i=-1veya i=1işaretine bağlı olarak yapılır Tx-Sx. Aynı şey Ty-Syve için de geçerli j.
Ilmari Karonen

1
Bu cevap böyle basit bir problem için inanılmaz derecede karmaşık. Minimum değer doğrudan hesaplanabildiği zaman doğrusal aramaya gerek yoktur.
sam hocevar

Güzel resim, ancak önerilen algoritma bu cevabın aldığı upvote'ların hiçbirini hak etmiyor.
RomanSt

5

En kısa olmasa bile olası bir yön vektörünü hesaplayın, sonra X koordinatını [-MapX/2,MapX/2]aralıkta olacak ve Y için aynı olacak şekilde sarın :

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

Bu kadar! Mesafeyi başka hesaplamalar yapmadan da alabilirsiniz:

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

Teşekkürler! GLSL sürümü:vec2 toroidalNearestWay (vec2 from, vec2 to, vec2 mapSize) { return (mod((to - from + 3.0 * mapSize / 2.0), mapSize)) - mapSize / 2.0; }
1j01

0

Sanırım bunu yapmanın birkaç yolu var. İşte kafamın üstünden düşünebileceğim 2 tane:

# 1: Vakaları manuel olarak ele alın

Gerçekleşebilecek tam 10 vaka vardır:

  • Aynı kiremit ile aynı S
  • 8 çevreleyen fayansın herhangi birinde
  • Hiç bulunamadı.

Çevredeki fayansların her biri için, X veya Y mesafe bileşeni için farklı hesaplamaların permütasyonlarıdır. Sonlu sayıda vaka olduğundan, bunların nasıl hesaplanacağını zor kodlayabilir ve hepsi arasındaki en kısa mesafeyi bulabilirsiniz.

İşte bulmak için 2 vaka bir örnek dx. Durum 1, burada Taynı döşemede S, dx sadece S.x - T.x. Sağdaki çiniler için, dxolarak hesaplanacaktır TileWidth - S.x + T.x.

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

Küçük bir optimizasyon olarak, karekök almadan önce minimum mesafeyi bulun. Sonra 7 sqrtaramaya kadar kendinizi kaydedersiniz.

# 2: Koordinatları özetleyin

Bir yol bulma algoritması gibi daha uzaylı bir şekilde "akışkan" bir şey yapmanız gerekiyorsa, sadece koordinatları soyutlayın, böylece yol bulma algoritmanız dünyanın tekrarlanan karolardan oluştuğunu bile fark etmez. Yol bulma algoritması teorik olarak herhangi bir yönde sonsuza kadar gidebilir (tamam, sayısal sınırlarla sınırlı olacaksınız, ancak noktayı anlıyorsunuz).

Basit mesafe hesaplaması için bunu yapma.


Sqrt almadan önce kare mesafe değerini karşılaştırma hakkında akıllı fikir!
Scott Chamberlain

Ah anlıyorum, @Kol daha matematiksel bir açıklama ile benzer bir cevabı var, teşekkürler bu bana çalışmak için bir şeyler veriyor

Kare mesafeyi karşılaştırmak, sqrt almaktan daha akıllı olabilir, ancak Manhattan mesafesini kullanmak daha akıllıdır çünkü hiç çarpma gerektirmez.
sam hocevar

0

"9 yön" ile rahatsız etmeyin. Bunun nedeni, bu 9 arasında 5 dejenere vaka olmasıdır: "düz kuzey", "düz batı", "düz güney", "düz doğu" ve "özdeş". Örneğin, kuzey kuzeyi dejenere olur, çünkü kuzeybatı ve kuzeydoğunun birleştiği ve aynı sonucu verdiği durumdur.

Böylece, hesaplamak için 4 yön var ve sadece minimum seçebilirsiniz.


Bunun doğru olduğunu düşünmüyorum ya da seni tamamen yanlış anladım. İkinin biri.

-1

Sonunda tüm cevaplar için teşekkürler Scott Chamberlain tarafından düzenlenen Toomai kullandım. Koordinat sistemimin sol üstte y ile başlaması ve aşağı doğru hareket ettikçe artması nedeniyle birkaç değişiklik yaptım (temelde y için normal grafik koordinatlarına kıyasla ters çevrildi).

Başka birinin bu sayfayı bulması ve aynı ters sisteme sahip olması durumunda gönderdim.

  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

Bu kod Toomai'den biraz daha iyidir, ancak işe yaramaz.
sam hocevar

1
Ayrıca, bu değişiklikleri neden yapmak zorunda olduğunuzu anlamanız gerekir . Bunun nedeni koordinat sisteminizin en üstte ile başlaması değildiry . İstenilen davranış için sözde çünkü var sarmak Dünya kenarında koordinatları kodu oysa yeniden, aynalı her sınırda koordinatları.
sam hocevar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.