# Homework 6: Search as a Programming Technique *CSci 2041: Advanced Programming Principles, Spring 2017* **Due:** 11:59pm Monday, April 17 ## Introduction You will solve two searching problems in this assignment. The first is a tautology checker, the second is a maze runner. ## Part 1: Tautology Checker ### Getting Started Your task for this homework is to write tautology checker for logical formulas. You should be familiar with the code we wrote for the ``subsetsum`` problem in lecture before starting this assignment. *If you don't understand that code, then the work below will be very difficult.* Below are a few type definitions and functions that you will want to use. As we've done before, place these in a file named ``tautology.ml`` in a directory named ``Hwk_06``. This ``Hwk_06`` directory should be in your individual repository. Our formulas are represented by the following OCaml type: ``` type formula = And of formula * formula | Or of formula * formula | Not of formula | Prop of string | True | False ``` You will implement the tautology checker in the style that uses exceptions, specifically the style that raises an exception to indicate that the search process should continue. Thus we will use the following exception for that. ``` exception KeepLooking ``` Our tautology checker will search for substitutions for the propositions in the formula for which the formula will evaluate to ``false``. The substitutions are similar in structure to the environments that we had in homework 4. We'll introduce a name for them, ``subst``, with the following type definition. ``` type subst = (string * bool) list ``` Here is a function for converting substitutions to strings. We'll use this in printing our results. ``` let show_list show l = let rec sl l = match l with | [] -> "" | [x] -> show x | x::xs -> show x ^ "; " ^ sl xs in "[ " ^ sl l ^ " ]" let show_string_bool_pair (s,b) = "(\"" ^ s ^ "\"," ^ (if b then "true" else "false") ^ ")" let show_subst = show_list show_string_bool_pair ``` You might also find these helpful ``` let is_elem v l = List.fold_right (fun x in_rest -> if x = v then true else in_rest) l false let rec explode = function | "" -> [] | s -> String.get s 0 :: explode (String.sub s 1 ((String.length s) - 1)) let dedup lst = let f elem to_keep = if is_elem elem to_keep then to_keep else elem::to_keep in List.fold_right f lst [] ``` ### Formula evaluation In the process of searching for substitutions for a formula that indicate that the formula is not a tautology, we will want to evaluate a formula for a given substitution. This is very much like the expression evaluators that we created for homework 4, except now the environment (or substitution as we've called it here) is a part of the input of the evaluation process. This is because our formulas have variables, here called propositions, but they don't have let-clauses that would introduce binding into an environment. So that complete environment/substitution must be passed in. Write a function named ``eval`` with the type ``formula -> subst -> bool`` that evaluates the input formula using the input substitution to return a value of ``true`` or ``false``. In case a proposition occurs in the formula that does not appear in the substitution, just raise an exception. The tests run on your code will only provide proper substitutions (no missing binding) to evaluations. For example: ``` eval (And ( Prop "P", Prop "Q")) [("P",true); ("Q",false)] ``` will evaluate to ``false`` and ``` eval (And ( Prop "P", Prop "Q")) [("P",true); ("Q",true)] ``` will evaluate to ``true``. ### "Freevars" for formulas The search space for our tautology checker is the set of substitutions for the propositions in a formula. Thus we must determine what variables are used in a formula. This is reminiscent of the ``freevars`` function we wrote for expressions in homework 4. Write a function ``freevars`` with the type ``formula -> string list`` that returns the names of all the propositions in the formula. Be sure to remove duplicates since when we create substitutions based on this list we cannot have more than one binding for the same propositional variable. Here are a few evaluations. ``` freevars (And ( Prop "P", Prop "Q")) ``` evaluates to ``["P"; "Q"]`` and ``` freevars (And ( Prop "Q", Prop "P")) ``` evaluates to ``["Q"; "P"]``. But order doesn't matter. Here are a few examples of possible tests that may be run on your solution: ``` List.exists ( (=) "P" ) (freevars (And ( Prop "P", Prop "Q"))) = true List.length (freevars (And ( Prop "P", Or (Prop "Q", Prop "P")))) = 2 ``` ### Tautology Checker Given the types and functions defined above we can now get to writing our tautology checker. The function will be named ``is_tautology`` and will have the type ``formula -> (subst -> subst option) -> subst option``. The first argument is, not surprisingly, the formula we wish to check. The second argument is a function similar to the ``process_solution`` functions we used in the implementations of ``subsetsum``. This function is called when the search process finds a substitution for which the formula evaluates to ``false``. The function gets this substitution as input and can decide to accept it (perhaps by interacting with the user) and in that case returns that substitution in a ``Some`` value. If the function does not accept the solution, it should raise the ``KeepLooking`` exception. We could then use ``is_tautology`` in some interesting ways, just like we did for ``subsetsum`` in lecture. First, we could define the following to return the first substitution that is found: ``` let is_tautology_first f = is_tautology f (fun s -> Some s) ``` Second, we could define the following to print out all substitutions and always raise the ``KeepLooking`` exception. It will then print all substitutions that make the formula evaluate to ``false`` and the finally return ``None`` as the value of the call to ``is_tautology_print_all``. ``` let is_tautology_print_all f = is_tautology f (fun s -> print_endline (show_subst s); raise KeepLooking) ``` For the formula ``` Or (Prop "P", Not (Prop "P")) ``` your tautology checker should find that it is a tautology and never generate a substitution. For the formula ``` Or (Prop "P", Prop "Q") ``` your tautology checker should find that it is not a tautology and generate (among others) the substitution ``` [ ("Q",false); ("P",false) ] ``` or ``` [ ("P",false); ("Q",false) ] ``` since the order in which binding appear in the substitution does not matter. For the formula ``` And ( Prop "P", Prop "Q") ``` your tautology checker should find that it is not a tautology and generate 3 substitutions (if they are all demanded) that make the formula false. Try ``` is_tautology_print_all (And (Prop "P", Prop "Q")) ``` and make sure you see the proper 3 substitutions. Your functions will also be checked that they conform to the proper style and use exceptions as described in class to implement backtracking search. ## Part 2: Maze Runner Consider the maze shown below, where "S" marks the start position and "G" marks the two goal positions. Maze Your task is to write a function ``maze`` that returns the first path from an "S" position to one of the "G" positions that is found. To keep this computation from happening when we load the file into utop, the function should take ``()`` the unit value as input and return a list of pairs on integer representing a path, wrapped up in an ``option`` type. This function will have some things in common with the wolf-goat-cabbage problem that we solved in class, so make sure you understand that before attempting this one. It is suggested that you write a function ``maze_moves`` that takes a position as input with the type ``int * int`` and then returns a list of positions, with type ``(int * int) list`` that are all the positions that could be moved to, while respecting the boundaries and lines in the maze. For example, one cannot move from position ``(3,1)`` to position ``(4,1)``. You can implement this with a rather large ``match`` expression that just hard-codes the moves seen in the image above. For example, your solution may execute the search in an order so that ``maze ()`` evaluates to ``` Some [ (2,3); (1,3); (1,2); (2,2); (3,2); (3,3); (3,4); (4,4); (4,5); (3,5) ] ``` Or, your ``maze`` function might instead return the path ``` Some [ (2,3); (1,3); (1,2); (2,2); (3,2); (3,3); (4,3); (5,3); (5,2); (5,1) ] ``` Thus, the type of ``maze`` must be ``` unit -> (int * int) list option ``` Your maze runner solution should use the same ``KeepLooking`` exception to indicate when a deadend in the search has been reached or when some other reason exists to keep looking for solutions. Your functions will also be checked that they conform to the proper style and use exceptions as described in class to implement backtracking search.