[{"data":1,"prerenderedAt":1595},["ShallowReactive",2],{"post-solve-puzzle-game-using-python":3},{"id":4,"title":5,"body":6,"canonicalUrl":1579,"cover":1580,"date":1581,"description":1582,"draft":1583,"extension":1584,"hashnodeId":1585,"meta":1586,"navigation":132,"path":1587,"seo":1588,"slug":1589,"stem":1589,"tags":1590,"__hash__":1594},"posts\u002Fsolve-puzzle-game-using-python.md","Solving the turtle tiles puzzle game using Python",{"type":7,"value":8,"toc":1558},"minimark",[9,14,26,30,33,37,40,47,50,69,72,76,85,91,94,104,373,379,513,520,523,1141,1145,1148,1188,1196,1203,1206,1209,1213,1216,1234,1237,1243,1373,1379,1526,1529,1532,1538,1541,1544,1548,1551,1554],[10,11,13],"h2",{"id":12},"introduction","Introduction",[15,16,17,18,25],"p",{},"This is a follow up of my ",[19,20,24],"a",{"href":21,"rel":22},"https:\u002F\u002Frajeev.dev\u002Fcreating-puzzle-game-using-python-turtle-1",[23],"nofollow","previous article"," where we learnt to create a puzzle game using Python Turtle module. There we could generate as many new games as we wanted, and also replay a particular game unlimited number of times.\nBut there was one important piece missing from the equation: finding the minimum number of moves needed to solve the puzzle (i.e. to remove all the tiles from the screen).",[27,28],"media-embed",{"url":29},"https:\u002F\u002Fmedia.giphy.com\u002Fmedia\u002F3o6Mb2KGFpsvvjWbPW\u002Fgiphy.gif",[15,31,32],{},"Worry not! In this article we will look at a crude way of solving the puzzle. Then we'll try to optimize our solution and make it faster. Strap your seatbelts, here we go...",[10,34,36],{"id":35},"approach","Approach",[15,38,39],{},"As we had learnt in the last article, to clear the board we need to click on the solid color tiles. Any tile which is in the bottom row is clickable (and hence, solid), and tiles which are connected to these bottom row tiles become clickable if they have the same color (see the below image for understanding). When we click on a solid tile, that tile and all the connected tiles get removed from the screen. New connections are formed after every click following the earlier logic.",[15,41,42],{},[43,44],"img",{"alt":45,"src":46},"figure.jpg","\u002Fimages\u002Fposts\u002Fsolve-puzzle-game-using-python\u002F08SxOhgmR-4844fb2c0f.jpg",[15,48,49],{},"Now that we understand the game, let's try to come up with a logic to find out the minimum number of moves needed to clear the screen. Below are my observations:",[51,52,53,57,60,63,66],"ol",{},[54,55,56],"li",{},"Even though we can click on any of the solid tiles (even if that tile is in, say 2nd or 3rd row), it is enough to always click on the bottom row tiles only",[54,58,59],{},"If more than one tile in the bottom row are connected to each other, it is enough to click on the tile having the lowest column index",[54,61,62],{},"For every move, we will have at most 5 choices (the 5 bottom row tiles). Overall there will be a lot of combinations of moves, and we'll need to go through all of them to find out the solution",[54,64,65],{},"After clicking a tile new connections are formed, and if we keep repeating the first 2 steps for any of the possible combinations, there will come a time when the board is clear",[54,67,68],{},"Finding one solution (one sequence of moves giving empty board) is sufficient for us",[15,70,71],{},"With these observations in mind, let's implement our algorithm.",[10,73,75],{"id":74},"implementation","Implementation",[15,77,78,79,84],{},"Below is a screenshot of Nov 16-17, 2022's puzzle from the ",[19,80,83],{"href":81,"rel":82},"https:\u002F\u002Ffigure.game\u002F",[23],"original site",". The same combination has been used to create this article's cover image. We'll use this as a reference to verify if our solution is correct (our solution should give us 8 minimum moves as the answer).",[15,86,87],{},[43,88],{"alt":89,"src":90},"Screen Shot 2022-11-16 at 10.59.22 PM.png","\u002Fimages\u002Fposts\u002Fsolve-puzzle-game-using-python\u002FRMiUT4MyE-45f5a2e90a.png",[15,92,93],{},"We won't be using any custom classes for this implementation, and will rely on good old lists and dictionaries.",[95,96,98,99,103],"h3",{"id":97},"the-play-function","The ",[100,101,102],"code",{},"play"," function",[105,106,111],"pre",{"className":107,"code":108,"language":109,"meta":110,"style":110},"language-python shiki shiki-themes github-light github-dark","from timeit import default_timer as timer\nimport copy\n\nimport AutoPlay\n\ndef play(colors):\n    # Single game object which holds the tiles in play,\n    # and the current connections groups and clickables\n    game = {\n        'tiles': [],\n        'clickables': [],\n        'connection_groups': []\n    }\n\n    # Set the board as per the input colors\n    for col in range(AutoPlay.MAX_COLS):\n        game['tiles'].append([])\n        for row in range(AutoPlay.MAX_ROWS):\n            tile = {'id': (row, col), \"connections\": [],\n                    \"clickable\": False, \"color\": colors[col][row]}\n            game['tiles'][col].append(tile)\n\n    # Go through the tiles and find out the connections\n    # between them, and also save the clickables\n    for col in range(AutoPlay.MAX_COLS):\n        AutoPlay.process_tile(game, 0, col)\n\n    start = timer()\n    print(f'start time: {start}')\n\n    # Create as many tasks as there are connection groups.\n    # We're using deepcopy to create a deeply cloned game\n    # object for each task. The current move is the first\n    # entry of every connection group (the lowest column\n    # index in the bottom row)\n    tasks = []\n    for connections in game['connection_groups']:\n        g = copy.deepcopy(game)\n        tasks.append(\n            {'game': g, 'curr_move': connections[0], 'past_moves': None})\n\n    # Find the minimum number of moves for the above tasks\n    result = find_min_moves(tasks)\n    print('result of operation:', result)\n","python","",[100,112,113,121,127,134,140,145,151,157,163,169,175,181,187,193,198,204,210,216,222,228,234,240,245,251,257,262,268,273,279,285,290,296,302,308,314,320,326,332,338,344,350,355,361,367],{"__ignoreMap":110},[114,115,118],"span",{"class":116,"line":117},"line",1,[114,119,120],{},"from timeit import default_timer as timer\n",[114,122,124],{"class":116,"line":123},2,[114,125,126],{},"import copy\n",[114,128,130],{"class":116,"line":129},3,[114,131,133],{"emptyLinePlaceholder":132},true,"\n",[114,135,137],{"class":116,"line":136},4,[114,138,139],{},"import AutoPlay\n",[114,141,143],{"class":116,"line":142},5,[114,144,133],{"emptyLinePlaceholder":132},[114,146,148],{"class":116,"line":147},6,[114,149,150],{},"def play(colors):\n",[114,152,154],{"class":116,"line":153},7,[114,155,156],{},"    # Single game object which holds the tiles in play,\n",[114,158,160],{"class":116,"line":159},8,[114,161,162],{},"    # and the current connections groups and clickables\n",[114,164,166],{"class":116,"line":165},9,[114,167,168],{},"    game = {\n",[114,170,172],{"class":116,"line":171},10,[114,173,174],{},"        'tiles': [],\n",[114,176,178],{"class":116,"line":177},11,[114,179,180],{},"        'clickables': [],\n",[114,182,184],{"class":116,"line":183},12,[114,185,186],{},"        'connection_groups': []\n",[114,188,190],{"class":116,"line":189},13,[114,191,192],{},"    }\n",[114,194,196],{"class":116,"line":195},14,[114,197,133],{"emptyLinePlaceholder":132},[114,199,201],{"class":116,"line":200},15,[114,202,203],{},"    # Set the board as per the input colors\n",[114,205,207],{"class":116,"line":206},16,[114,208,209],{},"    for col in range(AutoPlay.MAX_COLS):\n",[114,211,213],{"class":116,"line":212},17,[114,214,215],{},"        game['tiles'].append([])\n",[114,217,219],{"class":116,"line":218},18,[114,220,221],{},"        for row in range(AutoPlay.MAX_ROWS):\n",[114,223,225],{"class":116,"line":224},19,[114,226,227],{},"            tile = {'id': (row, col), \"connections\": [],\n",[114,229,231],{"class":116,"line":230},20,[114,232,233],{},"                    \"clickable\": False, \"color\": colors[col][row]}\n",[114,235,237],{"class":116,"line":236},21,[114,238,239],{},"            game['tiles'][col].append(tile)\n",[114,241,243],{"class":116,"line":242},22,[114,244,133],{"emptyLinePlaceholder":132},[114,246,248],{"class":116,"line":247},23,[114,249,250],{},"    # Go through the tiles and find out the connections\n",[114,252,254],{"class":116,"line":253},24,[114,255,256],{},"    # between them, and also save the clickables\n",[114,258,260],{"class":116,"line":259},25,[114,261,209],{},[114,263,265],{"class":116,"line":264},26,[114,266,267],{},"        AutoPlay.process_tile(game, 0, col)\n",[114,269,271],{"class":116,"line":270},27,[114,272,133],{"emptyLinePlaceholder":132},[114,274,276],{"class":116,"line":275},28,[114,277,278],{},"    start = timer()\n",[114,280,282],{"class":116,"line":281},29,[114,283,284],{},"    print(f'start time: {start}')\n",[114,286,288],{"class":116,"line":287},30,[114,289,133],{"emptyLinePlaceholder":132},[114,291,293],{"class":116,"line":292},31,[114,294,295],{},"    # Create as many tasks as there are connection groups.\n",[114,297,299],{"class":116,"line":298},32,[114,300,301],{},"    # We're using deepcopy to create a deeply cloned game\n",[114,303,305],{"class":116,"line":304},33,[114,306,307],{},"    # object for each task. The current move is the first\n",[114,309,311],{"class":116,"line":310},34,[114,312,313],{},"    # entry of every connection group (the lowest column\n",[114,315,317],{"class":116,"line":316},35,[114,318,319],{},"    # index in the bottom row)\n",[114,321,323],{"class":116,"line":322},36,[114,324,325],{},"    tasks = []\n",[114,327,329],{"class":116,"line":328},37,[114,330,331],{},"    for connections in game['connection_groups']:\n",[114,333,335],{"class":116,"line":334},38,[114,336,337],{},"        g = copy.deepcopy(game)\n",[114,339,341],{"class":116,"line":340},39,[114,342,343],{},"        tasks.append(\n",[114,345,347],{"class":116,"line":346},40,[114,348,349],{},"            {'game': g, 'curr_move': connections[0], 'past_moves': None})\n",[114,351,353],{"class":116,"line":352},41,[114,354,133],{"emptyLinePlaceholder":132},[114,356,358],{"class":116,"line":357},42,[114,359,360],{},"    # Find the minimum number of moves for the above tasks\n",[114,362,364],{"class":116,"line":363},43,[114,365,366],{},"    result = find_min_moves(tasks)\n",[114,368,370],{"class":116,"line":369},44,[114,371,372],{},"    print('result of operation:', result)\n",[95,374,98,376,103],{"id":375},"the-find_min_moves-function",[100,377,378],{},"find_min_moves",[105,380,382],{"className":107,"code":381,"language":109,"meta":110,"style":110},"def find_min_moves(jobs):\n    result = {'min_moves': None, 'min_moves_len': 0, 'count': 0}\n\n    while True:\n        # See if we've any jobs left. If there is no job, break the loop\n        job = jobs.pop() if len(jobs) > 0 else None\n        if job is None:\n            print(\n                f'No more jobs: final count: {result[\"count\"]}, time: {timer()}')\n            break\n\n        # Handle the current job. This will take of the combinations till its logical\n        # end (until the board is clear). Other encountered combinations will be added\n        # to the job list for processing in due course\n        final_moves_seq = AutoPlay.handle_job_recurse(job, jobs)\n\n        result['count'] += 1\n\n        # If the one processed combination has minimum length, then that is the minimum\n        # numbers of moves needed to solve the puzzle\n        if result['min_moves_len'] == 0 or len(final_moves_seq) \u003C result['min_moves_len']:\n            result['min_moves'] = final_moves_seq\n            result['min_moves_len'] = len(final_moves_seq)\n            print(\n                f'changed min_moves to: {result[\"min_moves_len\"]}, {final_moves_seq}, time: {timer()}')\n\n    return result\n",[100,383,384,389,394,398,403,408,413,418,423,428,433,437,442,447,452,457,461,466,470,475,480,485,490,495,499,504,508],{"__ignoreMap":110},[114,385,386],{"class":116,"line":117},[114,387,388],{},"def find_min_moves(jobs):\n",[114,390,391],{"class":116,"line":123},[114,392,393],{},"    result = {'min_moves': None, 'min_moves_len': 0, 'count': 0}\n",[114,395,396],{"class":116,"line":129},[114,397,133],{"emptyLinePlaceholder":132},[114,399,400],{"class":116,"line":136},[114,401,402],{},"    while True:\n",[114,404,405],{"class":116,"line":142},[114,406,407],{},"        # See if we've any jobs left. If there is no job, break the loop\n",[114,409,410],{"class":116,"line":147},[114,411,412],{},"        job = jobs.pop() if len(jobs) > 0 else None\n",[114,414,415],{"class":116,"line":153},[114,416,417],{},"        if job is None:\n",[114,419,420],{"class":116,"line":159},[114,421,422],{},"            print(\n",[114,424,425],{"class":116,"line":165},[114,426,427],{},"                f'No more jobs: final count: {result[\"count\"]}, time: {timer()}')\n",[114,429,430],{"class":116,"line":171},[114,431,432],{},"            break\n",[114,434,435],{"class":116,"line":177},[114,436,133],{"emptyLinePlaceholder":132},[114,438,439],{"class":116,"line":183},[114,440,441],{},"        # Handle the current job. This will take of the combinations till its logical\n",[114,443,444],{"class":116,"line":189},[114,445,446],{},"        # end (until the board is clear). Other encountered combinations will be added\n",[114,448,449],{"class":116,"line":195},[114,450,451],{},"        # to the job list for processing in due course\n",[114,453,454],{"class":116,"line":200},[114,455,456],{},"        final_moves_seq = AutoPlay.handle_job_recurse(job, jobs)\n",[114,458,459],{"class":116,"line":206},[114,460,133],{"emptyLinePlaceholder":132},[114,462,463],{"class":116,"line":212},[114,464,465],{},"        result['count'] += 1\n",[114,467,468],{"class":116,"line":218},[114,469,133],{"emptyLinePlaceholder":132},[114,471,472],{"class":116,"line":224},[114,473,474],{},"        # If the one processed combination has minimum length, then that is the minimum\n",[114,476,477],{"class":116,"line":230},[114,478,479],{},"        # numbers of moves needed to solve the puzzle\n",[114,481,482],{"class":116,"line":236},[114,483,484],{},"        if result['min_moves_len'] == 0 or len(final_moves_seq) \u003C result['min_moves_len']:\n",[114,486,487],{"class":116,"line":242},[114,488,489],{},"            result['min_moves'] = final_moves_seq\n",[114,491,492],{"class":116,"line":247},[114,493,494],{},"            result['min_moves_len'] = len(final_moves_seq)\n",[114,496,497],{"class":116,"line":253},[114,498,422],{},[114,500,501],{"class":116,"line":259},[114,502,503],{},"                f'changed min_moves to: {result[\"min_moves_len\"]}, {final_moves_seq}, time: {timer()}')\n",[114,505,506],{"class":116,"line":264},[114,507,133],{"emptyLinePlaceholder":132},[114,509,510],{"class":116,"line":270},[114,511,512],{},"    return result\n",[95,514,98,516,519],{"id":515},"the-autoplay-module",[100,517,518],{},"AutoPlay"," module",[15,521,522],{},"Some of the functions are almost the same as in the previous article (small modifications needed for adjusting to non-class approach). You can refer to the first article to get better clarity on these functions.",[105,524,526],{"className":107,"code":525,"language":109,"meta":110,"style":110},"import copy\n\nMAX_COLS = 5\nMAX_ROWS = 5\n\ndef get_node(tiles, row, col):\n    if 0 \u003C= col \u003C= MAX_COLS - 1 and 0 \u003C= row \u003C= MAX_ROWS - 1:\n        col_tiles = tiles[col]\n        return col_tiles[row] if row \u003C len(col_tiles) else None\n\ndef connectable(tiles, first_node, row, col):\n    other_node = get_node(tiles, row, col)\n    if other_node and first_node['color'] == other_node['color']:\n        if other_node['clickable']:\n            return True\n\n        return (row, col)\n\ndef process_tile(game, row, col):\n    curr_node = get_node(game['tiles'], row, col)\n    if not curr_node or curr_node['clickable']:\n        return\n\n    has_clickable_connections = {\n        'prev': connectable(game['tiles'], curr_node, row, col - 1),\n        'next': connectable(game['tiles'], curr_node, row, col + 1),\n        'below': connectable(game['tiles'], curr_node, row - 1, col),\n        'above': connectable(game['tiles'], curr_node, row + 1, col)\n    }\n\n    if row == 0 or True in has_clickable_connections.values():\n        curr_node['clickable'] = True\n        if has_clickable_connections['next']:\n            curr_node['connections'].append((row, col + 1))\n        if has_clickable_connections['above']:\n            curr_node['connections'].append((row + 1, col))\n\n        if (row, col) not in game['clickables']:\n            game['clickables'].append((row, col))\n\n        found = False\n        for connections in game['connection_groups']:\n            if (row, col) in connections:\n                found = True\n                break\n\n        if not found:\n            game['connection_groups'].append([(row, col)])\n\n        for value in has_clickable_connections.values():\n            if isinstance(value, tuple):\n                for connections in game['connection_groups']:\n                    if (row, col) in connections and value not in connections:\n                        connections.append(value)\n                        break\n                process_tile(game, *value)\n\ndef handle_tile_click(game, tile_id):\n    # Go through each of the connection groups and find the one containing this tile\n    for connections in game['connection_groups']:\n        if tile_id in connections:\n            # Sort the tiles in reverse order so that we remove them from screen\n            # from the top right\n            tiles_to_remove = sorted(connections, reverse=True)\n\n            # Make all the clickable tiles as unclickable, as connections will be reformed\n            for clickable in game['clickables']:\n                game['tiles'][clickable[1]][clickable[0]]['clickable'] = False\n\n            # Actually remove the tiles one by one from the screen\n            for tile_to_remove in tiles_to_remove:\n                game['tiles'][tile_to_remove[1]].pop(tile_to_remove[0])\n\n                # Change the id of each of the tiles above the removed tile\n                for row in range(tile_to_remove[0], len(game['tiles'][tile_to_remove[1]])):\n                    game['tiles'][tile_to_remove[1]][row]['id'] = (\n                        row, tile_to_remove[1])\n            break\n\n    game['clickables'].clear()\n    game['connection_groups'].clear()\n    # Form fresh connections and find the clickables\n    for col in range(MAX_COLS):\n        process_tile(game, 0, col)\n\ndef handle_job_recurse(job, job_list):\n    game = job['game']\n\n    handle_tile_click(game, job['curr_move'])\n\n    # Add the currently executed move to the past_moves sequence (Only storing the\n    # column id, as row id will always be 0)\n    if job['past_moves'] is None:\n        job['past_moves'] = f'{job[\"curr_move\"][1]}'\n    else:\n        job['past_moves'] = f'{job[\"past_moves\"]}{job[\"curr_move\"][1]}'\n\n    # If after the click no new connection groups are left, then that means we've\n    # cleared the screen, so return this sequence\n    if len(game['connection_groups']) == 0:\n        return job['past_moves']\n\n    # Add the other possible combinations to the job list (we'll be taking the\n    # 0th index till completion, hence slicing the list from the 1st index)\n    for connections in game['connection_groups'][1:]:\n        job_list.append({'game': copy.deepcopy(\n            game), 'curr_move': connections[0], 'past_moves': job['past_moves']})\n\n    # Take the 0th index to its logical end. 0th index gives us a list which\n    # contains all the tiles connected with each other. We take the first tile\n    # from this list (the 0th index), and use recursion to go further\n    job['curr_move'] = game['connection_groups'][0][0]\n\n    return handle_job_recurse(job, job_list)\n",[100,527,528,532,536,541,546,550,555,560,565,570,574,579,584,589,594,599,603,608,612,617,622,627,632,636,641,646,651,656,661,665,669,674,679,684,689,694,699,703,708,713,717,722,727,732,737,743,748,754,760,765,771,777,783,789,795,801,807,812,818,824,829,835,841,847,853,858,864,870,876,881,887,893,899,904,910,916,922,928,933,938,944,950,956,962,968,973,979,985,990,996,1001,1007,1013,1019,1025,1031,1037,1042,1048,1054,1060,1066,1071,1077,1083,1089,1095,1101,1106,1112,1118,1124,1130,1135],{"__ignoreMap":110},[114,529,530],{"class":116,"line":117},[114,531,126],{},[114,533,534],{"class":116,"line":123},[114,535,133],{"emptyLinePlaceholder":132},[114,537,538],{"class":116,"line":129},[114,539,540],{},"MAX_COLS = 5\n",[114,542,543],{"class":116,"line":136},[114,544,545],{},"MAX_ROWS = 5\n",[114,547,548],{"class":116,"line":142},[114,549,133],{"emptyLinePlaceholder":132},[114,551,552],{"class":116,"line":147},[114,553,554],{},"def get_node(tiles, row, col):\n",[114,556,557],{"class":116,"line":153},[114,558,559],{},"    if 0 \u003C= col \u003C= MAX_COLS - 1 and 0 \u003C= row \u003C= MAX_ROWS - 1:\n",[114,561,562],{"class":116,"line":159},[114,563,564],{},"        col_tiles = tiles[col]\n",[114,566,567],{"class":116,"line":165},[114,568,569],{},"        return col_tiles[row] if row \u003C len(col_tiles) else None\n",[114,571,572],{"class":116,"line":171},[114,573,133],{"emptyLinePlaceholder":132},[114,575,576],{"class":116,"line":177},[114,577,578],{},"def connectable(tiles, first_node, row, col):\n",[114,580,581],{"class":116,"line":183},[114,582,583],{},"    other_node = get_node(tiles, row, col)\n",[114,585,586],{"class":116,"line":189},[114,587,588],{},"    if other_node and first_node['color'] == other_node['color']:\n",[114,590,591],{"class":116,"line":195},[114,592,593],{},"        if other_node['clickable']:\n",[114,595,596],{"class":116,"line":200},[114,597,598],{},"            return True\n",[114,600,601],{"class":116,"line":206},[114,602,133],{"emptyLinePlaceholder":132},[114,604,605],{"class":116,"line":212},[114,606,607],{},"        return (row, col)\n",[114,609,610],{"class":116,"line":218},[114,611,133],{"emptyLinePlaceholder":132},[114,613,614],{"class":116,"line":224},[114,615,616],{},"def process_tile(game, row, col):\n",[114,618,619],{"class":116,"line":230},[114,620,621],{},"    curr_node = get_node(game['tiles'], row, col)\n",[114,623,624],{"class":116,"line":236},[114,625,626],{},"    if not curr_node or curr_node['clickable']:\n",[114,628,629],{"class":116,"line":242},[114,630,631],{},"        return\n",[114,633,634],{"class":116,"line":247},[114,635,133],{"emptyLinePlaceholder":132},[114,637,638],{"class":116,"line":253},[114,639,640],{},"    has_clickable_connections = {\n",[114,642,643],{"class":116,"line":259},[114,644,645],{},"        'prev': connectable(game['tiles'], curr_node, row, col - 1),\n",[114,647,648],{"class":116,"line":264},[114,649,650],{},"        'next': connectable(game['tiles'], curr_node, row, col + 1),\n",[114,652,653],{"class":116,"line":270},[114,654,655],{},"        'below': connectable(game['tiles'], curr_node, row - 1, col),\n",[114,657,658],{"class":116,"line":275},[114,659,660],{},"        'above': connectable(game['tiles'], curr_node, row + 1, col)\n",[114,662,663],{"class":116,"line":281},[114,664,192],{},[114,666,667],{"class":116,"line":287},[114,668,133],{"emptyLinePlaceholder":132},[114,670,671],{"class":116,"line":292},[114,672,673],{},"    if row == 0 or True in has_clickable_connections.values():\n",[114,675,676],{"class":116,"line":298},[114,677,678],{},"        curr_node['clickable'] = True\n",[114,680,681],{"class":116,"line":304},[114,682,683],{},"        if has_clickable_connections['next']:\n",[114,685,686],{"class":116,"line":310},[114,687,688],{},"            curr_node['connections'].append((row, col + 1))\n",[114,690,691],{"class":116,"line":316},[114,692,693],{},"        if has_clickable_connections['above']:\n",[114,695,696],{"class":116,"line":322},[114,697,698],{},"            curr_node['connections'].append((row + 1, col))\n",[114,700,701],{"class":116,"line":328},[114,702,133],{"emptyLinePlaceholder":132},[114,704,705],{"class":116,"line":334},[114,706,707],{},"        if (row, col) not in game['clickables']:\n",[114,709,710],{"class":116,"line":340},[114,711,712],{},"            game['clickables'].append((row, col))\n",[114,714,715],{"class":116,"line":346},[114,716,133],{"emptyLinePlaceholder":132},[114,718,719],{"class":116,"line":352},[114,720,721],{},"        found = False\n",[114,723,724],{"class":116,"line":357},[114,725,726],{},"        for connections in game['connection_groups']:\n",[114,728,729],{"class":116,"line":363},[114,730,731],{},"            if (row, col) in connections:\n",[114,733,734],{"class":116,"line":369},[114,735,736],{},"                found = True\n",[114,738,740],{"class":116,"line":739},45,[114,741,742],{},"                break\n",[114,744,746],{"class":116,"line":745},46,[114,747,133],{"emptyLinePlaceholder":132},[114,749,751],{"class":116,"line":750},47,[114,752,753],{},"        if not found:\n",[114,755,757],{"class":116,"line":756},48,[114,758,759],{},"            game['connection_groups'].append([(row, col)])\n",[114,761,763],{"class":116,"line":762},49,[114,764,133],{"emptyLinePlaceholder":132},[114,766,768],{"class":116,"line":767},50,[114,769,770],{},"        for value in has_clickable_connections.values():\n",[114,772,774],{"class":116,"line":773},51,[114,775,776],{},"            if isinstance(value, tuple):\n",[114,778,780],{"class":116,"line":779},52,[114,781,782],{},"                for connections in game['connection_groups']:\n",[114,784,786],{"class":116,"line":785},53,[114,787,788],{},"                    if (row, col) in connections and value not in connections:\n",[114,790,792],{"class":116,"line":791},54,[114,793,794],{},"                        connections.append(value)\n",[114,796,798],{"class":116,"line":797},55,[114,799,800],{},"                        break\n",[114,802,804],{"class":116,"line":803},56,[114,805,806],{},"                process_tile(game, *value)\n",[114,808,810],{"class":116,"line":809},57,[114,811,133],{"emptyLinePlaceholder":132},[114,813,815],{"class":116,"line":814},58,[114,816,817],{},"def handle_tile_click(game, tile_id):\n",[114,819,821],{"class":116,"line":820},59,[114,822,823],{},"    # Go through each of the connection groups and find the one containing this tile\n",[114,825,827],{"class":116,"line":826},60,[114,828,331],{},[114,830,832],{"class":116,"line":831},61,[114,833,834],{},"        if tile_id in connections:\n",[114,836,838],{"class":116,"line":837},62,[114,839,840],{},"            # Sort the tiles in reverse order so that we remove them from screen\n",[114,842,844],{"class":116,"line":843},63,[114,845,846],{},"            # from the top right\n",[114,848,850],{"class":116,"line":849},64,[114,851,852],{},"            tiles_to_remove = sorted(connections, reverse=True)\n",[114,854,856],{"class":116,"line":855},65,[114,857,133],{"emptyLinePlaceholder":132},[114,859,861],{"class":116,"line":860},66,[114,862,863],{},"            # Make all the clickable tiles as unclickable, as connections will be reformed\n",[114,865,867],{"class":116,"line":866},67,[114,868,869],{},"            for clickable in game['clickables']:\n",[114,871,873],{"class":116,"line":872},68,[114,874,875],{},"                game['tiles'][clickable[1]][clickable[0]]['clickable'] = False\n",[114,877,879],{"class":116,"line":878},69,[114,880,133],{"emptyLinePlaceholder":132},[114,882,884],{"class":116,"line":883},70,[114,885,886],{},"            # Actually remove the tiles one by one from the screen\n",[114,888,890],{"class":116,"line":889},71,[114,891,892],{},"            for tile_to_remove in tiles_to_remove:\n",[114,894,896],{"class":116,"line":895},72,[114,897,898],{},"                game['tiles'][tile_to_remove[1]].pop(tile_to_remove[0])\n",[114,900,902],{"class":116,"line":901},73,[114,903,133],{"emptyLinePlaceholder":132},[114,905,907],{"class":116,"line":906},74,[114,908,909],{},"                # Change the id of each of the tiles above the removed tile\n",[114,911,913],{"class":116,"line":912},75,[114,914,915],{},"                for row in range(tile_to_remove[0], len(game['tiles'][tile_to_remove[1]])):\n",[114,917,919],{"class":116,"line":918},76,[114,920,921],{},"                    game['tiles'][tile_to_remove[1]][row]['id'] = (\n",[114,923,925],{"class":116,"line":924},77,[114,926,927],{},"                        row, tile_to_remove[1])\n",[114,929,931],{"class":116,"line":930},78,[114,932,432],{},[114,934,936],{"class":116,"line":935},79,[114,937,133],{"emptyLinePlaceholder":132},[114,939,941],{"class":116,"line":940},80,[114,942,943],{},"    game['clickables'].clear()\n",[114,945,947],{"class":116,"line":946},81,[114,948,949],{},"    game['connection_groups'].clear()\n",[114,951,953],{"class":116,"line":952},82,[114,954,955],{},"    # Form fresh connections and find the clickables\n",[114,957,959],{"class":116,"line":958},83,[114,960,961],{},"    for col in range(MAX_COLS):\n",[114,963,965],{"class":116,"line":964},84,[114,966,967],{},"        process_tile(game, 0, col)\n",[114,969,971],{"class":116,"line":970},85,[114,972,133],{"emptyLinePlaceholder":132},[114,974,976],{"class":116,"line":975},86,[114,977,978],{},"def handle_job_recurse(job, job_list):\n",[114,980,982],{"class":116,"line":981},87,[114,983,984],{},"    game = job['game']\n",[114,986,988],{"class":116,"line":987},88,[114,989,133],{"emptyLinePlaceholder":132},[114,991,993],{"class":116,"line":992},89,[114,994,995],{},"    handle_tile_click(game, job['curr_move'])\n",[114,997,999],{"class":116,"line":998},90,[114,1000,133],{"emptyLinePlaceholder":132},[114,1002,1004],{"class":116,"line":1003},91,[114,1005,1006],{},"    # Add the currently executed move to the past_moves sequence (Only storing the\n",[114,1008,1010],{"class":116,"line":1009},92,[114,1011,1012],{},"    # column id, as row id will always be 0)\n",[114,1014,1016],{"class":116,"line":1015},93,[114,1017,1018],{},"    if job['past_moves'] is None:\n",[114,1020,1022],{"class":116,"line":1021},94,[114,1023,1024],{},"        job['past_moves'] = f'{job[\"curr_move\"][1]}'\n",[114,1026,1028],{"class":116,"line":1027},95,[114,1029,1030],{},"    else:\n",[114,1032,1034],{"class":116,"line":1033},96,[114,1035,1036],{},"        job['past_moves'] = f'{job[\"past_moves\"]}{job[\"curr_move\"][1]}'\n",[114,1038,1040],{"class":116,"line":1039},97,[114,1041,133],{"emptyLinePlaceholder":132},[114,1043,1045],{"class":116,"line":1044},98,[114,1046,1047],{},"    # If after the click no new connection groups are left, then that means we've\n",[114,1049,1051],{"class":116,"line":1050},99,[114,1052,1053],{},"    # cleared the screen, so return this sequence\n",[114,1055,1057],{"class":116,"line":1056},100,[114,1058,1059],{},"    if len(game['connection_groups']) == 0:\n",[114,1061,1063],{"class":116,"line":1062},101,[114,1064,1065],{},"        return job['past_moves']\n",[114,1067,1069],{"class":116,"line":1068},102,[114,1070,133],{"emptyLinePlaceholder":132},[114,1072,1074],{"class":116,"line":1073},103,[114,1075,1076],{},"    # Add the other possible combinations to the job list (we'll be taking the\n",[114,1078,1080],{"class":116,"line":1079},104,[114,1081,1082],{},"    # 0th index till completion, hence slicing the list from the 1st index)\n",[114,1084,1086],{"class":116,"line":1085},105,[114,1087,1088],{},"    for connections in game['connection_groups'][1:]:\n",[114,1090,1092],{"class":116,"line":1091},106,[114,1093,1094],{},"        job_list.append({'game': copy.deepcopy(\n",[114,1096,1098],{"class":116,"line":1097},107,[114,1099,1100],{},"            game), 'curr_move': connections[0], 'past_moves': job['past_moves']})\n",[114,1102,1104],{"class":116,"line":1103},108,[114,1105,133],{"emptyLinePlaceholder":132},[114,1107,1109],{"class":116,"line":1108},109,[114,1110,1111],{},"    # Take the 0th index to its logical end. 0th index gives us a list which\n",[114,1113,1115],{"class":116,"line":1114},110,[114,1116,1117],{},"    # contains all the tiles connected with each other. We take the first tile\n",[114,1119,1121],{"class":116,"line":1120},111,[114,1122,1123],{},"    # from this list (the 0th index), and use recursion to go further\n",[114,1125,1127],{"class":116,"line":1126},112,[114,1128,1129],{},"    job['curr_move'] = game['connection_groups'][0][0]\n",[114,1131,1133],{"class":116,"line":1132},113,[114,1134,133],{"emptyLinePlaceholder":132},[114,1136,1138],{"class":116,"line":1137},114,[114,1139,1140],{},"    return handle_job_recurse(job, job_list)\n",[95,1142,1144],{"id":1143},"the-result","The result",[15,1146,1147],{},"Running the code by using the board from today's puzzle (shown earlier) we get the following results",[105,1149,1151],{"className":107,"code":1150,"language":109,"meta":110,"style":110},"play([\n    ['hot pink', 'turquoise', 'hot pink', 'hot pink', 'yellow'],\n    ['white', 'turquoise', 'yellow', 'white', 'hot pink'],\n    ['yellow', 'white', 'hot pink', 'hot pink', 'turquoise'],\n    ['white', 'hot pink', 'turquoise', 'white', 'turquoise'],\n    ['white', 'hot pink', 'white', 'white', 'turquoise']\n])\n",[100,1152,1153,1158,1163,1168,1173,1178,1183],{"__ignoreMap":110},[114,1154,1155],{"class":116,"line":117},[114,1156,1157],{},"play([\n",[114,1159,1160],{"class":116,"line":123},[114,1161,1162],{},"    ['hot pink', 'turquoise', 'hot pink', 'hot pink', 'yellow'],\n",[114,1164,1165],{"class":116,"line":129},[114,1166,1167],{},"    ['white', 'turquoise', 'yellow', 'white', 'hot pink'],\n",[114,1169,1170],{"class":116,"line":136},[114,1171,1172],{},"    ['yellow', 'white', 'hot pink', 'hot pink', 'turquoise'],\n",[114,1174,1175],{"class":116,"line":142},[114,1176,1177],{},"    ['white', 'hot pink', 'turquoise', 'white', 'turquoise'],\n",[114,1179,1180],{"class":116,"line":147},[114,1181,1182],{},"    ['white', 'hot pink', 'white', 'white', 'turquoise']\n",[114,1184,1185],{"class":116,"line":153},[114,1186,1187],{},"])\n",[105,1189,1194],{"className":1190,"code":1192,"language":1193},[1191],"language-text","start time: 0.060289866\nchanged min_moves to: 13, 3000011111233, time: 0.068856064\nchanged min_moves to: 12, 300001111142, time: 0.069172283\nchanged min_moves to: 11, 30003114012, time: 2.164211921\nchanged min_moves to: 10, 3003114002, time: 9.825066422\nchanged min_moves to: 9, 303104002, time: 26.712916026\nchanged min_moves to: 8, 10010012, time: 110.70025369\nNo more jobs: final count: 1389898, time: 214.573611255\nresult of operation: {'min_moves': '10010012', 'min_moves_len': 8, 'count': 1389898}\n","text",[100,1195,1192],{"__ignoreMap":110},[15,1197,1198,1199,1202],{},"And voila, we got the minimum moves length as 8, same as the original puzzle wanted. It took us around 3 minutes and 35 seconds to get the solution. ",[100,1200,1201],{},"'min_moves': '10010012'"," gives us the column indices of the bottom row which we need to click one after the other to clear the screen. In total, we processed 1389898 sequences, that is close to 1.39 million sequences (Wow!).",[27,1204],{"url":1205},"https:\u002F\u002Fmedia.giphy.com\u002Fmedia\u002FUtEUhkfriklonVdweC\u002Fgiphy.gif",[15,1207,1208],{},"Or is it?",[10,1210,1212],{"id":1211},"first-optimization","First optimization",[15,1214,1215],{},"Even though this is working, for a different puzzle which requires, say 13 or 14 moves, our code will take much longer to give us a solution. Can we optimize our solution somehow?",[15,1217,1218,1219,1222,1223,1226,1227,1230,1231,1233],{},"If you think about it, we don't need to take all the sequences to their logical end. If any sequence has already had ",[100,1220,1221],{},"\"min_moves_len - 1\""," past moves, and there still are ",[100,1224,1225],{},"connection_groups"," left, then there is no point in pursuing this sequence. That is because, in the best case it will give us the same ",[100,1228,1229],{},"min_moves_len"," (if we assume that the next click will clear the screen), but in all the other cases we'll get a final sequence length which is more than the ",[100,1232,1229],{},".",[15,1235,1236],{},"Keeping the above observation in mind, we will not be adding such combinations to the job list. Let's make the needed changes:",[95,1238,1240,1241,103],{"id":1239},"modified-find_min_moves-function","Modified ",[100,1242,378],{},[105,1244,1246],{"className":107,"code":1245,"language":109,"meta":110,"style":110},"def find_min_moves(jobs):\n    result = {'min_moves': None, 'min_moves_len': 0, 'count': 0}\n\n    while True:\n        # See if we've any jobs left. If there is no job, break the loop\n        job = jobs.pop() if len(jobs) > 0 else None\n        if job is None:\n            print(\n                f'No more jobs: final count: {result[\"count\"]}, time: {timer()}')\n            break\n\n        # Handle the current job. This will take of the combinations till its logical\n        # end (until the board is clear). Other encountered combinations will be added\n        # to the job list for processing in due course\n        final_moves_seq = AutoPlay.handle_job_recurse(\n            job, jobs, result['min_moves_len']) # Sending the current min_moves_len\n\n        result['count'] += 1\n\n        # If the one processed combination has minimum length, then that is the minimum\n        # numbers of moves needed to solve the puzzle\n        # Now, final_moves_seq can be returned as None, so taking that into account\n        if result['min_moves_len'] == 0 or (final_moves_seq is not None\n                                            and len(final_moves_seq) \u003C result['min_moves_len']):\n            result['min_moves'] = final_moves_seq\n            result['min_moves_len'] = len(final_moves_seq)\n            print(\n                f'changed min_moves to: {result[\"min_moves_len\"]}, {final_moves_seq}, time: {timer()}')\n\n    return result\n",[100,1247,1248,1252,1256,1260,1264,1268,1272,1276,1280,1284,1288,1292,1296,1300,1304,1309,1314,1318,1322,1326,1330,1334,1339,1344,1349,1353,1357,1361,1365,1369],{"__ignoreMap":110},[114,1249,1250],{"class":116,"line":117},[114,1251,388],{},[114,1253,1254],{"class":116,"line":123},[114,1255,393],{},[114,1257,1258],{"class":116,"line":129},[114,1259,133],{"emptyLinePlaceholder":132},[114,1261,1262],{"class":116,"line":136},[114,1263,402],{},[114,1265,1266],{"class":116,"line":142},[114,1267,407],{},[114,1269,1270],{"class":116,"line":147},[114,1271,412],{},[114,1273,1274],{"class":116,"line":153},[114,1275,417],{},[114,1277,1278],{"class":116,"line":159},[114,1279,422],{},[114,1281,1282],{"class":116,"line":165},[114,1283,427],{},[114,1285,1286],{"class":116,"line":171},[114,1287,432],{},[114,1289,1290],{"class":116,"line":177},[114,1291,133],{"emptyLinePlaceholder":132},[114,1293,1294],{"class":116,"line":183},[114,1295,441],{},[114,1297,1298],{"class":116,"line":189},[114,1299,446],{},[114,1301,1302],{"class":116,"line":195},[114,1303,451],{},[114,1305,1306],{"class":116,"line":200},[114,1307,1308],{},"        final_moves_seq = AutoPlay.handle_job_recurse(\n",[114,1310,1311],{"class":116,"line":206},[114,1312,1313],{},"            job, jobs, result['min_moves_len']) # Sending the current min_moves_len\n",[114,1315,1316],{"class":116,"line":212},[114,1317,133],{"emptyLinePlaceholder":132},[114,1319,1320],{"class":116,"line":218},[114,1321,465],{},[114,1323,1324],{"class":116,"line":224},[114,1325,133],{"emptyLinePlaceholder":132},[114,1327,1328],{"class":116,"line":230},[114,1329,474],{},[114,1331,1332],{"class":116,"line":236},[114,1333,479],{},[114,1335,1336],{"class":116,"line":242},[114,1337,1338],{},"        # Now, final_moves_seq can be returned as None, so taking that into account\n",[114,1340,1341],{"class":116,"line":247},[114,1342,1343],{},"        if result['min_moves_len'] == 0 or (final_moves_seq is not None\n",[114,1345,1346],{"class":116,"line":253},[114,1347,1348],{},"                                            and len(final_moves_seq) \u003C result['min_moves_len']):\n",[114,1350,1351],{"class":116,"line":259},[114,1352,489],{},[114,1354,1355],{"class":116,"line":264},[114,1356,494],{},[114,1358,1359],{"class":116,"line":270},[114,1360,422],{},[114,1362,1363],{"class":116,"line":275},[114,1364,503],{},[114,1366,1367],{"class":116,"line":281},[114,1368,133],{"emptyLinePlaceholder":132},[114,1370,1371],{"class":116,"line":287},[114,1372,512],{},[95,1374,1240,1376,103],{"id":1375},"modified-handle_job_recurse-function",[100,1377,1378],{},"handle_job_recurse",[105,1380,1382],{"className":107,"code":1381,"language":109,"meta":110,"style":110},"def handle_job_recurse(job, job_list, curr_min_moves_len):\n    game = job['game']\n\n    handle_tile_click(game, job['curr_move'])\n\n    # Add the currently executed move to the past_moves sequence (Only storing the\n    # column id, as row id will always be 0)\n    if job['past_moves'] is None:\n        job['past_moves'] = f'{job[\"curr_move\"][1]}'\n    else:\n        job['past_moves'] = f'{job[\"past_moves\"]}{job[\"curr_move\"][1]}'\n\n    # If after the click no new connection groups are left, then that means we've\n    # cleared the screen, so return this sequence\n    if len(game['connection_groups']) == 0:\n        return job['past_moves']\n\n    # If there is some min_moves_len and past_moves sequence length is more than \n    # or equal to min_moves_len - 1, then discard all further combinations in this sequence\n    if curr_min_moves_len != 0 and len(job['past_moves']) >= (curr_min_moves_len - 1):\n        return None\n\n    # Add the other possible combinations to the job list (we'll be taking the\n    # 0th index till completion, hence slicing the list from the 1st index)\n    for connections in game['connection_groups'][1:]:\n        job_list.append({'game': copy.deepcopy(\n            game), 'curr_move': connections[0], 'past_moves': job['past_moves']})\n\n    # Take the 0th index to its logical end. 0th index gives us a list which\n    # contains all the tiles connected with each other. We take the first tile\n    # from this list (the 0th index), and use recursion to go further\n    job['curr_move'] = game['connection_groups'][0][0]\n\n    return handle_job_recurse(job, job_list, curr_min_moves_len)\n",[100,1383,1384,1389,1393,1397,1401,1405,1409,1413,1417,1421,1425,1429,1433,1437,1441,1445,1449,1453,1458,1463,1468,1473,1477,1481,1485,1489,1493,1497,1501,1505,1509,1513,1517,1521],{"__ignoreMap":110},[114,1385,1386],{"class":116,"line":117},[114,1387,1388],{},"def handle_job_recurse(job, job_list, curr_min_moves_len):\n",[114,1390,1391],{"class":116,"line":123},[114,1392,984],{},[114,1394,1395],{"class":116,"line":129},[114,1396,133],{"emptyLinePlaceholder":132},[114,1398,1399],{"class":116,"line":136},[114,1400,995],{},[114,1402,1403],{"class":116,"line":142},[114,1404,133],{"emptyLinePlaceholder":132},[114,1406,1407],{"class":116,"line":147},[114,1408,1006],{},[114,1410,1411],{"class":116,"line":153},[114,1412,1012],{},[114,1414,1415],{"class":116,"line":159},[114,1416,1018],{},[114,1418,1419],{"class":116,"line":165},[114,1420,1024],{},[114,1422,1423],{"class":116,"line":171},[114,1424,1030],{},[114,1426,1427],{"class":116,"line":177},[114,1428,1036],{},[114,1430,1431],{"class":116,"line":183},[114,1432,133],{"emptyLinePlaceholder":132},[114,1434,1435],{"class":116,"line":189},[114,1436,1047],{},[114,1438,1439],{"class":116,"line":195},[114,1440,1053],{},[114,1442,1443],{"class":116,"line":200},[114,1444,1059],{},[114,1446,1447],{"class":116,"line":206},[114,1448,1065],{},[114,1450,1451],{"class":116,"line":212},[114,1452,133],{"emptyLinePlaceholder":132},[114,1454,1455],{"class":116,"line":218},[114,1456,1457],{},"    # If there is some min_moves_len and past_moves sequence length is more than \n",[114,1459,1460],{"class":116,"line":224},[114,1461,1462],{},"    # or equal to min_moves_len - 1, then discard all further combinations in this sequence\n",[114,1464,1465],{"class":116,"line":230},[114,1466,1467],{},"    if curr_min_moves_len != 0 and len(job['past_moves']) >= (curr_min_moves_len - 1):\n",[114,1469,1470],{"class":116,"line":236},[114,1471,1472],{},"        return None\n",[114,1474,1475],{"class":116,"line":242},[114,1476,133],{"emptyLinePlaceholder":132},[114,1478,1479],{"class":116,"line":247},[114,1480,1076],{},[114,1482,1483],{"class":116,"line":253},[114,1484,1082],{},[114,1486,1487],{"class":116,"line":259},[114,1488,1088],{},[114,1490,1491],{"class":116,"line":264},[114,1492,1094],{},[114,1494,1495],{"class":116,"line":270},[114,1496,1100],{},[114,1498,1499],{"class":116,"line":275},[114,1500,133],{"emptyLinePlaceholder":132},[114,1502,1503],{"class":116,"line":281},[114,1504,1111],{},[114,1506,1507],{"class":116,"line":287},[114,1508,1117],{},[114,1510,1511],{"class":116,"line":292},[114,1512,1123],{},[114,1514,1515],{"class":116,"line":298},[114,1516,1129],{},[114,1518,1519],{"class":116,"line":304},[114,1520,133],{"emptyLinePlaceholder":132},[114,1522,1523],{"class":116,"line":310},[114,1524,1525],{},"    return handle_job_recurse(job, job_list, curr_min_moves_len)\n",[95,1527,1144],{"id":1528},"the-result-1",[15,1530,1531],{},"Running the same starting board as in the previous run, we get the following results",[105,1533,1536],{"className":1534,"code":1535,"language":1193},[1191],"start time: 0.116954505\nchanged min_moves to: 13, 3000011111233, time: 0.129312172\nchanged min_moves to: 12, 300001111142, time: 0.130486732\nchanged min_moves to: 11, 30003114012, time: 0.642993931\nchanged min_moves to: 10, 3003114002, time: 1.395034809\nchanged min_moves to: 9, 303104002, time: 2.094061232\nchanged min_moves to: 8, 10010012, time: 3.78454475\nNo more jobs: final count: 16724, time: 4.46511333\nresult of operation: {'min_moves': '10010012', 'min_moves_len': 8, 'count': 16724}\n",[100,1537,1535],{"__ignoreMap":110},[15,1539,1540],{},"It took us only 4.46 seconds to complete the job, and there were only 16.7K sequences which were taken to their logical end. Now we're talking :-)",[27,1542],{"url":1543},"https:\u002F\u002Fmedia.giphy.com\u002Fmedia\u002FKnKSXq9qxgZDa\u002Fgiphy.gif",[10,1545,1547],{"id":1546},"conclusion","Conclusion",[15,1549,1550],{},"In this post we looked at one crude way of solving the puzzle which we'd created in the last post. The solution is by no means complete, or fully optimized. We can do many optimizations to find the solution sooner. We will look at some of those optimizations in the next and the final post of this series.",[15,1552,1553],{},"Hope you enjoyed reading the post. Do let me know how would you solve the problem :-).",[1555,1556,1557],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":110,"searchDepth":123,"depth":123,"links":1559},[1560,1561,1562,1571,1578],{"id":12,"depth":123,"text":13},{"id":35,"depth":123,"text":36},{"id":74,"depth":123,"text":75,"children":1563},[1564,1566,1568,1570],{"id":97,"depth":129,"text":1565},"The play function",{"id":375,"depth":129,"text":1567},"The find_min_moves function",{"id":515,"depth":129,"text":1569},"The AutoPlay module",{"id":1143,"depth":129,"text":1144},{"id":1211,"depth":123,"text":1212,"children":1572},[1573,1575,1577],{"id":1239,"depth":129,"text":1574},"Modified find_min_moves function",{"id":1375,"depth":129,"text":1576},"Modified handle_job_recurse function",{"id":1528,"depth":129,"text":1144},{"id":1546,"depth":123,"text":1547},null,"\u002Fimages\u002Fposts\u002Fsolve-puzzle-game-using-python\u002FMtqYZMQ4g-ccc02810d2.png","2022-11-17T12:18:20.988Z","Learn how to solve a tiles puzzle game using python. A step by step guide listing the observations made for creating the algorithm with code.",false,"md","clal1gc70000508l51ya1ahj8",{},"\u002Fsolve-puzzle-game-using-python",{"title":5,"description":1582},"solve-puzzle-game-using-python",[109,1591,1592,1593],"python3","game-development","python-projects","Of2vkrwgZ30bZJKZyPa9vHj6t8Mf-0S4CoBJf1QPI6U",1780470201989]