{"id":17144107,"url":"https://github.com/szabolcsdombi/pocket-cube","last_synced_at":"2025-03-24T10:17:34.532Z","repository":{"id":183510945,"uuid":"670283879","full_name":"szabolcsdombi/pocket-cube","owner":"szabolcsdombi","description":"Finding all the Pocket Cube states with pure Python","archived":false,"fork":false,"pushed_at":"2023-07-24T19:21:06.000Z","size":8,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-29T15:35:19.445Z","etag":null,"topics":["alogirthm","puzzle","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/szabolcsdombi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-24T17:43:16.000Z","updated_at":"2023-07-24T19:33:58.000Z","dependencies_parsed_at":"2024-12-02T02:01:33.021Z","dependency_job_id":null,"html_url":"https://github.com/szabolcsdombi/pocket-cube","commit_stats":null,"previous_names":["szabolcsdombi/pocket-cube"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szabolcsdombi%2Fpocket-cube","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szabolcsdombi%2Fpocket-cube/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szabolcsdombi%2Fpocket-cube/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/szabolcsdombi%2Fpocket-cube/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/szabolcsdombi","download_url":"https://codeload.github.com/szabolcsdombi/pocket-cube/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245249231,"owners_count":20584497,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alogirthm","puzzle","python"],"created_at":"2024-10-14T20:43:06.739Z","updated_at":"2025-03-24T10:17:34.501Z","avatar_url":"https://github.com/szabolcsdombi.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pocket-cube\n\nA Pocket Cube is a $2\\times2\\times2$ [Combination Puzzle](https://en.wikipedia.org/wiki/Combination_puzzle). It has 3674160 different states.\n\n$$\\frac{8!\\times 3^7}{24}=7!\\times3^6=3,674,160$$\n\nWe can generate all of them by representing the cube states with permutations.\nThe solved cube will be the identity:\n\n```s\n+-------+\n|  0  1 |\n|  2  3 |\n+-------+-------+-------+-------+\n|  4  5 |  6  7 |  8  9 | 10 11 |\n| 12 13 | 14 15 | 16  . |  . 17 |\n+-------+-------+-------+-------+\n| 18 19 |\n|  . 20 |\n+-------+\n```\n\nThe dots represent a fixed corner to avoid repetition. There are 24 possible orientation of the same cube.\n\nWe can define the moves as permutations:\n\n### moves[0]\n\n```s\n+-------+\n|  2  0 |\n|  3  1 |\n+-------+-------+-------+-------+\n|  6  7 |  8  9 | 10 11 |  4  5 |\n| 12 13 | 14 15 | 16  . |  . 17 |\n+-------+-------+-------+-------+\n| 18 19 |\n|  . 20 |\n+-------+\n```\n\n### moves[1]\n\n```s\n+-------+\n|  0  1 |\n| 17 11 |\n+-------+-------+-------+-------+\n| 12  4 |  2  7 |  8  9 | 10 18 |\n| 13  5 |  3 15 | 16  . |  . 19 |\n+-------+-------+-------+-------+\n| 14  6 |\n|  . 20 |\n+-------+\n```\n\n### moves[2]\n\n```s\n+-------+\n|  0  5 |\n|  2 13 |\n+-------+-------+-------+-------+\n|  4 19 | 14  6 |  3  9 | 10 11 |\n| 12 20 | 15  7 |  1  . |  . 17 |\n+-------+-------+-------+-------+\n| 18 16 |\n|  .  8 |\n+-------+\n```\n\n### moves[3] (inverse of moves[0])\n\n```s\n+-------+\n|  1  3 |\n|  0  2 |\n+-------+-------+-------+-------+\n| 10 11 |  4  5 |  6  7 |  8  9 |\n| 12 13 | 14 15 | 16  . |  . 17 |\n+-------+-------+-------+-------+\n| 18 19 |\n|  . 20 |\n+-------+\n```\n\n### moves[4] (inverse of moves[1])\n\n```s\n+-------+\n|  0  1 |\n|  6 14 |\n+-------+-------+-------+-------+\n|  5 13 | 19  7 |  8  9 | 10  3 |\n|  4 12 | 18 15 | 16  . |  .  2 |\n+-------+-------+-------+-------+\n| 11 17 |\n|  . 20 |\n+-------+\n```\n\n### moves[5] (inverse of moves[2])\n\n```s\n+-------+\n|  0 16 |\n|  2  8 |\n+-------+-------+-------+-------+\n|  4  1 |  7 15 | 20  9 | 10 11 |\n| 12  3 |  6 14 | 19  . |  . 17 |\n+-------+-------+-------+-------+\n| 18  5 |\n|  . 13 |\n+-------+\n```\n\nThere is a little known Python method called [`bytes.translate()`](https://docs.python.org/3/library/stdtypes.html#bytes.translate) which allows us to multiply permutations.\u003cbr\u003e\nWe can define the moves as bytes objects and use them as translation tables.\n\n```py\nmoves = (\n    bytes([2, 0, 3, 1, 6, 7, 8, 9, 10, 11, 4, 5, 12, 13, 14, 15, 16, 17, 18, 19, 20]),\n    bytes([0, 1, 17, 11, 12, 4, 2, 7, 8, 9, 10, 18, 13, 5, 3, 15, 16, 19, 14, 6, 20]),\n    bytes([0, 5, 2, 13, 4, 19, 14, 6, 3, 9, 10, 11, 12, 20, 15, 7, 1, 17, 18, 16, 8]),\n    bytes([1, 3, 0, 2, 10, 11, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 20]),\n    bytes([0, 1, 6, 14, 5, 13, 19, 7, 8, 9, 10, 3, 4, 12, 18, 15, 16, 2, 11, 17, 20]),\n    bytes([0, 16, 2, 8, 4, 1, 7, 15, 20, 9, 10, 11, 12, 3, 6, 14, 19, 17, 18, 5, 13]),\n)\n```\n\nWe can now generate all the pocket cube states.\n\n```py\ndef build_distances():\n    solved = bytes(range(21))\n    distances = {}\n    visited = {solved}\n    todo = [solved]\n\n    for i in range(15):\n        distances[i] = len(todo)\n        cubes = todo\n        todo = []\n        for cube in cubes:\n            for move in moves:\n                moved = cube.translate(move)\n                if moved not in visited:\n                    visited.add(moved)\n                    todo.append(moved)\n\n    return distances\n```\n\nWe can run the above function and measure how long it takes to find all the possible cube states.\n\n```py\nimport time\n\nimport pocket_cube\n\nstart = time.time()\ndistances = pocket_cube.build_distances()\nend = time.time()\n\nfor distance, count in distances.items():\n    print(f'{count} pocket cubes with a solution of {distance} moves')\n\nprint(f'it took {end - start:.2f} seconds to finish')\n```\n\n```\n1 pocket cubes with a solution of 0 moves\n6 pocket cubes with a solution of 1 moves\n27 pocket cubes with a solution of 2 moves\n120 pocket cubes with a solution of 3 moves\n534 pocket cubes with a solution of 4 moves\n2256 pocket cubes with a solution of 5 moves\n8969 pocket cubes with a solution of 6 moves\n33058 pocket cubes with a solution of 7 moves\n114149 pocket cubes with a solution of 8 moves\n360508 pocket cubes with a solution of 9 moves\n930588 pocket cubes with a solution of 10 moves\n1350852 pocket cubes with a solution of 11 moves\n782536 pocket cubes with a solution of 12 moves\n90280 pocket cubes with a solution of 13 moves\n276 pocket cubes with a solution of 14 moves\nit took 7.08 seconds to finish\n```\n\nTo be able to solve the cube we must store the moves in a lookup table.\n\n```py\ndef build_lookup_table():\n    solved = bytes(range(21))\n    lookup = {solved: None}\n    todo = [solved]\n\n    for _ in range(15):\n        cubes = todo\n        todo = []\n        for cube in cubes:\n            for move in moves:\n                moved = cube.translate(move)\n                if moved not in lookup:\n                    lookup[moved] = move\n                    todo.append(moved)\n\n    return lookup\n```\n\nAny state now maps to a move that produced it. To invert a move we can apply it three times.\n\n```py\nimport random\n\nimport pocket_cube\n\nlookup = pocket_cube.build_lookup_table()\n\ncube = random.choice(list(lookup.keys()))\n\nwhile move := lookup[cube]:\n    inverse = move.translate(move).translate(move)\n    print('move', pocket_cube.moves.index(inverse))\n    cube = cube.translate(inverse)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszabolcsdombi%2Fpocket-cube","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fszabolcsdombi%2Fpocket-cube","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fszabolcsdombi%2Fpocket-cube/lists"}