Homework 4: Graph algorithms

CIS 194: Homework 4
Due Tuesday, September 27

The general remarks about style and submittion from the first week still apply.

Exercise 0: Import the list of mazes

We have collected your submitted mazes from last week. You can download the code of the mazes. It contains a list of mazes (mazes) and a longer list including broken mazes (extraMazes). Paste them into your file, at the very end.

Because the starting position is relevant, we added a data type to go along with the maze:

data Maze = Maze Coord (Coord -> Tile) 
mazes :: List Maze
mazes =extraMazes :: List Maze
extraMazes =

Exercise 1: More polymorphic list function

Implement these generally useful functions:

elemList :: Eq a => a -> List a -> Bool
appendList :: List a -> List a -> List a
listLength :: List a -> Integer
filterList :: (a -> Bool) -> List a -> List a
nth :: List a -> Integer -> a

These should do what their name and types imply:

(Read exercise 3 first, to have understand why this is an interesting function.)

The algorithm you have to implement below can be phrased very generally, and we want it to be general. So implement a function

isGraphClosed :: Eq a => a -> (a -> List a) -> (a -> Bool) -> Bool

so that in a call isGraphClosed initial adjacent isOk, where the parameters are

the function returns True if all reachable nodes are “ok” and False otherwise.

Note that the graph described by adjacent can have circles, and you do not want your program to keep running in circles. So you will have to remember what nodes you have already visted.

The algorithm follows quite naturally from handling the various cases in a local helper function go that takes two arguments, namely a list of seen nodes and a list of nodes that need to be handled. If the latter list is empty, you are done. If it is not empty, look at the first entry. Ignore it if you have seen it before. Otherwise, if it is not ok, you are also donw. Otherwise, add its adjacent elements to the list of nodes to look ak.

You might find it helpful to define a list allDirections :: List Direction and use mapList and filterList when implementing adjacent.

Exercise 3: Check closedness of mazes

Write a function

isClosed :: Maze -> Bool

that checks whether the maze is closed. A maze is closed if

Use isGraphClosed to do the second check. Implement adjacent so that isGraphClosed walks everywhere where there is not a Wall (including Blank). Implement isOk so that Blank tiles are not ok.

With the following function you can visualize a list of booleans:

pictureOfBools :: List Bool -> Picture
pictureOfBools xs = translated (-fromIntegral k /2) (fromIntegral k) (go 0 xs)
  where n = listLength xs
        k = findK 0 -- k is the integer square of n
        findK i | i * i >= n = i
                | otherwise  = findK (i+1)
        go _ Empty = blank
        go i (Entry b bs) =
          translated (fromIntegral (i `mod` k))
                     (-fromIntegral (i `div` k))
                     (pictureOfBool b)
          & go (i+1) bs

        pictureOfBool True =  colored green (solidCircle 0.4)
        pictureOfBool False = colored red   (solidCircle 0.4)

Let exercise3 :: IO () be the visualization of isClosed applied to every element of extraMazes. Obviously, mapList wants to be used here.

Exercise 4: Multi-Level Sokoban

Extend your game from last week (or the code from the lecture) to implement multi-level sokoban.

Let exercise4 :: IO () be this interaction, wrapped in withUndo, withStartScreen and resetable.