From dfb5090f2e31e4e33b9032ed9666bcb693afea27 Mon Sep 17 00:00:00 2001
From: Sean Paeglis <s.paeglis@student.unimelb.edu.au>
Date: Wed, 10 Apr 2019 13:59:35 +1000
Subject: [PATCH] New

---
 Board.py                         |  14 +-
 __pycache__/Block.cpython-36.pyc | Bin 409 -> 392 bytes
 __pycache__/Board.cpython-36.pyc | Bin 2401 -> 2757 bytes
 __pycache__/Piece.cpython-36.pyc | Bin 413 -> 413 bytes
 game.cprof                       | Bin 0 -> 24238 bytes
 game.py                          |  77 +++++--
 game2.py                         | 344 +++++++++++++++++++++++++++++++
 input6.json                      |   5 +
 input7.json                      |   5 +
 input8.json                      |   5 +
 out4                             | 102 +++++++++
 11 files changed, 526 insertions(+), 26 deletions(-)
 create mode 100644 game.cprof
 create mode 100644 game2.py
 create mode 100644 input6.json
 create mode 100644 input7.json
 create mode 100644 input8.json
 create mode 100644 out4

diff --git a/Board.py b/Board.py
index 41b1924..d3432ed 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
GIT binary patch
delta 165
zcmbQq+`-Ig%*)FacV$tG=S0prO)droh8o5ihIob)hF}IwMn6r)Tg(Od#hOgFSc+3~
z(u$ZE7#LPE6tPU)Z^sH^GfZY-w2Wb6U|=X>K_ys0Tt)^41`sX=spViS0<rxxAv87(
Vw>WHa^HWN5Qtd!0iy0Ug7ywP08<GG3

delta 153
zcmeBRp2^H<%*)GlBYsLu^hC}&3jqcOh8o5ihIoc_h8l)=#uSEN22DmkO{QDS1^LCd
z*vm4DGfPraG?{L(6sP8-6)`a|Fsx)KVx73bj-7>pfq{XMZQ^H3QFaCfh9Xu}f^D)R
dqoNwRAOi!#Ee@O9{FKt1R69ln28LpgDFDbzAJPB-

diff --git a/__pycache__/Board.cpython-36.pyc b/__pycache__/Board.cpython-36.pyc
index f9e8f3375bc235d21baf8a21e37f29b5d1235765..59ff8c998f3173122c9a776294ea3b006d6e8b36 100644
GIT binary patch
delta 1502
zcmaDTbX1hfn3tF9kj|PIN$!bUA@vyy3=F9ZQH&`JQA{a9DIzIiDdOoYDUvDDDY7YY
z=`1M<Dat8oDH`c4DXeptqnJ~)Qgl-EQuI>{QjF5sqF7ScQrKG<qF7TnQaD=}qS#Wn
zQn*_fqS#Y-Qg~Y!qBv6cQutdKqBv6of*CZ8Z?QV%Cl;l=WM*Jsc*(@Tz%X$ym$oMJ
zE#{)slv}LnMX9NIw^)*LN>h^=86W^egH$^+FfbI~n|SXcPdY;lLp*B=h-90*m9dJA
zlYxPuh;y<jlQpBz<U%Gd87>9}hFgqTMGy&)EDwm_Wnf?sntYu}pOJ4eJF^lqdlCO+
z9cC%3l6>F%vea8_1&Kwec_o^Rw^)i(bJEas-{Odm&&<m#iI3+8xkLrzI_Ak0%nFmI
zF$>p&yaEyd(I5<VEy&UuhAf5}#w><xhF}IwMn6r)B5?)=22G|SDUcgk^Gh;Pi_mRi
zi;vGKiH}zW*#k0+fw77Q9Bg_J33%ABF)%Q2GB7YW>w%Q+Vq{=QXQ*YIz*w;kWUULs
z1jg7!wM;ckS&TIdSs<EOk|CR|NNe&VX64|F8ip(uu$q`!)*9v-))Yob262WImTZ>d
z03<$hHbZeh4NDDc3Nw;Cg3p}IP&A`3poSrfb#erYEZdhF#w@m!$=a;alc%r<*E2UW
zGBOmx4NPGZfmp&=gf7OM%~}Mv2t^&#GN>3t9YYq}(GYP-2ACsZ{1k?44vwM#uzT1i
z>#$1KgDtONPGOQ@fcO`pG>s_@>dbJ4LcVZ@8pdD-O%A^zR#2(|CB7n1Dl8HLMHypm
z5erCI02GbvMWP@v=7Rj<TU@CXnI-WhnK`M&x7d>NbMi}zZn5U(m!%dLiGUQbB^GcL
zq~@h)vK2{9-p8t82Z}jJ)PoYrEspfm5>PrU)&xbS94Iw0vM>rT3o)`V@-gx;3NSh_
zGBGJJS}_GMYB73DR$<#`0W!Eq9^?`haDs|rD~V4l%Fm5rDTy!1F9Ib}hz(%7K-Lt2
zQkBDGDfUH-Qj_<xD@cN6zywJ87Dq~EaY<rca;p2}Z|ny3+Ms;;@Bjb*noLFDfY1R6
z{6`Yd1=$Xj1cwqxizZVMh(c)5WGVvbga|;jfGDtlCKDul!4`n@!34DHK_LxR2hy#{
zR0L;3j016DMuXU3TVa-iT>}z@=|dKPv%yAcGC|@Rsu~>cAQh9RbMOj51wj;8l_uBZ
zZ5*og3Lv*Bfe2|3AqyhZKm;g+i?l#2aIhi>PyiQ!GBgMmgHjU*6Bi>FqX4@A6NeZF
u3x_xdJ6K+mDT*Z|Gba_yL5e`IQ6N)pao7mu=BJeAq}qX;Sqw6egAoAZl^Qqz

delta 1176
zcmX>q`cR0=n3tDpSM7qBJdTN6A$}eV3=F9ZQH&`JQA{cPDS|1&DI)1CQOqe~DH17C
zDbgu2DRSv-Q7kE}DQqnaQLHKKDI6^fQEVxkDO@cKQS2$)DLgF<Q5-3}!3>)66VFJC
zCo?iY0ElL0U|?`&U|=Y|F<FT5B3lYW4MRNZ<oAqKY-|h+3`J~{^O>v}xhJn?@{(a^
zU|_h#m{kOk0LgNK2rdQ&2JXp%%=(PnlO36r0@#XpKnht)GILUkZ?P3*rY5Hr-(pO^
z#h7-Btst=|HLpaI@fJ&QYEBxOQMWka<1_OzOXA}}W)>@fT*)Xh`5?3K<O|F!L41sC
zU|7ZHl%H6Xq6bq9_YcTn95DZEU}RuOXQ*YIz*w=Qh9Qf=g<%3??2KBb8m2798ip*U
zY_=km$+K9LgHvi4vY5fL5w)x}%r&ejjFJrE3@I$xEX7_(eCBM1Vy_yO8rBqMBzXj%
zIh&zqQlVE3Ll(<q0ajU-H#Lk|lZ{#R>Y17u85s)UMy0TdK#XTBLKkDsW-WqSfuas-
z4O9%Gjv<Q$<~)eFBm>NOFn$U{Hb;GtR}EtpYYj^ZlLSKzGl)!Mg7UzAfhdN^Kzt7o
zNn=WbIz61BkT0B}hB25ylg+P)6%^~BbW;RMKt=o@E(-$#gC<*%5GdxE3-XI^aivyd
zmc)b7fMoG4*4+HE)Z!vRkStqbK|yL>iY8l;*yJuY4LeYzLDB&yNSY%(wZs>sS{9Tj
z<-kdRg;9W!hmnPmkCBg2fYE`GiAjmkf+>Jei_vZJ2ey3{Aajc(KrUb@5@TRsh+->=
zPb<pLjbbT@FUc>G0XY!Wnj(<thLfMMFJcs%+{vLJi7I`IBPFxABrz{J)qL_H4ud3R
z1_p+g|Nj5~ugO#d_P7d2;6IXpD#&)IBsf%{0w4+!Odysf6M|g?6#$2(>g0!<GD1)h
z5CsloP0q>uT&newAoojw2yqYr3KOvVi$EC!5_G7}FJfX~U;yD_P)_7v<Y5zF<Phc%
l;a~&HXfj2ygk<KVf;rgq-{P>z%}*)KNwoucW%52Qb^z{9<&*#b

diff --git a/__pycache__/Piece.cpython-36.pyc b/__pycache__/Piece.cpython-36.pyc
index 8b7c34fc71bb2295e61485daec010de8badf2e74..328793a07f0c93a3324c29969ba816760fac2bcc 100644
GIT binary patch
delta 16
XcmbQsJeQf>n3tC;?aHE!?9&(lC5Qyd

delta 16
XcmbQsJeQf>n3tC;W69i&?9&(lBwYlT

diff --git a/game.cprof b/game.cprof
new file mode 100644
index 0000000000000000000000000000000000000000..f7fc51a35b71419ccf7a62c295e3fee20e9585f5
GIT binary patch
literal 24238
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

literal 0
HcmV?d00001

diff --git a/game.py b/game.py
index d0cb9c0..49e87ca 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 0000000..8de9502
--- /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 0000000..a4cc4c4
--- /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 0000000..1494ac6
--- /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 0000000..c18e924
--- /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 0000000..f95cf7f
--- /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__)
+
+
-- 
GitLab