CPSC 420-200 Read-Only Bulletin Board

Last modified: 1/17/05, 01:15PM (Mon)

This page will have all relevant email transactions with the students regarding the AI course, so that everyone has equal information regarding the course material.

Newest transactions will be posted on the top. Regularly view this page to see what's going on between other students and the instructor.

All sensitive material such as your name, email address, etc. will be removed, and also any part of code that is not relevant to the discussion will not be uploaded here.


Article List

Date: 3/01 Title: Resolution solution (customs official)
Date: 2/23 Title: Lisp tips — clearing dupe-check list
Date: 2/23 Title: Lisp tips — careful with append and cons
Date: 2/23 Title: Lisp tips — sorting
Date: 2/23 Title: Lisp tips — misc. tips
Date: 2/23 Title: Program #2 — getting started
Date: 2/23 Title: Heuristic functions
Date: 2/23 Title: Compiling Lisp code
Date: 2/08 Title: Make-up dates


Articles

Date: 3/01 Title: Resolution solution (customs official)
(1a) ~E(x) \/ V(x) \/ S(x,f(x))
(1b) ~E(x) \/ V(x) \/ C(f(x))
(2a) E(a)
(2b) D(a)
(2c) ~S(a,y) \/ D(y)
 (3) ~D(x) \/ ~V(x)
 (4) ~D(x) \/ ~C(x)
--------------------------------
 (5) ~V(a)                       2b & 3
 (6) V(a) \/ S(a,f(a))           1a * 2a
 (7) S(a,f(a))                   5 & 6
 (8) D(f(a))                     2c & 7
 (9) ~C(f(a))                    4 & 8
(10) ~E(a) \/ V(a)               1b & 9
(11) V(a)                        2a & 10
(12) False                       5 & 11
Date: 2/23 Title: Lisp tips — clearing dupe-check list
[Q] My search works the first time I call it, but it seems to go on forever the second time I call it. What's wrong?

[A] It must be the case that you are not refreshing your duplicate check node list. Suppose it is *all-nodes-expanded*, then for each search insert this line before you call the search core:


	(setq *all-nodes-expanded* nil)

Note that for Iterative Deepening Search, you have to reset the list to NIL before each iteration!
Date: 2/23 Title: Lisp tips — careful with append and cons
[Q] My (expand ...) function always returns NIL!?

[A] You are probably just doing (cons (apply-op ...) return-node-list) or (append (list (apply-op ...)) return-node-list). What you should do is to assign the result of that cons or append back into the temporary variable that holds the list of expanded nodes (i.e., the return-node-list variable).

	(setq return-node-list (cons (apply-op ...) return-node-list))
Also, at the end of the expand function, you must put return-node-list so that it will be returned as the function value.
(defun expand (...)

  (let (return-node-list) 		; <--- declare local variable
	
	...

		(setq return-node-list (cons (apply-op ...) return-node-list))

	...


	return-node-list		; <-- (let ...) will return this
					;     This must be the last line
					;     in the (let ....) form.

  )					; <-- and that will be again returned
					;     by your expand function.
)
Date: 2/23 Title: Lisp tips — sorting
Here are some lisp idioms that may help you folks:
  1. Sorting: sort using the heuristic function value only:
    * (setq node-list '(     ((1 2 3 4 5 6 7 8 0) 10 2 (up down))
                            ((1 3 2 6 5 4 8 7 0) 15 1 (left))
                            ((3 4 2 5 6 1 0 7 8) 2  3 (left right down)) ))
    
    (((1 2 3 4 5 6 7 8 0) 10 2 (UP DOWN)) ((1 3 2 6 5 4 8 7 0) 15 1 (LEFT))
     ((3 4 2 5 6 1 0 7 8) 2 3 (LEFT RIGHT DOWN)))
    
    * (sort node-list #'(lambda (x y) (< (second x) (second y))) )
    
    (((3 4 2 5 6 1 0 7 8) 2 3 (LEFT RIGHT DOWN))
     ((1 2 3 4 5 6 7 8 0) 10 2 (UP DOWN))
     ((1 3 2 6 5 4 8 7 0) 15 1 (LEFT)))
    
    >
    

    By changing

    #'(lambda (x y) (< (second x) (second y)))
    
    to
    #'(lambda (x y) (< (+ (second x) (third x))
    		      (+ (second y) (third y))
    		   )
         )
    
    you can make the nodes sort according to f(n) = g(n)+h(n).

  2. Reversing a list: simply use reverse.
    * (reverse '(left right up down right down))
    
    (DOWN RIGHT DOWN UP RIGHT LEFT)
    
    
  3. While loops: you can define a macro like this:
    * (defvar x 0)
    
    X
    * (defmacro while (test &rest forms) `(loop (unless ,test (return)) ,@forms))
    
    WHILE
    
    * (setq x 0)
    
    0
    * (while (< x 10)			
          (setq x (+ x 1))
          (print (format nil "~d~%" x))
      )
    
    "1
    "
    "2
    "
    "3
    "
    "4
    "
    "5
    "
    "6
    "
    "7
    "
    "8
    "
    "9
    "
    "10
    "
    NIL
    
Date: 2/23 Title: Lisp tips — misc. tips
These are some scattered Lisp tips:
  1. To add a single item to a list, use (cons ...):
    	(let (node-list)
    
    	  (setq node-list '( a b c ))
    
    	  ...
    
    	  ; WRONG way: cons alone does NOT update node-list
    	  ; After the following operation, node-list will remain '(a b c)
    	  (cons 'x node-list)
    	
    	  ...
    
    	  ; RIGHT way: you have to use setq 
    	  ; so that node-list now contains '(x a b c)
    	  (setq node-list (cons 'x node-list))
    
    
    The same thing applies for sorting. When you sort, the list you give as an argument does not get updated. You have to make sure that you recover the result by using (setq ...).

  2. Checking for duplicates:
    Just keep a global variable *all-expanded-nodes*, and keep cons-ing to it when you bump into a state that you haven't seen so far. See the (dupe ...) function in the skeleton code for a clue. You first check if a given node is a dupelicate, and if not, add that to the *all-expanded-nodes* list.
    	(defvar *all-expanded-nodes* nil)
    
    	...
    
    	; if not dupe, add it to the *all-expanded-nodes* list
    	(setq *all-expanded-nodes* (cons given-node *all-expanded-nodes*))
    	
    	...
    

  3. [Q] I am confused about the (expand ....) function in the skeleton code!
    [A] It is just a wrapper. Because there can be different expand functions depending on whether you have to check for a depth limit or an f-limit, you may end up writing redundant code. Instead of doing that, you can take in the expand function name as an argument and use a generic expand as in the provided code.
    	(defvar *expand-func* 'vanilla-expand)
    
    	...
    	(defun general-search (state expand-func)
    		...
    
    		; note that we're setting the expand function to 
    		; the one supplied as an argument to general-search
    		(setq *expand-func* expand-func)
    		...
    		...
    		; call (expand ...) somewhere and it will use the
    		; desired (depth-check-expand ...) function.
    
    	)
    
    	....
    
    	(defun ids (state)
    		(general-search state 'depth-check-expand)
    	)
    
    	....
    
    	(defun dfs (state)
    		(general-search state 'vanilla-expand)
    	)
    
    	....
    
    
Date: 2/23 Title: Program #2 — getting started
Here's a quick guide to get you started on program#2. Because you can work on so many different aspects of the provided problem, it may not be entirely clear where to begin. Following the steps given below will give you a head-start:
  1. Node vs. state:
    The first matter is to distinguish between a node and a state. A node is a super-structure, containing extra information in addition to the game-board's state. Look carefully the arguments of the function. If you see state, provide a state. If you see node, provide a node.
  2. Implement (expand ....):
    First, implement a simple expand function, where a node is taken as an argument. The result is a list of child nodes.

    For each operator in 'up, 'down, 'left, 'right, you should go through a series of tests before actually expanding:

    • Is the operator applicable?
    • Is the resulting state a duplicate state?
    • Does the child node go over the depth- or the f-limit? (only applies to DLS, IDS, and IDA*; ignore this at first).
    Collect the new nodes by running (apply-op ...) and putting those into a list:
    (setq return-list nil)
    ...
    ... run checks ...
    ; apply operator, and use cons to paste it to the return-list
    (setq return-list (cons (apply-op ....) return-list))
    ...
    Finally, return that list as a result of (expand ....).
  3. Using the expand function, either get dfs or bfs to work.
    BFS is easier to test. Modify dfs-core to use your expand function and to take in initial state as necessary. Provide input that would only take 2 to 3 moves to reach the goal. Start from the goal state, and move the blank 2 to 3 times, and use that resulting state as the initial state.
  4. For example, if (0 2 3 7 1 8 4 5 6) was the initial state,
    • Your initial node would be:
           ( (0 2 3 7 1 8 4 5 6)
             0 ; let's assume we're doing uninformed search
             0 ; depth is 0
             () ; no operator applied yet
           )
      
    • When you run expand on the initial node above, you should get a list of two nodes:
         ( 
           ( (2 0 3 7 1 8 4 5 6)
             0; ignore this for now
             1; one operator has been applied so far
             (RIGHT)
           ); this is your first node, resulting from moving right.
      
           ( (7 2 3 0 1 8 4 5 6)
             0; ignore this for now
             1; one operator has been applied so far
             (DOWN)
           ); this is your second node, resulting from moving down.
      
         ) ; end of expanded node list
      
      This is the list that needs to be returned by your expand function. In your core routine, this list will be appended to your existing node list.

      Note that since I added the comments above, so the actual result would be more like:

         (((2 0 3 7 1 8 4 5 6) 0 1 (RIGHT)) ((7 2 3 0 1 8 4 5 6) 0 1 (DOWN)))
      
Date: 2/23 Title: Heuristic functions
[Q1] What is Manhattan distance?

[A1] Here's an approximate algorithm.

1. Pick a tile, say n.

2. Find it's current location in the given state: use the (location ...)
   function.

3. Calculate its desired location (from the goal state).

4. Get the x and y location for the current and desired location. Let's
   call them x_c, y_c, and x_d, y_d.

5. The manhattan distance for this tile n is

        manhattan(n) = abs(x_c-x_d) + abs(y_c-y_d).

6. Get the sum for all tiles

                        n=8
        sum_manhattan = Sum manhattan(n)
                        n=0

[Q2] How do I select different heuristics for a* and heuristic search?

[A2] It'e easier to have a global variable that holds the heuristic function name, let's say *heuristic-func*.

Your heuristic search function should take in the function to use as an argument:

	(defun heuristic (state h-func)

		...
		(setq *heuristic-func* h-func)

		...

	)
which can be called as:
	(heuristic '( 1 2 3 ... ) 'h1)
supposing that you defined your heuristic functions as
	(defun h1 (state) 		; tiles out of place
		...
	)

	(defun h2 (state)		; sum of Manhattan distance
		...
	)

In (expand ...) or in (apply-op ...), you should call a heuristic wrapper (h state)


	(defun apply-op (op node)
		...

		...  (h state) ...

	)

	(defun h (state)
		(funcall *heuristic-func* state)
	)
so that the apply-op function should call the heuristic that you selected in your main search wrapper function.

For example, try this to see what the main idea is:

	* (setq op '+)
	+

	* (funcall op 10 20)
	30

	* (setq op 'list)
	LIST

	* (funcall op 10 20 30)
	(10 20 30)

Date: 2/23 Title: Compiling Lisp code
To optimize and speed up execution, you should compile your program code. You can do this in CMUCL by running the (compile-file <filename>) function. See below for an example, where I am compiling function(s) in the file fibo.l. The file contained the definition for the (fibo ..) function. Of course you can have multiple function definitions in the file, and all of those will be compiled. Again, * is the lisp prompt, and my inputs to lisp are in bold.
* (compile-file "fibo.l")

; Python version 1.1, VM version UltraSparc/Solaris 7 on 09 SEP 04 10:49:52 am.
; Compiling: /user/choe/web_project/420/fibo.l 09 SEP 04 10:44:02 am

; Converted FIBO.
; Compiling DEFUN FIBO: 
; Byte Compiling Top-Level Form: 

; fibo.sparcf written.
; Compilation finished in 0:00:00.

#p"/user/choe/web_project/420/fibo.sparcf"
NIL
NIL

* (load "fibo.sparcf")

; Loading #p"/user/choe/web_project/420/fibo.sparcf".
T
* (fibo 20)

10946
* 

Note that you need to first compile it, and the note what the resulting compiled file is (the one highlighted in red above), and then finally (load ...) that.
Date: 2/08 Title: Make-up dates
Make-up dates (X indicates unavailablity so far):

 Thu 2/9 8:00-9:15am XX
 Thu 2/9 9:00-10:15am XX
 Thu 2/9 10:00-11:15am XX
 Thu 2/9 4:30-5:45pm X

 Fri 2/10 8:00-9:15am XX
 Fri 2/10 9:00-10:15am X
 Fri 2/10 10:00-11:15am XX
 Fri 2/10 3:00-4:15pm X
 Fri 2/10 4:10-5:25pm X

 Tue 2/14 5:00-6:15pm
 Tue 2/14 6:00-7:15pm


$Id: board.php,v 1.4 2003/09/04 21:56:27 choe Exp $