{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# The 8 Puzzle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Play the 8 puzzle on-line [here](http://www.tilepuzzles.com/default.asp?p=12)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's discuss how to implement the 8 puzzle in python. \n", "\n", "How do you want to represent the state of the 8 puzzle? Say the state\n", "is\n", "\n", " ------------- \n", " | 1 | 2 | 3 |\n", " ------------\n", " | 4 | | 5 |\n", " ------------\n", " | 6 | 7 | 8 |\n", " -------------\n", " \n", "You could use a list" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2, 3, 4, 0, 5, 6, 7, 8]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "state = [1, 2, 3, 4, 0, 5, 6, 7, 8]\n", "state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "with 0 representing the empty cell. You could represent it as a numpy\n", "array." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 3],\n", " [4, 0, 5],\n", " [6, 7, 8]])" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "state = np.array([[1, 2, 3], [4, 0, 5], [6, 7, 8]])\n", "state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This way you index into a cell using" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "state[1, 2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "for the second row and third column." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I found the simple list a little easier to work with. Then you can\n", "write a `print_state_8p` function to show it.\n", "\n", " In [9]: print_state_8p(state)\n", " 1 2 3\n", " 4 - 5\n", " 6 7 8" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another useful function is one that finds the blank in a given state." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " In [18]: find_blank_8p(state)\n", " Out[18]: (1, 1)\n", "\n", " In [19]: find_blank_8p([1,2,3, 4,7,5, 6,0,8])\n", " Out[19]: (2, 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Other useful functions include ones that convert between an index into\n", "the list state and a row and column pair.\n", "\n", "One bit of trickiness in the iterative deepening algorithm, repeated here\n", "from last time, is that sometimes a list of states is returned as the\n", "solution path, and other times the string `'cutoff'` or `'failure'` is returned." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def depth_limited_search(state, goal_state, actions_f, take_action_f, depth_limit):\n", " \n", " # If we have reached the goal, exit, returning an empty solution path.\n", " If state == goal_state, then\n", " return []\n", " \n", " # If we have reached the depth limit, return the string 'cutoff'.\n", " If depth_limit is 0, then\n", " Return the string 'cutoff' to signal that the depth limit was reached\n", " \n", " cutoff_occurred = False\n", " \n", " # For each possible action from state ...\n", " For each action in actions_f(state):\n", " \n", " # Apply the action to the current state to get a next state, named child_state\n", " child_state = take_action_f(state, action)\n", " \n", " # Recursively call this function to continue the search starting from the child_state.\n", " # Decrease by one the depth_limit for this search.\n", " result = depth_limited_search(child_state, goal_state, actions_f, take_action_f, depth_limit - 1)\n", " \n", " # If result was 'cufoff', just note that this happened.\n", " If result is 'cutoff', then\n", " cutoff_occurred = True\n", " \n", " # If result was not 'failure', search succeeded so add childState to front of solution path and\n", " # return that path.\n", " else if result is not 'failure' then\n", " Add child_state to front of partial solution path, in result, returned by depth_limited_search\n", " return result\n", " \n", " # We reach here only if cutoff or failure occurred. Return whichever occurred.\n", " If cutoff_occurred, then\n", " return 'cutoff'\n", " else\n", " return 'failure'" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def iterative_deepening_search(start_state, goal_state, actions_f, take_action_f, max_depth):\n", " \n", " # Conduct multiple searches, starting with smallest depth, then increasing it by 1 each time.\n", " for depth in range(max_depth):\n", " \n", " # Conduct search from startState\n", " result = depth_limited_search(start_state, goal_state, actions_f, take_action_f, depth)\n", " \n", " # If result was failure, return 'failure'.\n", " if result is 'failure':\n", " return 'failure'\n", " \n", " # Otherwise, if result was not cutoff, it succeeded, so add start_state to solution path and return it.\n", " if result is not 'cutoff', then\n", " Add start_state to front of solution path, in result, returned by depth_limited_search \n", " return result\n", " \n", " # If we reach here, no solution found within the max_depth limit.\n", " return 'cutoff'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember, for the 8 puzzle all actions are not available from all\n", "states. The state\n", "\n", " ------------- \n", " | | 2 | 3 |\n", " ------------\n", " | 1 | 4 | 5 |\n", " ------------\n", " | 6 | 7 | 8 |\n", " -------------\n", "\n", "only has two possible actions, 'down' and 'right'. It makes the most\n", "sense to implement this restriction in the `actions_f` function, so\n", "`take_action_f` can assume only valid actions are given to it.\n", "\n", "As implemented for this assignment, our depth-limited search generates\n", "a list of all valid actions from a state, stores them, then starts a\n", "`for` loop to try each one. At any point in the depth-first search,\n", "all siblings of states being explored are stored in the local\n", "variables of each recursive call. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Python Generators" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember that the \"backtracking\" version of depth-first search is one\n", "in which all sibling actions are not stored, but generated as needed.\n", "\n", "Sounds like a complicated implementation. Python [generators](http://www.neotitans.com/resources/python-generators-tutorial.html])\n", "to the rescue! This is a bit advanced and the solution to Assignment\n", "2 does not need generators, but, be curious!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a simplified version of `actions_f`, without the checks for\n", "valid actions." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "def actions_f(state):\n", " actions = []\n", " actions.append('left')\n", " actions.append('right')\n", " actions.append('up')\n", " actions.append('down')\n", " return actions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It just returns the actions.\n", "\n", " In [31]: actions_f(state)\n", " Out[31]: ['left', 'right', 'up', 'down']" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[1, 2, 3],\n", " [4, 0, 5],\n", " [6, 7, 8]])" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "state = np.array([[1, 2, 3], [4, 0, 5], [6, 7, 8]])\n", "state" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['left', 'right', 'up', 'down']" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "acts = actions_f(state)\n", "acts" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The function `actions_f` can be converted to one that returns a\n", "generator by using the `yield` statement." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "def actions_f(state):\n", " yield 'left'\n", " yield 'right'\n", " yield 'up'\n", " yield 'down'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sheesh. That's even simpler than the original. It's use must be more\n", "complicated. And it is, but just a bit." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "acts = actions_f(state)\n", "acts" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'left'" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(acts)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'right'" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(acts)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'up'" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(acts)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'down'" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "next(acts)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "ename": "StopIteration", "evalue": "", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mStopIteration\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mnext\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0macts\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mStopIteration\u001b[0m: " ] } ], "source": [ "next(acts)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That last one raised a `StopIteration` exception. The generator is\n", "often used in a `for` loop that stops correctly." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "left\n", "right\n", "up\n", "down\n" ] } ], "source": [ "for a in actions_f(state):\n", " print(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This looks exactly like the `for` loop when `actions_f` actually\n", "returns the whole list!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Debugging with pdb" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See the site [Python Conquers the Universe](http://pythonconquerstheuniverse.wordpress.com/category/python-debugger/) for a brief introduction to using the `pdb` module." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And don't forget good old `print` statements.\n", "\n", " debug = True\n", " .\n", " .\n", " .\n", " if debug:\n", " print('Just loaded data into list named nums whose length is', len(nums))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# ipython and jupyter startup settings" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`ipython` can be set to automatically start `pdb` when an error is encountered. Many other settings are available. See [IPython Tip Sheet](http://pages.physics.cornell.edu/~myers/teaching/ComputationalMethods/python/ipython.html).\n", "\n", "`jupyter` startup settings are discussed [here](https://jupyter-notebook.readthedocs.io/en/stable/config_overview.html)" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.3" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 1 }