import Data.Array import Control.Concurrent.STM import Control.Concurrent import Control.Monad ( forM, forever, void ) import System.Random import System.IO type Pos = (Int,Int) type Dir = Int data Ant = Ant { position :: TVar Pos , direction :: TVar Dir , moves :: TVar Int } data Cell = Cell { occupied :: TVar Bool } type Board = Array Pos Cell data World = World { size :: Pos , board :: Board , population :: [ Ant ] , generator :: TVar StdGen } ------------------------------------------------------------------- main :: IO () main = do w <- make_world (20,20) 10 forM ( population w ) $ \ ant -> forkIO $ forever $ walk w ant forever $ do pos <- snapshot w threadDelay $ 10^6 -- microseconds info :: Ant -> STM String info ant = do pos <- readTVar $ position ant dir <- readTVar $ direction ant mov <- readTVar $ moves ant return $ unwords [ "pos", show pos, "dir", show dir, "moves", show mov ] snapshot :: World -> IO () snapshot w = do infos <- atomically $ forM ( population w ) $ info putStrLn $ unlines infos --------------------------------------------------------------------------- -- | verschiebe in gegebene Richtung, -- mit wrap-around an den Rändern (d.h. Torus) shift :: (Int,Int) -> Pos -> Dir -> Pos shift (width,height) (x,y) d = let (dx,dy) = vector d in ( mod (x+dx) width, mod (y+dy) height ) vector :: Dir -> Pos vector d = case mod d 8 of 0 -> ( 1,0) ; 1 -> ( 1, 1) ; 2 -> (0, 1) ; 3 -> (-1, 1) 4 -> (-1,0) ; 5 -> (-1,-1) ; 6 -> (0,-1) ; 7 -> ( 1,-1) ------------------------------------------------------------------------------- randomRT :: Random a => TVar StdGen -> (a,a) -> STM a randomRT ref bnd = do g <- readTVar ref let (x, g') = randomR bnd g writeTVar ref g' return x random_selection :: TVar StdGen -> Int -> [a] -> STM [a] random_selection ref 0 xs = return [] random_selection ref k xs = do ( pre, y : post ) <- random_split ref xs ys <- random_selection ref (k-1) ( pre ++ post ) return $ y : ys random_split :: TVar StdGen -> [a] -> STM ( [a], [a] ) random_split ref xs = do k <- randomRT ref ( 0, length xs - 1 ) return $ splitAt k xs ------------------------------------------------------------------ make_world :: (Int,Int) -> Int -> IO World make_world (width,height) num_ants = do b <- make_board (width, height) gen <- newStdGen ref <- atomically $ newTVar gen ants <- make_ants ref b num_ants return $ World { size = (width, height), board = b, population = ants , generator = ref } make_board :: (Int,Int) -> IO Board make_board (width,height) = do let bnd = ((0,0),(width-1,height-1)) cells <- forM ( range bnd ) $ \ xy -> do occ <- atomically $ newTVar False return (xy, Cell { occupied = occ } ) return $ array bnd cells make_ants :: TVar StdGen -> Board -> Int -> IO [ Ant ] make_ants ref b num_ants = atomically $ do sel <- random_selection ref num_ants $ indices b forM sel $ \ pos -> do p <- newTVar pos enter $ b ! pos dir <- randomRT ref ( 0, 7 ) d <- newTVar dir m <- newTVar 0 return $ Ant { position = p, direction = d, moves = m }