Прост звезден алгоритъм Tower Defense Path Trapped

И така, първо, аз съм в колеж от 100 нива на CS, който използва Java. Задачата ни е да направим игра отбранителна кула и имам проблеми с пътя. От търсенето открих, че A* изглежда най-доброто за това. Въпреки че пътят ми се забива, когато сложа U около пътя. Ще покажа малко псевдо код за начинаещи, тъй като все още не съм взел клас по структури от данни и кодът ми изглежда доста объркан (работя върху това).

Да приемем, че няма да използвам диагонали.

while(Castle not reached){
    new OpenList
    if(up, down, left, right == passable && isn't previous node){
         //Adds in alternating order to create a more diagonal like path
         Openlist.add(passable nodes)
    }
    BestPath.add(FindLeasDistancetoEnd(OpenList));
    CheckCastleReached(BestPath[Last Index]);
{

private node FindLeastDistancetoEnd(node n){
    return first node with Calculated smallest (X + Y to EndPoint)
}

Намалих A* (твърде много, най-вероятно проблемът ми). Така че добавям родители към моите възли и изчислявам правилния родител, въпреки че не вярвам, че това ще реши проблема ми. Ето изображение на проблема ми.

X = непроходими (кули)

O = OpenList

b = ClosedList(BestPath)

C = Замък (крайна точка)

S = Начало

OOOOXX
SbbbBX   C
OOOOXX

Сега столицата B е мястото, където е проблемът ми. Когато кулите са поставени в тази конфигурация и моят навигационен път се преизчисли, той се забива. Нищо не се поставя в OpenList, тъй като предишният възел се игнорира, а останалите са непроходими.

Като го пиша сега, предполагам, че мога да направя B непроходим и да се върна назад... Хаха. Въпреки че започвам да правя много от това, което моят професор нарича „хакване на кода“, където продължавам да добавям корекции за отстраняване на проблеми, защото не искам да изтрия „бебето“ си и да започна отначало. Въпреки че съм готов да го преработя, гледайки колко разхвърлян и неорганизиран е част от кода ми ме притеснява, нямам търпение да взема структури от данни.

Всеки съвет ще бъде оценен.


person Spero    schedule 02.11.2013    source източник


Отговори (2)


Да, структурите от данни биха ви помогнали много при този вид проблем. Ще се опитам да обясня как работи A* и след това ще дам по-добър псевдокод.

A* е най-добрият алгоритъм за търсене. Това означава, че трябва да отгатне кои опции са най-добри и първо да се опита да ги проучи. Това изисква да следите списък с опции, обикновено наричан „Отпред“ (както в предната линия). Той не следи път, намерен досега, както в текущия ви алгоритъм. Алгоритъмът работи на две фази...

Фаза 1

По принцип започвате от началната позиция S, а всички съседни позиции (север, запад, юг и изток) ще бъдат отпред. След това алгоритъмът намира най-обещаващата от опциите отпред (да я наречем P) и я разширява. Позицията P се премахва от предната част, но вместо това се добавят всички нейни съседи. Е, не всички негови съседи; само съседите, които са действителни опции за отиване. Не можем да влезем в кула и не бихме искали да се върнем на място, което сме виждали преди. От новия Фронт се избира най-обещаващият вариант и т.н. Когато най-обещаващата опция е целта C, алгоритъмът спира и влиза във фаза 2.

Обикновено най-обещаващият вариант би бил този, който е най-близо до целта по права линия (без да се обръща внимание на препятствията). Така че обикновено винаги първо ще изследва този, който е най-близо до целта. Това кара алгоритъма да върви към целта по нещо като права линия. Ако обаче тази линия е блокирана от някакво препятствие, позициите на препятствието не трябва да се добавят към предната част. Те не са жизнеспособни опции. Така че в следващия кръг тогава някоя друга позиция отпред ще бъде избрана като най-добра опция и търсенето продължава оттам. Така се излиза от задънени улици като тази във вашия пример. Разгледайте тази илюстрация, за да разберете какво имам предвид: https://upload.wikimedia.org/wikipedia/commons/5/5d/Astar_progress_animation.gif Предната част е кухите сини точки и те маркират точки, където вече са били в сянка от червено до зелено, и непроходими места с плътни сини точки.

Във фаза 2 ще ни е необходима допълнителна информация, която да ни помогне да намерим най-краткия път обратно, когато намерихме целта. За тази цел ние съхраняваме във всяка позиция позицията, от която сме дошли. Ако алгоритъмът работи, позицията, от която идваме, задължително е по-близо до S от всеки друг съсед. Разгледайте псевдокода по-долу, ако не разбирате какво имам предвид.

Фаза 2

Когато замъкът C бъде намерен, следващата стъпка е да намерите пътя си обратно към началото, като съберете кой е най-добрият път. Във фаза 1 съхранихме позицията, от която дойдохме, във всяка позиция, която изследвахме. Знаем, че тази позиция винаги трябва да е по-близо до S (без да пренебрегваме препятствията). Следователно задачата във фаза 2 е много проста: следвайте пътя обратно до позицията, от която сме дошли, всеки път, и следете тези позиции в списък. В края ще имате списък, който формира най-краткия път от C до S. След това просто трябва да обърнете този списък и ще получите своя отговор.

Ще дам някакъв псевдокод, за да го обясня. Има много примери за реален код (също и в Java) в интернет. Този псевдокод предполага, че използвате 2D масив за представяне на мрежата. Алтернатива би била да имате обекти Node, което е по-лесно за разбиране в Pseudocode, но по-трудно за програмиране и подозирам, че все пак бихте използвали 2D масив.

//Phase 1
origins = new array[gridLength][gridWidth]; //Keeps track of 'where we came from'.
front = new Set(); //Empty set. You could use an array for this.
front.add(all neighbours of S);
while(true) { //This keeps on looping forever, unless it hits the "break" statement below.
    best = findBestOption(front);
    front.remove(best);
    for(neighbour in (best's neighbours)) {
        if(neighbour is not a tower and origins[neighbour x][neighbour y] == null) { //Not a tower, and not a position that we explored before.
            front.add(neighbour);
            origins[neighbour x][neighbour y] = best;
        }
    }
    if(best == S) {
        break; //Stops the loop. Ends phase 1.
    }
}

//Phase 2
bestPath = new List(); //You should probably use Java's ArrayList class for this if you're allowed to do that. Otherwise select an array size that you know is large enough.
currentPosition = C; //Start at the endpoint.
bestPath.add(C);
while(currentPosition != S) { //Until we're back at the start.
    currentPosition = origins[currentPosition.x][currentPosition.y];
    bestPath.add(currentPosition);
}
bestPath.reverse();

И за метода findBestOption в този псевдокод:

findBestOption(front) {
    bestPosition = null;
    distanceOfBestPosition = Float.MAX_VALUE; //Some very high number to start with.
    for(position in front) {
        distance = Math.sqrt(position.x * position.x - C.x * C.x + position.y * position.y - C.y * C.y); //Euclidean distance (Pythagoras Theorem). This does the diagonal thing for you.
        if(distance < distanceOfBestPosition) {
            distanceOfBestPosition = distance;
            bestPosition = position;
        }
    }
}

Надявам се това да помогне. Чувствайте се свободни да попитате!

person Ghostkeeper    schedule 03.11.2013
comment
Благодаря за цялата информация. Започнах отначало и внедрих с помощта на вашия псевдо код и обяснение. Няколко бележки. Първо, вашето евклидово разстояние просто има X стойности в псевдо кода. Използвах: 'Math.sqrt(Math.pow(X - EndPoint.x(), 2) + Math.pow(Y - EndPoint.y(),2));' Второ, не вярвам, че вашият метод включва присвояване на нови родители или произход на елиминиране във вашия пример, предполагам. Освен ако не съм пропуснал стъпка. В моята реализация получавам много допълнителни пътеки в затворено на U, след което се връща и намира правилния път. Работя по назначаването на нови родители. - person Spero; 05.11.2013
comment
Добър отговор, но е за предпочитане да можете да зададете нови родители, тъй като по-къси пътища до затворени възли могат да бъдат намерени след първоначалното откриване -- например по-кратък обратен път около препятствие. Затворените възли и техните обратни пътища остават уместни, тъй като те могат да се използват като родители (задни пътища) на отворени възли, които все още са в процес на изследване. - person Thomas W; 05.11.2013
comment
Функцията на евклидовото разстояние наистина беше грешна. Не знам какво си мислех. Актуализира го. Math.pow() работи, но умножаването е по-бързо. Изглежда, че родителите (origins) наистина могат да се променят, според Wikipedia. Моя грешка. Надявам се, че сте го разбрали досега. - person Ghostkeeper; 06.11.2013

Приложете правилно алгоритъма A*. Вижте: http://en.wikipedia.org/wiki/A%2A_search_algorithm

При всяка итерация трябва да:

  1. сортирайте отворените възли в евристичен ред,
  2. изберете най-доброто;
  3. -- проверете дали сте постигнали целта и евентуално прекратете, ако е така;
  4. маркирайте го като „затворен“ сега, тъй като ще бъде напълно проучен от.
  5. проучете всички съседи от него (като добавите към отворените възли картата/ или списъка, ако вече не са затворени).

Въз основа на ASCII диаграмата, която публикувахте, не е абсолютно ясно, че височината на дъската е повече от 3 и че всъщност има път наоколо -- но нека приемем, че има.

Правилният алгоритъм A* не „засяда“ -- когато отвореният списък е празен, не съществува път и той прекратява връщането на път без път null.

Подозирам, че може да не затваряте отворените възли (това трябва да се направи, когато започнете да ги обработвате) или може да не обработвате всички отворени възли на всяка итерация.

Използването на Map<GridPosition, AStarNode> ще помогне за производителността при проверката за всички тези съседни позиции, независимо дали са в отворените или затворените набори/списъци.

person Thomas W    schedule 03.11.2013
comment
Оценявайте отговора. Приложете правилно алгоритъма A*. Да, открих, че не мога да използвам преки пътища с алгоритми. - person Spero; 05.11.2013
comment
Редактирах публикацията си, за да отразя правилния алгоритъм A*, мислейки върху моя код и разбрах, че съм ви подвел кои възли (само най-добрия) да изследвате всяка итерация. Да, наистина трябва да внедрите алгоритмите правилно - в противен случай те правят нещо друго. Надявам се това да помогне. @Сперо - person Thomas W; 05.11.2013