MazesOfMonad-1.0.7: src/MoresmauJP/Maze1/Maze.hs
-- | Maze handling code: generate and move in the maze
-- (c) JP Moresmau 2009
module MoresmauJP.Maze1.Maze
where
import MoresmauJP.Util.Lists
import MoresmauJP.Util.Random
import Data.List
import qualified Data.Map as DataMap
import qualified Data.Set as DataSet
import Data.Maybe
data GameWorld = GameWorld {
maze::Maze
,position::Cell,
explored::CellSet}
deriving (Show, Read,Eq)
type Cell=(Int,Int)
type Size=(Int,Int)
type Edges=[Cell]
type CellMap = DataMap.Map Cell Edges
type CellSet = DataSet.Set Cell
data Maze=Maze {cellmap::CellMap
,start::Cell
,end::Cell
,size::Size
}
deriving (Read,Show,Eq)
isWinning :: GameWorld -> Bool
isWinning gw= (position gw) == (end $ maze gw)
moveInMaze :: GameWorld -> Cell -> Maybe (GameWorld,Bool)
moveInMaze gw toCell |
isMovePossible (position gw) toCell (maze gw) =
let
expls=explored gw
expls2=DataSet.union expls (DataSet.fromList (getNeighbours toCell (maze gw)))
--nub (expls ++ (getNeighbours toCell (maze gw)))
gw2=gw{position=toCell,explored=expls2}
win=isWinning gw2
in Just (gw2,win)
| otherwise = Nothing
randomMove :: (MonadRandom m) => GameWorld -> m GameWorld
randomMove gw=do
let expls=explored gw
toCell <- randomPickp (DataSet.elems (DataSet.delete (position gw) expls))
let expls2=DataSet.union expls (DataSet.fromList (getNeighbours toCell (maze gw)))
return (gw{position=toCell,explored=expls2})
isMovePossible :: Cell -> Cell -> Maze -> Bool
isMovePossible from to mz = elem to (getNeighbours from mz)
getNeighbours :: Cell -> Maze -> Edges
getNeighbours from mz =
fromJust $ DataMap.lookup from (cellmap mz)
generateGameWorld:: (MonadRandom m) =>Size -> m GameWorld
generateGameWorld sz= do
maze <- generateMaze sz
let startCell=start maze
let explored= DataSet.insert startCell (DataSet.fromList (getNeighbours startCell maze))
return GameWorld {maze=maze,position=startCell,explored=explored}
generateMaze :: (MonadRandom m) =>Size -> m Maze
generateMaze sz = do
firstX<-getRandomRange (1,fst sz)
firstY<-getRandomRange (1,snd sz)
--(firstX,firstY)
let m1=DataMap.fromList [((firstX,firstY),[(firstX,firstY)])]
let frontiers=getUnprocessedNeighbours (firstX,firstY) sz m1
let m2=DataMap.union m1 (DataMap.fromList(map (\x->(x,[])) frontiers))
randomFrontiers<-randomHeadp frontiers
(m3,end) <- mazeStep m2 sz randomFrontiers
let m4=removeFirstCell m3 (firstX,firstY)
return (Maze {cellmap=m4,start=(firstX,firstY),end=end,size=sz})
removeFirstCell :: CellMap -> Cell -> CellMap
removeFirstCell cm c=
let
Just l = DataMap.lookup c cm
in
DataMap.insert c (init l) cm
mazeStep :: (MonadRandom m) =>CellMap -> Size -> Edges -> m (CellMap,Cell)
mazeStep _ _ []=error "mazeStep: empty edges"
mazeStep cm sz (f:fs) = do
let nbs=getInNeighbours f sz cm
nb <- randomPickp nbs
let edges=DataMap.lookup nb cm
let cm1=
case edges of
Just l -> DataMap.insert nb (f:l) cm
Nothing -> DataMap.insert nb [f] cm
let cm2 = DataMap.insert f [nb] cm1
let frontiers=getUnprocessedNeighbours f sz cm2
let cm3= DataMap.union cm2 (DataMap.fromList(map (\x->(x,[])) frontiers))
randomFrontiers <- randomHeadp (fs ++ frontiers)
if null randomFrontiers
then
return (cm3,f)
else
mazeStep cm3 sz randomFrontiers
getLeftNeighbour :: Cell -> Edges
getLeftNeighbour (1,_) =[]
getLeftNeighbour (x,y) =[(x-1,y)]
getRightNeighbour :: Cell -> Size -> Edges
getRightNeighbour (x,y) (width,_)
| (x==width) =[]
| otherwise =[(x+1,y)]
getAllNeighbours :: Cell -> Size -> Edges
getAllNeighbours c sz =
(getLeftNeighbour c) ++ (getRightNeighbour c sz)
++ (map swap (getLeftNeighbour (swap c)))
++ (map swap (getRightNeighbour (swap c) (swap sz)))
getUnprocessedNeighbours :: Cell -> Size -> CellMap -> Edges
getUnprocessedNeighbours c sz cm = filter (flip DataMap.notMember cm)(getAllNeighbours c sz)
getInNeighbours :: Cell -> Size -> CellMap -> Edges
getInNeighbours c sz cm = filter (isIn cm) (getAllNeighbours c sz)
where isIn lm key =
let val=DataMap.lookup key lm
in
case val of
Just l ->not (null l)
Nothing ->False
concatRev:: [a] -> [a] -> [a]
concatRev [] l2=l2
concatRev (x:xs) l2=concatRev xs (x:l2)