8er-Puzzle



  • Hallo!
    Ich habe ein Problem. Ich möchte ein sogenanntes 8er-Puzzle lösen.
    Ich habe zwar ein Programm das funktioniert, dieses verwendet aber eine eigene Klasse als queue. Nun möchte ich aber eine priority_queue der STL verwenden. Kann mir jemand helfen, wie ich das anstellen kann??
    Vielen Dank schon im Voraus!
    mfg

    main:
    #include <iostream>
    #include "puzzle.h"
    #include <queue>
    #include <deque>
    #include <functional>

    using namespace std;

    int main()
    {

    cout << "Programm zur Loesung des 8er-Puzzle" << endl;

    bool flag = true;

    //Heuristik waehlen
    cout << "Welche Heuristik?" << endl;
    cout << "1 fuer Spielsteine" << endl;
    cout << "2 fuer Manhattan" << endl;
    cout << "3 zum Beenden des Programms" << endl;
    int Eingabe;
    cin >> Eingabe;

    if(Eingabe == 3)
    return 0;

    //Startzustand
    //int arrStart[3][3] = {{1,0,3},{8,2,4},{7,6,5}};
    int arrStart[3][3];
    cout << "Geben Sie den Startzustand ein:" << endl;
    cout << "Zahlen von 0-8 in einer beliebigen Reihenfolge" << endl;

    for (int i = 0; i < 9; i++)
    cin >> arrStart[i/3][i%3];

    //Endzustand
    int arrZiel[3][3] = {{1,2,3},{8,0,4},{7,6,5}};

    cout << "Startzustand:" << endl;
    for(int x = 0; x<3; x++){
    for(int y = 0; y<3 ; y++)
    cout << arrStart[x][y];
    cout << endl;
    }
    cout << endl;

    //priority_queue<int,deque<int> > puzzle;
    //puzzle.push(arrStart);
    //puzzle.push(arrZiel);

    expsortqueue puzzle(arrStart, arrZiel);
    knoten *best;

    //priority_queue<knoten *, deque<int> > OPEN;
    list<knoten *> CLOSED;

    best = puzzle.pop();

    if(Eingabe == 2){
    while (best->manhattan() != 0 && flag){
    puzzle.expand(best);
    best = puzzle.pop();
    best->printKnoten();
    //Aktuellen Stand drucken?
    flag = !puzzle.isemptyqueue();
    }
    }
    else{
    while (best->spielsteine() != 0 && flag){
    puzzle.expand(best);
    best = puzzle.pop();
    best->printKnoten();
    //Aktuellen Stand drucken?
    flag = !puzzle.isemptyqueue();
    }

    }

    if (flag){
    cout << "Solution found!" << endl;
    cout << "The sequence to get to the goal is: ";
    best->printWeg();
    cout << "Zielzustand:" << endl;
    best->printKnoten();
    }
    else
    cout << "Problem has no solution, search tree exausted. Expanded " << best->level() << " levels" << endl;

    //cout << puzzle.nodesexpanded << " nodes expanded" << endl;

    delete [] best;

    return 0;
    }

    puzzle.h:
    #ifndef _PUZZLE_
    #define _PUZZLE_

    #include <list>
    #include <queue>
    #include <cmath>
    using namespace std;

    //moegliche Richtungen
    enum richtung{UP, RIGHT, DOWN, LEFT, CENTER};

    //Zielzustand
    int ziel[9];

    //ein Punkt auf dem Brett
    struct punkt{
    int x;
    int y;
    };

    class knoten {
    private:
    //int * puzzle; //Puzzle des Knotens
    knoten *nextKnoten; //Nächster Knoten in der queue
    int h,g; //Schätzwert Heuristik und Pfadkosten
    punkt leer; //Position des leeren Steins (0)
    richtung weg[2020]; //Speichert die Schritte der Leerstelle
    //struct listenelement *wegende; //Zeiger auf letzten Wegknoten
    //struct listenelement *weg; //Zeiger auf ersten Wegknoten
    public:
    knoten(knoten *vorgaenger, richtung dir); //Konstruktor
    knoten(int brt[3][3]); //Konstruktor
    int brett[3][3]; //8-puzzle brett
    void insert(knoten *next); //Fügt knoten ein
    knoten *getNext(); //Gibt adresse des nächsten Knoten zurück
    int f(); //Gibt f(n) zurück
    int manhattan(); //Gibt Manhattan-Distanz oder Spielsteine zurück
    int spielsteine();
    bool schieben(richtung dir); //Gibt an ob die Leerstelle in diese Richtung verschoben werden kann
    int level(); //Gibt den Level zurück, basierend auf g(n)
    richtung letzteDir(); //Gibt die Richtung an, in welche die Leerstelle als letztes verschoben wurde
    void printWeg(); //Gibt den Loesungsweg des Puzzles aus
    void printKnoten();
    };

    //Prototypen
    int position(knoten *square);
    void change(int &n1, int &n2); //switch two items of a matrix
    punkt copyboard(int src[3][3], int dest[3][3]); //copy a matrix representing a board and return the zero position
    richtung invers(richtung dir);

    // Priority queue
    class expsortqueue {
    private:
    knoten *first; //pointer to first node in queue (biggest f(n))
    knoten *last; //pointer to last node in queue (smallest f(n))
    int initial[3][3]; //initial state
    char visits[45360]; //binary hash to store the states already used (positions = 9!/8bits)
    public:
    //int queuesize; //just for testing
    //int nodesexpanded; //just for testing
    expsortqueue(int init[3][3], int ending[3][3]); //constructor
    ~expsortqueue(); //destructor
    knoten *pop(); //get node on top of the queue
    void pushnsort(knoten *newnode); //put a new node in the queue
    void expand(knoten *parent); //expands a node
    bool isemptyqueue(); //determines if there are more states left in the queue
    bool wasvisited(knoten *square);
    void dovisit(knoten *square);
    };

    //struct liste {
    // struct listenelement *anfang;
    // struct listenelement *ende;
    //};

    //struct knoten V[

    //Implementation der Klasse knoten

    //Konstruktor fuer Startknoten
    knoten::knoten(int brt[3][3])
    {
    g = 0;

    //copy brd into this node's board
    leer = copyboard(brt, brett);

    h = manhattan(); //get manhatan distance

    //setup linked list
    weg[0]=CENTER;
    weg[1]=CENTER;
    nextKnoten = NULL;

    }

    //Konstruktor fuer weitere Knoten
    knoten::knoten(knoten *vorgaenger, richtung dir)
    {
    int i;
    punkt lastblank;
    g = vorgaenger->g + 1;

    //make a copY of the parent's board into this node's board
    leer = copyboard(vorgaenger->brett, brett);

    //move positions according to direction dir
    lastblank = leer;
    switch(dir){
    case UP: leer.y--; break;
    case RIGHT: leer.x++; break;
    case DOWN: leer.y++; break;
    case LEFT: leer.x--; break;
    }
    change(brett[lastblank.y][lastblank.x], brett[leer.y][leer.x]);

    h = manhattan(); //get manhatan distance

    //setup linked list
    for (i=1; vorgaenger->weg[i] != CENTER; i++)
    weg[i]=vorgaenger->weg[i];
    weg[i] = dir;
    weg[i+1] = CENTER;
    nextKnoten = NULL;
    }

    bool knoten::schieben(richtung dir)
    {
    switch(dir){
    case UP: if (leer.y > 0) return true; break;
    case RIGHT: if (leer.x < 2) return true; break;
    case DOWN: if (leer.y < 2) return true; break;
    case LEFT: if (leer.x > 0) return true; break;
    }
    return false;
    }

    int knoten::manhattan()
    {
    int i, pos;
    int result=0;
    int x, y;

    //the goal is defined by putting the position (1 trhough 9)
    //of the square at the array item with index equal to the
    //face number of the square.
    //smallnum ending[9] = {4,0,1,2,5,8,7,6,3}; //bonus = {8,0,1,2,3,4,5,6,7}

    for (i = 0; i < 9; i++){
    y = i/3;
    x = i%3;
    pos = brett[y][x];
    result+=abs(x - ziel[pos]%3); //addiere x distanz
    result+=abs(y - ziel[pos]/3); //addiere y distanz
    }
    return result;
    }

    int knoten::spielsteine()
    {
    int i, pos;
    int result=0;
    int x, y;

    //the goal is defined by putting the position (1 trhough 9)
    //of the square at the array item with index equal to the
    //face number of the square.
    //smallnum ending[9] = {4,0,1,2,5,8,7,6,3}; //bonus = {8,0,1,2,3,4,5,6,7}

    for (i = 0; i < 9; i++){
    y = i/3;
    x = i%3;
    pos = brett[y][x];
    if((abs(x - ziel[pos]%3) + abs(y - ziel[pos]/3)) != 0)
    result++;
    //Addiere 1 fuer jeden Spielstein, der nicht an der richtigen Position liegt
    }
    return result;
    }

    void knoten::printKnoten()
    {
    int i;

    for (i = 0; i < 9; i++){
    cout << brett[i/3][i%3];
    if (i%3 == 2)
    cout << endl;
    }
    cout << endl;
    }

    int knoten::level()
    {
    return g;
    }

    richtung knoten::letzteDir()
    {
    return weg[g];
    }

    void knoten::printWeg()
    {
    int i = 1;

    //Print solution using the history
    while (weg[i] != CENTER){
    switch(weg[i]){
    case UP: cout << "up, "; break;
    case DOWN: cout << "down, "; break;
    case LEFT: cout << "left, "; break;
    case RIGHT: cout << "right, "; break;
    }
    i++;
    }
    cout << endl;
    }

    knoten *knoten::getNext()
    {
    return nextKnoten;
    }

    void knoten::insert(knoten *next)
    {
    nextKnoten = next;
    }

    int knoten::f()
    {
    return g + h;
    }

    //Implementation der queue Klasse

    expsortqueue::expsortqueue(int init[3][3], int ending[3][3]) //constructor
    {
    int i;

    //copy starting and ending states
    for (i = 0; i < 9; i++){
    ziel[ending[i/3][i%3]]=i;
    initial[i/3][i%3] = init[i/3][i%3];
    }

    //initialize visited states to 0
    for (i = 0; i < 45360; i++)
    visits[i] = 0;

    //initialize linked list
    first = new knoten(initial);
    last = first;
    //queuesize = 1;
    //nodesexpanded = 0;
    }

    expsortqueue::~expsortqueue(){
    knoten *i, *tmp;

    for (i = pop(); i!=NULL; i = pop())
    delete [] i;
    }

    // create the children for parent node
    void expsortqueue::expand(knoten *vorgaenger)
    {
    richtung i;
    knoten *newnode;
    int h, g; //heuristics and path cost
    int brett[3][3]; //8-puzzle board
    punkt leer; //position of blank space (0)

    i = UP;
    //make just the necessary objects
    //avoid making imposible states and states coming from the state before de parent
    if (invers(vorgaenger->letzteDir())!=i && vorgaenger->schieben(i)){
    newnode = new knoten(vorgaenger, i);
    //don't push new state if it was already used
    if (!wasvisited(newnode)){

    //nodesexpanded++;
    pushnsort (newnode);
    }
    else{
    delete [] newnode;
    }
    }

    i = RIGHT;
    //make just the necessary objects
    //avoid making imposible states and states coming from the state before de parent
    if (invers(vorgaenger->letzteDir())!=i && vorgaenger->schieben(i)){
    newnode = new knoten(vorgaenger, i);
    //don't push new state if it was already used
    if (!wasvisited(newnode)){

    //nodesexpanded++;
    pushnsort (newnode);
    }
    else{
    delete [] newnode;
    }
    }
    i = DOWN;
    //make just the necessary objects
    //avoid making imposible states and states coming from the state before de parent
    if (invers(vorgaenger->letzteDir())!=i && vorgaenger->schieben(i)){
    newnode = new knoten(vorgaenger, i);
    //don't push new state if it was already used
    if (!wasvisited(newnode)){

    //nodesexpanded++;
    pushnsort (newnode);
    }
    else{
    delete [] newnode;
    }
    }
    i = LEFT;
    //make just the necessary objects
    //avoid making imposible states and states coming from the state before de parent
    if (invers(vorgaenger->letzteDir())!=i && vorgaenger->schieben(i)){
    newnode = new knoten(vorgaenger, i);
    //don't push new state if it was already used
    if (!wasvisited(newnode)){

    //nodesexpanded++;
    pushnsort (newnode);
    }
    else{
    delete [] newnode;
    }
    }

    //register use of state of parent node
    dovisit(vorgaenger);

    delete [] vorgaenger;

    }

    void expsortqueue::dovisit(knoten *square)
    {
    int pos, i; //position number of a given configuration of the board
    int power;

    //get position number of the board of state "square"
    pos = position(square);

    //get 2^(pos%8)
    power = 1;
    power = ( pos%8 == 0)?power = 1:power << (pos%8);

    //mark position as visited
    visits[pos/8] = visits[pos/8] | power;

    }

    bool expsortqueue::wasvisited(knoten *square)
    {
    int pos; //position number of a given configuration of the board
    int power;

    //get position number of the board of state "square"
    pos = position(square);

    //get 2^(pos%8)
    power = 1;
    power = (pos%8 == 0)?power = 1:power << (pos%8);

    //determine if position has been visited
    return ((visits[pos/8] & power) > 0);
    }

    //return first node in priority queue (delete it from queue)
    knoten *expsortqueue::pop()
    {
    knoten *oldfirst;

    oldfirst = first;
    if (first != NULL){
    first = first->getNext();
    }

    //queuesize--;
    return oldfirst;

    }

    //push node to queue
    void expsortqueue::pushnsort(knoten *newNode)
    {
    knoten *n, *prev;

    //in case the node is the first one to be pushed
    if ((first == NULL) || (first->f() > newNode->f())){
    newNode->insert(first);
    first = newNode;
    }
    else{
    //look for ordered place in queue
    for (n = first->getNext(), prev = first; n != NULL; prev = n, n = n->getNext()){
    if (newNode->f() < n->f()){
    prev->insert(newNode);
    newNode->insert(n);
    n==NULL;
    }
    }
    }
    //in case the node is the last one to be pushed
    if (newNode->getNext() == NULL){
    last->insert(newNode);
    last = newNode;
    }
    //queuesize++;

    }

    bool expsortqueue::isemptyqueue()
    {
    return (first == NULL);
    }

    //Hilfsfunktionen

    punkt copyboard(int src[3][3], int dest[3][3])
    {
    int i;
    punkt zero;

    //copy array src to dest
    for (i = 0; i < 9; i++){
    dest[i/3][i%3] = src[i/3][i%3];
    if (src[i/3][i%3] == 0){
    zero.x = i%3;
    zero.y = i/3;
    }
    }

    return zero;
    }

    //switch operation
    void change(int &n1, int &n2)
    {
    int tmp;

    tmp = n1;
    n1 = n2;
    n2 = tmp;

    }

    //return the opsosite direction to each possible direction
    richtung invers(richtung dir)
    {
    switch(dir){
    case UP: return DOWN;
    case DOWN: return UP;
    case LEFT: return RIGHT;
    case RIGHT: return LEFT;
    case CENTER: return CENTER;
    }
    }

    int position(knoten *square)
    {
    char i, j; //loop counters
    char h=0; //unit count for a given position
    int pos = 0; //position number of a given configuration of the board
    int bases[] = {1, 9, 72, 504, 3024, 15120, 60480, 181440, 362880}; //bases to use to count number of possible positions in the board
    int power, reg = 0; //register for numbers already used

    //for each position in the board
    for (i=0; i < 9; i++){
    //check if each number is used in the corresponding order...
    for (j=0; j < 9; j++){
    power = 1;
    power = (j == 0)?power = 1:power << j;
    //...skiping numbers that were already used
    if ((reg & power) == 0){
    if (square->brett[i/3][i%3] == j){
    pos += h*(bases[i]); //increment position number
    //reset counters
    reg = reg | power;
    h = 0;
    j = 9;
    }
    else
    h++; //increment to next usable number in sequence
    }
    }
    }

    return pos;
    }

    #endif



  • Kannst du das mal in cpp-Tags einrahmen, da blickt ja kein Schwein durch.
    Ansonsten solltest du deine Zwischenpositionen in eine struct kapseln und dieser eine Vergleichfunktion (operator<) spendieren, danach kannst du sie in die priority_queue pushen.


Anmelden zum Antworten