Title: Search
1Search
- Much of AI has traditionally revolved around
search - Search means searching among all the
possibilities for an answer (or possibly the
best answer) - consider, as a programmer, you are facing a
logical error in your program and want to fix it - usually the programs behavior gives you some
indication of the problem - doesnt print results? Stuck in an infinite loop
- garbage output? Possible pointer dereferencing
problem - wrong values output? Possible wrong computation
- you draw conclusions based on your own experience
with previous debugging sessions - A computer on the other hand has no experience to
draw on, so it might have to consider all
possibilities, evaluating each one using some
kind of test - this is the basic idea of search
2Example 8 Puzzle
- Consider the 8 puzzle
- One of those toys at carnivals, made of plastic
- move the numbers using the one opening so that
the numbers eventually wind up in order 1-8 - How do you solve it?
- at any given state (board configuration), you
have 2-4 options based on where the space is - slide tile up, down, left, right
- remember each state you have already visited so
that you dont get caught in an infinite loop
This tree represents the search space of one
move from the starting point how large is this
tree?
38 Puzzle Search Space
- We consider all possible board configurations
of the 8 puzzle to be the search space - From any one board, you can manipulate the board
into a few new positions - Each board configuration is called a state
- We start in a starting state and our goal is to
reach a goal state - All of the states can be illustrated in a tree or
graph form
Notice the cycle, we want to avoid this
48 Puzzle Algorithm
- Lets store the 8 puzzle as a list storing the
number of each tile in the given position, for
instance our starting point from the last slide
would be (8 2 7 3 4 0 5 1 6) - 0 denotes the blank
- Recursive function given the board
- Compare board to goal, are we done yet? If so,
return that a solution has been found - Otherwise, add this board configuration to the
list of tried states - Generate the possible moves from this position
(this depends on where the blank is) - For each possible move, see if the board achieved
by taking that move is already in our tried
states list, if so, skip it - Otherwise, recursively call this function passing
it the new board
5Eight Puzzle Solution
(defun eightpuzzle (board) (declare (special
tried goal)) (when (equal board goal)
(print board) (return-from eightpuzzle t)) (if
(memberlis board tried) (return-from
eightpuzzle nil)) (setf tried (append tried
(list board))) (let ((blank (position 0
board)) (moves (generate-boards blank
board)) temp) (do ((i 0 ( i 1))) ((or temp
( i (length moves)))) (setf temp
(eightpuzzle (nth i moves)))) (if temp (print
board)) temp)) (defun memberlis (a lis)
(dolist (i lis) (if (equal i a) (return-from
memberlis t))) nil)
6Continued
(defun generate-boards (blank board) (let
(temp1 temp2) (setf temp1 (generate-moves
blank)) (dolist (i temp1) (setf temp2
(append temp2 (list (swapem board i blank)))))
temp2)) (defun generate-moves (blank)
(cond (( blank 0) '(1 3)) (( blank 1) '(0 2
4)) (( blank 2) '(1 5))
(( blank 3) '(0 4 6)) (( blank 4) '(1
3 5 7)) (( blank 5) '(2 4 8)) (( blank 6)
'(3 7)) (( blank 7) '(4 6 8)) (( blank 8)
'(5 7))))
(defun swapem (b i j) (let (temp (value (nth i
b))) (dotimes (a 9) (if ( a i)
(setf temp (append temp '(0))) (if
( a j) (setf temp (append temp (list
value))) (setf temp (append temp
(list (nth a b))))))) temp))
7How Good is Our Algorithm?
- We have two significant problems with our
algorithm - First, because of recursion, we will push a lot
of states on the stack before we might hit on a
solution - how many? how deep is our tree?
- for the 8 puzzle, it could be as deep as 8! does
our stack space have enough room? - Second, our tried list grows with each new
state visited - in the 8 puzzle, it can be as long as 8! near the
end, which means that memberlis has a lot of work
to do! - It is common to run out of stack space when
trying to run such programs as the 8 puzzle
8Improvement Heuristic Search
- Lets assume that we have another function, h,
which can evaluate how likely a given move will
lead us quickly to the solution - We slightly alter our previous algorithm as
follows - Let temp F(S)
- Remove all items in temp that are already in T
- Sort temp in order using h
- that is, (sort temp key h)
- For each item i in temp, call try(i)
- This allows us to try successor states in order
of most likely to reach a solution - How much can this improve our search?
- In the best case, the heuristic tells us the
right move every time, so instead of an O(n!)
complexity, it is reduced to perhaps as little as
O(n) - In the worst case, it does nothing for us at all
9Good Heuristics?
- One of the things we study in AI is what makes up
a good heuristic - A good heuristic is one that should reduce the
complexity of the search by usually telling us
the right move - Is there a good heuristic for the 8 puzzle?
- Consider two
- Local heuristic add up the number of tiles that
are currently in the right position with respect
to the goal state and subtract from it the number
of tiles that are out of position, select the
move that leads us to the state with the highest
heuristic value - Global heuristic add up the number of moves
that each tile would have to make to get to its
proper position in the goal state, select the
move that leads us to the state with the lowest
heuristic value - Some problems may not have heuristics (8 Queens?
Knights Tour?)
10Heuristic Version
(defun compute-distance (a b) (cond (( a b)
0) ((and ( a 0) (or ( b 1) ( b 3)))
1) ((and ( a 0) (or ( b 2) ( b 4) (
b 6))) 2) (( a 0) 3) ((and ( a
1) (or ( b 0) ( b 2) ( b 5))) 1)
((and ( a 1) (or ( b 3) ( b 5) ( b 7))) 2)
(( a 1) 3) for a 2, 3, 4,
5, 6, 7 ((and ( a 8) (or ( b 5) ( b
7))) 1) ((and ( a 8) (or ( b 2) ( b
4) ( b 6))) 2) (t 3)))
(defun generate-boards (blank board) (let
(temp1 temp2) (setf temp1 (generate-moves
blank)) (dolist (i temp1) (setf temp2
(append temp2 (list (swapem board i blank)))))
(sort temp2 'temp2)) (defun heuristic (board) (declare
(special goal)) (let ((sum 0) a b)
(dotimes (i 9) (setf a (position i
board)) (setf b (position i goal))
(setf sum ( sum (compute-distance a b))))
sum))
11General Purpose Search Algorithm
- Let S initial state, G be goal state, T be
states already reached (initialized to nil) and F
be a function that, given S, will generate
successor states - try(S)
- If S G, then return success
- Let temp F(S)
- Remove all items in temp that are already in T
- For each item i in temp, call try(i)
- Notice that this algorithm will not return T if
it never reaches G, so it is implied to return
nil - The only difficulty is in creating a proper
function F - For the 8 puzzle, it was merely a list of new
boards reachable from the given state and since
there are no more than 8 possible locations for
the blank, there are only 8 possible sets of
moves, so it was easy to create F
12Backtracking
- What if we get stuck in a deadend when searching?
- In doing a maze, if we reach a location where
either we cannot continue, or we have tried all
of the different branches, we cannot move
forward, so instead we backtrack to the most
recent intersection and try a different branch - To add backtracking to our algorithm, we have to
know if our recursive function call ends with
success or failure - if success, then return from this recursive call
as well - if failure, backtrack and try a new branch from
this point forward - Basic algorithm
- Backtrack(current)
- If current is a goal node, return success
- If current is nil (or a deadend state), return
failure - For each child c of current,
- value Backtrack(C)
- If value success then return success else
try the next possible choice from current
13Example 8 Queens
- We want to place 8 queens on an 8x8 chessboard
such that no queen can take any other queen - We will store our solution as a list where list
element i will be the row of the queen in column
i - We start with nil and extend it
- Position a queen in column i at row 1
- If the queen cannot be captured, then extend else
continue to try rows 2-8, if we exceed row 8,
return failure - By returning failure, a previous recursive call
to extend will move its current queen from the
current row to the next, and continue
Cant place another queen at this point, so we
must backtrack This queen is moved, we wind
up continuing to backtrack until we have to move
this queen
148 Queens Code
(defun queens (lis) (if (not (badboard lis))
(when ( (length lis) 8) (print lis)))
(return-from queens t))) (if (or (badboard
lis) ( (length lis) 8)) (return-from queens
nil) (progn (let (temp)
(setf temp (queens (append lis '(1))))
(if (not temp) (setf temp (queens (append lis
'(2))))) (if (not temp) (setf temp
(queens (append lis '(3))))) (if (not
temp) (setf temp (queens (append lis '(4)))))
(if (not temp) (setf temp (queens
(append lis '(5))))) (if (not temp)
(setf temp (queens (append lis '(6)))))
(if (not temp) (setf temp (queens (append lis
'(7))))) (if (not temp) (setf temp
(queens (append lis '(8))))) temp))))
(defun badboard (lis) (let ((board (butlast
lis)) (a (car (last lis)))) (if (member
a board) t (progn (let (x
(return-value nil) (size (length
board))) (dolist (temp board) (setf
x (position temp board)) (if ( (/ (abs (- a
temp)) (abs (- size x))) 1)
(setf return-value t)))) return-value)))))
15Knights Tour
- A related problem is the knights tour
- Can you move a knight on a chess board so that it
eventually lands on every square of the chess
board without repeating any board node twice? - Basic algorithm
- Store the chessboard as an NxN array initialized
to nil - move(x y num)
- If is a legal move (still on the board and
this particular position has not been tried
before) then set boardxy num - Recursively try move (x y num1) where x and
y is the next available knight move from this
position - this requires a loop that tries any of the 8
possible knight moves - If the recursive call does not permit the
placement of the knight (no legal moves), then
reset the position to nil and try the next
position - Return nil if you were unable to place a knight
at (x y num)
16Example
Early on, there are numerous choices, it doesnt
seem like any choice will lead us astray, but
later on, we might find ourselves boxed in and
we will have to backtrack to a previous decision
point in order to continue
17Knights Tour Code
(defun knight (x y num) (declare (special
board size)) (if (done) (return-from
knight t)) (if (not (withinbounds x y))
(return-from knight nil)) (if (aref board x
y) (return-from knight nil)) (setf (aref
board x y) num) (let (tempx tempy legal)
(do ((i 0 ( i 1))) ((or legal ( i 8)))
(setf tempx ( x (nextx i))) (setf
tempy ( y (nexty i))) (setf legal
(knight tempx tempy ( num 1)))) (if (not
legal) (setf (aref board x y) nil))
legal)) (defun nextx (i) (case i (0 2) (1 2)
(2 1) (3 -1) (4 -2) (5 -2) (6 -1) (7 1))) (defun
nexty (i) (case i (0 -1) (1 1) (2 2) (3 2) (4
1) (5 -1) (6 -2) (7 -2)))
(defun withinbounds (x y) (declare (special
size)) (and ( ( x 0) ( y 0)))
18Continued
(defun run (optional size) (declare (special
board size)) (if size (setf size
size)) (setf board (make-array (list size
size))) (if (knight 0 0 0) (print-board)
(format t "Failed"))) (defun done ()
(declare (special board size)) (dotimes
(i size) (dotimes (j size) (if (null
(aref board i j)) (return-from done nil))))
t) (defun print-board () (declare (special
board size)) (dotimes (i size)
(format t "") (dotimes (j size)
(format t "4D" (aref board i j)))))
19Water Jugs
- You have 2 water jugs, one can hold exactly 4
gallons and one can hold exactly 3 gallons - Goal Fill the 4 gallon jug with exactly 2
gallons of water - Assume An infinite amount of water is available
- Operations
- fill a jug to the top
- dump the contents of a jug out (back into the
well or onto the ground) - pour the contents of one jug into the other jug
until the other jug is full or the current jug is
empty - Search space will be represented as a list (x y)
where x is the current contents in gallons of the
4 gallon jug and y is the current contents of the
3 gallon jug - Initial state (0 0)
- Goal state (2 0)
- We can search either recursively (depth-first) or
using a queue (breadth-first) - Solution on the website
20Water Jugs Code
(defun waterjugs (x y) (declare (special
tried)) (if (memberlis (list x y) tried)
(return-from waterjugs nil) (setf tried
(append tried (list (list x y))))) (when
(and ( x 2) ( y 0)) (print (list x y))
(return-from waterjugs t)) (if (not (legal x
y)) (return-from waterjugs nil)) (let
(temp) (setf temp (waterjugs 4 y)) (if (not
temp) (setf temp (waterjugs x 3))) (if (not
temp) (setf temp (waterjugs 0 y))) (if (not
temp) (setf temp (waterjugs x 0))) (if (not
temp) (if (and ( ( x y)
4) ( y 0)) (setf temp
(waterjugs 4 (- y (- 4 x))))))
21Continued
(if (not temp) (if (and (
( x y) 3) ( x 0)) (setf temp
(waterjugs (- x (- 3 y)) 3)))) (if (not temp)
(if (and ( 0 y))
(setf temp (waterjugs ( x y) 0)))) (if (not
temp) (if (and ( x 0))
(setf temp (waterjugs 0 ( x y))))) (if
temp (print (list x y))) temp))
(defun legal (x y) (and ( x 0) (
y 0) ((dolist (i lis) (if (equal a i) (return-from
memberlis t))) nil)
22Consider Chess
- In Chess, your goal state is a checkmate state
- However, unlike the 8-puzzle, you cant just
search all states for a checkmate or use a
heuristic - the opponent will make moves too
- We need a different kind of search
- Make this assumption your opponent will always
select a move that maximizes his board positions
heuristic worth - Therefore, as a player, you want to not only
maximize your board positions worth, but also
minimize your opponents - this is called minimax
- Compute the heuristic worth of all board
configurations for x moves ahead - If x is even, take the minimum value from each
subtree - If x is odd, take the maximum value from each
subtree - Even or odd indicates whose turn that particular
layer is (odd being your move and even being your
opponents move)
23Example
Assume we have looked 1 level further and found
the maximum heuristic values, we select only the
best move Now, we know that our opponent will
make an intelligent move such that he/she limits
our options, so we assume that the opponent
will look over the maximum heuristic values and
select the move that is worst for us (so instead
of offering us a move of a possible 10, our
opponent will select a move that results in a
move worth at most 3)
Our best choice is to make the move on the right
if our opponent selects wisely, we can reach a
state worth as much as 5
24Other Search Strategies
- Alpha-beta pruning
- If we were to look ahead m turns, and at each
turn, there are n possible moves to make, then we
have to look ahead mn - Consider chess, you want to look ahead 5 turns,
and there are 20 possible moves per turn, you
have to consider around 520 board
configurations! - Alpha-beta pruning uses minimum and maximum
threshold values to determine if a move should
continue to be searched because it has already
exceeded the maximum that we want the opponent to
achieve or the minimum that we would desire to
achieve - AND Search
- In some cases, we are not searching for a single
move, but a collection of moves - We may have a tree in which some nodes are OR
nodes, you can pursue any one of the subtrees,
but if we have AND nodes, then we must pursue all
subtrees - This is more common in design/planning problems
than in game playing