topkata-0.2: Labygen.hs
module Labygen where
import Data.Ix
import Data.List
import Data.Array
import System.Random
import Debug.Trace
data Block = Block | Free deriving (Show, Eq, Ord)
class Ix a => WorldIdx a where
adjacent :: (a, a) -> a -> [a]
borders :: (a, a) -> a -> [a]
type World p = Array p Block
newtype Pos2 = Pos2 (Int, Int) deriving (Ix, Ord, Eq, Show, Read)
newtype Pos3 = Pos3 (Int, Int, Int) deriving (Ix, Ord, Eq, Show, Read)
instance WorldIdx Pos2 where
adjacent bnds (Pos2 (x,y)) =
[ e | e <- map Pos2 [(x-1,y), (x+1,y), (x,y-1), (x,y+1)], inRange bnds e]
borders bnds p@(Pos2 (x,y)) =
adjacent bnds p ++
[ e | e <- map Pos2 [(x-1,y-1), (x+1,y-1), (x-1,y+1), (x+1,y+1)], inRange bnds e]
instance WorldIdx Pos3 where
adjacent bnds (Pos3 (x,y,z)) =
[ e | e <- map Pos3 [(x-1,y,z), (x+1,y,z), (x,y-1,z), (x,y+1,z), (x,y,z-1), (x,y,z+1)], inRange bnds e]
borders bnds p@(Pos3 (x,y,z)) =
adjacent bnds p ++
[ e | e <- map Pos3 [(x-1,y-1,z-1), (x-1,y-1,z+1), (x-1,y+1,z-1), (x-1,y-1,z+1),
(x-1,y,z-1), (x-1,y,z+1), (x+1,y,z-1), (x+1,y,z+1),
(x+1,y-1,z-1), (x+1,y-1,z+1), (x+1,y+1,z-1), (x+1,y-1,z+1)], inRange bnds e]
instance Random Pos3 where
random g = (Pos3 (x, y, z), g'')
where (g', g'') = split g
x:y:z:_ = randoms g'
randomR bnds g = (range bnds !! idx, g')
where size = rangeSize bnds
(idx, g') = randomR (0, size-1) g
shuffle g list = map snd $ sort $ zip (randomRs (maxBound::Int, minBound) g) list
-- labygen :: WorldIdx p => a -> b -> c -> p -> Either (World p) (World p)
labygen g bounds origin target =
aux g [(origin,[origin])] laby False
where laby = array bounds [ (ix, Block) | ix <- range bounds ]
aux g [] laby True = Right laby
aux g [] laby False = Left laby
aux g ((p,slist):t) laby ok | canNotExtend laby slist p = aux g t laby ok
| otherwise = aux g2 paths laby' ok'
where ok' = ok || p == target
laby' = laby // [ (p, Free) ]
adj = adjacent bounds p
paths | target `elem` adj = (target, (p:adj)) : t
| otherwise = shuffle g1 (zip adj (repeat (p:adj)) ++ t)
(g1, g2) = split g
canNotExtend laby slist p = length [ a | a <- borders bounds p, laby ! a == Free, not (a `elem` slist)] > 0
tr p s a = trace ("p=" ++ show p ++ ", a=" ++show a ++ ", slist=" ++ show s ) a
labygenIO bounds origin target =
rep $ \g ->labygen g bounds origin target
where
rep f = do
g <- newStdGen
case f g of
Right a -> return a
Left _ -> rep f
inWall laby x y z =
{- trace (show p) $ -} not (inRange bnds p) ||
laby ! p == Block
where bnds = bounds laby
p = Pos3 ((truncate x), (truncate y), (truncate z))
randomFreePos laby g =
if laby ! pos == Block
then randomFreePos laby g'
else result
where result@(pos, g') = randomR (bounds laby) g
randomFreePosIO laby = do
g <- newStdGen
return $ fst $ randomFreePos laby g