topkata-0.0: Topkata/World/Build.hs
module Topkata.World.Build where
import Topkata.World.Base
import Data.Array
import Data.Ix
import System.Random
import Data.Maybe
import Data.List
import Control.Monad
import Debug.Trace
neighborSide SideFront = SideBack
neighborSide SideBack = SideFront
neighborSide SideLeft = SideRight
neighborSide SideRight = SideLeft
neighborSide SideTop = SideBottom
neighborSide SideBottom = SideTop
neighborPos pos@(x,y,z) _ SideBack | z > 1 = Just (x, y, z-1)
neighborPos pos@(x,y,z) (_, _, mz) SideFront | z < mz = Just (x, y, z+1)
neighborPos pos@(x,y,z) _ SideLeft | x > 1 = Just (x-1, y, z)
neighborPos pos@(x,y,z) (mx, _, _) SideRight | x < mx = Just (x+1, y, z)
neighborPos pos@(x,y,z) _ SideBottom | y > 1 = Just (x, y-1, z)
neighborPos pos@(x,y,z) (_, my, _) SideTop | y < my = Just (x, y+1, z)
neighborPos _ _ _ = Nothing
neighborUpdate world p side ws =
case np of
Nothing -> Just world
Just np ->
let nb = world ! np
nb' = nb // [(side, ws)]
in
if (nb ! ns) == NotSet
then Just (world // [(np, nb')])
else if (nb ! ns) == ws
then Just world
else Nothing
where np = neighborPos p (snd $ bounds world) ns
ns = neighborSide side
shuffle g list = map snd $ sort $ zip (randomRs (maxBound::Int, minBound) g) list
makeWorld :: (Int, Int, Int) -> World
inWorld world (x, y, z) =
minx <= x && x <= maxx &&
miny <= y && y <= maxy &&
minz <= z && z <= maxz
where ((minx, miny, minz), (maxx, maxy, maxz)) = bounds world
isExtendPos world pos =
inWorld world pos && null (world ! pos)
mapIndexed :: (a -> b) -> [a] -> [(a, b)]
mapIndexed f l = zip l (map f l)
makeWorld size =
aux g world init []
where aux g world [] [] = trace "empty" world
aux g world [] acc =
trace ("shuffle " ++ show acc) aux g1 world (shuffle g2 $ nub acc) []
where (g1, g2) = split g
aux g world (pos:t) acc =
if null extPos
then trace ("delete: " ++ show pos) $ aux g world t acc
else trace ("extend: " ++ show pos ++ " " ++ show extPos) $ aux g2 world' t acc''
where extPos = filter (isExtendPos world . snd) $ mapIndexed (nextPos pos) allSides
(side, pos') = head $ shuffle g1 extPos
nside = neighborSide side
(g1, g2) = split g
world' = world // [(pos, side:(world ! pos)), (pos', [nside])]
acc' = pos' : acc
acc'' = if length extPos > 1 then pos: acc' else acc
init = [origin]
g = mkStdGen (0::Int)
world = trace "happy new world" $ emptyWorld size
nextPos :: Pos -> BlockSide -> Pos
nextPos (x, y, z) SideBack = (x, y, z+1)
nextPos (x, y, z) SideFront = (x, y, z-1)
nextPos (x, y, z) SideRight = (x+1, y, z)
nextPos (x, y, z) SideLeft = (x-1, y, z)
nextPos (x, y, z) SideBottom = (x, y-1, z)
nextPos (x, y, z) SideTop = (x, y+1, z)