Java 滑块解算器空指针异常

Java 滑块解算器空指针异常,java,nullpointerexception,Java,Nullpointerexception,我正在编写一个滑块解算器,它有一个块对象列表(包含块大小和左上角的位置)和一个表示托盘的二维数组。只要有块,数组中的位置就指向块对象,否则为空 在我的解算器中,我将生成尚未看到的可能移动,对它们进行散列,然后选择要执行的移动(这将更改托盘布局),并在新托盘布局上递归调用解算器。如果在我返回呼叫之前没有看到更多可能的移动布局,请反转上一个移动并继续检查上一个呼叫,依此类推,直到问题解决或移动用尽(无解决方案) 问题是,当我移动时,我得到一个空指针异常。奇怪的是,它只发生在多次递归调用之后。这个程序

我正在编写一个滑块解算器,它有一个块对象列表(包含块大小和左上角的位置)和一个表示托盘的二维数组。只要有块,数组中的位置就指向块对象,否则为空

在我的解算器中,我将生成尚未看到的可能移动,对它们进行散列,然后选择要执行的移动(这将更改托盘布局),并在新托盘布局上递归调用解算器。如果在我返回呼叫之前没有看到更多可能的移动布局,请反转上一个移动并继续检查上一个呼叫,依此类推,直到问题解决或移动用尽(无解决方案)

问题是,当我移动时,我得到一个空指针异常。奇怪的是,它只发生在多次递归调用之后。这个程序运行了好几个调用/移动,然后看起来就一团糟了

generateMoves()通过调用move(),然后在检查移动后反转移动,来测试之前是否看到移动。我认为空指针是在调用move()之后发生的,move()设置为move=layout[]。显然,它是在查找数组中的空位置,而不是带有块的位置。块列表和托盘阵列之间似乎存在差异。。。因为当move()调用setTrayAfterMove()时,它会抛出异常。我不明白的是,为什么它对solveHelper()的几个递归调用有效,但随后中断

import java.io.*; 
import java.util.*; 

public class Solver { 
    Tray initial; 
    Tray goal; 
    HashSet<Integer> visited; 
    LinkedList<Integer> movesToSolution; // list of moves leading to solution 
    int recursionCounter; 
    boolean isSolved; 

    public Solver(String initial, String goal) { 
        this.initial = new Tray(initial); 
        this.goal = new Tray(this.initial, goal); 
        visited = new HashSet<Integer>(); 
        movesToSolution = new LinkedList<Integer>(); 
        recursionCounter = 0; 
        isSolved = false; 
    } 

    public void solve() { 
        if (goal.equals(initial)) { 
            System.out.println("Solver finished no moves"); 
            return; 
        } 
        solveHelper(initial); 
        if (movesToSolution.isEmpty()) { 
            System.out.println("No solution"); 
            System.exit(1); 
        } 
        printMoves(); 
        System.out.println("Solver finished"); 
    } 

    private void solveHelper(Tray t) { 
        Stack<Integer> possibleMoves = new Stack<Integer>(); 
        int lastMoveMade = 0; 
        if (recursionCounter > 5000 || isSolved) { 
            return; 
        } 
        if (goal.equals(t)) { 
            isSolved = true; 
            // movesToSolution.addFirst(lastMoveMade); 
            return; 
        } 
        recursionCounter++; 

        LinkedList<Integer> movesToAdd = t.generateMoves(); 
        Iterator<Integer> movesIter = movesToAdd.iterator(); 
        while (movesIter.hasNext()) { 
            possibleMoves.push(movesIter.next()); 
        } 

        while (!possibleMoves.isEmpty()) { 
            lastMoveMade = possibleMoves.pop(); 
            boolean isMoved = t.move(lastMoveMade, false); 

            if (isMoved) { 
                int moveHash = t.hashCode(); 
                visited.add(moveHash); 
                solveHelper(t); 
            } 

            if (isSolved) { 
                movesToSolution.addFirst(lastMoveMade); 
                return; 
            } 
        } 
        t.move(lastMoveMade, true); 
        return; 
    } 

    public void printMoves() { 
        for (Integer move : movesToSolution) { 
            System.out.println(move); 
        } 
    }      

    public class Tray { 
        private int length; // number of rows 
        private int width; // number of columns 
        private LinkedList<Block> blocks; 
        private Block[][] layout; 

        public Tray(String file) { 
            blocks = new LinkedList<Block>(); 
            try { 
                Scanner s = new Scanner(new FileReader(file)); 
                length = s.nextInt(); 
                width = s.nextInt(); 
                layout = new Block[width][length]; 

                while (s.hasNextLine()) { 
                    int l = s.nextInt(); 
                    int w = s.nextInt(); 
                    int r = s.nextInt(); 
                    int c = s.nextInt(); 
                    Block b = new Block(l, w, r, c); 
                    blocks.add(b); 

                    for (int blockX = b.col; blockX < b.col + b.width; blockX++) { 
                        for (int blockY = b.row; blockY < b.row + b.length; blockY++) { 
                            layout[blockX][blockY] = b; 
                        } 
                    } 
                    s.nextLine(); 
                    // isOK(); 
                } 
            } catch (FileNotFoundException e) { 
                System.out.println("File not found"); 
            } 
        } 

        public Tray(Tray t, String file) { 
            blocks = new LinkedList<Block>(); 
            try { 
                this.length = t.length; 
                this.width = t.width; 
                Scanner s = new Scanner(new FileReader(file)); 
                layout = new Block[this.width][this.length]; 

                while (s.hasNextLine()) { 
                    int l = s.nextInt(); 
                    int w = s.nextInt(); 
                    int r = s.nextInt(); 
                    int c = s.nextInt(); 
                    Block b = new Block(l, w, r, c); 
                    blocks.add(b); 

                    for (int blockX = b.col; blockX < b.col + b.width; blockX++) { 
                        for (int blockY = b.row; blockY < b.row + b.length; blockY++) { 
                            layout[blockX][blockY] = b; 
                        } 
                    } 
                    s.nextLine(); 
                    // isOK(); 
                } 
            } catch (FileNotFoundException e) { 
                System.out.println("File not found"); 
            } 
        } 

        public void print() { 
            for (Block b : blocks) { 
                System.out.println(b.length + " " + b.width + " " + b.col + " "
                        + b.row); 
            } 
        } 

        public boolean equals(Object o) { 
            for (int x = 0; x < this.width; x++) { 
                for (int y = 0; y < this.length; y++) { 
                    if (this.layout[x][y] != null
                            && (((Tray) o).layout[x][y] == null || !((Tray) o).layout[x][y] 
                                    .equals(this.layout[x][y]))) { 
                        return false; 
                    } 
                } 
            } 
            return true; 
        } 

        public int hashCode() { 
            // TODO come up with hashcode unique to layout taking in 
            // consideration block at each coordinate, size of block 
            int hashCode = 0; 
            for (Block b : blocks) { 
                hashCode += (17 * (b.width * b.col)) + (7 * (b.length * b.row)); 
            } 
            return hashCode; 
        } 

        public boolean isOK() { 
            Block[][] trayChecker = new Block[width][length]; 
            Iterator<Block> blockIter = blocks.iterator(); 

            while (blockIter.hasNext()) { 
                Block b = blockIter.next(); 
                for (int x = b.col; x < x + b.width; x++) { 
                    for (int y = b.row; y < y + b.length; y++) { 
                        if (trayChecker[x][y] != null) { 
                            throw new IllegalStateException( 
                                    "Two blocks cannot be in the same location"); 
                        } 
                        if (x < 0 || x > width || y < 0 || y > length) { 
                            throw new IllegalStateException( 
                                    "Block must be completely on the board"); 
                        } 
                        trayChecker[x][y] = b; 
                    } 
                } 
            } 
            return true; 
        } 

        // only returns possible valid moves that haven't been seen before 
        public LinkedList<Integer> generateMoves() { 
            LinkedList<Integer> movesToTry = new LinkedList<Integer>(); 
            // TODO: generate moves that haven't been seen 
            int[] moveDir = { -10, 10, -1, 1 }; 
            for (Block b : blocks) { 
                for (int m : moveDir) { 
                    if (canMove(b, m)) { 
                        int trayMove = createMove(b, m); 
                        move(trayMove, false); 
                        if (!visited.contains(hashCode())) { 
                            movesToTry.add(trayMove); 
                        } 
                        move(trayMove, true); // reverse the move 
                    } 
                } 
            } 
            return movesToTry; 
        } 

        public boolean canMove(Block b, int dir) { 
            int tmp = Math.abs(dir); 
            int y = tmp % 10; 
            int x = tmp / 10; 
            if (dir < 0) { 
                x = -x; 
                y = -y; 
            } 

            if ((b.col + x < 0 || b.col + b.width + x > this.width) 
                    || (b.row + y < 0 || b.row + b.length + y > this.length)) { 
                return false; 
            } 

            if (x == 0) { 
                for (int xBlock = b.col; xBlock < b.col + b.width; xBlock++) { 
                    if (layout[xBlock][b.row + y] != null) { 
                        return false; 
                    } 
                    // } else if(x > 0 && layout[xBlock][b.row + y + b.length - 
                    // 1] != null) { 
                    // return false; 
                    // } 
                } 
            } 

            if (y == 0) { 
                for (int yBlock = b.row; yBlock < b.row + b.length; yBlock++) { 
                    if (layout[b.col + x][yBlock] != null) { 
                        return false; 
                    } 
                    // } else if(x > 0 && layout[b.col + x + b.width - 
                    // 1][yBlock] != null) { 
                    // return false; 
                    // } 
                } 
            } 

            return true; 
        } 

        // only takes valid input 
        public boolean move(int moveDirections, boolean reverse) { 
            Block toMove = null; 
            if (moveDirections == 0) { 
                return false; 
            } 

            // System.out.println(moveDirections + " " + recursionCounter); 
            int tmp = Math.abs(moveDirections); 
            int moveY = tmp % 10; 
            tmp /= 10; 
            int moveX = tmp % 10; 
            tmp /= 10; 
            int blockY = tmp % 1000; 
            tmp /= 1000; 
            int blockX = tmp; 
            System.out.println(blockX + " + " + blockY); 

            if (reverse) { 
                if (moveDirections > 0) { 
                    toMove = layout[blockX + moveX][blockY + moveY]; 
                } else { 
                    toMove = layout[blockX - moveX][blockY - moveY]; 
                } 
                setTrayAfterMove(toMove, true); 
                if (moveDirections < 0) { 
                    toMove.col += moveX; 
                    toMove.row += moveY; 
                } else { 
                    toMove.col -= moveX; 
                    toMove.row -= moveY; 
                } 

                setTrayAfterMove(toMove, false); 
            } else { 
                toMove = layout[blockX][blockY]; 
                setTrayAfterMove(toMove, true); 
                if (moveDirections < 0) { 
                    toMove.col -= moveX; 
                    toMove.row -= moveY; 
                } else { 
                    toMove.col += moveX; 
                    toMove.row += moveY; 
                } 
                setTrayAfterMove(toMove, false); 
            } 
            return true; 
            // 256x256 
            // 1x256 23x256 
            // 100x01 100x001 100x100 
            // 1x01 1x001 1x100 
            // 10x01 10x001 10x100 
        } 

        private int createMove(Block b, int dir) { 
            // multiply b.x to get 8 digits 
            // multiply bh .y to get 5 digits 
            int move = b.col * 100000; 
            move += (b.row * 100); 
            move += Math.abs(dir); 
            if (dir < 0) { 
                move *= -1; 
            } 
            return move; 
        } 

        private void setTrayAfterMove(Block b, boolean isBeforeMove) { 
            for (int blockX = b.col; blockX < b.col + b.width; blockX++) { 
                for (int blockY = b.row; blockY < b.row + b.length; blockY++) { 
                    if(isBeforeMove) { 
                        layout[blockX][blockY] = null; 
                    } else { 
                        layout[blockX][blockY] = b; 
                    } 
                } 
            } 
        } 
    } 

    public class Block { 
        private int length; 
        private int width; 
        private int row; 
        private int col; 

        public Block(int l, int w, int r, int c) { 
            length = l; 
            width = w; 
            row = r; 
            col = c; 
        } 

        public boolean equals(Block b) { 
            return this.length == b.length && this.width == b.width 
                    && this.row == b.row && this.col == b.col; 
        } 
    } 

    public static void main(String[] args) { 
        if (args.length < 2 || args.length > 3) { 
            throw new IllegalArgumentException( 
                    "Must have at least 2 and no more than 3 arguments"); 
        } 

        String initialLayout = args[0]; 
        String goalLayout = args[1]; 
        String debug = ""; 

        if (args.length == 3) { 
            if (args[0].substring(0, 2).equals("-o")) { 
                debug = args[0].substring(2, args[0].length()); 
                switch (debug) { 
                // cases for debugging arguments go here 
                } 
            } else { 
                throw new IllegalArgumentException( 
                        "First argument must start with -o"); 
            } 
            initialLayout = args[1]; 
            goalLayout = args[2]; 
        } 

        Solver s = new Solver(initialLayout, goalLayout); 
        s.solve(); 
    } 
} 
import java.io.*;
导入java.util.*;
公共类求解器{
托盘初始值;
托盘目标;
访问哈什集;
LinkedList movesToSolution;//导致解决方案的移动列表
int递归计数器;
布尔解;
公共解算器(字符串初始值,字符串目标){
this.initial=新托盘(initial);
this.goal=新托盘(this.initial,goal);
visited=新HashSet();
movesToSolution=newlinkedlist();
递归计数器=0;
isSolved=false;
} 
public void solve(){
如果(目标等于(初始值)){
System.out.println(“解算器完成无移动”);
返回;
} 
助理律师(首字母);
if(movesToSolution.isEmpty()){
System.out.println(“无解决方案”);
系统出口(1);
} 
printMoves();
System.out.println(“解算器完成”);
} 
私人助理(信盒t){
堆栈可能移动=新堆栈();
int lastmovemake=0;
如果(递归计数器>5000 | |已解决){
返回;
} 
如果(目标等于(t)){
isSolved=真;
//movesToSolution.addFirst(lastmovemake);
返回;
} 
递归计数器++;
LinkedList movesToAdd=t.generateMoves();
迭代器movesIter=movesToAdd.Iterator();
而(movesIter.hasNext()){
possibleMoves.push(movesIter.next());
} 
而(!possibleMoves.isEmpty()){
LastMoveMake=possibleMoves.pop();
布尔值isMoved=t.move(LastMoveMake,false);
如果(isMoved){
int moveHash=t.hashCode();
已访问。添加(moveHash);
助理员(t);
} 
如果(已解决){
movesToSolution.addFirst(lastmovemake);
返回;
} 
} 
t、 移动(LastMoveMake,true);
返回;
} 
public void printMoves(){
对于(整数移动:movesToSolution){
系统输出打印项次(移动);
} 
}      
公共类托盘{
private int length;//行数
private int width;//列数
私有链接列表块;
专用区块[]布局;
公用托盘(字符串文件){
blocks=新的LinkedList();
试试{
扫描仪s=新扫描仪(新文件读取器(文件));
长度=s.nextInt();
宽度=s.nextInt();
布局=新块[宽度][长度];
而(s.hasNextLine()){
int l=s.nextInt();
int w=s.nextInt();
int r=s.nextInt();
int c=s.nextInt();
块b=新块(l、w、r、c);
块。添加(b);
对于(int blockX=b.col;blockX