SearchNode(Board b, SearchNode prevNode) { board = b; prev = prevNode; existingMoves = prevNode.getMoves() + 1; manhattan = b.manhattan(); priority = existingMoves + manhattan; }
// find a solution to the initial board (using the A* algorithm) public Solver(Board initial) { Board twin = initial.twin(); SearchNode minNode = null; boolean isTwinRound = false; MinPQ<SearchNode> currentPQ = null; minPQ.insert(new SearchNode(initial)); minPQForTwin.insert(new SearchNode(twin)); while (true) { // Searching solution in the initial board and and its twin board // simultaneously(alternating in loops). // It has been proven by Math theory that exactly one of the two // will lead to the goal board. If a solution is found in the twin // board, it immediately proves that the initial board is not solvable, // and the search will terminate there. // Otherwise, the initial board will reach a solution. if (isTwinRound) { currentPQ = minPQForTwin; } else { currentPQ = minPQ; } minNode = currentPQ.delMin(); if (minNode.getBoard().isGoal()) { break; } else { for (Board neighbor : minNode.getBoard().neighbors()) { // Insert the neighbors into the MinPQ if: // 1. Current node contains the initial board(has no prev node) // 2. The new neighbor is not the same as current node's previous search node. // This is a critical optimization used to reduce unnecessary // exploration of already visited search nodes. if (minNode.getPrev() == null || !neighbor.equals(minNode.getPrev().getBoard())) { currentPQ.insert(new SearchNode(neighbor, minNode)); } } // Flip the state of the isTwinRound flag isTwinRound = isTwinRound ? false : true; } } if (isTwinRound) { isSolvable = false; solution = null; } else { isSolvable = true; solution = minNode; moves = solution.getMoves(); } }