packages feed

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)