search-algorithms-0.1.0: test/Algorithm/SearchSpec.hs
module Algorithm.SearchSpec (
main,
spec
) where
import Test.Hspec
import Algorithm.Search
import qualified Data.Map as Map
main :: IO ()
main = hspec spec
-- | Example cyclic directed unweighted graph
cyclicUnweightedGraph :: Map.Map Int [Int]
cyclicUnweightedGraph = Map.fromList [
(0, [1, 2, 3]),
(1, [4, 6]),
(2, [0, 1, 6, 8]),
(3, [1, 2]),
(4, [0]),
(5, [4]),
(6, [4]),
(8, [0, 5])
]
-- | Example acyclic directed unweighted graph
acyclicUnweightedGraph :: Map.Map Int [Int]
acyclicUnweightedGraph = Map.fromList [
(0, [1, 2, 3]),
(1, [4]),
(2, [5]),
(3, [2]),
(4, []),
(5, [])
]
-- | Example cyclic directed weighted graph
cyclicWeightedGraph :: Map.Map Char [(Int, Char)]
cyclicWeightedGraph = Map.fromList [
('a', [(1, 'b'), (2, 'c')]),
('b', [(1, 'a'), (2, 'c'), (5, 'd')]),
('c', [(1, 'a'), (2, 'd')]),
('d', [])
]
-- | Example for taxicab path finding
taxicabNeighbors :: (Int, Int) -> [(Int, Int)]
taxicabNeighbors (x, y) = [(x, y + 1), (x - 1, y), (x + 1, y), (x, y - 1)]
isWall :: (Int, Int) -> Bool
isWall(x,y)=x==1&&(-2)<=y&&y<=1
taxicabDistance :: (Int, Int) -> (Int, Int) -> Int
taxicabDistance (x1, y1) (x2, y2) = abs (x2 - x1) + abs (y2 - y1)
spec :: Spec
spec = do
describe "bfs" $ do
it "performs breadth-first search" $
bfs (cyclicUnweightedGraph Map.!) [] (== 4) 0
`shouldBe` Just [1, 4]
it "handles pruning" $
bfs (cyclicUnweightedGraph Map.!) [odd] (== 4) 0
`shouldBe` Just [2, 6, 4]
it "returns Nothing when no path is possible" $
bfs (cyclicUnweightedGraph Map.!) [odd, (== 6)] (== 4) 0
`shouldBe` Nothing
describe "dfs" $ do
it "performs depth-first search" $
dfs (cyclicUnweightedGraph Map.!) [] (== 4) 0
`shouldBe` Just [3, 2, 8, 5, 4]
it "handles pruning" $
dfs (cyclicUnweightedGraph Map.!) [odd] (== 4) 0
`shouldBe` Just [2, 6, 4]
it "returns Nothing when no path is possible" $
dfs (cyclicUnweightedGraph Map.!) [odd, (== 6)] (== 4) 0
`shouldBe` Nothing
it "handles doubly-inserted nodes" $
dfs (acyclicUnweightedGraph Map.!) [] (==4) 0
`shouldBe` Just [1, 4]
describe "dijkstra" $ do
it "performs dijkstra's algorithm" $
dijkstra (cyclicWeightedGraph Map.!) [] (== 'd') 'a'
`shouldBe` Just (4, [(2, 'c'), (2, 'd')])
it "handles pruning" $
dijkstra (cyclicWeightedGraph Map.!) [(== 'c')] (== 'd') 'a'
`shouldBe` Just (6, [(1, 'b'), (5, 'd')])
it "returns Nothing when no path is possible" $
dijkstra (cyclicWeightedGraph Map.!) [(== 'b'), (== 'c')] (== 'd') 'a'
`shouldBe` Nothing
it "handles zero-length solutions" $
dijkstra (cyclicWeightedGraph Map.!) [] (== 'd') 'd'
`shouldBe` Just (0, [])
describe "aStar" $ do
let start = (0, 0)
end = (2, 0)
next =
map (\pt -> (1, taxicabDistance pt end, pt))
. taxicabNeighbors
it "performs the A* algorithm" $
aStar next [] (== end) start
`shouldBe` Just (2, [(1, (1, 0)), (1, (2, 0))])
it "handles pruning" $
aStar next [isWall] (== end) start
`shouldBe` Just (6, [(1, (0, 1)),
(1, (0, 2)),
(1, (1, 2)),
(1, (2, 2)),
(1, (2, 1)),
(1, (2, 0))
])
it "returns Nothing when no path is possible" $
aStar next [isWall, \ p -> taxicabDistance p (0,0) > 1] (== end) start
`shouldBe` Nothing