diff --git a/Board.py b/Board.py
index 41b1924f7abbb5962353d50ed39e704d34489d11..d3432ed6cd3a2968805fc303f3cd1f2469031e3d 100644
--- a/Board.py
+++ b/Board.py
@@ -1,7 +1,9 @@
 import Tile
 class Board:
     # List of exit tiles. Only red at the moment
-    exit_tiles = [(3,-3), (3,-2), (3,-1), (3,0)]
+    exit_tiles = {'red':[(3,-3), (3,-2), (3,-1), (3,0)],
+            'green':[(-3,3),(-2,3),(-1,3),(0,3)],
+            'blue':[(0,-3),(-1,-2),(-2,-1),(-3,0)]}
     """
     directions = ['l', 'tl', 'tr', 'r', 'br', 'bl']
     moves = {'l':(-1,0), 'tl':(0,-1), 'tr':(1,-1), 'r':(1,0),
@@ -29,15 +31,19 @@ class Board:
         self.pieces = []
         self.g = 0
         self.f = 0
+        self.toMove = []
         self.parent = None
     
+    def __lt__(self, other):
+        return (self.f < other.f)
+
     # Returns a list of valid moves for this board state in the form: ((pos_from),(pos_to))
     def getMoves(self):
         valid_moves = []
         for piece in self.pieces:
 
             # If the piece is on an exit tile, return that piece
-            if piece.pos in self.exit_tiles:
+            if piece.pos in self.exit_tiles[piece.colour]:
                 return piece
             p_r = piece.pos[1]+3
             p_q = piece.pos[0]+3
@@ -46,11 +52,11 @@ class Board:
                     continue
 
                 if self.tiles[p_r + move[1]][p_q + move[0]] is None:
-                    valid_moves.append(((p_q-3, p_r-3),(p_q-3 + move[0], p_r-3 + move[1])))
+                    valid_moves.append(('m',(p_q-3, p_r-3),(p_q-3 + move[0], p_r-3 + move[1])))
 
                 elif p_r + move[1]*2 <= 6 and p_r + move[1]*2 >= 0 and p_q + move[0]*2 <= 6 and p_q + move[0]*2 >= 0:
                     if self.tiles[p_r + move[1]*2][p_q + move[0]*2] is None:
-                        valid_moves.append(((p_q-3, p_r-3),(p_q-3 + move[0]*2, p_r-3 + move[1]*2)))
+                        valid_moves.append(('j',(p_q-3, p_r-3),(p_q-3 + move[0]*2, p_r-3 + move[1]*2)))
         
         return valid_moves
     
diff --git a/__pycache__/Block.cpython-36.pyc b/__pycache__/Block.cpython-36.pyc
index cca19998a05c47e7c43fcdab353fedf8e2026b3a..acf1176829b08e7bc81689adab3eebd1ff33ce58 100644
Binary files a/__pycache__/Block.cpython-36.pyc and b/__pycache__/Block.cpython-36.pyc differ
diff --git a/__pycache__/Board.cpython-36.pyc b/__pycache__/Board.cpython-36.pyc
index f9e8f3375bc235d21baf8a21e37f29b5d1235765..59ff8c998f3173122c9a776294ea3b006d6e8b36 100644
Binary files a/__pycache__/Board.cpython-36.pyc and b/__pycache__/Board.cpython-36.pyc differ
diff --git a/__pycache__/Piece.cpython-36.pyc b/__pycache__/Piece.cpython-36.pyc
index 8b7c34fc71bb2295e61485daec010de8badf2e74..328793a07f0c93a3324c29969ba816760fac2bcc 100644
Binary files a/__pycache__/Piece.cpython-36.pyc and b/__pycache__/Piece.cpython-36.pyc differ
diff --git a/game.cprof b/game.cprof
new file mode 100644
index 0000000000000000000000000000000000000000..f7fc51a35b71419ccf7a62c295e3fee20e9585f5
Binary files /dev/null and b/game.cprof differ
diff --git a/game.py b/game.py
index d0cb9c03ce08475d97d9ab7abdf4822e3f2d2aab..49e87cabaf1deb06192deaca406b9ff2d2ac0ec0 100644
--- a/game.py
+++ b/game.py
@@ -1,12 +1,17 @@
+import cProfile
 import sys
+import heapq
 import json
 import copy
 import Board
 import Piece
 import Block
 
+
 board = Board.Board()
-exit_tiles = {'red':[(3,-3),(3,-2),(3,-1),(3,0)]}
+exit_tiles = {'red':[(3,-3),(3,-2),(3,-1),(3,0)],
+        'green':[(-3,3),(-2,3),(-1,3),(0,3)],
+        'blue':[(0,-3),(-1,-2),(-2,-1),(-3,0)]}
 
 def main():
     with open(sys.argv[1]) as file:
@@ -24,15 +29,30 @@ def main():
         for b in data['blocks']:
             block = Block.Block(tuple(b))
             board.tiles[block.pos[1]+3][block.pos[0]+3] = block
-        
+
+        colour = data['colour']
+        bdict = make_bdict(board)
+        print_board(bdict)
         # Run a* search and print solution if one exists
         path = A_Star(board)
         if path is None:
             print("No path found")
         else:
             for node in path:
+                """
                 bdict = make_bdict(node)
                 print_board(bdict)
+                """
+                if not node.toMove:
+                    continue
+                if node.toMove[0] == 'e':
+                    print("EXIT from (" + str(node.toMove[1]) + ", " + str(node.toMove[2]) + ").")
+
+                elif node.toMove[0] == 'm':
+                    print("MOVE from " + str(node.toMove[1]) + " to " + str(node.toMove[2]) + ".")
+
+                elif node.toMove[0] == 'j':
+                    print ("JUMP from " + str(node.toMove[1]) + " to " + str(node.toMove[2]) + ".")
 
 
 def A_Star(start):
@@ -49,18 +69,21 @@ def A_Star(start):
     while openSet:
 
         # Find node in open set with lowest f value and set it as the "current" node
+        """
         minF = 1000000
         for node in openSet:
             if node.f <= minF:
                 current = node
-                minF = current.f 
+                minF = current.f
+        """
+        current = heapq.heappop(openSet)
         
         # If found node is the goal, retrace the path, and return the solution
         if checkGoal(current):
             return retrace(current) 
 
         # Remove the current node from the open set and add it to the closed set
-        openSet.remove(current)
+        #openSet.remove(current)
         closedSet.append(current)
         
         #Find all neighbors of the current node
@@ -74,6 +97,7 @@ def A_Star(start):
             for node in closedSet:
                 if listCompare(neighbor.tiles, node.tiles):
                     inClosed = True
+                    break
             if inClosed:
                 continue
             
@@ -84,20 +108,21 @@ def A_Star(start):
             for node in openSet:
                 if listCompare(neighbor.tiles, node.tiles):
                     inOpen = True
+                    if tentative_gScore < node.g:
+                        node.parent = current
+                        node.g = tentative_gScore
+                        node.f = node.g + heuristic(node)
+                        heapq.heapify(openSet)
+                    break
             
             # If not, add it
             if not inOpen:
-                openSet.append(neighbor)
+                neighbor.parent = current
+                neighbor.g = tentative_gScore
+                neighbor.f = neighbor.g + heuristic(neighbor)
+                heapq.heappush(openSet, neighbor)
+                #openSet.append(neighbor) 
             
-            # If it is, but the neighbor has a higher g score than the node already in the list, skip it
-            elif tentative_gScore >= neighbor.g:
-                continue
-
-            # Set the parent and g and f scores of the node
-            neighbor.parent = current
-            neighbor.g = tentative_gScore
-            neighbor.f = neighbor.g + heuristic(neighbor)
-
 # Compare two board states
 def listCompare(list1, list2):
     for i in range(0,7):
@@ -112,10 +137,10 @@ def heuristic(node):
     h = 0
     for piece in node.pieces:
         min_dist = 10
-        for tile in exit_tiles['red']:
+        for tile in exit_tiles[piece.colour]:
             if board.distance(piece.pos, tile) <= min_dist:
                 min_dist = board.distance(piece.pos, tile)
-        h += (min_dist + 1) / 2
+        h += (min_dist / 2) + 1
     return h
 
 # Check if the goal has been reached
@@ -127,12 +152,13 @@ def checkGoal(state):
 
 # Retrace the path from the goal state to the initial state
 def retrace(goal):
-    path = []
+    path = [goal]
     cur = goal
     while cur.parent is not None:
-        path.insert(0,cur)
         cur = cur.parent
-    path.insert(0,cur)
+        path.insert(0,cur)
+        #cur = cur.parent
+    # path.insert(0,cur)
     return path
 
 # Find the neighbor states of a given board state
@@ -143,8 +169,9 @@ def getNeighbors(current):
     # getMoves returns only a Piece object if a piece is on an exit tile
     # In that case, the only neighbor is the same board with that piece removed
     if isinstance(moves, Piece.Piece):
-        neighbor = copy.deepcopy(current)
-        
+        neighbor = copy.deepcopy(current) 
+        neighbor.toMove = ['e'] + list(moves.pos)
+
         for piece in neighbor.pieces:
             if piece.pos == moves.pos:
                 neighbor.pieces.remove(piece)
@@ -159,7 +186,8 @@ def getNeighbors(current):
     # In that case, return a list of board states with those moves applied
     for move in moves:
         neighbor = copy.deepcopy(current)
-        neighbor.move(move[0], move[1])
+        neighbor.move(move[1], move[2])
+        neighbor.toMove = list(move)
         neighbors.append(neighbor)
     return neighbors
 
@@ -259,4 +287,9 @@ def print_board(board_dict, message="", debug=False, **kwargs):
     print(board, **kwargs)
 
 if __name__ == '__main__':
+    pr = cProfile.Profile()
+    pr.enable()
     main()
+    pr.disable()
+    pr.print_stats(sort="cumulative")
+
diff --git a/game2.py b/game2.py
new file mode 100644
index 0000000000000000000000000000000000000000..8de950219d961ba72a58ef83eab82dc02a073154
--- /dev/null
+++ b/game2.py
@@ -0,0 +1,344 @@
+import cProfile
+import sys
+import heapq
+import json
+import copy
+import Board
+import Piece
+import Block
+
+
+board = Board.Board()
+exit_tiles = {'red':[(3,-3),(3,-2),(3,-1),(3,0)],
+        'green':[(-3,3),(-2,3),(-1,3),(0,3)],
+        'blue':[(0,-3),(-1,-2),(-2,-1),(-3,0)]}
+
+def main():
+    with open(sys.argv[1]) as file:
+        
+        # Load initial board state
+        data = json.load(file)
+        bdict = {}
+
+        # Add pieces and blocks to the board
+        for p in data['pieces']:
+            piece = Piece.Piece(tuple(p), data['colour'])
+            board.tiles[piece.pos[1]+3][piece.pos[0]+3] = piece
+            board.pieces.append(piece)
+        
+        for b in data['blocks']:
+            block = Block.Block(tuple(b))
+            board.tiles[block.pos[1]+3][block.pos[0]+3] = block
+
+        colour = data['colour']
+        bdict = make_bdict(board)
+        print_board(bdict)
+        # Run a* search and print solution if one exists
+        path = A_Star(board)
+        if path is None:
+            print("No path found")
+        else:
+            for node in path:
+                """
+                bdict = make_bdict(node)
+                print_board(bdict)
+                """
+                if not node.toMove:
+                    continue
+                if node.toMove[0] == 'e':
+                    print("EXIT from (" + str(node.toMove[1]) + ", " + str(node.toMove[2]) + ").")
+
+                elif node.toMove[0] == 'm':
+                    print("MOVE from " + str(node.toMove[1]) + " to " + str(node.toMove[2]) + ".")
+
+                elif node.toMove[0] == 'j':
+                    print ("JUMP from " + str(node.toMove[1]) + " to " + str(node.toMove[2]) + ".")
+
+
+def A_Star(start):
+
+    # Initialise open and closed sets
+    closedSet = {}
+    openSet = [start]
+    openSet2 = {}
+    idx = str(len(start.pieces)) + ":"
+    for p in start.pieces:
+        idx += str(p.pos[0]) + ":"
+        idx += str(p.pos[1])
+    openSet2[idx] = start
+    
+    # Initial path length and heuristic
+    start.g = 0
+
+    start.f = heuristic(start)
+    
+    while openSet:
+
+        # Find node in open set with lowest f value and set it as the "current" node
+        """
+        minF = 1000000
+        for node in openSet:
+            if node.f <= minF:
+                current = node
+                minF = current.f
+        """
+        current = heapq.heappop(openSet)
+        
+        # If found node is the goal, retrace the path, and return the solution
+        if checkGoal(current):
+            return retrace(current) 
+
+        # Remove the current node from the open set and add it to the closed set
+        #openSet.remove(current)
+        idx = str(len(current.pieces)) + ":"
+        for p in current.pieces:
+            idx += str(p.pos[0]) + ":"
+            idx += str(p.pos[1])
+        del openSet2[idx]
+        closedSet[idx] = current
+        
+        #Find all neighbors of the current node
+        neighbors = getNeighbors(current, closedSet)
+        
+        # Iterate through the neighbors of the current node
+        for neighbor in neighbors: 
+            
+            # If the neighbor is already in the closed set, skip it
+            """
+            inClosed = False
+            for node in closedSet:
+                if listCompare(neighbor.tiles, node.tiles):
+                    inClosed = True
+                    break
+            if inClosed:
+                continue
+            
+            idx = str(len(neighbor.pieces)) + ":"
+            for p in neighbor.pieces:
+                idx += str(p.pos[0]) + ":"
+                idx += str(p.pos[1])
+            if idx in closedSet:
+                continue
+            """
+            
+            tentative_gScore = current.g + 1
+            
+            # Check if the neighbor is in the open set
+            """
+            inOpen = False
+            for node in openSet:
+                if listCompare(neighbor.tiles, node.tiles):
+                    inOpen = True
+                    if tentative_gScore < node.g:
+                        node.parent = current
+                        node.g = tentative_gScore
+                        node.f = node.g + heuristic(node)
+                        heapq.heapify(openSet)
+                    break
+            
+            # If not, add it
+            if not inOpen:
+                neighbor.parent = current
+                neighbor.g = tentative_gScore
+                neighbor.f = neighbor.g + heuristic(neighbor)
+                heapq.heappush(openSet, neighbor)
+                #openSet.append(neighbor)
+            """
+            idx = str(len(neighbor.pieces)) + ":"
+            for p in neighbor.pieces:
+                idx += str(p.pos[0]) + ":"
+                idx += str(p.pos[1])
+            if idx in openSet2:
+                node = openSet2[idx]
+                if tentative_gScore < node.g:
+                    node.parent = current
+                    node.g = tentative_gScore
+                    node.f = node.g + heuristic(node)
+                    heapq.heapify(openSet)
+
+            else:
+                neighbor.parent = current
+                neighbor.g = tentative_gScore
+                neighbor.f = neighbor.g + heuristic(neighbor)
+                heapq.heappush(openSet, neighbor)
+                openSet2[idx] = neighbor
+                    
+            
+# Compare two board states
+def listCompare(list1, list2):
+    for i in range(0,7):
+        for j in range(0,7):
+            if type(list1[i][j]) is not type(list2[i][j]):
+                return False
+
+    return True
+
+# Calculates the heuristic for a given board state
+def heuristic(node):
+    h = 0
+    for piece in node.pieces:
+        min_dist = 10
+        for tile in exit_tiles[piece.colour]:
+            if board.distance(piece.pos, tile) <= min_dist:
+                min_dist = board.distance(piece.pos, tile)
+        h += (min_dist / 2) + 1
+    return h
+
+# Check if the goal has been reached
+def checkGoal(state):
+    if not state.pieces:
+        return True
+    else:
+        return False
+
+# Retrace the path from the goal state to the initial state
+def retrace(goal):
+    path = [goal]
+    cur = goal
+    while cur.parent is not None:
+        cur = cur.parent
+        path.insert(0,cur)
+        #cur = cur.parent
+    # path.insert(0,cur)
+    return path
+
+# Find the neighbor states of a given board state
+def getNeighbors(current, closedSet):
+    neighbors = []
+    moves = current.getMoves()
+    
+    # getMoves returns only a Piece object if a piece is on an exit tile
+    # In that case, the only neighbor is the same board with that piece removed
+    if isinstance(moves, Piece.Piece):
+        neighbor = copy.deepcopy(current) 
+        neighbor.toMove = ['e'] + list(moves.pos)
+
+        for piece in neighbor.pieces:
+            if piece.pos == moves.pos:
+                neighbor.pieces.remove(piece)
+                break
+
+        neighbor.tiles[moves.pos[1]+3][moves.pos[0]+3] = None
+        
+        neighbors.append(neighbor)
+        return neighbors
+    
+    # Otherwise, it returns a list of possible moves
+    # In that case, return a list of board states with those moves applied
+    for move in moves:
+        current.move(move[1], move[2]) 
+        idx = str(len(current.pieces)) + ":"
+        for p in current.pieces:
+            idx += str(p.pos[0]) + ":"
+            idx += str(p.pos[1])
+
+        if idx not in closedSet:
+            neighbor = copy.deepcopy(current)
+            neighbor.toMove = list(move)
+            neighbors.append(neighbor)
+
+        current.move(move[2], move[1]) 
+
+    return neighbors
+
+# Functions for printing board state
+def make_bdict(state):
+
+    bdict = {}
+
+    for row in state.tiles:
+        for col in row:
+            if isinstance(col, Piece.Piece):
+                bdict[col.pos] = 'p'
+            elif isinstance(col, Block.Block):
+                bdict[col.pos] = 'b'
+
+    return bdict
+
+def print_board(board_dict, message="", debug=False, **kwargs):
+    """
+    Helper function to print a drawing of a hexagonal board's contents.
+    
+    Arguments:
+
+    * `board_dict` -- dictionary with tuples for keys and anything printable
+    for values. The tuple keys are interpreted as hexagonal coordinates (using 
+    the axial coordinate system outlined in the project specification) and the 
+    values are formatted as strings and placed in the drawing at the corres- 
+    ponding location (only the first 5 characters of each string are used, to 
+    keep the drawings small). Coordinates with missing values are left blank.
+
+    Keyword arguments:
+
+    * `message` -- an optional message to include on the first line of the 
+    drawing (above the board) -- default `""` (resulting in a blank message).
+    * `debug` -- for a larger board drawing that includes the coordinates 
+    inside each hex, set this to `True` -- default `False`.
+    * Or, any other keyword arguments! They will be forwarded to `print()`.
+    """
+
+    # Set up the board template:
+    if not debug:
+        # Use the normal board template (smaller, not showing coordinates)
+        template = """# {0}
+#           .-'-._.-'-._.-'-._.-'-.
+#          |{16:}|{23:}|{29:}|{34:}| 
+#        .-'-._.-'-._.-'-._.-'-._.-'-.
+#       |{10:}|{17:}|{24:}|{30:}|{35:}| 
+#     .-'-._.-'-._.-'-._.-'-._.-'-._.-'-.
+#    |{05:}|{11:}|{18:}|{25:}|{31:}|{36:}| 
+#  .-'-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'-.
+# |{01:}|{06:}|{12:}|{19:}|{26:}|{32:}|{37:}| 
+# '-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'
+#    |{02:}|{07:}|{13:}|{20:}|{27:}|{33:}| 
+#    '-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'
+#       |{03:}|{08:}|{14:}|{21:}|{28:}| 
+#       '-._.-'-._.-'-._.-'-._.-'-._.-'
+#          |{04:}|{09:}|{15:}|{22:}|
+#          '-._.-'-._.-'-._.-'-._.-'"""
+    else:
+        # Use the debug board template (larger, showing coordinates)
+        template = """# {0}
+#              ,-' `-._,-' `-._,-' `-._,-' `-.
+#             | {16:} | {23:} | {29:} | {34:} | 
+#             |  0,-3 |  1,-3 |  2,-3 |  3,-3 |
+#          ,-' `-._,-' `-._,-' `-._,-' `-._,-' `-.
+#         | {10:} | {17:} | {24:} | {30:} | {35:} |
+#         | -1,-2 |  0,-2 |  1,-2 |  2,-2 |  3,-2 |
+#      ,-' `-._,-' `-._,-' `-._,-' `-._,-' `-._,-' `-. 
+#     | {05:} | {11:} | {18:} | {25:} | {31:} | {36:} |
+#     | -2,-1 | -1,-1 |  0,-1 |  1,-1 |  2,-1 |  3,-1 |
+#  ,-' `-._,-' `-._,-' `-._,-' `-._,-' `-._,-' `-._,-' `-.
+# | {01:} | {06:} | {12:} | {19:} | {26:} | {32:} | {37:} |
+# | -3, 0 | -2, 0 | -1, 0 |  0, 0 |  1, 0 |  2, 0 |  3, 0 |
+#  `-._,-' `-._,-' `-._,-' `-._,-' `-._,-' `-._,-' `-._,-' 
+#     | {02:} | {07:} | {13:} | {20:} | {27:} | {33:} |
+#     | -3, 1 | -2, 1 | -1, 1 |  0, 1 |  1, 1 |  2, 1 |
+#      `-._,-' `-._,-' `-._,-' `-._,-' `-._,-' `-._,-' 
+#         | {03:} | {08:} | {14:} | {21:} | {28:} |
+#         | -3, 2 | -2, 2 | -1, 2 |  0, 2 |  1, 2 | key:
+#          `-._,-' `-._,-' `-._,-' `-._,-' `-._,-' ,-' `-.
+#             | {04:} | {09:} | {15:} | {22:} |   | input |
+#             | -3, 3 | -2, 3 | -1, 3 |  0, 3 |   |  q, r |
+#              `-._,-' `-._,-' `-._,-' `-._,-'     `-._,-'"""
+
+    # prepare the provided board contents as strings, formatted to size.
+    ran = range(-3, +3+1)
+    cells = []
+    for qr in [(q,r) for q in ran for r in ran if -q-r in ran]:
+        if qr in board_dict:
+            cell = str(board_dict[qr]).center(5)
+        else:
+            cell = "     " # 5 spaces will fill a cell
+        cells.append(cell)
+
+    # fill in the template to create the board drawing, then print!
+    board = template.format(message, *cells)
+    print(board, **kwargs)
+
+if __name__ == '__main__':
+    pr = cProfile.Profile()
+    pr.enable()
+    main()
+    pr.disable()
+    pr.print_stats(sort="time")
diff --git a/input6.json b/input6.json
new file mode 100644
index 0000000000000000000000000000000000000000..a4cc4c49ef0117cfcb4664f904c53e3d308afdb7
--- /dev/null
+++ b/input6.json
@@ -0,0 +1,5 @@
+{
+	"colour": "red",
+	"pieces": [[0,3],[1,-3],[2,-3]],
+	"blocks": [[3,-3],[3,-2],[3,-1],[2,-1],[2,0],[1,0],[1,1],[0,1],[0,2],[-1,2]]
+}
diff --git a/input7.json b/input7.json
new file mode 100644
index 0000000000000000000000000000000000000000..1494ac6793f27909ab4fe657d19264845f4f66d1
--- /dev/null
+++ b/input7.json
@@ -0,0 +1,5 @@
+{
+	"colour": "green",
+	"pieces": [[0,0],[0,-1],[-2,1]],
+	"blocks": [[-1,0],[-1,1],[1,1],[3,-1]]
+}
diff --git a/input8.json b/input8.json
new file mode 100644
index 0000000000000000000000000000000000000000..c18e92476c28363781540a260be076a2e83726f5
--- /dev/null
+++ b/input8.json
@@ -0,0 +1,5 @@
+{
+	"colour":"red",
+	"pieces":[[2,1],[1,2],[0,3],[-1,3]],
+	"blocks":[[0,2],[-1,2],[-2,2],[1,1],[0,1],[-1,1],[-2,1],[2,0],[1,0],[0,0],[-1,0],[-2,0],[3,0],[2,-1],[1,-1],[0,-1],[-1,-1],[2,-2],[1,-2],[0,-2]]
+}
diff --git a/out4 b/out4
new file mode 100644
index 0000000000000000000000000000000000000000..f95cf7f6c6f4ef66e1f77fba55033f851d97ce51
--- /dev/null
+++ b/out4
@@ -0,0 +1,102 @@
+# 
+#           .-'-._.-'-._.-'-._.-'-.
+#          |     |     |     |     | 
+#        .-'-._.-'-._.-'-._.-'-._.-'-.
+#       |     |     |     |     |     | 
+#     .-'-._.-'-._.-'-._.-'-._.-'-._.-'-.
+#    |     |     |  p  |     |     |  b  | 
+#  .-'-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'-.
+# |  p  |     |  b  |  p  |     |     |     | 
+# '-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'
+#    |     |  p  |  b  |     |  b  |     | 
+#    '-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'
+#       |     |     |     |     |     | 
+#       '-._.-'-._.-'-._.-'-._.-'-._.-'
+#          |     |     |     |     |
+#          '-._.-'-._.-'-._.-'-._.-'
+MOVE from (-3, 0) to (-2, 0).
+MOVE from (-2, 0) to (-1, -1).
+JUMP from (-1, -1) to (1, -1).
+JUMP from (-2, 1) to (0, 1).
+JUMP from (0, -1) to (2, -1).
+JUMP from (0, 1) to (2, 1).
+JUMP from (0, 0) to (2, -2).
+JUMP from (1, -1) to (3, -3).
+EXIT from (3, -3).
+MOVE from (2, 1) to (3, 0).
+EXIT from (3, 0).
+MOVE from (2, -1) to (3, -2).
+EXIT from (3, -2).
+MOVE from (2, -2) to (3, -3).
+EXIT from (3, -3).
+         568057445 function calls (483975328 primitive calls) in 241.139 seconds
+
+   Ordered by: internal time
+
+   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
+71237512/79022   82.996    0.000  213.082    0.003 copy.py:132(deepcopy)
+145111869   20.880    0.000   20.880    0.000 {method 'get' of 'dict' objects}
+5273690/1582107   16.499    0.000  190.758    0.000 copy.py:210(_deepcopy_list)
+4695044/79022   15.684    0.000  212.355    0.003 copy.py:268(_reconstruct)
+  5026161   12.114    0.000   30.281    0.000 copy.py:219(_deepcopy_tuple)
+     1509   11.717    0.008   22.342    0.015 {built-in method _heapq.heapify}
+ 43876398   10.721    0.000   10.721    0.000 Board.py:37(__lt__)
+4695044/79022   10.654    0.000  211.468    0.003 copy.py:236(_deepcopy_dict)
+105670251   10.594    0.000   10.594    0.000 {built-in method builtins.id}
+ 14663778    7.589    0.000   10.837    0.000 copy.py:252(_keep_alive)
+ 47712449    5.137    0.000    5.137    0.000 {method 'append' of 'list' objects}
+ 49489374    4.713    0.000    4.713    0.000 copy.py:190(_deepcopy_atomic)
+  4695044    4.655    0.000    4.655    0.000 {method '__reduce_ex__' of 'object' objects}
+  9390088    4.175    0.000    4.175    0.000 {built-in method builtins.getattr}
+  5026161    3.711    0.000   17.685    0.000 copy.py:220(<listcomp>)
+  4695047    3.074    0.000    3.074    0.000 {built-in method builtins.hasattr}
+  9390088    2.572    0.000    9.921    0.000 copy.py:273(<genexpr>)
+  4695044    2.319    0.000    3.128    0.000 copyreg.py:87(__newobj__)
+  9397928    1.886    0.000    1.886    0.000 {built-in method builtins.isinstance}
+     7745    1.809    0.000  215.417    0.028 game2.py:206(getNeighbors)
+        1    1.714    1.714  241.036  241.036 game2.py:58(A_Star)
+  4695044    1.467    0.000    1.467    0.000 {method 'update' of 'dict' objects}
+  4695044    0.810    0.000    0.810    0.000 {built-in method __new__ of type object at 0x9e3d20}
+  4695044    0.804    0.000    0.804    0.000 {built-in method builtins.issubclass}
+  4695044    0.778    0.000    0.778    0.000 {method 'items' of 'dict' objects}
+  1010061    0.714    0.000    0.922    0.000 Board.py:71(distance)
+    37241    0.425    0.000    1.346    0.000 game2.py:177(heuristic)
+   201026    0.268    0.000    0.268    0.000 Board.py:64(move)
+  3030183    0.207    0.000    0.207    0.000 {built-in method builtins.abs}
+     7745    0.202    0.000    0.214    0.000 Board.py:41(getMoves)
+        1    0.103    0.103  241.139  241.139 game2.py:16(main)
+     7746    0.056    0.000    0.122    0.000 {built-in method _heapq.heappop}
+    35731    0.047    0.000    0.076    0.000 {built-in method _heapq.heappush}
+   187282    0.042    0.000    0.042    0.000 {built-in method builtins.len}
+     7746    0.004    0.000    0.004    0.000 game2.py:188(checkGoal)
+     2206    0.001    0.000    0.001    0.000 {method 'remove' of 'list' objects}
+        1    0.000    0.000    0.000    0.000 {built-in method io.open}
+        1    0.000    0.000    0.000    0.000 game2.py:258(print_board)
+       16    0.000    0.000    0.000    0.000 {built-in method builtins.print}
+        1    0.000    0.000    0.000    0.000 game2.py:195(retrace)
+        1    0.000    0.000    0.000    0.000 game2.py:245(make_bdict)
+        1    0.000    0.000    0.000    0.000 game2.py:328(<listcomp>)
+        1    0.000    0.000    0.000    0.000 decoder.py:345(raw_decode)
+       15    0.000    0.000    0.000    0.000 {method 'insert' of 'list' objects}
+        1    0.000    0.000    0.000    0.000 {method 'read' of '_io.TextIOWrapper' objects}
+        3    0.000    0.000    0.000    0.000 copyreg.py:96(_slotnames)
+        1    0.000    0.000    0.000    0.000 __init__.py:274(load)
+        1    0.000    0.000    0.000    0.000 {method 'format' of 'str' objects}
+        2    0.000    0.000    0.000    0.000 {method 'match' of '_sre.SRE_Pattern' objects}
+        1    0.000    0.000    0.000    0.000 __init__.py:302(loads)
+        1    0.000    0.000    0.000    0.000 decoder.py:334(decode)
+        1    0.000    0.000    0.000    0.000 codecs.py:318(decode)
+        8    0.000    0.000    0.000    0.000 {method 'center' of 'str' objects}
+        1    0.000    0.000    0.000    0.000 {built-in method _locale.nl_langinfo}
+        3    0.000    0.000    0.000    0.000 {method 'get' of 'mappingproxy' objects}
+        4    0.000    0.000    0.000    0.000 Piece.py:2(__init__)
+        4    0.000    0.000    0.000    0.000 Block.py:2(__init__)
+        1    0.000    0.000    0.000    0.000 codecs.py:308(__init__)
+        1    0.000    0.000    0.000    0.000 _bootlocale.py:23(getpreferredencoding)
+        2    0.000    0.000    0.000    0.000 {method 'end' of '_sre.SRE_Match' objects}
+        1    0.000    0.000    0.000    0.000 {built-in method _codecs.utf_8_decode}
+        1    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
+        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
+        1    0.000    0.000    0.000    0.000 codecs.py:259(__init__)
+
+