Created
August 6, 2010 03:47
-
-
Save bemasher/510807 to your computer and use it in GitHub Desktop.
Given an 8-Puzzle which is essentially a jig-saw puzzle with numbers instead of images this program is meant to solve them using a tree. Starting with the initial puzzle state as the root of a tree each possible move is added to the tree as a child and tr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.LinkedList; | |
import java.util.Queue; | |
import java.util.SortedMap; | |
import java.util.TreeMap; | |
import java.io.BufferedReader; | |
import java.io.BufferedWriter; | |
import java.io.FileNotFoundException; | |
import java.io.FileReader; | |
import java.io.FileWriter; | |
import java.io.IOException; | |
public class Puzzle { | |
public static void main(String[] args) { | |
try { | |
//Setup file input and output | |
FileReader iFile = new FileReader("bin\\input_boards"); | |
BufferedReader in = new BufferedReader(iFile); | |
FileWriter oFile = new FileWriter("bin\\output_sequences"); | |
BufferedWriter out = new BufferedWriter(oFile); | |
//Initialize tree and solution | |
String boardValue = ""; | |
String solution = ""; | |
TreeNode head = null; | |
//For each line in input_boards | |
while(in.ready()) { | |
//Get the boardValue | |
boardValue = in.readLine(); | |
//If the board is solvable and it is actually a board of proper length | |
if(isSolvable(boardValue) == true && boardValue.length() == 9) { | |
//Print the board | |
System.out.println(boardValue); | |
//Run garbage collection to clean up after previous boards | |
System.gc(); | |
//Create a new tree head | |
head = new TreeNode(boardValue, ' ', null); | |
try { | |
//Start populating the tree | |
populateTree(head); | |
//The tree has been populated now so get the solution | |
solution = getPath(head); | |
//Print the solution backwards since we started from the solution | |
//and followed bread crumbs back to the head of the tree | |
for(int i = solution.length() - 1; i >= 0; i--) { | |
System.out.print(solution.charAt(i)); | |
out.write(solution.charAt(i)); | |
} | |
System.out.println(); | |
out.write("\n"); | |
} catch(java.lang.OutOfMemoryError e) { | |
//Oops we ran out of memory, make note of it and | |
//continue to next board | |
out.write("Out of Memory\n"); | |
System.out.println("Out of Memory"); | |
} | |
} else { | |
//The board was unsolvable, make note and continue to next board | |
out.write("No Sequence Found\n"); | |
} | |
} | |
//Close both files | |
out.close(); | |
in.close(); | |
} catch (FileNotFoundException e) { | |
//Oops we can't find input_boards | |
e.printStackTrace(); | |
System.out.println("Cannot find file."); | |
} catch (IOException e) { | |
//Oops permissions won't let us read//write from one of the files. | |
System.out.println("Cannot read/write from/to file."); | |
} | |
} | |
public static class TreeNode { | |
public TreeNode child[] = new TreeNode[4]; //Children nodes, max of 4 moves if in the center of puzzle | |
public String boardValue = ""; //Value of the board for current node. | |
public char direction; //Direction moved to arrive at this node, used for printing path | |
public TreeNode parent = null; //Reference to the parent node for traversing the depth of the tree from bottom up | |
public TreeNode(String boardValue, char direction, TreeNode parent) { | |
this.boardValue = boardValue; | |
this.direction = direction; | |
this.parent = parent; | |
} | |
} | |
public static boolean isSolvable(String boardValue) { | |
return inversionCount(boardValue) % 2 == 0; | |
} | |
public static int inversionCount(String boardValue) { | |
int inversions = 0; | |
for(int i = 0; i < boardValue.length(); i++) { | |
for(int j = i + 1; j < boardValue.length(); j++) { | |
if(Character.getNumericValue(boardValue.charAt(i)) > Character.getNumericValue(boardValue.charAt(j)) && boardValue.charAt(i) != '9') { | |
inversions++; | |
} | |
} | |
} | |
return inversions; | |
} | |
public static int[] findBlank(String boardValue) { | |
int[] position = new int[2]; | |
position[0] = boardValue.indexOf('9') / 3; | |
position[1] = boardValue.indexOf('9') % 3; | |
return position; | |
} | |
public static String moveBlank(String boardValue, int row, int col) { | |
char temp; | |
temp = boardValue.charAt(row * 3 + col); | |
boardValue = boardValue.replace('9', ' '); | |
boardValue = boardValue.replace(temp, '9'); | |
boardValue = boardValue.replace(' ', temp); | |
return boardValue; | |
} | |
public static boolean checkBounds(int row, int col) { | |
if (row < 3 && row >= 0 && col < 3 && col >= 0) { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
public static char getDirection(int row, int col) { | |
if (row == 1) { | |
return 'D'; | |
} | |
if (row == -1) { | |
return 'U'; | |
} | |
if (col == 1) { | |
return 'R'; | |
} | |
if (col == -1) { | |
return 'L'; | |
} | |
return ' '; | |
} | |
public static void populateTree(TreeNode currentNode) { | |
Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>(); | |
nodeQueue.add(currentNode); | |
//Until we've generated all possible moves poll the queue. | |
while(nodeQueue.isEmpty() == false) { | |
currentNode = nodeQueue.poll(); | |
//If the currentNode is the solution then we're done. | |
if(currentNode.boardValue.equals("123456789")) { | |
return; | |
} | |
//Generate all possible moves from the current puzzle state. | |
possibleMoves(currentNode, nodeQueue); | |
//Add all of the moves we just generated to the queue. | |
for(TreeNode child : currentNode.child) { | |
if(child != null) { | |
nodeQueue.add(child); | |
} | |
} | |
} | |
} | |
public static void possibleMoves(TreeNode currentNode, Queue<TreeNode> nodeQueue) { | |
int[] blankPosition = findBlank(currentNode.boardValue); | |
String boardValue; | |
int currentMove = 0; | |
boolean nodeExists = false; | |
//Used to sort new puzzle states in order of how close it is to the solution | |
SortedMap<Integer, TreeNode> nodeList = new TreeMap<Integer, TreeNode>(); | |
for (int row = -1; row < 2; row += 1) { | |
for (int col = -1; col < 2; col += 1) { | |
//We can't move diagonally so if one of row or col is non-zero the other must be 0 | |
if (Math.abs(row) != Math.abs(col)) { | |
//Make sure that the current position row and col represent is a valid position | |
if (checkBounds(blankPosition[0] + row, blankPosition[1] + col)) { | |
//Move the blank piece | |
boardValue = moveBlank(currentNode.boardValue, blankPosition[0] + row, blankPosition[1] + col); | |
//If we're not at the root of the tree | |
if(currentNode.parent != null) { | |
//Make sure we're not backtracking to the parent node. | |
if(currentNode.parent.boardValue != boardValue) { | |
//Look through the queue of all nodes generated so far | |
//Make sure we're not a duplicate | |
for(TreeNode node : nodeQueue) { | |
if(node.boardValue.equals(boardValue)) { | |
nodeExists = true; | |
break; | |
} | |
} | |
//If we're not a duplicate add it to nodeList for sorting | |
if(nodeExists == false) { | |
//System.out.println(nodeQueue.size()); | |
nodeList.put(Integer.parseInt(boardValue), new TreeNode(boardValue, getDirection(row, col), currentNode)); | |
} | |
//Reset nodeExists for next queue search | |
nodeExists = false; | |
} | |
} else { | |
//We're the head of the tree so just add all the children | |
nodeList.put(Integer.parseInt(boardValue), new TreeNode(boardValue, getDirection(row, col), currentNode)); | |
} | |
} | |
} | |
} | |
} | |
//Add each child from the nodeList to the child array of the currentNode from smallest boardValue to greatest. | |
for(int childIndex : nodeList.keySet()) { | |
if(nodeList.get(childIndex) != null) { | |
currentNode.child[currentMove] = nodeList.get(childIndex); | |
currentMove++; | |
} | |
} | |
} | |
public static String getPath(TreeNode head) { | |
Queue<TreeNode> nodeList = new LinkedList<TreeNode>(); | |
String solutionPath = ""; | |
TreeNode currentNode = null; | |
nodeList.add(head); | |
//While the queue isn't empty poll the queue | |
while(nodeList.isEmpty() == false) { | |
currentNode = nodeList.poll(); | |
//Make sure we don't try to check up null children | |
if(currentNode != null) { | |
//If we're at the solution then start back-tracking | |
if(currentNode.boardValue.equals("123456789")) { | |
//Concatenate the direction of each node to solutionPath until we | |
//reach the head of the tree | |
while(currentNode.parent != null) { | |
solutionPath += currentNode.direction; | |
currentNode = currentNode.parent; | |
} | |
return solutionPath; | |
} | |
//We haven't found the solution yet so add all children to | |
//the queue for the next loop | |
for(TreeNode child : currentNode.child) { | |
nodeList.add(child); | |
} | |
} | |
} | |
//We never found a solution so return nothing. | |
return ""; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class TreeNode: | |
child = [] | |
boardValue = "" | |
direction = "" | |
parent = None | |
def __init__(self, boardValue, direction, parent): | |
self.boardValue = boardValue | |
self.direction = direction | |
self.parent = parent | |
def isSolvable(boardValue): | |
return inversionCount(boardValue) % 2 == 0 | |
def inversionCount(boardValue): | |
inversions = 0 | |
for i in xrange(0, len(boardValue)): | |
for j in xrange(i, len(boardValue)): | |
if int(boardValue[i]) > int(boardValue[j]) and boardValue[i] != '9': | |
inversions += 1 | |
return inversions | |
def findBlank(boardValue): | |
blank = boardValue.index('9') | |
return (blank / 3, blank % 3) | |
def moveBlank(boardValue, row, col): | |
src = row * 3 + col | |
dest = boardValue.index('9') | |
boardValue = list(boardValue) | |
boardValue[src], boardValue[dest] = (boardValue[dest], boardValue[src]) | |
return ''.join(boardValue) | |
def checkBounds(row, col): | |
return row < 3 and row >= 0 and col < 3 and col >= 0 | |
def getDirection(row, col): | |
dir = {(1,0): 'D', (-1,0): 'U', (0,1): 'R', (0,-1): 'L'} | |
if (row,col) not in dir: | |
return " " | |
else: | |
return dir[(row,col)] | |
def populateTree(currentNode): | |
nodeQueue = {} | |
nodeQueue[int(currentNode.boardValue)] = currentNode | |
while(len(nodeQueue) > 0): | |
boardValue, currentNode = nodeQueue.popitem() | |
if currentNode.boardValue == "123456789": | |
return | |
possibleMoves(nodeQueue, currentNode) | |
for child in currentNode.child: | |
nodeQueue[int(child.boardValue)] = child | |
def possibleMoves(nodeQueue, currentNode): | |
blank = findBlank(currentNode.boardValue) | |
possibleChildren = {} | |
for row in xrange(-1, 2): | |
for col in xrange(-1, 2): | |
if abs(row) != abs(col): | |
if checkBounds(blank[0] + row, blank[1] + col): | |
boardValue = moveBlank(currentNode.boardValue, blank[0] + row, blank[1] + col) | |
if currentNode.parent != None: | |
if currentNode.parent.boardValue != boardValue: | |
if int(boardValue) not in nodeQueue: | |
possibleChildren[int(boardValue)] = TreeNode(boardValue, getDirection(row, col), currentNode) | |
else: | |
possibleChildren[int(boardValue)] = TreeNode(boardValue, getDirection(row, col), currentNode) | |
sortedChildren = possibleChildren.keys() | |
sortedChildren.sort() | |
for key in sortedChildren: | |
currentNode.child.append(possibleChildren[key]) | |
def getPath(head): | |
nodeQueue = [] | |
nodeQueue.append(head) | |
currentNode = None | |
solutionPath = "" | |
while(len(nodeQueue) > 0): | |
currentNode = nodeQueue.pop(0) | |
if currentNode.boardValue == "123456789": | |
while(currentNode.parent != None): | |
solutionPath += currentNode.direction | |
currentNode = currentNode.parent | |
return solutionPath | |
else: | |
if len(currentNode.child) > 0: | |
nodeQueue.extend(currentNode.child) | |
return "" | |
boardValue = "123456798" | |
if isSolvable(boardValue): | |
print boardValue | |
head = TreeNode(boardValue, " ", None) | |
populateTree(head) | |
solution = getPath(head) | |
print solution |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment