Goal-based agents for which states of the environment are considered as atomic representations, ones with no visible internal structure.

Formulating the search problem. We assume

- environment's state is
**observable** - environment's state is
**discrete** - environment is
**deterministic** - environment is
**sequential** - environment is
**static** **single**agent

Problem definition:

- set of possible states
- initial state
- possible actions available at each state
- transition model, from state, action pair to next state
- goal test
- path cost, in this chapter assumed to be sum of step costs

Definition of state set is critical. Want enough detail to enable discovery of useful solution, but want minimal detail to ensure a practical search.

Consider the sliding tile 8-puzzle. Example of

- too much detail
- a state includes positions of every tile, orientation in three dimensions of puzzle, time of day, what you had for breakfast, the age of your older sister

- too little detail
- state is either "initial" or "solved"

How would you write the problem definition for our simple graph?

- set of possible states?
- initial state?
- possible actions available at each state?
- goal test?
- path cost?

How would you write the problem definition for the 8 or 15 puzzle?

- set of possible states?
- initial state?
- possible actions available at each state?
- goal test?
- path cost?

How would you write the problem definition for the Towers of Hanoi puzzle?

- set of possible states?
- initial state?
- possible actions available at each state?
- goal test?
- path cost?

How would you write the problem definition for the Peg Board Puzzle?

- set of possible states?
- initial state?
- possible actions available at each state?
- goal test?
- path cost?

How would you write the problem definition for scheduling 10 different observations using the Hubble Space Telescope?

- set of possible states?
- initial state?
- possible actions available at each state?
- goal test?
- path cost?

Uninformed search means that the choice of action is not "informed" by any knowledge of the goal.

Breadth-first search completely explores each level of the search space before proceeding to the next.

Depth-first search completely explores a path until it ends, then backs up a level and tries again.

In [2]:

```
from IPython.display import IFrame
IFrame("https://www.cs.colostate.edu/~anderson/cs440/notebooks/simplegraphsteps.pdf", width=800, height=600)
```

Out[2]:

This example does not show the reduced space requirements of depth-first search. If the solution path was via the second child of 'a', then we would see that the nodes through the first child of 'a' do not need to be saved.

Here is an algorithm definition for both breadth-first and depth-first
search, tailored to fit the python implementation you must complete
for
Assignment 1. The
algorithm maintains a local variable named `un_expanded`

to be a list
of nodes whose children have not yet been generated (like the authors'
`frontier`

variable), and a dictionary named `expanded`

to keep
the nodes for which we have generated the children (like the authors'
`explored`

variable). In each a node is stored with its parent,
allowing a solution path to be generated be stepping backwards from
the goal node once it is found.

Given the `start_state`

, `goal_state`

, `successors_f`

, and
`breadth_first`

(a boolean variable):

- Initialize
`expanded`

to be an empty dictionary - Initialize
`un_expanded`

to be a list containing the pair`(start_state, None)`

- If
`start_state`

is the`goal_state`

, return the list containing just`start_state`

- Repeat the following steps while
`un_expanded`

is not empty:- Pop from the end of
`un_expanded`

a (`state`

,`parent`

) pair. - Generate the
`children`

of`state`

using the`successors_f`

function. - Add
`tuple(state): parent`

to the`expanded`

dictionary - For efficiency, remove from
`children`

any states that are already in`expanded`

or`un_expanded`

. When looking for states in`expanded`

, remember to apply`tuple`

to them first. - If the goal has been found (in python,
`goal_state`

is in`children`

):- Initialize the solution path with the list
`[state, goal_state]`

. - While
`parent`

exists:- Insert
`parent`

to the front of the solution path. - Set
`parent`

to the parent of`parent`

.

- Insert
- Return the solution path.

- Initialize the solution path with the list
- Sort and reverse the list of states in
`children`

, so that we all find the same solution paths. - Create a modified
`children`

list by changing each entry to be a pair (`child`

,`parent`

), where`parent`

is the parent of the child. - Insert the modified
`children`

list into the`un_expanded`

list at the front if doing breadth-first search, or at the back if doing depth-first search. Use the boolean variable`breadth_first`

provided as the last argument in the call to this function to control inserting at the front the back. Do this insertion with one statement, not a for loop, to preserve the order of the children.

- Pop from the end of

The line that starts with "For efficiency, remove from children any states ..." can be tricky, especially if you try to implement this with a for loop that steps through the elements of `children`

and remove them.

Here is an example of a result that might surprise you.

In [4]:

```
nums = [0, 6, 4, 5, 2, 3, 9, 7, 8]
nums
```

Out[4]:

In [5]:

```
for num in nums:
print(num)
```

In [6]:

```
for num in nums:
if num < 5:
nums.remove(num)
nums
```

Out[6]:

Why is 3 still in this list?

This is because you are modifying the list `nums`

which is controlling the for loop. After 2 is removed, the for loop moves on to the value at 9. (Try printing the value of `nums`

at the end of each repetition to see this.)

How can we fix this? There are two ways. The first is to use a copy of the `nums`

list to control the for loop, so you are free to modify the original `nums`

list.

In [7]:

```
import copy
nums = [0, 6, 4, 5, 2, 3, 9, 7, 8]
for num in copy.copy(nums):
if num < 5:
nums.remove(num)
nums
```

Out[7]:

A second way is to use a list comprehension. This ends up constructing a new list, keeping just the elements you want.

In [10]:

```
nums = [0, 6, 4, 5, 2, 3, 9, 7, 8]
nums = [num for num in nums if num >= 5]
nums
```

Out[10]: