Laziness

Complete this assignment with the same team you worked with for Substitution (Written). You and your partner must each understand the answers to all the problems, so don't just split up the work.

For this assignment, we will use the Haskell programming language. The Glasgow Haskell Compiler (GHC) is freely available for most platforms and is installed on the CS department network. To start the interactive evaluator, simply run ghci.

Important Note: Yes, we realize that you don't know any Haskell. We expect that you'll spend much of your time on this assignment learning Haskell. Learning a new language shouldn't be that big of a deal. You are taking a programming languages course, after all.

Haskell has wonderful standard libraries which probably contain the function you're looking for. To see tree-style browsable docs, check out the Haskell Hierarchical Libraries . You should particularly take advantage of the Prelude , which is the set of functions that are "built in" to Haskell. You're welcome to use other functions; if you want to do so, you must put an import statement at the top of your code (there's an example in the template). Hoogle is a useful search engine for the Haskell API.

The following are library functions that you may find particularly helpful, and which are all in the Haskell Prelude :

Haskell Function Scheme Analog == eq? <, >, <=, >= <, >, <=, >= mod modulo div quotient not not !! list-ref filter filter all andmap any ormap take (returns a prefix of a list) takeWhile (returns the prefix of a list satisfying a predicate)

Any function whose name begins with a symbolic character is an infix operator. Other binary functions can be used as infix operators by enclosing in backquotes (e.g., x `mod` y ). Also, infix operators can be used as ordinary functions by enclosing them in parentheses (e.g., (!!) [1, 2, 3] 2 ).

Please submit a README file explaining how you tested your code. Actual test cases are preferable, but a clear summary of what you did will be sufficient as long as your code actually works as you claim it does.

Problem 1: Prime Numbers

Write isPrime :: Integer -> Bool , which determines whether a given integer is prime. Define primes :: [Integer] , the list of all primes. Revise isPrime so that it only tests divisibility by prime factors. Just turn in the revised version.

Problem 2: Longest Common Subsequence

Write buildList :: Int -> (Int -> a) -> [a] , where ((buildList n f) !! i) == (f i) (for all i in [0 .. n-1]). Write buildTable :: Int -> Int -> (Int -> Int -> a) -> [[a]] , where (((buildTable n m f) !! i) !! j) == (f i j) (for all i in [0 .. n-1], j in [0 .. m-1]). Write lcsLength :: String -> String -> Int , which quickly computes the length of the longest common subsequence of two strings s1 and s2. Hint: you can easily compute lcsLength (take i s1) (take j s2) from lcsLength (take (i-1) s1) (take j s2), lcsLength (take i s1) (take (j-1) s2), and lcsLength (take (i-1) s1) (take (j-1) s2). and the knowledge of whether (s1 !! i) == (s2 !! j) . Note that you will lose credit if you use a slow, "brute force" method; make use of your table!

Problem 3: Minimax Search

In this exercise, you will implement a strategy to play a simple game. The game is called Mancala, but you won't need to worry about the specific rules, since we have implemented that part for you. Your job is to build a tree of possible move sequences and choose the move that appears best.

The support code below provides the following set of data types and functions:

Player : values of this type represent the players of the game ( PlayerA or PlayerB ).

: values of this type represent the players of the game ( or ). State : values of this type represent game configurations.

: values of this type represent game configurations. GameValue : values of this type represent scores used to evaluate the game state. Since GameValue is an instance of Ord , GameValues can be compared using standard comparison operators.

: values of this type represent scores used to evaluate the game state. Since is an instance of , GameValues can be compared using standard comparison operators. initialState :: Player -> State represents the initial configuration of the game board (the given player goes first).

represents the initial configuration of the game board (the given player goes first). getPlayer: State -> Player : given a configuration, returns the player who makes the next move.

: given a configuration, returns the player who makes the next move. gameValue :: State -> GameValue returns Player A's score minus Player B's score. (Player A wants a big value and Player B wants a small value.)

returns Player A's score minus Player B's score. (Player A wants a big value and Player B wants a small value.) nextStates :: State -> [State] gives the possible configurations after the next move. If the returned list is empty, then the game is over.

gives the possible configurations after the next move. If the returned list is empty, then the game is over. simulateGame :: IO () uses an IO loop to allow you to play Mancala. The valid moves are the numbers 1 through 6 and they indicate the player playing the nth cup on her side. Player A goes first; her cups are on the top row and are numbered from right to left. Player B is the bottom row numbered from left to right.

uses an IO loop to allow you to play Mancala. The valid moves are the numbers 1 through 6 and they indicate the player playing the nth cup on her side. Player A goes first; her cups are on the top row and are numbered from right to left. Player B is the bottom row numbered from left to right. simulateAIGame :: (Show a, Ord a) => (State -> a) -> IO () consumes a scoring function and plays the game where Player A is the AI using that function. We give you the helper function, level :: Int -> IO () in the stub file, which consumes a difficulty and runs simulateAIGame using your functions. This may be helpful for testing.

Define a datatype GameTree to represent the game state after any sequence of moves. Each node should have its current configuration and a list of trees, where each tree corresponds to the game states obtainable after making any one legal move. Define mkTree :: State -> GameTree , which consumes a state and returns the tree of all legal board configurations (those obtainable by repeated application of nextStates to the given state). Use this to define mancala :: GameTree , the entire game of Mancala as a GameTree (remember that Player A goes first). Define prune :: Int -> GameTree -> GameTree , which prunes a tree to a given depth. Define minimax :: GameTree -> GameValue , which consumes a (pruned) tree and evaluates the configuration by looking ahead and applying the minimax algorithm. If a node has no children, its value is the gameValue of its State . Remember, Player A, Maxi, is always trying to get the maximum value, and Player B, Mini, is always trying to get the minimum value.

Support Code

Note that you must download both files (even for problems 1 and 2), and you must put them both in the same directory. To test your code interactively, first cd into the directory with your code, and then do ghci Laziness.hs at which point you should either see a parse error, type error, or this:

___ ___ _ / _ \ /\ /\/ __(_) / /_\// /_/ / / | | GHC Interactive, version 6.6, for Haskell 98. / /_\\/ __ / /___| | http://www.haskell.org/ghc/ \____/\/ /_/\____/|_| Type :? for help. Loading package base ... linking ... done. [1 of 2] Compiling Game ( Game.hs, interpreted ) [2 of 2] Compiling Laziness ( Laziness.hs, interpreted ) Ok, modules loaded: Game, Laziness. *Laziness>

ghci

supports tab completion, which is a very handy feature.

A Haskell stub file for the problem set: Laziness.hs.

The support code for Problem 3: Game.hs. Remember, you need neither read nor understand this code.

Handin