ค้นหาทิศทางของการเดินทางในโลกที่มีขอบล้อมรอบ


20

ฉันต้องการค้นหาทิศทางระยะทางที่สั้นที่สุดจากจุดหนึ่งในโลก 2 มิติของฉันไปยังอีกจุดที่ขอบถูกห่อ (เช่นดาวเคราะห์น้อย ฯลฯ ) ฉันรู้วิธีหาระยะทางที่สั้นที่สุด แต่ฉันพยายามหาทิศทางที่มันอยู่

ระยะทางที่สั้นที่สุดจะได้รับจาก:

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 ดังนั้นระยะทางที่สั้นที่สุดคือการทำซ้ำด้านบนขวาของ T แต่ฉันจะคำนวณทิศทางในการลดระดับจาก S ถึง T ซ้ำในด้านบนขวาได้อย่างไร

ฉันรู้ตำแหน่งของทั้ง S และ T แต่ฉันคิดว่าฉันจำเป็นต้องค้นหาตำแหน่งของ T ซ้ำแล้วซ้ำอีก แต่มีมากกว่า 1

ระบบพิกัดโลกเริ่มต้นที่ 0,0 ที่มุมบนซ้ายและ 0 องศาสำหรับทิศทางอาจเริ่มที่ตะวันตก

ดูเหมือนว่ามันจะไม่ยากเกินไป แต่ฉันก็ไม่สามารถหาทางออกได้ ฉันหวังว่าบางคนสามารถช่วยได้ เว็บไซต์ใด ๆ ที่จะได้รับการชื่นชม


พิกัดสำหรับ T ที่มุมบนขวาคืออะไร?

ฉันไม่เคยเห็นเกมที่มีเส้นทแยงมุม โดยปกติคุณจะมีหนึ่งห่อสำหรับแต่ละทิศทาง (N, E, S, W)

5
เกมใดก็ตามที่มีการตัดทั้งแนวนอนและแนวตั้งจะมีการตัดตามแนวทแยงตามค่าเริ่มต้น

คิดว่าแต่ละพิกัดอยู่ในวงกลมและหาระยะทางที่สั้นที่สุดของสองระยะทางที่เป็นไปได้สำหรับแต่ละพิกัด
Kerrek SB

1
@crazy: เงยหน้าขึ้นมอง "torus" บน Wikipedia ...
Kerrek SB

คำตอบ:


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
Scott Chamberlain

จากนั้นฉันจะแก้ไขมัน

1
ยังมีข้อผิดพลาดเล็กน้อยเกี่ยวกับเครื่องหมายของ dx และ dy ที่จะถูกพูดและทำทั้งหมดโปรดทราบว่าฉันจะแก้ไขหรือไม่
Scott Chamberlain

ทำไมนี่เป็นคำตอบที่ยอมรับได้? มันไม่ได้ทำงาน สมมติว่าMapXเป็น 100, T.X90 และS.X10 dxควรชัดเจนว่า 20 แต่อัลกอริทึมนี้จะคืนค่า 30!
sam hocevar

นี่เป็นสิ่งที่เกิดขึ้นเมื่อคุณไม่ต้องทดสอบรหัสก่อนโพสต์ จะแก้ไข หากมีคนพบข้อผิดพลาดอื่นด้วยสิ่งนี้ฉันจะลบก่อนที่คนจำนวนมากจะเข้าใจผิด
Toomai

11

เช่นในโลกที่มีอนันต์จำนวนเส้นทางจาก S เพื่อแสดง T. Let 's พิกัดของ T โดย(Tx, Ty)พิกัดของ S โดยและขนาดของโลกโดย(Sx, Sy) (Wx, Wy)พิกัดห่อของ T เป็น(Tx + i * Wx, Ty + j * Wy)ที่iและเป็นจำนวนเต็มที่เป็นองค์ประกอบของชุดj {..., -2, -1, 0, 1, 2, ...}เวกเตอร์เชื่อมต่อ S (Dx, Dy) := (Tx + i * Wx - Sx, Ty + j * Wy - Sy)เพื่อที สำหรับให้(i, j)คู่ระยะทางที่มีความยาวของเวกเตอร์ที่และทิศทางในเรเดียนคือsqrt(Dx * Dx + Dy * Dy) เส้นทางที่สั้นที่สุดเป็นหนึ่งในเส้นทางที่ 9 ที่และอยู่ใน: atan(Dy / Dx)ij{-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
Ilmari Karonen

1
คำตอบนี้ซับซ้อนอย่างไม่น่าเชื่อสำหรับปัญหาง่ายๆ ไม่จำเป็นต้องใช้การค้นหาเชิงเส้นเมื่อสามารถคำนวณค่าน้อยที่สุดได้โดยตรง
sam hocevar

รูปที่ดี แต่อัลกอริทึมที่แนะนำไม่สมควรได้รับ upvotes คำตอบนี้
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เป็นเพียง สำหรับกระเบื้องไปทางขวาจะถูกคำนวณเป็นdxTileWidth - S.x + T.x

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

ในการปรับให้เหมาะสมเล็กน้อยให้ค้นหาระยะทางขั้นต่ำก่อนที่คุณจะหาสแควร์รูท จากนั้นคุณประหยัดได้ถึง 7 sqrtสาย

# 2: บทคัดย่อพิกัด

ถ้าคุณต้องการทำอะไรที่เป็น "spacially" แบบ spacially เพิ่มเติมเช่นอัลกอริธึมการค้นหาเส้นทางเพียงแค่นามธรรมพิกัดดังนั้นอัลกอริทึมการค้นหาเส้นทางของคุณไม่ได้ตระหนักถึงโลกที่ทำจากกระเบื้องซ้ำ อัลกอริธึมการค้นหาพา ธ สามารถไปในทิศทางใดก็ได้ตามทฤษฎี (ไม่เป็นไรคุณจะถูก จำกัด ด้วยขีด จำกัด ตัวเลข แต่คุณได้คะแนน)

สำหรับการคำนวณระยะทางแบบง่ายๆไม่ต้องกังวลกับเรื่องนี้


แนวคิดที่ชาญฉลาดเกี่ยวกับการเปรียบเทียบค่าระยะทางกำลังสองก่อนรับ sqrt!
Scott Chamberlain

ฉันเห็นแล้ว @Kol มีคำตอบที่คล้ายกันพร้อมกับคำอธิบายทางคณิตศาสตร์มากกว่านี้ขอบคุณสิ่งนี้ทำให้ฉันได้ทำงานด้วย

การเปรียบเทียบระยะทางกำลังสองอาจฉลาดกว่าการมองสี่เหลี่ยมจัตุรัส แต่การใช้ระยะทางแมนฮัตตันนั้นฉลาดกว่าเพราะไม่ต้องมีการคูณเลย
sam hocevar

0

อย่าไปสนใจกับ "9 ทิศทาง" เหตุผลก็คือว่ามี 5 กรณีเลวลงในบรรดา 9: "ตรงเหนือ", "straigt ตะวันตก", "ตรงไปทางทิศใต้", "ตรงไปทางทิศตะวันออก" และ "เหมือนกัน" ยกตัวอย่างเช่นทิศตะวันตกเฉียงเหนือตรงนั้นเสื่อมโทรมเนื่องจากเป็นกรณีที่ทิศตะวันตกเฉียงเหนือและทิศตะวันออกเฉียงเหนือเข้าร่วมและให้ผลลัพธ์เดียวกัน

ดังนั้นคุณมี 4 ทิศทางในการคำนวณและสามารถเลือกขั้นต่ำได้


ฉันไม่คิดว่ามันถูกต้องหรือฉันเข้าใจผิดอย่างสิ้นเชิง หนึ่งในสอง

-1

ขอบคุณสำหรับคำตอบทั้งหมดในที่สุดฉันใช้ Toomai แก้ไขโดย Scott Chamberlain ฉันได้ทำการเปลี่ยนแปลงเล็กน้อยเนื่องจากระบบพิกัดของฉันเริ่มต้นด้วย y ที่มุมบนซ้ายและเพิ่มขึ้นเมื่อคุณเลื่อนลง (โดยทั่วไปกลับหัวเมื่อเทียบกับพิกัดกราฟปกติสำหรับ y)

ฉันโพสต์ในกรณีที่คนอื่นพบหน้านี้และมีระบบ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.