![]() |
Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2011 mgr
Jerzy Wałaszek
|
Znajdowanie drogi w labiryncie metodą DFS
Prezentowany tutaj program jest modyfikacją programu z DFS, który wyszukiwał w labiryncie jakąkolwiek drogę pomiędzy dwoma zadanymi punktami: S - startowym i K - końcowym. Opis działania tego programu znajdziecie pod podanym powyżej linkiem. Metoda BFS wykorzystuje kolejkę do przechowywania wierzchołków, które ma odwiedzić. Kolejkę można realizować na wiele różnych sposobów. Najprościej jednak będzie wykorzystanie gotowej struktury danych z biblioteki STL. W tym celu na początku programu dołączamy plik nagłówkowy:
#include <queue>
Plik ten definiuje klasę szablonową queue, czyli kontener kolejki. Zmienną należy zadeklarować następująco:
queue<typ_elementów> nazwa_kolejki;
Klasa szablonowa queue udostępnia nam następujące funkcje składowe:
| empty() | - | Zwraca true, jeśli kolejka jest pusta. Inaczej zwraca false. |
| front() | - | Udostępnia element na początku kolejki. |
| push(el) | - | Umieszcza element na końcu kolejki. |
| pop() | - | Usuwa element z początku kolejki. |
Zasada działania algorytmu BFS w naszym programie jest następująca:
Rozpoczynamy przeglądanie labiryntu od komórki startowej, o współrzędnych xs i ys. Komórkę tą oznaczamy stałą ODWIEDZONE i umieszczamy w kolejce.
Rozpoczynamy pętlę. Na początku pętli sprawdzamy, czy kolejka zawiera jakiś element. Jeśli nie, kończymy - oznacza to, że droga od punktu S do K nie mogła być znaleziona i punkty te leżą w dwóch oddzielnych i nie połączonych ze sobą sekcjach labiryntu. Jeśli kolejka nie jest pusta, przechodzimy do punktu 3.
Pobieramy współrzędne xp i yp z kolejki. Sprawdzamy, czy są one równe współrzędnym punktu K. Jeśli nie, to przechodzimy do punktu 4. Inaczej znaleziona została droga od S do K. Musimy tylko tę drogę odpowiednio oznaczyć. Zatem cofamy się po pozostawionych w komórkach śladach aż do punktu S, wymazując ślady po drodze za pomocą stałej ODWIEDZONE. Następnie wyświetlamy cały labirynt i kończymy.
Sprawdzamy, czy w komory leżące na lewo, u góry, na prawo lub u dołu są wolne. Jeśli tak, wpisujemy do nich odpowiednią stałą W_LEWO, W_GORE, W_PRAWO lub W_DOL i umieszczamy ich współrzędne w kolejce. Wpisana stałe posłużą w punkcie 3 do odszukania zaznaczonej drogi od K do S. Pętlę kontynuujemy
Ponieważ operujemy w programie kolorem, wykorzystujemy opracowaną w naszej szkole bibliotekę newconio. Informacje na temat tej biblioteki znajdziecie tutaj.
| Code::Blocks |
// Demonstracja działania procedury BFS
// wykorzystanej do znajdowania najkrótszej
// drogi w labiryncie.
//-------------------------------------
// (C)2011 Koło Informatyczne
// I LO w Tarnowie
//-------------------------------------
#include <iostream>
#include <string>
#include <ctime>
#include <queue>
#include "newconio.h"
using namespace std;
// Stałe
const int PUSTE = 0; // pusta komora/korytarz
const int SCIANA = 1; // komórka zawierająca ścianę
const int W_LEWO = 2; // komórka z nitką w lewo
const int W_PRAWO = 3; // komora z nitką w prawo
const int W_GORE = 4; // komora z nitką w górę
const int W_DOL = 5; // komora z nitką w dół
const int ODWIEDZONE = 6; // komora już odwiedzona, bez nitki
const int KROK = 10; // opóźnienie kroków w ms
const int LX = 79; // rozmiar labiryntu w poziomie = 1 + 2 * k
const int LY = 47; // rozmiar labiryntu w pionie = 1 + 2 * k
// Zmienne globalne
int labirynt[LX][LY]; // labirynt
int xs,ys; // współrzędne punktu startowego
int xk,yk; // współrzędne punktu końcowego
int xp,yp; // współrzędne poszukiwacza drogi
int lk; // licznik kroków poszukiwacza
queue<int>Q; // kolejka z STL do przechowywania współrzędnych
// Funkcja wyświetla przekazany jej tekst na środku ostatniego
// wiersza ekranu, oczekuje na wciśnięcie przez użytkownika
// klawisza i zwraca jego kod.
//------------------------------------------------------------
int czekaj(string t)
{
int klawisz;
fillrect(' ',0xf0,0,49,79,49);
textcolor(RED); textbackground(WHITE);
center(49,_pl(t));
while(!(klawisz = getch()));
return klawisz;
}
// Funkcja tworzy labirynt w tablicy labirynt
//-------------------------------------------
void tworz_labirynt()
{
int x,y,c;
// wypełniamy cały labirynt ścianami;
for(x=0; x<LX; x++)
for(y=0; y<LY; y++) labirynt[x][y] = SCIANA;
// tworzymy komory i korytarze. Komory tworzone są co 2 komórki.
// Korytarze łączą komory z lewej i z góry
for(x=1; x<LX; x+=2)
for(y=1; y<LY; y+=2)
{
labirynt[x][y] = PUSTE; // komora
c = 0; // licznik korytarzy
if(rand() % 3 == 0)
{
if(x > 1)
labirynt[x-1][y] = PUSTE; // korytarz z lewej
c++; // zwiększamy licznik korytarzy
}
if(rand() % 3 == 0)
{
if(y > 1)
labirynt[x][y-1] = PUSTE; // korytarz z góry
c++; // zwiększamy licznik korytarzy
}
if(c == 0) // jeśli nie został utworzony korytarz,
{ // tworzymy go z lewej albo u góry
if(rand() % 2)
{
if(x > 1) labirynt[x-1][y] = PUSTE;
}
else
{
if(y > 1) labirynt[x][y-1] = PUSTE;
}
}
}
// zerujemy współrzędne początku, końca ścieżki, poszukiwacza
// oraz licznik kroków
xs = ys = xk = yk = xp = yp = lk = 0;
}
// Procedura wyświetla labirynt na ekranie
//----------------------------------------
void pokaz_labirynt()
{
int x,y,rxs,rxk,rys,ryk;
if(xp) // jeśli poszukiwacz jest w labiryncie,
{
rxs = xp-1; rxk = xp+1; // to wyświetlimy tylko fragment
rys = yp-1; ryk = yp+1; // wokół niego - reszta labiryntu już wyświetlona
}
else
{
rxs = 0; rxk = LX-1;
rys = 0; ryk = LY-1;
}
for(x = rxs; x <= rxk; x++)
for(y = rys; y <= ryk; y++)
switch(labirynt[x][y])
{
case PUSTE : putxy(' ',0xe0,x,y); break;
case SCIANA : putxy(' ',0x10,x,y); break;
case ODWIEDZONE : putxy(' ',0xa0,x,y); break;
default : putxy(' ',0x40,x,y); break;
}
if(xs + ys > 0) putxy('S',0x0c,xs,ys); // Start
if(xk + yk > 0) putxy('K',0x0a,xk,yk); // Koniec
if(lk) // Licznik kroków
{
gotoxy(0,48);
textattr(0x0e);
cout << _pl("Liczba kroków : ");
textcolor(LIGHTRED);
cout << lk;
}
}
// Procedura umożliwia użytkownikowi wybranie
// pozycji startowej i końcowej w labiryncie
//------------------------------------------
void ustaw_start_koniec(string t,int & x, int & y)
{
x = y = 1; // rozpoczynamy w lewym górnym rogu
while(true)
{
pokaz_labirynt(); // wyświetlamy labirynt
switch(czekaj(t)) // czekamy na klawisz
{ // i przesuwamy punkt wg klawisza
case 72: if(y > 1) y -= 2; // w górę
break;
case 77: if(x < LX-2) x += 2; // w w prawo
break;
case 80: if(y < LY-2) y += 2; // w dół
break;
case 75: if(x > 1) x -= 2; // w lewo
break;
case 13: return; // klawisz ENTER kończy
}
}
}
// Procedura BFS szukająca drogi w labiryncie
// od punktu startowego do punktu końcowego
//-------------------------------------------
void szukaj_wyjscia()
{
fillrect(' ',0xf0,0,49,79,49);
textcolor(BLACK); textbackground(WHITE);
center(49,_pl("Poszukiwanie najkrótszej ścieżki do punktu końcowego"));
labirynt[xs][ys] = ODWIEDZONE; // zaznaczamy komórkę startową jako odwiedzoną
Q.push(xs); Q.push(ys); // współrzędne S do kolejki
// Teraz w pętli wykonujemy przejście BFS
while(!Q.empty())
{
lk++; // zwiększamy licznik
xp = Q.front(); Q.pop(); // odczytujemy z kolejki współrzędne
yp = Q.front(); Q.pop();
pokaz_labirynt(); // wyświetlamy labirynt (fragment)
delay(KROK); // odczekujemy
if(xp == xk && yp == yk) // sprawdzamy, czy doszliśmy do punktu K
{
// cofamy się do punktu S, wymazując kroki
while(labirynt[xp][yp] != ODWIEDZONE)
{
switch(labirynt[xp][yp])
{
case W_LEWO : labirynt[xp++][yp] = ODWIEDZONE; break;
case W_GORE : labirynt[xp][yp++] = ODWIEDZONE; break;
case W_PRAWO : labirynt[xp--][yp] = ODWIEDZONE; break;
case W_DOL : labirynt[xp][yp--] = ODWIEDZONE; break;
}
pokaz_labirynt(); // wymuszamy wyświetlenie całego labiryntu
delay(KROK);
}
break; // wychodzimy z pętli
}
// szukamy wszystkich dróg wyjścia z bieżącej pozycji. Współrzędne
// komórek umieszczamy w kolejce, w komórkach umieszczamy kierunek przejścia
if(labirynt[xp-1][yp] == PUSTE)
{
labirynt[xp-1][yp] = W_LEWO; // ślad przejścia
Q.push(xp-1); Q.push(yp); // współrzędne do kolejki
}
if(labirynt[xp][yp-1] == PUSTE)
{
labirynt[xp][yp-1] = W_GORE; // ślad przejścia
Q.push(xp); Q.push(yp-1); // współrzędne do kolejki
}
if(labirynt[xp+1][yp] == PUSTE)
{
labirynt[xp+1][yp] = W_PRAWO; // ślad przejścia
Q.push(xp+1); Q.push(yp); // współrzędne do kolejki
}
if(labirynt[xp][yp+1] == PUSTE)
{
labirynt[xp][yp+1] = W_DOL; // ślad przejścia
Q.push(xp); Q.push(yp+1); // współrzędne do kolejki
}
}
}
int main()
{
_cinit();
srand(time(NULL));
fullscreen(true);
cursoroff();
tworz_labirynt();
ustaw_start_koniec("Wybierz punkt startowy i naciśnij ENTER",xs,ys);
ustaw_start_koniec("Wybierz punkt końcowy i naciśnij ENTER",xk,yk);
szukaj_wyjscia();
czekaj("Koniec przejścia. Naciśnij dowolny klawisz, aby zakończyć program.");
textattr(7);
cursoron();
fullscreen(false);
return 0;
}
|
![]()
|
W przypadku, gdy ścieżka nie zostanie znaleziona, BFS, podobnie jak DFS, przechodzi przez cały dostępny graf. To co zostanie, to niespójne składowe, czyli fragmenty grafu, które nie są połączone krawędziami z tą częścią grafów, po której porusza się BFS.
![]() | I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe