Here's a adjacency-list representation for graphs in Haskell:
type Graph a = [(a, [a])] adjacent :: Eq a => Graph a -> a -> [a] adjacent graph v = fromJust (lookup v graph)
For example, here's an undirected graph and its representation:
graph :: Graph Char graph = [ ('a', "ce"), ('b', "de"), ('c', "adf"), ('d', "bcef"), ('e', "bdf"), ('f', "cdeg"), ('g', "f") ]
-- Return a list of visited vertices in the order they were visited. dfs :: Eq a => Graph a -> a -> [a] dfs graph start = reverse (visit [start] start) where visit visited v = let ns = adjacent graph v \\ visited in foldl visit (ns ++ visited) ns
-- Return a list of visited vertices in the order they were visited. dfs1 :: Eq a => Graph a -> a -> [a] dfs1 graph start = reverse (loop [start] [start]) where loop visited [] = visited loop visited (v : vs) = let ns = adjacent graph v \\ visited in loop (ns ++ visited) (ns ++ vs)
This function is very similar to the depth-first search function in the previous section. The only code difference is highlighted below.
-- return list of vertices visited, in order bfs :: Eq a => Graph a -> a -> [a] bfs graph start = reverse (loop [start] [start]) where loop visited [] = visited loop visited (v : vs) = let ns = adjacent graph v \\ visited in loop (ns ++ visited) (vs ++ ns)
In the function below, the queue holds a list of paths. Each path is a list of vertices from the start vertex to a vertex v, in reverse order.
-- return a list of vertices along the shortest path from start to end bfs1 :: Eq a => Graph a -> a -> a -> Maybe [a] bfs1 graph start end = loop [start] [[start]] where loop visited [] = Nothing loop visited ((v : vs) : ps) | v == end = Just (reverse (v : vs)) | otherwise = let ns = adjacent graph v \\ visited in loop (ns ++ visited) (ps ++ [n : v : vs | n <- ns])
We worked on implementing the fox and hounds game, which is exercise #7 on this page. We solved parts (a) - (e) in the tutorial, and will continue with the rest of the exercise next week.
-- (x, y) position, indexed from 0 type Pos = (Int, Int) data Player = Fox | Hounds type Game = (Player, Pos, [Pos]) -- turn, fox pos, hounds pos start :: Game start = (Fox, (0, 7), [(x, 0) | x <- [1, 3, 5, 7]]) show_game :: Game -> String show_game (_, fox, hounds) = let str (x, y) | (x, y) == fox = "F " | elem (x, y) hounds = "H " | odd (x + y) = ". " | otherwise = " " line y = concat [str (x, y) | x <- [0 .. 7]] in unlines (map line [0 .. 7]) type Move = (Pos, Pos) validPos :: Pos -> Bool validPos (x, y) = 0 <= x && x < 8 && 0 <= y && y < 8 possible :: Game -> [Move] possible (Fox, (x, y), hounds) = [((x, y), (x1, y1)) | x1 <- [x + 1, x - 1], y1 <- [y + 1, y - 1], validPos (x1, y1), notElem (x1, y1) hounds] possible (Hounds, fox, hounds) = [((x, y), (x1, y1)) | (x, y) <- hounds, x1 <- [x + 1, x - 1], let y1 = y + 1, validPos (x1, y1), notElem (x1, y1) (fox : hounds)] move :: Game -> Move -> Game move (Fox, fox, hounds) (from, to) = (Hounds, to, hounds) move (Hounds, fox, hounds) (from, to) = (Fox, fox, to : delete from hounds)