Meine Lösung

Wir nehmen also A* oder Dijkstra mit modifizierten Kosten und Heuristiken, oder? Ja, fast – aber nicht ganz. Der Code dafür folgt weiter unten, zusammen mit einigen Extras zur Minimierung von Pfadabbiegungen. Insgesamt ist es eher nicht Dijkstra oder A* und es gibt eine Menge Code, bei dem nicht offensichtlich ist, warum er so sein muss.

Wie gesagt: Ich habe das 2020 geschrieben und es mittlerweile aus meinem Gedächtnis gelöscht. Ich weiß nicht mehr, warum der Code so ist, wie er ist. Es war viel Trial-and-Error nötig, damit er funktioniert, ich habe eine Menge hin und her geschoben und leichte Änderungen am Algorithmus vorgenommen.

In Vorbereitung auf diesen Blogbeitrag habe ich alles noch einmal überprüft und versucht, das modifizierte A* wiederherzustellen. Allerdings hat jede Änderung zur Vereinfachung nur einen Haufen Fehler bei der Wegfindung verursacht. Also bleibt es, wie es ist. Nur der Gedankengang, der dahin geführt hat, wurde größtenteils aus meinem Kopf gestrichen; er hat ja schon funktioniert.

Warum wird der Pfad in den Zellen gespeichert, die der Prioritätswarteschlange hinzugefügt werden, statt dass wie bei A* die Knoten speichern, von welchem Knoten aus sie erreicht wurden? Warum wird geprüft, ob der Pfad der beste Pfad ist, wenn er aus der Warteschlange herausgenommen wird, und nicht, wenn er in die Warteschlange hineinkommt? Beides sind ziemlich große Abweichungen von A*, aber anscheinend notwendig, damit es funktioniert.

  1. struct advanced_weighted_cell {
  2. iVec2D cell;
  3. int cost;
  4. int desire;
  5. int bends = -1;
  6. temppodvector<char> current_path;
  7.  
  8. int path_value() const {
  9. return cost*100+bends;
  10. }
  11.  
  12. std::weak_ordering operator <=>(const advanced_weighted_cell& rhs) const {
  13. if(desire != rhs.desire) return desire <=> rhs.desire;
  14. if(cost != rhs.cost) return cost <=> rhs.cost;
  15. if(bends != rhs.bends) return bends <=> rhs.bends;
  16. return std::weak_ordering::equivalent;
  17. }
  18. };
  19.  
  20. podvector<iVec2D> TacticsGrid::advanced_pathfind(Character* source, iVec2D begin, iVec2D end, int max_range, bool sunken_only){
  21. max_range *= pathfind_cost_resolution;
  22.  
  23. podvector<iVec2D> path;
  24. dynamic_grid<int> pathvalues(width, depth);
  25. for(auto& t:pathvalues) t = INT_MAX;
  26.  
  27. if(!Debug.CheckAssert(pathvalues.in_bounds(begin)) || !Debug.CheckAssert(pathvalues.in_bounds(end))) {
  28. return path;
  29. }
  30.  
  31. auto costs = pathfind_costs(source, sunken_only);
  32.  
  33. std::priority_queue<advanced_weighted_cell, std::vector<advanced_weighted_cell>, std::greater<advanced_weighted_cell>> queue;
  34.  
  35. queue.push({begin, 0, 0, 0});
  36.  
  37. int total_checked = 0;
  38.  
  39. while(!queue.empty()){
  40. auto tile = queue.top();
  41. queue.pop();
  42.  
  43. if(tile.path_value() <= pathvalues[tile.cell]){
  44. pathvalues[tile.cell] = tile.path_value();
  45.  
  46. if(tile.cell == end) {
  47. iVec2D current = begin;
  48. for(auto i : tile.current_path){
  49. current += all_orientations[i];
  50. path.push_back(current);
  51. }
  52.  
  53. //Debug.DisplayVariable(total_checked);
  54.  
  55. return path;
  56. break;
  57. }
  58.  
  59. for(int i = 0; i<4; i++){
  60. auto &v = all_orientations[i];
  61. iVec2D t2 = v+tile.cell;
  62.  
  63. if(pathvalues.in_bounds(t2)){
  64. advanced_weighted_cell next = {t2,
  65. tile.cost+costs[t2].enter_cost + costs[tile.cell].exit_cost,
  66. tile.desire+(t2==end?0:costs[t2].desire_cost),// + iso_dist(t2-end), //heuristic is more efficient, but results in paths with non-optimal numbers of bends
  67. tile.bends};
  68.  
  69. if(tile.current_path.size() > 0 && i != tile.current_path.back()) next.bends++;
  70.  
  71. if(next.cost <= max_range && next.path_value() <= pathvalues[t2]){
  72. next.current_path.resize(tile.current_path.size()+1); //minimize reallocations
  73. if(tile.current_path.size() > 0) {
  74. std::memcpy(next.current_path.data(), tile.current_path.data(), tile.current_path.size()*sizeof(char));
  75. }
  76. next.current_path.back() = i;
  77.  
  78. queue.push(next);
  79. total_checked++;
  80. }
  81. }
  82. }
  83. }
  84. }
  85.  
  86. return path;
  87. }

Das sind alle Hintergrundinformationen zu diesem Problem. Jeder kann gern versuchen, es selbst zu lösen, wenn er oder sie ein Gefühl dafür bekommen will, wie wenig trivial es ist (oder denkt, dass es auch mit A* und einer modifizierten Heuristik gehen könnte.) Wer mir Verbesserungsvorschläge machen möchten, sollte die Lösung zuerst implementieren und im Kontext ausprobieren – und sich bewusst sein, dass ich sie nicht verwenden werde, weil ich bereits etwas habe, das gut funktioniert, in Tausenden von Fällen gründlich getestet wurde und keine Leistungsprobleme hat.

Bitte aktivieren Sie Javascript.
Oder nutzen Sie das Golem-pur-Angebot
und lesen Golem.de
  • ohne Werbung
  • mit ausgeschaltetem Javascript
  • mit RSS-Volltext-Feed
 KI im Programmierertest: Kann GPT-4 wirklich Code schreiben?Wie gut schlägt sich GPT-4? 
  1.  
  2. 1
  3. 2
  4. 3
  5. 4
  6.  


Trollversteher 05. Apr 2023

Das trifft auf die konkrete Formulierung zu - im Gegensatz zu ChatGPT hat der Mensch...

Trollversteher 05. Apr 2023

Haha, also Ich würde im Job nicht auf eine solche Idee kommen, und kenne auch keinen...

Insomnia88 30. Mär 2023

Wenn ich 10 Jahre zurück gehe, fällt mir bis auf Ki nichts ein, was es nicht auch schon...

herding_cats 28. Mär 2023

Die Programmiersprachen sind deutlich exakter als natürliche Sprachen. Du hast auf jeden...



Aktuell auf der Startseite von Golem.de
Grace Hopper Superchip
Nvidia zeigt den DGX GH200 AI-Supercomputer

Die Kombination aus Grace Hopper, Bluefield 3 und NVLink ergibt funktional eine riesige GPU mit der Rechenkapazität eines Supercomputers und 144 TByte Grafikspeicher.

Grace Hopper Superchip: Nvidia zeigt den DGX GH200 AI-Supercomputer
Artikel
  1. Gefangen im Zeitstrom, verloren im All: Die zehn besten Sci-Fi-Serien der 1960er
    Gefangen im Zeitstrom, verloren im All
    Die zehn besten Sci-Fi-Serien der 1960er

    Sie sind die Klassiker, auf denen das ganze Genre aufbaut: die großen Science-Fiction-Serien der 1960er. Neben Star Trek gab es hier noch viel mehr.
    Von Peter Osteried

  2. SPD-Chefin: Esken will Konsequenzen für Twitters Ausstieg bei EU-Gesetz
    SPD-Chefin
    Esken will Konsequenzen für Twitters Ausstieg bei EU-Gesetz

    Twitter lasse sexistischen, rassistischen Hass zu. Esken will dagegen vorgehen, dass Elon Musk das EU-Gesetz über digitale Dienste ignoriert.

  3. Speicherleaks vermeiden: Ressourcen- und typensicheres Programmieren in C++
    Speicherleaks vermeiden
    Ressourcen- und typensicheres Programmieren in C++

    Bei C++ liegt alles in der Hand der Entwickler - und das kann gut und schlecht sein. Richtig angewendet, ist die Sprache aber alles andere als unsicher.
    Eine Anleitung von Adam Jaskowiec

Du willst dich mit Golem.de beruflich verändern oder weiterbilden?
Zum Stellenmarkt
Zur Akademie
Zum Coaching
  • Schnäppchen, Rabatte und Top-Angebote
    Die besten Deals des Tages
    • Daily Deals • Microsoft Xbox Wireless Controller 40,70€ • Lexar Play 1 TB 99,60€ • DAMN!-Deals mit AMD-Bundle-Aktion • MindStar: AMD Ryzen 9 5950X 429€, MSI RTX 3060 Gaming Z Trio 12G 329€, GIGABYTE RTX 3060 Eagle OC 12G 299€, be quiet! Pure Base 500DX 89€ • Logitech bis -46% [Werbung]
    •  /