Allure-0.4.2: src/AreaRnd.hs
module AreaRnd where
import qualified Data.List as L
import qualified Data.Set as S
import qualified System.Random as R
import Geometry
import Area
import Random
xyInArea :: Area -> Rnd (X, Y)
xyInArea (x0, y0, x1, y1) = do
rx <- randomR (x0, x1)
ry <- randomR (y0, y1)
return (rx, ry)
connectGrid' :: (X, Y) -> S.Set (X, Y) -> S.Set (X, Y) -> [((X, Y), (X, Y))] ->
Rnd [((X, Y), (X, Y))]
connectGrid' (nx, ny) unconnected candidates acc
| S.null candidates = return (L.map normalize acc)
| otherwise = do
c <- oneOf (S.toList candidates)
-- potential new candidates:
let ns = S.fromList $ neighbors (0, 0, nx-1, ny-1) c
nu = S.delete c unconnected -- new unconnected
-- (new candidates, potential connections):
(nc, ds) = S.partition (`S.member` nu) ns
new <- if S.null ds
then return id
else do
d <- oneOf (S.toList ds)
return ((c, d) :)
connectGrid' (nx, ny) nu (S.delete c (candidates `S.union` nc)) (new acc)
connectGrid :: (X, Y) -> Rnd [((X, Y), (X, Y))]
connectGrid (nx, ny) = do
let unconnected = S.fromList [ (x, y) | x <- [0..nx-1], y <- [0..ny-1] ]
-- candidates are neighbors that are still unconnected; we start with
-- a random choice
rx <- randomR (0, nx-1)
ry <- randomR (0, ny-1)
let candidates = S.fromList [ (rx, ry) ]
connectGrid' (nx, ny) unconnected candidates []
randomConnection :: (X, Y) -> Rnd ((X, Y), (X, Y))
randomConnection (nx, ny) = do
rb <- randomR (False, True)
if rb
then do
rx <- randomR (0, nx-2)
ry <- randomR (0, ny-1)
return (normalize ((rx, ry), (rx+1, ry)))
else do
ry <- randomR (0, ny-2)
rx <- randomR (0, nx-1)
return (normalize ((rx, ry), (rx, ry+1)))
data HV = Horiz | Vert
deriving (Eq, Show, Bounded)
fromHV :: HV -> Bool
fromHV Horiz = True
fromHV Vert = False
toHV :: Bool -> HV
toHV True = Horiz
toHV False = Vert
instance R.Random HV where
randomR (a, b0) g = case R.randomR (fromHV a, fromHV b0) g of
(b, g') -> (toHV b, g')
random = R.randomR (minBound, maxBound)
-- | Create a corridor, either horizontal or vertical, with
-- a possible intermediate part that is in the opposite direction.
mkCorridor :: HV -> ((X, Y), (X, Y)) -> Area -> Rnd [(X, Y)] {- straight sections of the corridor -}
mkCorridor hv ((x0, y0), (x1, y1)) b =
do
(rx, ry) <- xyInArea b
-- (rx, ry) is intermediate point the path crosses
-- hv decides whether we start in horizontal or vertical direction
case hv of
Horiz -> return [(x0, y0), (rx, y0), (rx, y1), (x1, y1)]
Vert -> return [(x0, y0), (x0, ry), (x1, ry), (x1, y1)]
-- | Try to connect two rooms with a corridor.
-- The condition passed to mkCorridor is tricky; there might not always
-- exist a suitable intermediate point if the rooms are allowed to be close
-- together ...
connectRooms :: Area -> Area -> Rnd [(X, Y)]
connectRooms sa@(_, _, sx1, sy1) ta@(tx0, ty0, _, _) =
do
(sx, sy) <- xyInArea sa
(tx, ty) <- xyInArea ta
let xok = sx1 < tx0 - 3
let xarea = normalizeArea (sx1+2, sy, tx0-2, ty)
let yok = sy1 < ty0 - 3
let yarea = normalizeArea (sx, sy1+2, tx, ty0-2)
let xyarea = normalizeArea (sx1+2, sy1+2, tx0-2, ty0-2)
(hv, area) <- if xok && yok then fmap (\ hv -> (hv, xyarea)) (binaryChoice Horiz Vert)
else if xok then return (Horiz, xarea)
else return (Vert, yarea)
mkCorridor hv ((sx, sy), (tx, ty)) area