Przejdź do treści

Labirynt

Opis problemu

TODO

Specyfikacja

Dane

  • \(n\) - wymiar kwadratowego labiryntu
  • \(labirynt[n][n]\) - dwuwymiarowa tablica definiująca labirynt, w którym spacja oznacza wolne miejsce, a znak hasz oznacza ścianę
  • \(startK, startW\) - współrzędne punktu startowego (kolumna i wiersz)
  • \(stopK, stopW\) - współrzędne punktu docelowego (kolumna i wiersz)

Wynik

  • Dotarcie z punktu startowego do końcowego.

Opis pomysłu

Każdemu polu w labiryncie przypisujemy wartość liczbową, która będzie służyła za licznik odwiedzin. Na początku każdemu polu przypisujemy wartość 0. Gdy w trakcie poruszania się po labiryncie odwiedzimy dane pole, to zwiększymy wartość jego licznika o 1. W każdym kolejnym kroku wybieramy jedno z sąsiednich pól o najmniejszej wartości licznika.

Implementacja

#include <iostream>
#include <cstdlib>
#include <windows.h>

using namespace std;

int main() {
    int n = 10; /// Labirynt ma wymiar n x n
    string labirynt[10];

    labirynt[0] = "##########";
    labirynt[1] = "#       ##";
    labirynt[2] = "##  ######";
    labirynt[3] = "##  ######";
    labirynt[4] = "##  ######";
    labirynt[5] = "#       ##";
    labirynt[6] = "###### ###";
    labirynt[7] = "###### ###";
    labirynt[8] = "######  ##";
    labirynt[9] = "##########";

    int startK = 1; /// Kolumna
    int startW = 1; /// Wiersz

    int stopK = 7; /// Kolumna
    int stopW = 8; /// Wiersz

    /*
        Pomysl 1:
            - Poruszamy sie tak jak wczesniej
            - Odwiedzone pola odznaczamy, np. kropka
            - Gdy natrafimy na rozgalezienie, to zapamietujemy jego wspolrzedne
            - Gdy dotrzemy do slepego zaulka, to wracamy do rozgalezienia i idziemy dalej
            - problem: pamietac kilka rozgalezien

        Pomysl 2:
            - losowe wybieranie drogi na rozgalezieniu
            - w petli zaczynamy droge od punktu startowego
            - gdy dojdziemy do slepego zaulka, to zaczynamy od nowa
            - pomysl: zapamietywanie drog, przez ktore przeszlismy, zeby wybierac inne

        Pomysl 3:
            - Algorytm/zasada prawej/lewej reki

        Pomysl 4:
            - Kazdemu polu przypisujemy wartosc liczbowa, zaczynamy od 0
            - Gdy odwiedzamy pole, to zwiekszamy jego wartosc o 1
            - W kazdym kroku wybieramy pole z najmniejsza wartoscia jako kolejny ruch

        Pomysl 5:
            - Przedstawic labirynt jako graf
            - Skrzyzowania jako wierzcholki
            - Krawedzie wazone z waga proporcjonalna do dlugosci krawedzi
    */

    /// Implementujemy pomysl 4

    /// Tworzymy tablice do zapamietania licznikow dla pol
    int odwiedziny[n][n];

    /// Wypelniamy tablice odwiedzin
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            if(labirynt[i][j] == ' ') {
                odwiedziny[i][j] = 0;
            } else {
                /// W miejsce scian wpisujemy duza liczbe,
                /// zeby te pola nie zostaly wybrane przez algorytm
                odwiedziny[i][j] = 10000000;
            }
        }
    }

    /// Zmienne do pozycji komputera
    int pozycjaK;
    int pozycjaW;

    /// Zapamietujemy, ile wykonalismy ruchow
    int ileRuchow = 0;

    /// Na poczatku komputer jest w miejscu start
    pozycjaK = startK;
    pozycjaW = startW;

    /// Dopoki nie jestesmy w miejscu stop
    while(pozycjaK != stopK || pozycjaW != stopW) {

        /// Zapamietujemy liczby przypisane do pol naokolo
        int lewo, prawo, gora, dol;

        lewo = odwiedziny[pozycjaW][pozycjaK - 1];
        prawo = odwiedziny[pozycjaW][pozycjaK + 1];
        dol = odwiedziny[pozycjaW + 1][pozycjaK];
        gora = odwiedziny[pozycjaW - 1][pozycjaK];

        /// Zwiekszamy licznik obecnego pola
        odwiedziny[pozycjaW][pozycjaK]++;

        if(lewo <= prawo && lewo <= dol && lewo <= gora) {
            /// Idziemy w lewo
            pozycjaK--; /// Poruszamy sie w lewo
            ileRuchow++; /// Zwiekszamy licznik krokow
        } else if(prawo <= lewo && prawo <= dol && prawo <= gora) {
            /// Idziemy w prawo
            pozycjaK++;
            ileRuchow++;
        } else if(gora <= lewo && gora <= dol && gora <= prawo) {
            /// Idziemy w gore
            pozycjaW--;
            ileRuchow++;
        } else {
            /// Idziemy w dol
            pozycjaW++;
            ileRuchow++;
        }

        /// Czyscimy ekran konsoli
        system("cls");

        /// Wyswietlamy labirynt na ekranie
        for(int w = 0; w < n; w++)
        {
            for(int k = 0; k < n; k++)
            {
                /// Jezeli w i k sa pozycjami robota, to wypisz @
                /// W przeciwnym przypadku wypisz znak z labiryntu
                if(w == pozycjaW && k == pozycjaK)
                {
                    cout << "@";
                }
                else
                {
                    cout << labirynt[w][k];
                }
            }

            cout << endl;
        }

        cout << endl << endl;

        /// Wyswietlamy labirynt na ekranie
        for(int w = 0; w < n; w++)
        {
            for(int k = 0; k < n; k++)
            {
                if(odwiedziny[w][k] == 10000000) {
                    cout << "- ";
                }
                 else {
                    cout << odwiedziny[w][k] << " ";
                 }
            }

            cout << endl;
        }

        cout << "Liczba ruchow: " << ileRuchow << endl;

        /// Czekamy chwile, np. 1 sekunde
        Sleep(1000);
    }

    cout << "Liczba ruchow: " << ileRuchow << endl;

    return 0;
}

https://replit.com/@damiankurpiewski/Maze#main.cpp

Opis implementacji

TODO

Warning

Pod systemem Linux zamiast polecenia cls używamy polecenia clear do wyczyszczenia ekranu terminala.

Warning

Pod systemem Linux polecenie Sleep należy zastąpić poleceniem usleep, które jako parametr przyjmuje mikrosekundy, a nie milisekundy. Trzeba także zmienić bibliotekę "windows.h" na "unistd.h".