简体   繁体   中英

BFS Queue is empty before solution is found

The goal of this BFS is to find a solution to a 3x2 puzzle game(0 is blank space and you can only move pieces to that space)

start:

1 4 2

5 3 0

Goal:

0 1 2

3 4 5

The problem is that my queue becomes empty before a solution is found, how is that possible? One of the paths in the search tree must return a solution here. Please let me know if I can clarify anything.

Node Class (represents a state of the game):

mport java.lang.reflect.Array;
import java.util.*;

public class Node {
    public int[] state = new int[6];
    private Node parent;

    public Node(int[] initialState, Node parent){
        this.parent = parent;
        this.state = initialState;
    }

    public boolean isGoal(){
        int[] goal = {0,1,2,3,4,5};
        return Arrays.equals(state, goal);
    }

    public ArrayList<Node> getChildren(){
        ArrayList<Node> children = new ArrayList<>();
        Integer[] newInt = new Integer[getState().length];
        for (int i = 0; i < getState().length; i++) {
            newInt[i] = Integer.valueOf(getState()[i]);
        }
        int position = Arrays.asList(newInt).indexOf(0);
        switch(position){
            case 0:
                children.add(right());
                children.add(down());
                break;
            case 1:
                children.add(down());
                children.add(left());
                children.add(right());
                break;
            case 2:
                children.add(down());
                children.add(left());
                break;
            case 3:
                children.add(up());
                children.add(right());
                break;
            case 4:
                children.add(up());
                children.add(left());
                children.add(right());
                break;
            case 5:
                children.add(up());
                children.add(left());
                break;

        }
        return children;
    }

    public int[] getState(){
        return this.state;
    }


    public int getBlankIndex() {
        for (int i = 0; i < state.length; i++)
            if (state[i] == 0) return i;
        return -1;
    }

    public Node up(){
        int[] newer = state.clone();
        int blankIndex = getBlankIndex();
        int temp = newer[blankIndex - 3];
        newer[blankIndex] = temp;
        newer[blankIndex - 3] = 0;
        return new Node(newer, this);
    }
    public Node down(){
        int[] newer = state.clone();
        int blankIndex = getBlankIndex();
        int temp = newer[blankIndex + 3];
        newer[blankIndex] = temp;
        newer[blankIndex + 3] = 0;
        return new Node(newer, this);
    }

    public Node left(){
        int[] newer = state.clone();
        int blankIndex = getBlankIndex();
        int temp = newer[blankIndex - 1];
        newer[blankIndex] = temp;
        newer[blankIndex - 1] = 0;
        return new Node(newer, this);
    }
    public Node right(){
        int[] newer = state.clone();
        int blankIndex = getBlankIndex();
        int temp = newer[blankIndex + 1];
        newer[blankIndex] = temp;
        newer[blankIndex + 1] = 0;
        return new Node(newer, this);
    }

    public void print(){
        System.out.println("---------");
        System.out.println(Arrays.toString(Arrays.copyOfRange(getState(), 0, 3)));
        System.out.println(Arrays.toString(Arrays.copyOfRange(getState(), 3, 6)));
        System.out.println("---------");
    }

    public void printTrace(){
        Stack<Node> stack = new Stack<>();
        Node current = this;
        while (current.parent != null){
            stack.push(current);
            current = current.parent;
        }
        while (!stack.isEmpty()){
            stack.pop().print();
        }
    }

    @Override
    public boolean equals(Object object){
        if (object instanceof Node) {
            Node node2 = (Node) object;
            return (Arrays.equals(node2.getState(), this.getState()));
        }
        return false;
    }
    @Override
    public int hashCode() {
        return this.hashCode();
    }
}

Driver Class:

import java.util.*;

public class Driver {

    public static void main(String[] args){
        Node test = new Node(new int[]{1, 4, 2, 5, 3, 0}, null);
        BFS(test);
        System.out.println("done");
    }

    public static void BFS(Node initial){
        Queue<Node> queue = new LinkedList<>();
        ArrayList<Node> explored = new ArrayList<>();
        queue.add(initial);
        Node current = initial;
        while (!queue.isEmpty() && !current.isGoal()){
            current = queue.remove();
            for (Node child: current.getChildren()){
                if (!explored.contains(child)) {
                    queue.add(child);
                    explored.add(current);
                }
            }
        }

        System.out.println("DONEDONEDONE");
        current.printTrace();
    }

}

This is a very surprising problem!

I haven't looked at the code yet, it seemed more or less ok. I'll instead address the question: The problem is that my queue becomes empty before a solution is found, how is that possible?

The code is not the problem. The problem is that your puzzle is unsolvable.

The funny thing is that

parity(permutation) * (-1)^{manhattanMetric(positionOfZeroTile)}

is an invariant that is preserved during the entire game.

Let me briefly explain what it means. (It's essentially the same argument as here: https://en.wikipedia.org/wiki/15_puzzle )

The parity of a permutation is (-1)^{numberOfTranspositions} . The number of transpositions is essentially just the number of swaps that the bubble-sort would need to sort the sequence. The manhattan metric of the zero-tile position is x-coordinate of the zero-tile added with the y-coordinate of the zero-tile.

Each time you swap a tile with zero, the parity of the permutation changes the sign.

At the same time, the manhattan metric between the upper left corner and the position of the zero-tile changes by +1 or -1. In both cases, (-1)^{manhattanDist} also changes the sign.

Thus, the product of the parity and (-1)^{manhattanDist} is constant.

If you now look at the solved game

0 1 2
3 4 5

then the number of transpositions is 0, parity is 1, the manhattan distance is 0. Thus, the invariant is (+1).

However, if you look at this:

1 4 2
5 3 0

then you can calculate that the number of transpositions is even (bubble-sort it!), parity is (+1), and the manhattan distance is 2 + 1 = 3, and thus uneven. Thus, the invariant is (+1) * (-1)^3 = (-1) .

But (-1) is not (+1). Therefore, your game is unsolvable in principle, no matter how good your BFS is.


Another (more intuitive but less rigorous) way to see quickly that your puzzle is "broken" is to swap two non-zero tiles in the beginning.

1 4 2
3 5

This is almost immediately solvable:

1 4 2       1   2         1 2
3   5       3 4 5       3 4 5

So, if you don't want to waste any time searching for bugs that aren't there, don't skip the Group Theory lectures next time ;)

An error I can find is that you only add current to the explored list if it doesn't contain child. Additionally, you do this within the loop through the children, so it might also be that you add it multiple times. (Though, this shouldn't affect your result)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM