Katerina Chernykh
Katerina Chernykh

Reputation: 15

Breadth-First Search implementation not Working

I have a problem with the implementation of the breadth-first search algorithm, I have a method that gives me an array of integers from 0-8, in random order. I also have an integer m that tells me which number is blank. Here are the rules:

I get a block of numbers, like:

456           
782        
301

And lets say that 8 is the blank value, I can swap it with 5, 7, 2, and 0. since they are directly next to it. I have to use breadth-first search to solve this puzzle. Here is the code I have written so far:

package application;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Vector;

public class Solution {

    /******************************************
     * Implementation Here
     ***************************************/

    /*
     * Implementation here: you need to implement the Breadth First Search
     * Method
     */
    /* Please refer the instruction document for this function in details */

    public static LinkedHashSet<int[]> OPEN = new LinkedHashSet<int[]>();
        public static HashSet<int[]> CLOSED = new HashSet<int[]>();
    public static boolean STATE = false;
    public static int empty;

    public static void breadthFirstSearch(int[] num, int m, Vector solution1) {
        int statesVisited = 0;
        for(int i : num) {
            if(num[i] == m) {
                empty = i;
            }
        }

        int[] start = num;
        int[] goal = {0,1,2,3,4,5,6,7,8};
        int[] X;
        int[] temp = {};

        OPEN.add(start);

        while (OPEN.isEmpty() == false && STATE == false) {

            X = OPEN.iterator().next();
            OPEN.remove(X);

            int pos = empty; // get position of ZERO or EMPTY SPACE
            if (compareArray(X,goal)) {
                System.out.println("SUCCESS");

                STATE = true;
            } else {
                // generate child nodes
                CLOSED.add(X);

                temp = up(X, pos);
                if (temp != null)
                    OPEN.add(temp);
                temp = left(X, pos);
                if (temp != null)
                    OPEN.add(temp);
                temp = down(X, pos);
                if (temp != null)
                    OPEN.add(temp);
                temp = right(X, pos);
                if (temp != null)
                    OPEN.add(temp);
                if(OPEN.isEmpty()) 
                    System.out.println("Ending loop");
            }
        }

    }
    public static boolean compareArray(int[] a, int[] b) {
        for(int i: a) 
            if(a[i] != b[i])
                return false;

        return true;

    }

    public static int[] up(int[] s, int p) {
        int[] str = s;
        if (p > 3) {
            int temp = str[p-3];
            str[p-3] = str[p];
            str[p] = temp;


        }
        // Eliminates child of X if its on OPEN or CLOSED
        if (!OPEN.contains(str) && CLOSED.contains(str) == false)
            return str;
        else
            return null;
    }


    public static int[] down(int[] s, int p) {
        int[] str = s;
        if (p < 6) {
            int temp = str[p+3];
            str[p+3] = str[p];
            str[p] = temp;

        }

        // Eliminates child of X if its on OPEN or CLOSED
        if (!OPEN.contains(str) && CLOSED.contains(str) == false)
            return str;
        else
            return null;
    }


    public static int[] left(int[] s, int p) {
        int[] str = s;
        if (p != 0 && p != 3 && p != 6) {
            int temp = str[p-1];
            str[p-1] = str[p];
            str[p] = temp;

        }
        // Eliminates child of X if its on OPEN or CLOSED
        if (!OPEN.contains(str) && CLOSED.contains(str) == false)
            return str;
        else
            return null;
    }


    public static int[] right(int[] s, int p) {
        int[] str = s;
        if (p != 2 && p != 5 && p != 8) {
            int temp = str[p+1];
            str[p+1] = str[p];
            str[p] = temp;
        }
        // Eliminates child of X if its on OPEN or CLOSED
        if (!OPEN.contains(str) && CLOSED.contains(str) == false)
            return str;
        else
            return null;
    }
    public static void print(String s) {
        System.out.println(s.substring(0, 3));
        System.out.println(s.substring(3, 6));
        System.out.println(s.substring(6, 9));
        System.out.println();
    }
}

This code just immediately ends, and never finds an answer. Perhaps I have done something wrong? Please help.

Please Note: This is my first question on StackOverFlow, so if anyone has any criticisms please tell me and I will fix them right away.

Upvotes: 2

Views: 294

Answers (2)

Luke Garrigan
Luke Garrigan

Reputation: 5021

First of all, you have a parameter which isn't doing anything, Vector solution in:

public static void breadthFirstSearch(int[] num, int m, Vector solution1)

Also you are passing in the position of the zero element which you are representing as m, then assigning a local variable to that position, seems a little pointless to me there's no need to pass in the zero position if you're going to search for it anyway.

Updated breadth first search method:

public static void breadthFirstSearch(int[] num) {
    for (int i : num) {
        if (num[i] == 0) {
            empty = i;
        }
    }

    int[] start = num;
    int[] goal = {1, 2, 3, 4, 5, 6, 7, 8, 0};
    int[] X;
    int[] temp = {};

    OPEN.add(start);

    while (OPEN.isEmpty() == false && STATE == false) {

        X = OPEN.iterator().next();
        OPEN.remove(X);
        int pos = empty; // get position of ZERO or EMPTY SPACE
        if (Arrays.equals(X, goal)) {
            System.out.println("SUCCESS");
            STATE = true;
        } else {
            // generate child nodes
            CLOSED.add(X);
            temp = up(X, pos);
            if (temp != null) {
                OPEN.add(temp);
            }
            temp = left(X, pos);
            if (temp != null) {
                OPEN.add(temp);
            }
            temp = down(X, pos);
            if (temp != null) {
                OPEN.add(temp);
            }
            temp = right(X, pos);
            if (temp != null) {
                OPEN.add(temp);
            }
            if (OPEN.isEmpty()) {
                System.out.println("Ending loop");
            }
        }
    }
}

The main issue with your program was that within your movement methods up(), down(), left(), right(). You weren't creating complete copies of the arrays, thus resulting in modifications happening to the original array.

Thus this assignment: int[] str = s;

must be changed to:

   int[] str = new int[s.length];
   System.arraycopy(s, 0, str, 0, s.length);

Here's an example of a completed method:

public static int[] up(int[] s, int p) {
    int[] str = new int[s.length];
    System.arraycopy(s, 0, str, 0, s.length);

    if (p > 3) {
        int temp = str[p - 3];
        str[p - 3] = str[p];
        str[p] = temp;
    }
    // Eliminates child of X if its on OPEN or CLOSED
    if (!OPEN.contains(str) && !CLOSED.contains(str)) {
        return str;
    } else {
        return null;
    }
}

SIDE NOTE (Not essential):

There are certain permutations of the array which won't result in the goal state. This puzzle itself can have a total number of 9! configurations, but actually only 9!/2 of these are solvable.

I wrote an algorithm for checking the parity of the puzzle, which can be done as a kind of preprocessing, I used it in order to create random instances for testing the data.

public  boolean isSolvable(int[] puzzle) {
    boolean parity = true;
    int gridWidth = (int) Math.sqrt(puzzle.length);
    boolean blankRowEven = true; // the row with the blank tile

    for (int i = 0; i < puzzle.length; i++) {
        if (puzzle[i] == 0) { // the blank tile
            blankRowEven = (i / gridWidth) % 2==0;
            continue;
        }
        for (int j = i + 1; j < puzzle.length; j++) {
            if (puzzle[i] > puzzle[j] && puzzle[j] != 0) {
                parity = !parity;
            }
        }
    }

    // even grid with blank on even row; counting from top
    if (gridWidth % 2 == 0 && blankRowEven) { 
        return !parity;
    }
    return parity;
}

For the vector

You want to be able to print out the path that has been taken to get to the goal state, I would recommend having a class for the State such:

private State previousState;
private int[] current;

public State(int[] current, State previousState) {
    this.current = current;
    this.previousState = previousState
}

public State getPreviouState(){
    return previousState;
}
public int[] getCurrentState(){
    return currentState;
}

Then when you have the goal State you can loop through all the previous States to see the path it took.

State current = GOAL;
while(current != null){
    System.out.println(Arrays.toString(current));
    current = current.getPreviousState();
}

Upvotes: 2

Carlos Cook
Carlos Cook

Reputation: 151

The method up(...) has an error:

You have:

 str[p] = str[p-3];

Which I'm guessing should be:

 str[p] = temp;

Upvotes: 1

Related Questions