diff --git a/Board.py b/Board.py
index 3ca9c7a80b7c720f050f0acb4abacdf2dba5aa1c..2e6ff1a0a3e2ac2a4a0ae5ebfc89de48a3a84406 100644
--- a/Board.py
+++ b/Board.py
@@ -12,7 +12,7 @@ class Board:
     def __init__(self):
         # Create board state array
         # None means an empty tile and 0 means a tile not on the board
-        
+        self.duplicate = False
         self.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)]}
@@ -34,6 +34,7 @@ class Board:
         self.f = 0
         self.toMove = []
         self.parent = None
+        self.intermediate_goal = None
     
     def __lt__(self, other):
         return (self.f < other.f)
diff --git a/__pycache__/Board.cpython-36.pyc b/__pycache__/Board.cpython-36.pyc
index 16ddc245c98bb8521a0a6d91764c5285997893a0..41baf69eb89908f10a344bfcfa8e0fc229062c60 100644
Binary files a/__pycache__/Board.cpython-36.pyc and b/__pycache__/Board.cpython-36.pyc differ
diff --git a/game2.py b/game2.py
index d06f48ccede005249296b4a391867dc08a1861d3..6e9ddf6547bb59a2dac3f32c175c21847b3cc054 100644
--- a/game2.py
+++ b/game2.py
@@ -40,7 +40,7 @@ def main():
         colour = data['colour']
         bdict = make_bdict(board)
         print_board(bdict)
-        
+         
         nonBlockTiles = []
         for tile in exit_tiles[colour]:
             if type(board.tiles[tile[1]+3][tile[0]+3]) == Block.Block:
@@ -50,7 +50,7 @@ def main():
         exit_tiles[colour] = nonBlockTiles
         board.exit_tiles[colour] = nonBlockTiles
         print(exit_tiles[colour])
-
+        
 
         # Run a* search and print solution if one exists
         path = A_Star(board)
@@ -92,12 +92,6 @@ def A_Star(start):
     openSet = [start]
     openSet2 = {}
     idx = makeHash(start)
-    """
-    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
@@ -108,14 +102,9 @@ 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
-        """
         current = heapq.heappop(openSet)
+        while current.duplicate == True:
+            current = heapq.heappop(openSet)
         
         # If found node is the goal, retrace the path, and return the solution
         if checkGoal(current):
@@ -124,12 +113,6 @@ def A_Star(start):
         # Remove the current node from the open set and add it to the closed set
         #openSet.remove(current)
         idx = makeHash(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
         
@@ -140,67 +123,21 @@ def A_Star(start):
         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 = makeHash(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)"""
                     del openSet2[idx]
-                    openSet.remove(node)
+                    #openSet.remove(node)
+                    node.duplicate = True
                     neighbor.parent = current
                     neighbor.g = tentative_gScore
                     neighbor.f = neighbor.g + heuristic(neighbor)
-                    heapq.heapify(openSet)
+                    #heapq.heapify(openSet)
                     heapq.heappush(openSet, neighbor)
                     openSet2[idx] = neighbor
 
@@ -246,15 +183,22 @@ def heuristic(node):
 def position_value(node, piece):
     adjacent_piece = pickle.loads(pickle.dumps(piece))
     value = 0
+    if piece.pos in exit_tiles[piece.colour]:
+        return value
     for move in node.moves:
         adjacent_piece.pos = (piece.pos[0] + move[0], piece.pos[1] + move[1])
         adjacent_min = min_dist(adjacent_piece)
         current_min = min_dist(piece)
-        if checkJump(node, piece.pos, move):
+        if checkJump(node, piece.pos, move) and on_board(adjacent_piece.pos):
             if adjacent_min < current_min:
-                value += 0.75
+                value += 0.5
             if adjacent_min == current_min:
-                value += 0.3	
+                value += 0.25
+
+            if adjacent_min == current_min+1:
+                value += 0.15
+        if on_board(adjacent_piece.pos) and type(node.tiles[adjacent_piece.pos[1]+3][adjacent_piece.pos[0]+3]) == Piece.Piece:
+            value += 0.1
     return value
         
 
diff --git a/game3.py b/game3.py
new file mode 100644
index 0000000000000000000000000000000000000000..3abe4a902f3e6aeeba6a0ca3b4dc0963d5aa0061
--- /dev/null
+++ b/game3.py
@@ -0,0 +1,446 @@
+import cProfile
+import pickle
+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)]}
+
+tile_map = {"0-3":1,"1,-3":2,"2-3":3,"3-3":4,"-1-2":5,"0-2":6,"1-2":7,"2-2":8,
+        "3-2":9,"-2-1":10,"-1-1":11,"0-1":12,"1-1":13,"2-1":14,"3-1":15,
+        "-30":16,"-20":17,"-10":18,"00":19,"10":20,"20":21,"30":22,"-31":23,
+        "-21":24,"-11":25,"01":26,"11":27,"21":28,"-32":29,"-22":30,"-12":31,
+        "02":32,"12":33,"-33":34,"-23":35,"-13":36,"03":37}
+
+target_intermediate = False
+
+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)
+         
+        nonBlockTiles = []
+        for tile in exit_tiles[colour]:
+            if type(board.tiles[tile[1]+3][tile[0]+3]) == Block.Block:
+                pass
+            else:
+                nonBlockTiles.append(tile)
+        exit_tiles[colour] = nonBlockTiles
+        board.exit_tiles[colour] = nonBlockTiles
+        print(exit_tiles[colour])
+        
+        newBoard = pickle.loads(pickle.dumps(board))
+        for piece in newBoard.pieces:
+            newBoard.tiles[piece.pos[1]+3][piece.pos[0]+3] = None
+        
+        paths = []
+        for i in range(len(board.pieces)):
+            newBoard.pieces = [pickle.loads(pickle.dumps(piece))]
+            newBoard.tiles[piece.pos[1]+3][piece.pos[0]+3] = piece
+            path = A_Star(newBoard)
+        for node in path:
+         
+            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]) + ".")
+
+            paths.append(path)
+            newBoard.tiles[piece.pos[1]+3][piece.pos[0]+3] = None
+"""
+        co_ords=[]
+        for i in range(len(paths)):
+            for  j  in  range(len(path[0])):
+                co_ords[i][j]=paths[i][j].pieces[0].pos
+                print(co-ords[i][j])
+
+
+         
+        for i in range(len(paths[0])):
+            value = 0
+            for j in range(len(paths)):
+                if paths[0][i].pieces and paths[j][i].pieces:
+                    print(paths[0][i].pieces[0].pos, paths[j][i].pieces[0].pos)
+                    if paths[0][i].pieces[0].pos == paths[j][i].pieces[0].pos:
+                        value += 1
+                        print(value)
+            
+            if value == len(paths):
+                board.intermediate_goal = paths[0][i].pieces[0].pos
+                target_intermediate = True
+                print(board.intermediate_goal)
+            else:
+                break
+"""
+
+        # 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:
+         
+                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 makeHash(node):
+    h = str(len(node.pieces))
+    spos = []
+    for p in node.pieces:
+        s = str(p.pos[0]) + str(p.pos[1])
+        spos.append(s)
+    spos.sort()
+    for piece in spos:
+        h += piece
+    return h
+
+
+
+
+def A_Star(start):
+
+    # Initialise open and closed sets
+    closedSet = {}
+    openSet = [start]
+    openSet2 = {}
+    idx = makeHash(start)
+    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
+        current = heapq.heappop(openSet)
+        while current.duplicate == True:
+            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 = makeHash(current)
+        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
+            
+            tentative_gScore = current.g + 1
+            
+            # Check if the neighbor is in the open set
+            idx = makeHash(neighbor)
+            if idx in openSet2:
+                node = openSet2[idx]
+                if tentative_gScore < node.g:
+                    del openSet2[idx]
+                    #openSet.remove(node)
+                    node.duplicate = True
+                    neighbor.parent = current
+                    neighbor.g = tentative_gScore
+                    neighbor.f = neighbor.g + heuristic(neighbor)
+                    #heapq.heapify(openSet)
+                    heapq.heappush(openSet, neighbor)
+                    openSet2[idx] = neighbor
+
+
+            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 node.distance(piece.pos, tile) <= min_dist:
+                min_dist = node.distance(piece.pos, tile)
+        h += (min_dist / 2) + 1
+    return h
+"""
+
+# Calculates the heuristic for a given board state
+def heuristic(node):
+    h = 0
+    for piece in node.pieces:
+        
+        h += (min_dist(node, piece)-position_value(node, piece) / 2) + 1
+    return h
+
+def position_value(node, piece):
+    adjacent_piece = pickle.loads(pickle.dumps(piece))
+    value = 0
+    if piece.pos in exit_tiles[piece.colour]:
+        return value
+    for move in node.moves:
+        adjacent_piece.pos = (piece.pos[0] + move[0], piece.pos[1] + move[1])
+        adjacent_min = min_dist(node, adjacent_piece)
+        current_min = min_dist(node, piece)
+        if checkJump(node, piece.pos, move):
+            if adjacent_min < current_min:
+                value += 0.6
+            if adjacent_min == current_min:
+                value += 0.35
+
+            if adjacent_min == current_min+1:
+                value += 0.15
+    return value
+        
+
+def min_dist(node, piece):
+    if target_intermediate == True:
+        return board.distance(piece.pos, node.intermediate_goal)
+    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)
+    return min_dist
+
+def on_board(position):
+    if position[0] >= -3 and position[0] <= 3 and position[1] >= -3 and position[1] <= 3:
+        return True
+    else:
+        return False
+
+def checkJump(node, position, move):
+    new = (position[0] + move[0], position[1] + move[1])
+    new_jump = (position[0] + 2*move[0], position[1] + 2*move[1])
+    if on_board(new) and on_board(new_jump):
+        if type(board.tiles[new[0]+3][new[1]+3]) == Block.Block or type(board.tiles[new[0]+3][new[1]+3]) == Piece.Piece and type(board.tiles[new_jump[0]+3][new_jump[1]+3]) == None:
+            return True
+        else:
+            return False
+
+# 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 = pickle.loads(pickle.dumps(current))
+        neighbor.toMove = None
+        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 = makeHash(current)
+        """
+        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 = pickle.loads(pickle.dumps(current))
+            if move[2] == neighbor.intermediate_goal:
+                target_intermediate = False
+            #neighbor = copy.deepcopy(current)
+            neighbor.toMove = None
+            neighbor.toMove = list(move) 
+            if current.toMove == neighbor.toMove:
+                print(current.toMove)
+            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/game4.py b/game4.py
new file mode 100644
index 0000000000000000000000000000000000000000..d225b073dfb55a549e65cc286a7bdcd9d8fa9f79
--- /dev/null
+++ b/game4.py
@@ -0,0 +1,401 @@
+import cProfile
+import pickle
+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)]}
+
+tile_map = {"0-3":1,"1,-3":2,"2-3":3,"3-3":4,"-1-2":5,"0-2":6,"1-2":7,"2-2":8,
+        "3-2":9,"-2-1":10,"-1-1":11,"0-1":12,"1-1":13,"2-1":14,"3-1":15,
+        "-30":16,"-20":17,"-10":18,"00":19,"10":20,"20":21,"30":22,"-31":23,
+        "-21":24,"-11":25,"01":26,"11":27,"21":28,"-32":29,"-22":30,"-12":31,
+        "02":32,"12":33,"-33":34,"-23":35,"-13":36,"03":37}
+
+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)
+         
+        nonBlockTiles = []
+        for tile in exit_tiles[colour]:
+            if type(board.tiles[tile[1]+3][tile[0]+3]) == Block.Block:
+                pass
+            else:
+                nonBlockTiles.append(tile)
+        exit_tiles[colour] = nonBlockTiles
+        board.exit_tiles[colour] = nonBlockTiles
+        print(exit_tiles[colour])
+        
+
+        # 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:
+         
+                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 makeHash(node):
+    h = str(len(node.pieces))
+    spos = []
+    for p in node.pieces:
+        s = str(p.pos[0]) + str(p.pos[1])
+        spos.append(s)
+    spos.sort()
+    for piece in spos:
+        h += piece
+    return h
+
+
+
+
+def A_Star(start):
+
+    # Initialise open and closed sets
+    closedSet = {}
+    openSet = [start]
+    openSet2 = {}
+    idx = makeHash(start)
+    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
+        current = heapq.heappop(openSet)
+        while current.duplicate == True:
+            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 = makeHash(current)
+        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
+            
+            tentative_gScore = current.g + 1
+            
+            # Check if the neighbor is in the open set
+            idx = makeHash(neighbor)
+            if idx in openSet2:
+                node = openSet2[idx]
+                if tentative_gScore < node.g:
+                    del openSet2[idx]
+                    #openSet.remove(node)
+                    node.duplicate = True
+                    neighbor.parent = current
+                    neighbor.g = tentative_gScore
+                    neighbor.f = neighbor.g + heuristic(neighbor)
+                    #heapq.heapify(openSet)
+                    heapq.heappush(openSet, neighbor)
+                    openSet2[idx] = neighbor
+
+
+            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 node.distance(piece.pos, tile) <= min_dist:
+                min_dist = node.distance(piece.pos, tile)
+        h += (min_dist / 2) + 1
+    return h
+"""
+
+# Calculates the heuristic for a given board state
+def heuristic(node):
+    h = 0
+    for piece in node.pieces:
+        
+        h += (min_dist(node, piece)-position_value(node, piece) / 2) + 1
+    return h
+
+def position_value(node, piece):
+    adjacent_piece = pickle.loads(pickle.dumps(piece))
+    value = 0
+    if piece.pos in exit_tiles[piece.colour]:
+        return value
+    for move in node.moves:
+        adjacent_piece.pos = (piece.pos[0] + move[0], piece.pos[1] + move[1])
+        if checkJump(node, piece.pos, move) and on_board(adjacent_piece.pos):
+
+            adjacent_min = min_dist(node, adjacent_piece)
+            current_min = min_dist(node, piece)
+            if adjacent_min < current_min:
+                value += 0.6
+            if adjacent_min == current_min:
+                value += 0.35
+
+            if adjacent_min == current_min+1:
+                value += 0.15
+    return value
+        
+
+def min_dist(node, piece):
+    min_dist = 10
+    if len(node.pieces) > 1:
+        newBoard = pickle.loads(pickle.dumps(node))
+        newPiece = pickle.loads(pickle.dumps(piece))
+        for piece2 in newBoard.pieces:
+            newBoard.tiles[piece2.pos[1]+3][piece2.pos[0]+3] = None
+        newBoard.pieces = [newPiece]
+        newBoard.tiles[newPiece.pos[1]+3][newPiece.pos[0]+3] = newPiece
+        path = A_Star(newBoard)
+        return len(path)
+    else:
+        for tile in exit_tiles[piece.colour]:
+            if board.distance(piece.pos, tile) <= min_dist:
+                min_dist = board.distance(piece.pos, tile)
+    
+    return min_dist
+
+def on_board(position):
+    if position[0] >= -3 and position[0] <= 3 and position[1] >= -3 and position[1] <= 3:
+        return True
+    else:
+        return False
+
+def checkJump(node, position, move):
+    new = (position[0] + move[0], position[1] + move[1])
+    new_jump = (position[0] + 2*move[0], position[1] + 2*move[1])
+    if on_board(new) and on_board(new_jump):
+        if type(board.tiles[new[0]+3][new[1]+3]) == Block.Block or type(board.tiles[new[0]+3][new[1]+3]) == Piece.Piece and type(board.tiles[new_jump[0]+3][new_jump[1]+3]) == None:
+            return True
+        else:
+            return False
+
+# 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 = pickle.loads(pickle.dumps(current))
+        neighbor.toMove = None
+        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 = makeHash(current)
+        """
+        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 = pickle.loads(pickle.dumps(current))
+            #neighbor = copy.deepcopy(current)
+            neighbor.toMove = None
+            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/input10.json b/input10.json
index 5fcca17a2fc649ef9a8d89853558048d2fb1731d..7b3ce6fe11ef3c44be311927c768c72d354eb00a 100644
--- a/input10.json
+++ b/input10.json
@@ -1,5 +1,5 @@
 {
 	"colour": "red",
 	"pieces": [[0,-3],[1,-3],[2,-3],[-3,0]],
-	"blocks": [[3,-3],[3,-2],[3,-1],[2,-1],[2,0],[1,0],[1,1],[0,1],[0,2],[-1,2]]
+	"blocks": [[3,-3],[3,-2],[3,-1],[2,-1],[2,0],[1,0],[1,1],[0,1]]
 }
diff --git a/input11.json b/input11.json
new file mode 100644
index 0000000000000000000000000000000000000000..9b61c4b5fc584417dc4671edfcf14238d70efd6b
--- /dev/null
+++ b/input11.json
@@ -0,0 +1,5 @@
+{
+	"colour": "red",
+	"pieces": [[0,-3],[1,-3],[2,-3],[-3,0]],
+	"blocks": [[3,-3],[3,-2],[3,-1],[2,0],[1,0],[1,1],[0,1],[0,2],[-1,2]]
+}
diff --git a/input12.json b/input12.json
new file mode 100644
index 0000000000000000000000000000000000000000..7b3ce6fe11ef3c44be311927c768c72d354eb00a
--- /dev/null
+++ b/input12.json
@@ -0,0 +1,5 @@
+{
+	"colour": "red",
+	"pieces": [[0,-3],[1,-3],[2,-3],[-3,0]],
+	"blocks": [[3,-3],[3,-2],[3,-1],[2,-1],[2,0],[1,0],[1,1],[0,1]]
+}
diff --git a/input13.json b/input13.json
new file mode 100644
index 0000000000000000000000000000000000000000..ba8c801a5b28d7984c125ac681e8e6b362f53a80
--- /dev/null
+++ b/input13.json
@@ -0,0 +1,5 @@
+{
+	"colour": "red",
+	"pieces": [[0,-3], [-2,-1], [-1,-2], [-3,3]],
+	"blocks": [[1,-3],[2,-3],[1,-2],[2,-2],[1,-1],[2,-1],[1,0],[2,0],[2,1]]
+}
diff --git a/input14.json b/input14.json
new file mode 100644
index 0000000000000000000000000000000000000000..7450c41d32336c56a2c7ec85c59de6c3057685df
--- /dev/null
+++ b/input14.json
@@ -0,0 +1,5 @@
+{
+	"colour": "red",
+	"pieces": [[0,-3], [-3,1], [-3,2], [0,3]],
+	"blocks": [[3,-2],[3,0],[2,-2],[1,-2],[1,-1],[2,-1],[2,0],[2,1],[-1,2],[0,2],[-1,3],[1,2],[1,1],[0,1]]
+}
diff --git a/out13 b/out13
new file mode 100644
index 0000000000000000000000000000000000000000..70cad28f2ff8ef84af98a1ceedcdd0fe7291e95f
--- /dev/null
+++ b/out13
@@ -0,0 +1,21 @@
+# 
+#           .-'-._.-'-._.-'-._.-'-.
+#          |  p  |  b  |  b  |     | 
+#        .-'-._.-'-._.-'-._.-'-._.-'-.
+#       |  p  |     |  b  |  b  |     | 
+#     .-'-._.-'-._.-'-._.-'-._.-'-._.-'-.
+#    |  p  |     |     |  b  |  b  |     | 
+#  .-'-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'-.
+# |     |     |     |     |  b  |  b  |     | 
+# '-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'
+#    |     |     |     |     |     |  b  | 
+#    '-._.-'-._.-'-._.-'-._.-'-._.-'-._.-'
+#       |     |     |     |     |     | 
+#       '-._.-'-._.-'-._.-'-._.-'-._.-'
+#          |  p  |     |     |     |
+#          '-._.-'-._.-'-._.-'-._.-'
+[(3, -3), (3, -2), (3, -1), (3, 0)]
+None
+None
+None
+None