RosettaCodeData/Task/Maze-generation/Haskell/maze-generation.hs

79 lines
2.3 KiB
Haskell
Raw Permalink Normal View History

2016-12-05 22:15:40 +01:00
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
2013-04-10 21:29:02 -07:00
import Data.Array.ST
2020-02-17 23:21:07 -08:00
(STArray, freeze, newArray, readArray, writeArray)
import Data.STRef (STRef, newSTRef, readSTRef, writeSTRef)
import System.Random (Random(..), getStdGen, StdGen)
import Control.Monad (forM_, unless)
import Control.Monad.ST (ST, stToIO)
import Data.Array (Array, (!), bounds)
import Data.Bool (bool)
2013-04-10 21:29:02 -07:00
2020-02-17 23:21:07 -08:00
rand
:: Random a
=> (a, a) -> STRef s StdGen -> ST s a
2013-04-10 21:29:02 -07:00
rand range gen = do
2020-02-17 23:21:07 -08:00
(a, g) <- randomR range <$> readSTRef gen
gen `writeSTRef` g
return a
2013-04-10 21:29:02 -07:00
2020-02-17 23:21:07 -08:00
data Maze = Maze
{ rightWalls, belowWalls :: Array (Int, Int) Bool
}
2013-04-10 21:29:02 -07:00
maze :: Int -> Int -> StdGen -> ST s Maze
maze width height gen = do
2020-02-17 23:21:07 -08:00
visited <- mazeArray False
rWalls <- mazeArray True
bWalls <- mazeArray True
gen <- newSTRef gen
(,) <$> rand (0, maxX) gen <*> rand (0, maxY) gen >>=
visit gen visited rWalls bWalls
Maze <$> freeze rWalls <*> freeze bWalls
where
visit gen visited rWalls bWalls here = do
writeArray visited here True
let ns = neighbors here
i <- rand (0, length ns - 1) gen
forM_ (ns !! i : take i ns ++ drop (i + 1) ns) $
\there -> do
seen <- readArray visited there
unless seen $
do removeWall here there
visit gen visited rWalls bWalls there
where
removeWall (x1, y1) (x2, y2) =
writeArray (bool rWalls bWalls (x1 == x2)) (min x1 x2, min y1 y2) False
neighbors (x, y) =
bool [(x - 1, y)] [] (0 == x) ++
bool [(x + 1, y)] [] (maxX == x) ++
bool [(x, y - 1)] [] (0 == y) ++ bool [(x, y + 1)] [] (maxY == y)
maxX = width - 1
maxY = height - 1
mazeArray =
newArray ((0, 0), (maxX, maxY)) :: Bool -> ST s (STArray s (Int, Int) Bool)
2013-04-10 21:29:02 -07:00
printMaze :: Maze -> IO ()
printMaze (Maze rWalls bWalls) = do
2020-02-17 23:21:07 -08:00
putStrLn $ '+' : concat (replicate (maxX + 1) "---+")
forM_ [0 .. maxY] $
\y -> do
putStr "|"
forM_ [0 .. maxX] $
\x -> do
putStr " "
putStr $ bool " " "|" (rWalls ! (x, y))
putStrLn ""
forM_ [0 .. maxX] $
\x -> do
putStr "+"
putStr $ bool " " "---" (bWalls ! (x, y))
putStrLn "+"
where
maxX = fst (snd $ bounds rWalls)
maxY = snd (snd $ bounds rWalls)
2013-04-10 21:29:02 -07:00
2020-02-17 23:21:07 -08:00
main :: IO ()
2013-04-10 21:29:02 -07:00
main = getStdGen >>= stToIO . maze 11 8 >>= printMaze