{"id":16662,"date":"2020-11-11T09:00:57","date_gmt":"2020-11-11T08:00:57","guid":{"rendered":"https:\/\/www.happycoders.eu\/?p=16662"},"modified":"2025-06-12T09:17:59","modified_gmt":"2025-06-12T07:17:59","slug":"shortest-path-problem-java","status":"publish","type":"post","link":"https:\/\/www.happycoders.eu\/de\/algorithmen\/shortest-path-problem-java\/","title":{"rendered":"Shortest Path Problem (K\u00fcrzeste-Wege-Problem)"},"content":{"rendered":"\n<p>Wie findet ein Navigationssystem den k\u00fcrzesten Weg vom Start zum Ziel? Wie orientieren sich Bot-Gegner im Ego-Shooter? Diesen Problemstellungen geht diese Artikelserie \u00fcber Shortest-Path-Algorithmen (und allgemeiner: Pathfinding-Algorithmen) nach.<\/p>\n\n\n\n<p>Dieser erste Artikel behandelt folgende Themen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Was ist der Unterschied zwischen \"Shortest Path\" und \"Pathfinding\"?<\/li>\n\n\n\n<li>Welche Shortest-Path-Algorithmen gibt es?<\/li>\n\n\n\n<li>Beispiel: Wie findet man den k\u00fcrzesten Weg zwischen zwei Punkten in einem Labyrinth?<\/li>\n<\/ul>\n\n\n\n<p>Die Quellcodes zum Artikel findest du in meinem <a href=\"https:\/\/github.com\/SvenWoltmann\/pathfinding\" target=\"_blank\" rel=\"noopener\">GitHub-Repository<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"shortest-path-oder-pathfinding\">Shortest Path oder Pathfinding?<\/h2>\n\n\n\n<p>Ein <em>Shortest-Path<\/em>-Algorithmus l\u00f6st das Problem der Suche nach dem k\u00fcrzesten Weg zwischen zwei Punkten in einem Graph (z. B. auf einer Stra\u00dfenkarte). Der Begriff \"kurz\" meint damit nicht unbedingt die <em>physische Distanz<\/em>. Es kann sich dabei beispielsweise auch um <em>Zeit<\/em> (Autobahnen werden bevorzugt) oder <em>Kosten<\/em> (Maut-Stra\u00dfen werden gemieden) oder eine Kombination aus mehreren Faktoren handeln.<\/p>\n\n\n\n<p>Graphen k\u00f6nnen sehr komplex sein und Millionen von Knoten und Kanten enthalten (beispielsweise in einer Videospiel-Welt, in der sich hunderte oder tausende Charaktere frei bewegen k\u00f6nnen), so dass die Suche nach dem optimalen Weg sehr aufw\u00e4ndig w\u00e4re.<\/p>\n\n\n\n<p>F\u00fcr bestimmte Anwendungsgebiete gen\u00fcgt es einen <em>einigerma\u00dfen<\/em> kurzen (oder sogar <em>irgendeinen<\/em>) Weg zu finden. Hier spricht man dann allgemein von <em>Pathfinding<\/em>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"shortest-path-algorithmen\">Shortest-Path-Algorithmen<\/h2>\n\n\n\n<p>Die bekanntesten Shortest-Path-Algorithmen sind:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>der <a href=\"\/de\/algorithmen\/dijkstra-algorithmus-java\/\">Dijkstra-Algorithmus<\/a><\/li>\n\n\n\n<li>der <a href=\"\/de\/algorithmen\/a-stern-algorithmus-java\/\">A*-Algorithmus<\/a> (ausgesprochen \"A Stern\" oder englisch \"A Star\")<\/li>\n\n\n\n<li>der <a href=\"\/de\/algorithmen\/bellman-ford-algorithmus-java\/\">Bellman-Ford-Algorithmus<\/a><\/li>\n\n\n\n<li>der <a href=\"\/de\/algorithmen\/floyd-warshall-algorithmus-java\/\">Floyd-Warshall-Algorithmus<\/a><\/li>\n<\/ul>\n\n\n\n<p>Auf zweidimensionalen, in Kacheln unterteilte Karten, wie sie in fr\u00fchen Computerspielen \u00fcblich waren, kann auch eine Form der Breitensuche \u2013 bekannt als <a rel=\"noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Lee-Algorithmus\" target=\"_blank\">Lee-Algorithmus<\/a> \u2013 durchgef\u00fchrt werden.<\/p>\n\n\n\n<p>Eine optimierte Variante davon erl\u00e4utere ich im verbleibenden Teil dieses Artikels an einem Beispiel mit Animationen und Java-Quellcode.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"labyrinth-algorithmus-wie-findet-man-den-kuerzesten-weg-in-einem-labyrinth\">Labyrinth-Algorithmus: Wie findet man den k\u00fcrzesten Weg in einem Labyrinth?<\/h2>\n\n\n\n<p>Mein pers\u00f6nliches Lieblingsbeispiel f\u00fcr die L\u00f6sung des Shortest-Path-Problems ist das Spiel \"FatCat\" auf dem <a rel=\"noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/HP-85\" target=\"_blank\">HP-85<\/a>, einem Computer aus den 1980er Jahren, mit dem ich als Kind bei meinem Onkel experimentieren durfte.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"518\" height=\"392\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-playing.gif\" alt=\"FatCat - Shortest-Path-Problem mit &quot;Katze und Maus&quot;-Spiel auf dem HP85\" class=\"wp-image-17101\" style=\"width:518px;height:392px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-playing.gif 518w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-playing-224x170.gif 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-playing-336x254.gif 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-playing-504x381.gif 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-playing-400x303.gif 400w\" sizes=\"(max-width: 518px) 100vw, 518px\" \/><figcaption class=\"wp-element-caption\">\"FatCat\" auf einem <a rel=\"noopener\" href=\"http:\/\/www.kaser.com\/hp85.html\" target=\"_blank\">HP85-Emulator<\/a> (Kassette \"GamesPac2\")<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Aufgabe war es (wie bei Pac-Man) eine Maus alle K\u00e4sest\u00fccke in einem Labyrinth fressen zu lassen, ohne selbst von der Katze gefressen zu werden. Das Schwierige dabei war (abgesehen von der Steuerung mit nur zwei Tasten zum Drehen der Maus nach links und rechts), dass die Katze (im Gegensatz zu den Geistern bei Pac-Man) immer auf dem k\u00fcrzesten Weg zur Maus lief.<\/p>\n\n\n<div class=\"convertkit-form wp-block-convertkit-form\" style=\"\"><script async data-uid=\"5c918c0177\" src=\"https:\/\/happycoders.kit.com\/5c918c0177\/index.js\" data-jetpack-boost=\"ignore\" data-no-defer=\"1\" data-no-optimize=\"1\" nowprocket><\/script><\/div>\n\n\n\n<p>Lediglich durch ein Mauseloch, dass den linken und rechten Rand verband, konnte man der Katze ausweichen. Dar\u00fcber hinaus konnte man die Maus einmal pro Leben an eine andere Stelle beamen \u2013 was insbesondere in Sackgassen hilfreich war:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"520\" height=\"392\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-mouse-beaming-v2.gif\" alt=\"FatCat: Beamen an eine zuf\u00e4llige Zielposition\" class=\"wp-image-17099\" style=\"width:520px;height:392px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-mouse-beaming-v2.gif 520w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-mouse-beaming-v2-224x169.gif 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-mouse-beaming-v2-336x253.gif 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-mouse-beaming-v2-504x380.gif 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat-mouse-beaming-v2-400x302.gif 400w\" sizes=\"(max-width: 520px) 100vw, 520px\" \/><figcaption class=\"wp-element-caption\">Beamen an eine zuf\u00e4llige Zielposition<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Ich hatte mich damals (ich war etwa zehn) schon f\u00fcr Programmierung interessiert und wollte das Spiel auf meinem C64 nachprogrammieren. Die Anzeige der Labyrinthe und die Steuerung der Maus hatte ich schnell implementiert. Doch die Berechnung des k\u00fcrzesten Weges zwischen Katze und Maus bereitete mir monatelang Kopfzerbrechen.<\/p>\n\n\n\n<p>Letztlich l\u00f6ste ich es \u2013 wie ich Jahre sp\u00e4ter in meinem Informatikstudium herausfinden sollte \u2013 mit einer Variante des <a rel=\"noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Lee-Algorithmus\" target=\"_blank\">Lee-Algorithmus<\/a>. Ohne Kenntnis dieses Algorithmus hatte ich eine optimierte Variante davon entwickelt, die ich im Folgenden Schritt f\u00fcr Schritt vorstelle.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"optimierter-lee-algorithmus\">Optimierter Lee-Algorithmus<\/h3>\n\n\n\n<p>Der Lee-Algorithmus hat den Nachteil, dass man am Ende den gefundenen Weg zur\u00fcckgehen muss (\"Backtrace\"), um herauszufinden, welche Richtung die Katze einschlagen muss.<\/p>\n\n\n\n<p>Au\u00dferdem spezifiziert der Algorithmus nicht, <em>wie<\/em> man <em>\"Felder, deren Nachbar mit i markiert ist\"<\/em> findet. Es w\u00e4re unperformant in jedem Schritt das gesamte Labyrinth zu durchsuchen. An dieser Stelle verwende ich eine aus der <a rel=\"noopener\" href=\"https:\/\/de.wikipedia.org\/wiki\/Breitensuche#Algorithmus\" target=\"_blank\">Breitensuche<\/a> bekannte Queue, um die jeweils im n\u00e4chsten Schritt zu \u00fcberpr\u00fcfenden Felder zu speichern.<\/p>\n\n\n\n<p>Die folgenden Grafiken und Animationen verwenden das oben gezeigte Labyrinth mit der Katze an Position (15,7) und der Maus an Position (11,5). Das Koordinatensystem startet an der linken oberen Ecke des Labyrinths mit (0,0).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Vorbereitung<\/h4>\n\n\n\n<p>Das Labyrinth wird in einem zweidimensionalen <code>boolean<\/code>-Array namens <code>lab<\/code> gespeichert. W\u00e4nde werden durch den Wert <code>true<\/code> gekennzeichnet. Die \u00e4u\u00dfere Wand mit in diesem Array abzuspeichern vereinfacht den Code, der so keine separaten Pr\u00fcfungen auf das Erreichen des Randes ben\u00f6tigt.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"529\" height=\"385\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/labyrinth_matrix-v2.png\" alt=\"Labyrinth als boolean-Array\" class=\"wp-image-17069\" style=\"width:529px;height:385px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/labyrinth_matrix-v2.png 529w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/labyrinth_matrix-v2-224x163.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/labyrinth_matrix-v2-336x245.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/labyrinth_matrix-v2-504x367.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/labyrinth_matrix-v2-400x291.png 400w\" sizes=\"(max-width: 529px) 100vw, 529px\" \/><figcaption class=\"wp-element-caption\">boolean-Array \"lab\"<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Um bei der Suche nicht im Kreis zu laufen, wird ein weiteres zweidimensionales <code>boolean<\/code>-Array mit dem Namen <code>discovered<\/code> erstellt, in dem diejenigen Felder markiert werden, die bei der Suche bereits entdeckt wurden. Die aktuelle Position der Katze wird darin initial auf <code>true<\/code> gesetzt.<\/p>\n\n\n\n<p>Die W\u00e4nde des Labyrinths habe ich etwas dunkler gef\u00e4rbt, die Position der Katze ist rot markiert, die der Maus gelb. Diese Informationen sind im <code>discovered<\/code>-Array <em>nicht<\/em> enthalten. Sie werden im Folgenden mit angezeigt, um das Verst\u00e4ndnis zu erleichtern.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"529\" height=\"385\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_00-v2.png\" alt=\"boolean-Array &quot;discovered&quot; mit markierter Katzenposition\" class=\"wp-image-17071\" style=\"width:529px;height:385px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_00-v2.png 529w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_00-v2-224x163.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_00-v2-336x245.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_00-v2-504x367.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_00-v2-400x291.png 400w\" sizes=\"(max-width: 529px) 100vw, 529px\" \/><figcaption class=\"wp-element-caption\">boolean-Array \"discovered\" mit markierter Katzenposition<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Weiterhin wird die Queue f\u00fcr die als n\u00e4chstes zu besuchenden Felder angelegt. In diese wird die aktuelle Position der Katze (15,7) ohne Richtungsangabe (daher \"null\") eingef\u00fcgt:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"464\" height=\"77\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_1-v3.png\" alt=\"Pathfinding-Queue mit erstem Element: Position der Katze\" class=\"wp-image-17025\" style=\"width:464px;height:77px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_1-v3.png 464w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_1-v3-224x37.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_1-v3-336x56.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_1-v3-400x66.png 400w\" sizes=\"(max-width: 464px) 100vw, 464px\" \/><figcaption class=\"wp-element-caption\">Pathfinding-Queue mit erstem Element: Position der Katze<\/figcaption><\/figure>\n<\/div>\n\n\n<p>(Lerne alles \u00fcber Queues im <a href=\"\/de\/algorithmen\/queue-datenstruktur\/\">HappyCoders-Queue-Tutorial<\/a>.)<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Schritt 1<\/h4>\n\n\n\n<p>Das soeben in die Queue gelegte Element (also die Startposition der Katze) wird wieder entnommen:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"464\" height=\"78\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_2-v5.png\" alt=\"Pathfinding-Queue: erstes Element wieder entnommen\" class=\"wp-image-17026\" style=\"width:464px;height:78px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_2-v5.png 464w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_2-v5-224x38.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_2-v5-336x56.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_2-v5-400x67.png 400w\" sizes=\"(max-width: 464px) 100vw, 464px\" \/><figcaption class=\"wp-element-caption\">Pathfinding-Queue: erstes Element wieder entnommen<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Dann werden alle von der Katze in einem Schritt erreichbaren Felder in die Queue geschrieben \u2013 mit ihren X- und Y-Koordinaten sowie der jeweiligen Richtung relativ zum Startpunkt:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"472\" height=\"236\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step1.png\" alt=\"Pathfinding: Im ersten Schritt erreichbare Felder\" class=\"wp-image-17118\" style=\"width:472px;height:236px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step1.png 472w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step1-224x112.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step1-336x168.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step1-400x200.png 400w\" sizes=\"(max-width: 472px) 100vw, 472px\" \/><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"464\" height=\"77\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3-v3.png\" alt=\"Pathfinding-Queue: im ersten Schritt erreichbare Felder\" class=\"wp-image-17027\" style=\"width:464px;height:77px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3-v3.png 464w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3-v3-224x37.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3-v3-336x56.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3-v3-400x66.png 400w\" sizes=\"(max-width: 464px) 100vw, 464px\" \/><figcaption class=\"wp-element-caption\">Pathfinding-Queue: im ersten Schritt erreichbare Felder<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Diese Felder werden ebenfalls als \"entdeckt\" markiert:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"529\" height=\"385\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_03-v2.png\" alt=\"boolean-Array &quot;discovered&quot; mit im n\u00e4chsten Schritt erreichbaren Feldern\" class=\"wp-image-17072\" style=\"width:529px;height:385px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_03-v2.png 529w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_03-v2-224x163.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_03-v2-336x245.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_03-v2-504x367.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_03-v2-400x291.png 400w\" sizes=\"(max-width: 529px) 100vw, 529px\" \/><figcaption class=\"wp-element-caption\">boolean-Array \"discovered\" mit im n\u00e4chsten Schritt erreichbaren Feldern<\/figcaption><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">Schritte 2 bis n<\/h4>\n\n\n\n<p>Solange die Queue nicht leer ist, wird nun jeweils ein Positions-Element entnommen und alle wiederum von dieser Position erreichbaren Felder in die Queue geschrieben \u2013 es sei denn sie sind bereits als \"entdeckt\" markiert.<\/p>\n\n\n\n<p>Dabei wird nicht die in <em>diesem<\/em> Schritt gegangene Richtung gespeichert, sondern die Richtung des entnommenen Elements kopiert. Denn wir wollen ja wissen, welche Richtung die Katze von ihrer <em>Ausgangsposition<\/em> aus einschlagen muss.<\/p>\n\n\n\n<p>Das erste Element in der Queue ist die Position (15,6) oberhalb der Katze:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"464\" height=\"78\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3b.png\" alt=\"Pathfinding-Queue: Feld (15,6) entnommen\" class=\"wp-image-17051\" style=\"width:464px;height:78px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3b.png 464w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3b-224x38.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3b-336x56.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_3b-400x67.png 400w\" sizes=\"(max-width: 464px) 100vw, 464px\" \/><figcaption class=\"wp-element-caption\">Pathfinding-Queue: Feld (15,6) entnommen<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Von dieser Position sind wiederum die Felder dar\u00fcber (15,5) und darunter (15,7) erreichbar:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"472\" height=\"280\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step2.png\" alt=\"Pathfinding: Von der Katze im zweiten Schritt erreichbare Felder\" class=\"wp-image-17120\" style=\"width:472px;height:280px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step2.png 472w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step2-224x133.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step2-336x199.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/cat_directions_step2-400x237.png 400w\" sizes=\"(max-width: 472px) 100vw, 472px\" \/><figcaption class=\"wp-element-caption\">Pathfinding: Von der Katze im zweiten Schritt erreichbare Felder<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Das untere Feld (15,7) ist bereits als \"entdeckt\" markiert (von dort sind wir gekommen) und wird ignoriert. Das obere Feld (15,5) wird in die Queue geschrieben und ebenfalls als \"entdeckt\" markiert:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"529\" height=\"385\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_04-v2.png\" alt=\"boolean-Array &quot;discovered&quot; mit neu entdecktem Feld (15,5)\" class=\"wp-image-17073\" style=\"width:529px;height:385px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_04-v2.png 529w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_04-v2-224x163.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_04-v2-336x245.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_04-v2-504x367.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_step_04-v2-400x291.png 400w\" sizes=\"(max-width: 529px) 100vw, 529px\" \/><figcaption class=\"wp-element-caption\">boolean-Array \"discovered\" mit neu entdecktem Feld (15,5)<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"464\" height=\"77\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_4-v4.png\" alt=\"Pathfinding-Queue: Feld (15,5) eingef\u00fcgt\" class=\"wp-image-17077\" style=\"width:464px;height:77px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_4-v4.png 464w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_4-v4-224x37.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_4-v4-336x56.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_4-v4-400x66.png 400w\" sizes=\"(max-width: 464px) 100vw, 464px\" \/><figcaption class=\"wp-element-caption\">Pathfinding-Queue: Feld (15,5) eingef\u00fcgt<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Dieser Prozess wird nun so lange wiederholt, bis die Position der Maus \"entdeckt\" wurde. Die folgende Animation zeigt, wie sich das <code>discovered<\/code>-Array dabei nach und nach f\u00fcllt:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"529\" height=\"385\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-discovered-v2.gif\" alt=\"Pathfinding: Entdecken der erreichbaren Felder\" class=\"wp-image-17105\" style=\"width:529px;height:385px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-discovered-v2.gif 529w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-discovered-v2-224x163.gif 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-discovered-v2-336x245.gif 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-discovered-v2-504x367.gif 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-discovered-v2-400x291.gif 400w\" sizes=\"(max-width: 529px) 100vw, 529px\" \/><figcaption class=\"wp-element-caption\">Pathfinding: Entdecken der erreichbaren Felder<\/figcaption><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">Abbruchbedingung<\/h4>\n\n\n\n<p>Sobald die Position der Maus erreicht wird, ist der Algorithmus beendet. Der zuletzt entnommene Queue-Eintrag zeigt die Richtung an, in die die Katze gehen muss. Im Beispiel ist das <code>(11,4)\/LEFT<\/code> (das Feld \u00fcber der Maus):<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"464\" height=\"77\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_5.png\" alt=\"Pathfinding-Queue: das zuletzt entnommene Element zeigt die einzuschlagene Richtung an\" class=\"wp-image-17030\" style=\"width:464px;height:77px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_5.png 464w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_5-224x37.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_5-336x56.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_queue_5-400x66.png 400w\" sizes=\"(max-width: 464px) 100vw, 464px\" \/><figcaption class=\"wp-element-caption\">Pathfinding-Queue: das zuletzt entnommene Element zeigt die einzuschlagene Richtung an<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Der k\u00fcrzeste Weg von der Katze zur Maus f\u00fchrt also nach links. In der folgenden Grafik habe ich den Weg gelb markiert:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"529\" height=\"385\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_terminated-v2.png\" alt=\"Labyrinth-Algorithmus: Abbruchbedingung erreicht - K\u00fcrzester Weg gefunden\" class=\"wp-image-17059\" style=\"width:529px;height:385px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_terminated-v2.png 529w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_terminated-v2-224x163.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_terminated-v2-336x245.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_terminated-v2-504x367.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding_terminated-v2-400x291.png 400w\" sizes=\"(max-width: 529px) 100vw, 529px\" \/><figcaption class=\"wp-element-caption\">Abbruchbedingung erreicht - K\u00fcrzester Weg gefunden<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Der Weg kann den Daten zu diesem Zeitpunkt nicht mehr entnommen werden. Er ist irrelevant, da die Katze nur einen einzigen Schritt machen muss, und danach der k\u00fcrzeste Weg neu berechnet wird (denn die Maus bewegt sich ja auch, und der k\u00fcrzeste Weg k\u00f6nnte im n\u00e4chsten Schritt in eine andere Richtung f\u00fchren).<\/p>\n\n\n\n<p>Sollte der Fall eintreten, dass die Queue leer ist, ohne dass die Maus gefunden wurde, bedeutet dies, dass es keinen Weg zwischen Katze und Maus gibt. Dieser Fall kann im FatCat-Spiel nicht eintreten, sollte f\u00fcr andere Anwendungen allerdings behandelt werden.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"shortest-path-java-code\">Shortest Path Java Code<\/h3>\n\n\n\n<p class=\"hc-footnote has-background\" style=\"background-color:#eeeeee\"><strong>Quellcode von 1990<br><\/strong><br>Den C64-Code habe ich leider nicht mehr. Ein paar Jahre sp\u00e4ter habe ich das Spiel auf einem 286er in Turbo Pascal neu implementiert, und diesen Code konnte ich tats\u00e4chlich wiederfinden. Du findest ihn \u2013 gek\u00fcrzt auf die relevanten Teile \u2013 hier: <a href=\"https:\/\/github.com\/SvenWoltmann\/pathfinding\/blob\/main\/src\/main\/pas\/KATZE.PAS\" target=\"_blank\" rel=\"noopener\">KATZE.PAS<br><\/a><br>Der Pascal-Code ist etwas in die Jahre gekommen und f\u00fcr Unge\u00fcbte schwer zu lesen. Daher habe ich den Quellcode \u2013 ohne \u00c4nderung der Algorithmen und Datenstrukturen \u2013 in Java \u00fcbersetzt. Die Java-Adaption findest du hier: <a href=\"https:\/\/github.com\/SvenWoltmann\/pathfinding\/blob\/main\/src\/main\/java\/eu\/happycoders\/pathfinding\/fatcat\/algorithm\/CatAlgorithmFrom1990.java\" target=\"_blank\" rel=\"noopener\">CatAlgorithmFrom1990.java<\/a><\/p>\n\n\n\n<p>Der folgende Code implementiert den Algorithmus mit modernen Sprachmitteln und Datenstrukturen wie dem <code>ArrayDeque<\/code> als Queue. Du findest ihn im GitHub-Repository in der Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/pathfinding\/blob\/main\/src\/main\/java\/eu\/happycoders\/pathfinding\/fatcat\/algorithm\/CatAlgorithmFrom2020.java\" target=\"_blank\" rel=\"noopener\">CatAlgorithmFrom2020<\/a>.<\/p>\n\n\n\n<p>Den Code des Enums <a href=\"https:\/\/github.com\/SvenWoltmann\/pathfinding\/blob\/main\/src\/main\/java\/eu\/happycoders\/pathfinding\/fatcat\/common\/Direction.java\" target=\"_blank\" rel=\"noopener\">Direction<\/a> findest du am Ende.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-comment\">\/**\n * Finds the shortest path from cat to mouse in the given labyrinth.\n *\n * <span class=\"hljs-doctag\">@param<\/span> lab the labyrinth's matrix with walls indicated by {<span class=\"hljs-doctag\">@code<\/span> true}\n * <span class=\"hljs-doctag\">@param<\/span> cx the cat's X coordinate\n * <span class=\"hljs-doctag\">@param<\/span> cy the cat's Y coordinate\n * <span class=\"hljs-doctag\">@param<\/span> mx the mouse's X coordinate\n * <span class=\"hljs-doctag\">@param<\/span> my the mouse's Y coordinate\n * <span class=\"hljs-doctag\">@return<\/span> the direction of the shortest path\n *\/<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> Direction <span class=\"hljs-title\">findShortestPathToMouse<\/span>\n    <span class=\"hljs-params\">(<span class=\"hljs-keyword\">boolean<\/span>&#091;]&#091;] lab, <span class=\"hljs-keyword\">int<\/span> cx, <span class=\"hljs-keyword\">int<\/span> cy, <span class=\"hljs-keyword\">int<\/span> mx, <span class=\"hljs-keyword\">int<\/span> my)<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ Create a queue for all nodes we will process in breadth-first order.<\/span>\n  <span class=\"hljs-comment\">\/\/ Each node is a data structure containing the cat's position and the<\/span>\n  <span class=\"hljs-comment\">\/\/ initial direction it took to reach this point.<\/span>\n  Queue&lt;Node&gt; queue = <span class=\"hljs-keyword\">new<\/span> ArrayDeque&lt;&gt;();\n\n  <span class=\"hljs-comment\">\/\/ Matrix for \"discovered\" fields<\/span>\n  <span class=\"hljs-comment\">\/\/ (I know we're wasting a few bytes here as the cat and mouse can never<\/span>\n  <span class=\"hljs-comment\">\/\/ reach the outer border, but it will make the code easier to read. Another<\/span>\n  <span class=\"hljs-comment\">\/\/ solution would be to not store the outer border at all - neither here nor<\/span>\n  <span class=\"hljs-comment\">\/\/ in the labyrinth. But then we'd need additional checks in the code<\/span>\n  <span class=\"hljs-comment\">\/\/ whether the outer border is reached.)<\/span>\n  <span class=\"hljs-keyword\">boolean<\/span>&#091;]&#091;] discovered = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-keyword\">boolean<\/span>&#091;<span class=\"hljs-number\">23<\/span>]&#091;<span class=\"hljs-number\">31<\/span>];\n\n  <span class=\"hljs-comment\">\/\/ \"Discover\" and enqueue the cat's start position<\/span>\n  discovered&#091;cy]&#091;cx] = <span class=\"hljs-keyword\">true<\/span>;\n  queue.add(<span class=\"hljs-keyword\">new<\/span> Node(cx, cy, <span class=\"hljs-keyword\">null<\/span>));\n\n  <span class=\"hljs-keyword\">while<\/span> (!queue.isEmpty()) {\n    Node node = queue.poll();\n\n    <span class=\"hljs-comment\">\/\/ Go breath-first into each direction<\/span>\n    <span class=\"hljs-keyword\">for<\/span> (Direction dir : Direction.values()) {\n      <span class=\"hljs-keyword\">int<\/span> newX = node.x + dir.getDx();\n      <span class=\"hljs-keyword\">int<\/span> newY = node.y + dir.getDy();\n      Direction newDir = node.initialDir == <span class=\"hljs-keyword\">null<\/span> ? dir : node.initialDir;\n\n      <span class=\"hljs-comment\">\/\/ Mouse found?<\/span>\n      <span class=\"hljs-keyword\">if<\/span> (newX == mx &amp;&amp; newY == my) {\n        <span class=\"hljs-keyword\">return<\/span> newDir;\n      }\n\n      <span class=\"hljs-comment\">\/\/ Is there a path in the direction (= is it a free field in the labyrinth)?<\/span>\n      <span class=\"hljs-comment\">\/\/ And has that field not yet been discovered?<\/span>\n      <span class=\"hljs-keyword\">if<\/span> (!lab&#091;newY]&#091;newX] &amp;&amp; !discovered&#091;newY]&#091;newX]) {\n        <span class=\"hljs-comment\">\/\/ \"Discover\" and enqueue that field<\/span>\n        discovered&#091;newY]&#091;newX] = <span class=\"hljs-keyword\">true<\/span>;\n        queue.add(<span class=\"hljs-keyword\">new<\/span> Node(newX, newY, newDir));\n      }\n    }\n  }\n\n  <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> IllegalStateException(<span class=\"hljs-string\">\"No path found\"<\/span>);\n}\n\n<span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Node<\/span> <\/span>{\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-keyword\">int<\/span> x;\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-keyword\">int<\/span> y;\n  <span class=\"hljs-keyword\">final<\/span> Direction initialDir;\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">Node<\/span><span class=\"hljs-params\">(<span class=\"hljs-keyword\">int<\/span> x, <span class=\"hljs-keyword\">int<\/span> y, Direction initialDir)<\/span> <\/span>{\n    <span class=\"hljs-keyword\">this<\/span>.x = x;\n    <span class=\"hljs-keyword\">this<\/span>.y = y;\n    <span class=\"hljs-keyword\">this<\/span>.initialDir = initialDir;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Und die Klasse <code>Direction<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">enum<\/span> Direction {\n  UP(<span class=\"hljs-number\">0<\/span>, -<span class=\"hljs-number\">1<\/span>),\n  RIGHT(<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">0<\/span>),\n  DOWN(<span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">1<\/span>),\n  LEFT(-<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">0<\/span>);\n\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-keyword\">int<\/span> dx;\n  <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-keyword\">int<\/span> dy;\n\n  Direction(<span class=\"hljs-keyword\">int<\/span> dx, <span class=\"hljs-keyword\">int<\/span> dy) {\n    <span class=\"hljs-keyword\">this<\/span>.dx = dx;\n    <span class=\"hljs-keyword\">this<\/span>.dy = dy;\n  }\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">int<\/span> <span class=\"hljs-title\">getDx<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> dx;\n  }\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">int<\/span> <span class=\"hljs-title\">getDy<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> dy;\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Testen kannst du den Code mit der Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/pathfinding\/blob\/main\/src\/main\/java\/eu\/happycoders\/pathfinding\/fatcat\/CatAlgorithmsTest.java\" target=\"_blank\" rel=\"noopener\">CatAlgorithmsTest<\/a>. Diese erstellt ein Labyrinth, platziert Katze und Maus an zuf\u00e4lligen Positionen und l\u00e4sst die Katze auf k\u00fcrzestem Weg zur Maus laufen.<\/p>\n\n\n\n<p>Das Demo-Programm visualisiert das Labyrinth mit ASCII-Bl\u00f6cken, und die einzelnen Schritte werden der Einfachheit halber untereinander gedruckt (der Pathfinding-Algorithmus steht im Vordergrund, nicht die Darstellung). Die folgende Animation zeigt die ausgegebenen Schritte in zeitlicher Abfolge:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"622\" height=\"474\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-java-v2.gif\" alt=\"Pathfinding im Labyrinth: Test-Ausgabe des Java-Programms\" class=\"wp-image-17107\" style=\"width:622px;height:474px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-java-v2.gif 622w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-java-v2-224x171.gif 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-java-v2-336x256.gif 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-java-v2-504x384.gif 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-java-v2-400x305.gif 400w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/pathfinding-java-v2-600x457.gif 600w\" sizes=\"(max-width: 622px) 100vw, 622px\" \/><figcaption class=\"wp-element-caption\">Pathfinding im Labyrinth: Test-Ausgabe des Java-Programms<\/figcaption><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"performance-des-algorithmus\">Performance des Algorithmus<\/h3>\n\n\n\n<p>Mit dem Tool <a href=\"https:\/\/github.com\/SvenWoltmann\/pathfinding\/blob\/main\/src\/main\/java\/eu\/happycoders\/pathfinding\/fatcat\/CatAlgorithmsBenchmark.java\" target=\"_blank\" rel=\"noopener\">CatAlgorithmsBenchmark<\/a> l\u00e4sst sich die Performance der alten und neuen Implementierung vergleichen. Folgende Tabelle zeigt den Median der Messwerte aus 20 Test-Iterationen mit jeweils 100.000 Berechnungen des k\u00fcrzesten Pfades. Dem Test gingen 10 Warmup-Runden voraus.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Algorithmus<\/th><th class=\"has-text-align-center\" data-align=\"center\">Zeit f\u00fcr 100.000 Pfad-Berechnungen<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\">CatAlgorithmFrom1990<\/td><td class=\"has-text-align-center\" data-align=\"center\">530 ms<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">CatAlgorithmFrom2020<\/td><td class=\"has-text-align-center\" data-align=\"center\">662 ms<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Der Algorithmus, den ich als 15-j\u00e4hriger geschrieben habe, ist auf den ersten Blick schneller als mein neuer Algorithmus. Wie kann das sein?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"optimierung-fuer-katze-und-maus-labyrinthe\">Optimierung f\u00fcr Katze-und-Maus-Labyrinthe<\/h3>\n\n\n\n<p>Ein erneuter Blick in den alten Code zeigt, dass w\u00e4hrend des Pathfindings nur jeder zweite Wegpunkt betrachtet wird. Dies macht insoweit Sinn, als dass durch den spezifischen Aufbau der Labyrinthe die Katze nur nach jedem zweiten Schritt ihre Richtung \u00e4ndern kann:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img decoding=\"async\" width=\"518\" height=\"392\" src=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat_graph_nodes-v5.png\" alt=\"FatCat - nur jeder zweite Wegpunkt ist ein Knoten des Graphs\" class=\"wp-image-17103\" style=\"width:518px;height:392px\" srcset=\"https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat_graph_nodes-v5.png 518w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat_graph_nodes-v5-224x170.png 224w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat_graph_nodes-v5-336x254.png 336w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat_graph_nodes-v5-504x381.png 504w, https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/fatcat_graph_nodes-v5-400x303.png 400w\" sizes=\"(max-width: 518px) 100vw, 518px\" \/><figcaption class=\"wp-element-caption\">Nur jeder zweite Wegpunkt ist ein Knoten des Graphs<\/figcaption><\/figure>\n<\/div>\n\n\n<p>Ich habe den Java-Code noch einmal dahingehend optimiert. Es \u00e4ndert sich nur der Teil innerhalb der Schleife. Die Zwischenschritte der Katze d\u00fcrfen dabei nicht komplett au\u00dfer Acht gelassen werden \u2013 denn dort k\u00f6nnte ja die Maus sitzen.<\/p>\n\n\n\n<p>Den optimierten Code findest du im folgenden Listing und in der Klasse <a href=\"https:\/\/github.com\/SvenWoltmann\/pathfinding\/blob\/main\/src\/main\/java\/eu\/happycoders\/pathfinding\/fatcat\/algorithm\/CatAlgorithmFrom2020Opt.java\" target=\"_blank\" rel=\"noopener\">CatAlgorithmFrom2020Opt<\/a> im GitHub-Repository.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Java\" data-shcb-language-slug=\"java\"><span><code class=\"hljs language-java\"><span class=\"hljs-keyword\">while<\/span> (!queue.isEmpty()) {\n  Node node = queue.poll();\n\n  <span class=\"hljs-comment\">\/\/ Go *two* steps breath-first into each direction<\/span>\n  <span class=\"hljs-keyword\">for<\/span> (Direction dir : Direction.values()) {\n    <span class=\"hljs-comment\">\/\/ First step<\/span>\n    <span class=\"hljs-keyword\">int<\/span> newX = node.x + dir.getDx();\n    <span class=\"hljs-keyword\">int<\/span> newY = node.y + dir.getDy();\n    Direction newDir = node.initialDir == <span class=\"hljs-keyword\">null<\/span> ? dir : node.initialDir;\n\n    <span class=\"hljs-comment\">\/\/ Mouse found after first step?<\/span>\n    <span class=\"hljs-keyword\">if<\/span> (newX == mx &amp;&amp; newY == my) {\n      <span class=\"hljs-keyword\">return<\/span> newDir;\n    }\n\n    <span class=\"hljs-comment\">\/\/ Is there a path in the direction (= is it a free field in the labyrinth)?<\/span>\n    <span class=\"hljs-comment\">\/\/ No -&gt; continue to next direction<\/span>\n    <span class=\"hljs-keyword\">if<\/span> (lab&#091;newY]&#091;newX]) <span class=\"hljs-keyword\">continue<\/span>;\n\n    <span class=\"hljs-comment\">\/\/ Second step<\/span>\n    newX += dir.getDx();\n    newY += dir.getDy();\n\n    <span class=\"hljs-comment\">\/\/ Mouse found after second step?<\/span>\n    <span class=\"hljs-keyword\">if<\/span> (newX == mx &amp;&amp; newY == my) {\n      <span class=\"hljs-keyword\">return<\/span> newDir;\n    }\n\n    <span class=\"hljs-comment\">\/\/ Target field has not yet been discovered?<\/span>\n    <span class=\"hljs-keyword\">if<\/span> (!discovered&#091;newY]&#091;newX]) {\n      <span class=\"hljs-comment\">\/\/ \"Discover\" and enqueue that field<\/span>\n      discovered&#091;newY]&#091;newX] = <span class=\"hljs-keyword\">true<\/span>;\n      queue.add(<span class=\"hljs-keyword\">new<\/span> Node(newX, newY, newDir));\n    }\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">Java<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">java<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Und hier das Ergebnis eines erneuten Performance-Vergleichs:<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Algorithmus<\/th><th class=\"has-text-align-center\" data-align=\"center\">Zeit f\u00fcr 100.000 Pfad-Berechnungen<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\">CatAlgorithmFrom1990<\/td><td class=\"has-text-align-center\" data-align=\"center\">540 ms<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">CatAlgorithmFrom2020<\/td><td class=\"has-text-align-center\" data-align=\"center\">687 ms<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">CatAlgorithmFrom2020Opt<\/td><td class=\"has-text-align-center\" data-align=\"center\">433 ms<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Der neue Code ist nun etwa 25 % schneller als der von 1990.<\/p>\n\n\n\n<p>Falls du in den <a rel=\"noopener\" href=\"https:\/\/github.com\/SvenWoltmann\/pathfinding\/blob\/main\/src\/main\/pas\/KATZE.PAS\" target=\"_blank\">Code von 1990<\/a> geschaut hast: Der Grund liegt darin, dass ich damals keine Queue verwendet habe, sondern zwei separate Datenstrukturen f\u00fcr die Ausgangs- und Endpunkte jedes Pathfinding-Schritts. Nach jedem Schritt wurden alle Endpunkte in die Datenstruktur der Ausgangspunkte kopiert.<\/p>\n\n\n\n<p>Dass ich mit 15 nicht an die Verwendung einer Queue gedacht habe (die ich damals auch gar nicht einfach aus dem Werkzeugkoffer h\u00e4tte ziehen k\u00f6nnen), m\u00f6ge man mir verzeihen ;-)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" class=\"wp-block-heading\" id=\"zusammenfassung-und-ausblick\">Zusammenfassung und Ausblick<\/h2>\n\n\n\n<p>Dieser Artikel hat das \"Shortest-Path-Problem\" beschrieben und am Beispiel des \"FatCat\"-Spiels (bei uns hie\u00df das Spiel \u00fcbrigens \"Katze und Maus\") gezeigt, wie man die Problemstellung mit einem Pathfinding-Algorithmus in Java l\u00f6sen kann.<\/p>\n\n\n\n<p>Der hier vorgestellte Algorithmus kann nur auf kachelbasierte Karten, bzw. auf Graphen, die kachelbasierte Karten repr\u00e4sentieren, angewendet werden.<\/p>\n\n\n\n<p>In den folgenden Teilen der Serie werde ich allgemeine Shortest-Path-Algorithmen wie den <a href=\"\/de\/algorithmen\/dijkstra-algorithmus-java\/\">Dijkstra-Algorithmus<\/a> und den <a href=\"\/de\/algorithmen\/a-stern-algorithmus-java\/\">A*-Algorithmus<\/a> vorstellen.<\/p>\n<aside><p>Wenn dir der Artikel weitergeholfen hat, w\u00fcrde ich mich sehr \u00fcber eine positive Bewertung auf meinem <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">ProvenExpert-Profil<\/a> freuen. Dein Feedback hilft mir, meine Inhalte weiter zu verbessern und motiviert mich, neue informative Artikel zu schreiben.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"https:\/\/www.provenexpert.com\/de-de\/sven-woltmann-happycoders-eu\/7smk\/\" target=\"_blank\" rel=\"noopener\">Bewertung abgeben<\/a><\/p>\r\n                        <p>M\u00f6chtest du auf dem Laufenden bleiben und informiert werden, wenn neue Artikel auf HappyCoders.eu ver\u00f6ffentlicht werden? Dann <a href=\"#\" data-formkit-toggle=\"d8ee997126\">klicke hier<\/a>, um dich f\u00fcr den HappyCoders-Newsletter anzumelden.<\/p>\r\n                        <p>\ud83d\udc49 <a href=\"#\" data-formkit-toggle=\"d8ee997126\">Newsletter-Anmeldung<\/a><\/p><\/aside>","protected":false},"excerpt":{"rendered":"<p>Was ist der Unterschied zwischen \"Shortest Path\" und \"Pathfinding\"? Welche Shortest-Path-Algorithmen gibt es? Wie findet man den k\u00fcrzesten Weg zwischen zwei Punkten in einem Labyrinth?<\/p>\n","protected":false},"author":1,"featured_media":43215,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_seopress_robots_primary_cat":"none","_seopress_titles_title":"","_seopress_titles_desc":"Welche Shortest-Path-Algorithmen gibt es? Was ist der Unterschied zu \"Pathfinding\"? Labyrinth-Algorithmus: Wie findet man den k\u00fcrzesten Weg?","_seopress_robots_index":"","_uag_custom_page_level_css":"","_wp_convertkit_post_meta":{"form":"-1","landing_page":"","tag":"0","restrict_content":"0"},"_metis_text_type":"standard","_metis_text_length":15606,"_post_count":0,"footnotes":""},"categories":[127],"tags":[137],"class_list":["post-16662","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-algorithmen","tag-algorithmen-pathfinding"],"uagb_featured_image_src":{"full":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image.jpg",1770,986,false],"thumbnail":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image.jpg",150,84,false],"medium":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image.jpg",300,167,false],"medium_large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image.jpg",768,428,false],"large":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image.jpg",1024,570,false],"feature_thumb_224":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-224x125.jpg",224,125,true],"feature_thumb_336":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-336x187.jpg",336,187,true],"feature_thumb_504":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-504x281.jpg",504,281,true],"feature_thumb_672":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-672x374.jpg",672,374,true],"half_400":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-400x223.jpg",400,223,true],"half_600":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-600x334.jpg",600,334,true],"full_800":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-800x446.jpg",800,446,true],"full_944":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-944x526.jpg",944,526,true],"full_1200":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-1200x668.jpg",1200,668,true],"wide_1180":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-1180x490.jpg",1180,490,true],"wide_1770":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image-1770x735.jpg",1770,735,true],"1536x1536":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image.jpg",1536,856,false],"2048x2048":["https:\/\/www.happycoders.eu\/wp-content\/uploads\/2020\/11\/shortest-path-algorithms-java-feature-image.jpg",1770,986,false]},"uagb_author_info":{"display_name":"Sven Woltmann","author_link":"https:\/\/www.happycoders.eu\/de\/author\/sven\/"},"uagb_comment_info":0,"uagb_excerpt":"Was ist der Unterschied zwischen \"Shortest Path\" und \"Pathfinding\"? Welche Shortest-Path-Algorithmen gibt es? Wie findet man den k\u00fcrzesten Weg zwischen zwei Punkten in einem Labyrinth?","public_identification_id":"7d0a410d656e4739a29b7c740e44a860","private_identification_id":"ea941d904176401799bb0288ccd1b87d","_links":{"self":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/16662","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/comments?post=16662"}],"version-history":[{"count":10,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/16662\/revisions"}],"predecessor-version":[{"id":52498,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/posts\/16662\/revisions\/52498"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media\/43215"}],"wp:attachment":[{"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/media?parent=16662"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/categories?post=16662"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.happycoders.eu\/de\/wp-json\/wp\/v2\/tags?post=16662"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}