working 2d animation, but leaks memory
This commit is contained in:
		
							parent
							
								
									b45de145fe
								
							
						
					
					
						commit
						d582c20af3
					
				|  | @ -18,6 +18,8 @@ build-type:          Simple | ||||||
| 
 | 
 | ||||||
| executable cellularAutomata | executable cellularAutomata | ||||||
|   main-is:             Main.hs |   main-is:             Main.hs | ||||||
|  |   ghc-options:       -threaded | ||||||
|  |                      -O2 | ||||||
|   -- other-modules: |   -- other-modules: | ||||||
|   -- other-extensions: |   -- other-extensions: | ||||||
|   build-depends:       base >=4.13 && <4.14 |   build-depends:       base >=4.13 && <4.14 | ||||||
|  | @ -25,6 +27,12 @@ executable cellularAutomata | ||||||
|                      , turtle |                      , turtle | ||||||
|                      , brick |                      , brick | ||||||
|                      , process |                      , process | ||||||
|  |                      , containers | ||||||
|  |                      , linear | ||||||
|  |                      , microlens | ||||||
|  |                      , microlens-th | ||||||
|  |                      , vty | ||||||
|  |                      , deepseq | ||||||
|   hs-source-dirs:      src |   hs-source-dirs:      src | ||||||
|   default-language:    Haskell2010 |   default-language:    Haskell2010 | ||||||
|   extra-libraries:     ncurses |   extra-libraries:     ncurses | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| { mkDerivation, base, brick, lib, ncurses, process, random, turtle | { mkDerivation, base, brick, containers, deepseq, lib, linear | ||||||
|  | , microlens, microlens-th, ncurses, process, random, turtle, vty | ||||||
| }: | }: | ||||||
| mkDerivation { | mkDerivation { | ||||||
|   pname = "cellularAutomata"; |   pname = "cellularAutomata"; | ||||||
|  | @ -6,7 +7,10 @@ mkDerivation { | ||||||
|   src = ./..; |   src = ./..; | ||||||
|   isLibrary = false; |   isLibrary = false; | ||||||
|   isExecutable = true; |   isExecutable = true; | ||||||
|   executableHaskellDepends = [ base brick process random turtle ]; |   executableHaskellDepends = [ | ||||||
|  |     base brick containers deepseq linear microlens microlens-th process | ||||||
|  |     random turtle vty | ||||||
|  |   ]; | ||||||
|   executableSystemDepends = [ ncurses ]; |   executableSystemDepends = [ ncurses ]; | ||||||
|   license = "unknown"; |   license = "unknown"; | ||||||
|   hydraPlatforms = lib.platforms.none; |   hydraPlatforms = lib.platforms.none; | ||||||
|  |  | ||||||
							
								
								
									
										89
									
								
								src/Automata.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Automata.hs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,89 @@ | ||||||
|  | {-# LANGUAGE DeriveGeneric #-} | ||||||
|  | 
 | ||||||
|  | module Automata where | ||||||
|  | 
 | ||||||
|  | import Comonad | ||||||
|  | import Spaces | ||||||
|  | import System.Random | ||||||
|  | import GHC.Generics | ||||||
|  | import Control.DeepSeq | ||||||
|  | 
 | ||||||
|  | ----------------------- | ||||||
|  | -- cellular automata -- | ||||||
|  | ----------------------- | ||||||
|  | 
 | ||||||
|  | -- the states our cells can be in | ||||||
|  | -- may need to provide an ordering | ||||||
|  | -- may need to generalise the number | ||||||
|  | -- of states | ||||||
|  | data CellState = Rock | Paper | Scissors | ||||||
|  |   deriving (Eq, Bounded, Enum, Generic) | ||||||
|  | 
 | ||||||
|  | instance NFData CellState | ||||||
|  | 
 | ||||||
|  | instance Random CellState where | ||||||
|  |     random g = case randomR (fromEnum (minBound :: CellState), fromEnum (maxBound :: CellState)) g of | ||||||
|  |                  (r, g') -> (toEnum r, g') | ||||||
|  |     randomR (a,b) g = case randomR (fromEnum a, fromEnum b) g of | ||||||
|  |                         (r, g') -> (toEnum r, g') | ||||||
|  | 
 | ||||||
|  | -- how the states are displayed on screen | ||||||
|  | -- this should probably be input to a function | ||||||
|  | -- rather than hardcoded | ||||||
|  | instance Show CellState | ||||||
|  |   where | ||||||
|  |     show Rock = "⬤" | ||||||
|  |     show Paper = " " | ||||||
|  |     show Scissors = "_" | ||||||
|  | 
 | ||||||
|  | -- -- a rule stating how a cell is determined | ||||||
|  | -- rule :: Space CellState -> CellState | ||||||
|  | -- rule (Space (l:_) _ (r:_)) | ||||||
|  | --   | l == r = Dead | ||||||
|  | --   | otherwise = Alive | ||||||
|  | --  | ||||||
|  | -- -- a second rule for example | ||||||
|  | -- rule2 :: Space CellState -> CellState | ||||||
|  | -- rule2 (Space (l1:l2:_) m (r1:r2:_)) | ||||||
|  | --   | m == Alive && numAlive == 1 = Dead | ||||||
|  | --   | m == Alive && numAlive == 4 = Dead | ||||||
|  | --   | m == Dead && numAlive == 3 = Alive | ||||||
|  | --   | otherwise = m | ||||||
|  | --   where | ||||||
|  | --     ns = [l1, l2, r1, r2] | ||||||
|  | --     numAlive = length $ filter (== Alive) ns | ||||||
|  | --  | ||||||
|  | -- rule3 :: Space CellState -> CellState | ||||||
|  | -- rule3 (Space (l:_) m (r:_)) | ||||||
|  | --   | l == r = m | ||||||
|  | --   | otherwise = if m == Alive then Dead else Alive | ||||||
|  | 
 | ||||||
|  | -------------- | ||||||
|  | -- 2d rules -- | ||||||
|  | -------------- | ||||||
|  | 
 | ||||||
|  | rps :: Space2 CellState -> CellState | ||||||
|  | rps (Space2 u m d) | ||||||
|  |   = case me of | ||||||
|  |       Rock -> if (length $ filter (== Paper) ns) > 2 then Paper else Rock | ||||||
|  |       Paper -> if (length $ filter (== Scissors) ns) > 2 then Scissors else Paper | ||||||
|  |       Scissors -> if (length $ filter (== Rock) ns) > 2 then Rock else Scissors | ||||||
|  |   where | ||||||
|  |     f b (Space (l:_) m (r:_)) = [l,r] ++ (if b then [m] else []) | ||||||
|  |     f b (Space [] m (r:_)) = [r] ++ (if b then [m] else []) | ||||||
|  |     f b (Space (l:_) m []) = [l] ++ (if b then [m] else []) | ||||||
|  |     f b (Space [] m []) = if b then [m] else [] | ||||||
|  |     safeHead _ [] = [] | ||||||
|  |     safeHead b (x:_) = f b x | ||||||
|  |     ns = concat [ (safeHead True u), (f False m), (safeHead True d) ] | ||||||
|  |     me = extract m | ||||||
|  | 
 | ||||||
|  | --conway :: Space2 CellState -> CellState | ||||||
|  | --conway (Space2 (u:_) m (d:_)) | ||||||
|  | --  = case me of | ||||||
|  | --      Alive -> if (length ns) == 2 || (length ns == 3) then Alive else Dead | ||||||
|  | --      Dead -> if (length ns) == 3 then Alive else Dead | ||||||
|  | --  where | ||||||
|  | --    f b (Space (l:_) m (r:_)) = [l,r] ++ (if b then [m] else []) | ||||||
|  | --    ns = filter (== Alive) $ concat [ (f True u), (f False m), (f True d) ] | ||||||
|  | --    me = extract m | ||||||
							
								
								
									
										12
									
								
								src/Comonad.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Comonad.hs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | module Comonad where | ||||||
|  | 
 | ||||||
|  | ------------------- | ||||||
|  | -- comonad class -- | ||||||
|  | ------------------- | ||||||
|  | 
 | ||||||
|  | class Functor w => Comonad w | ||||||
|  |   where | ||||||
|  |     (=>>)     :: w a -> (w a -> b) -> w b | ||||||
|  |     extract   :: w a -> a | ||||||
|  |     duplicate :: w a -> w (w a) | ||||||
|  |     x =>> f = fmap f (duplicate x) | ||||||
							
								
								
									
										284
									
								
								src/Main.hs
									
									
									
									
									
								
							
							
						
						
									
										284
									
								
								src/Main.hs
									
									
									
									
									
								
							|  | @ -1,3 +1,5 @@ | ||||||
|  | {-# LANGUAGE OverloadedStrings #-} | ||||||
|  | 
 | ||||||
| module Main where | module Main where | ||||||
| 
 | 
 | ||||||
| import Control.Monad | import Control.Monad | ||||||
|  | @ -6,122 +8,45 @@ import System.Random | ||||||
| import System.Console.GetOpt | import System.Console.GetOpt | ||||||
| import System.Environment(getArgs, getProgName) | import System.Environment(getArgs, getProgName) | ||||||
| import Data.Maybe (fromMaybe) | import Data.Maybe (fromMaybe) | ||||||
|  | import Comonad | ||||||
|  | import Spaces | ||||||
|  | import Automata | ||||||
|  | import Brick | ||||||
|  | import Brick.BChan (newBChan, writeBChan) | ||||||
|  | import qualified Brick.Widgets.Border as B | ||||||
|  | import qualified Brick.Widgets.Border.Style as BS | ||||||
|  | import qualified Brick.Widgets.Center as C | ||||||
|  | import qualified Graphics.Vty as V | ||||||
|  | import Control.Applicative | ||||||
|  | import Control.Monad.IO.Class | ||||||
|  | import Control.Concurrent | ||||||
|  | import Control.DeepSeq | ||||||
| 
 | 
 | ||||||
| ------------------- | ----------------- | ||||||
| -- comonad class -- | -- brick stuff -- | ||||||
| ------------------- | ----------------- | ||||||
| 
 | 
 | ||||||
| class Functor w => Comonad w | data Tick = Tick | ||||||
|   where | type Name = () | ||||||
|     (=>>)     :: w a -> (w a -> b) -> w b |  | ||||||
|     extract   :: w a -> a |  | ||||||
|     duplicate :: w a -> w (w a) |  | ||||||
|     x =>> f = fmap f (duplicate x) |  | ||||||
| 
 | 
 | ||||||
| ------------ | -- App definition | ||||||
| -- spaces -- |  | ||||||
| ------------ |  | ||||||
| 
 | 
 | ||||||
| -- a locally focussed space | app :: Int -> Int -> App (Space2 CellState) Tick Name | ||||||
| data Space t = Space [t] t [t] | app h w = App { appDraw = drawUI h w | ||||||
|  |           , appChooseCursor = neverShowCursor | ||||||
|  |           , appHandleEvent = handleEvent | ||||||
|  |           , appStartEvent = return | ||||||
|  |           , appAttrMap = const theMap | ||||||
|  |           } | ||||||
| 
 | 
 | ||||||
| -- spaces are also functors | -- Handling events | ||||||
| instance Functor Space where |  | ||||||
|   fmap f (Space l c r) = Space (map f l) (f c) (map f r) |  | ||||||
| 
 | 
 | ||||||
| -- our space is a comonad | theMap :: AttrMap | ||||||
| instance Comonad Space where | theMap = attrMap V.defAttr | ||||||
|   -- duplicate will create a new space where |   [ (rockAttr, V.red `on` V.blue) | ||||||
|   -- the focussed element is our original space |   , (scissorsAttr, V.green `on` V.red) | ||||||
|   -- and each side is increasingly shifted copies |   , (paperAttr, V.blue `on` V.green) | ||||||
|   -- in that direction |   ] | ||||||
|   duplicate w = |  | ||||||
|     Space (tail $ iterate left w) |  | ||||||
|           w |  | ||||||
|           (tail $ iterate right w) |  | ||||||
|   -- extract simply returns the focussed element |  | ||||||
|   extract (Space _ c _) = c |  | ||||||
| 
 |  | ||||||
| -- functions for moving the point |  | ||||||
| -- of locality. |  | ||||||
| -- todo: question the empty list cases |  | ||||||
| -- most spaces should be infinite |  | ||||||
| right :: Space t -> Space t |  | ||||||
| right s@(Space l c []) = s |  | ||||||
| right (Space l c (r:rs)) = Space (c:l) r rs |  | ||||||
| 
 |  | ||||||
| left :: Space t -> Space t |  | ||||||
| left s@(Space [] c r) = s |  | ||||||
| left (Space (l:ls) c r) = Space ls l (c:r) |  | ||||||
| 
 |  | ||||||
| -- bound will take an infinite space |  | ||||||
| -- and bound it by i and j on each side |  | ||||||
| -- (not including the focus) and |  | ||||||
| -- turn it into a list for printing |  | ||||||
| bound :: Int -> Int -> Space t -> [t] |  | ||||||
| bound i j (Space l c r) = (reverse (take i l)) ++ (c:(take j r)) |  | ||||||
| 
 |  | ||||||
| -- boundw works as above, but the |  | ||||||
| -- entire list will be the size |  | ||||||
| -- given |  | ||||||
| boundw :: Int -> Space t -> [t] |  | ||||||
| boundw n = bound (x-m) x |  | ||||||
|   where |  | ||||||
|     o = if odd n then 1 else 0 |  | ||||||
|     m = if even n then 1 else 0 |  | ||||||
|     x = (n - o) `div` 2 |  | ||||||
| 
 |  | ||||||
| ----------------------- |  | ||||||
| -- cellular automata -- |  | ||||||
| ----------------------- |  | ||||||
| 
 |  | ||||||
| -- the states our cells can be in |  | ||||||
| -- may need to provide an ordering |  | ||||||
| -- may need to generalise the number |  | ||||||
| -- of states |  | ||||||
| data CellState = Alive | Dead |  | ||||||
|   deriving (Eq, Bounded, Enum) |  | ||||||
| 
 |  | ||||||
| instance Random CellState where |  | ||||||
|     random g = case randomR (fromEnum (minBound :: CellState), fromEnum (maxBound :: CellState)) g of |  | ||||||
|                  (r, g') -> (toEnum r, g') |  | ||||||
|     randomR (a,b) g = case randomR (fromEnum a, fromEnum b) g of |  | ||||||
|                         (r, g') -> (toEnum r, g') |  | ||||||
| 
 |  | ||||||
| -- how the states are displayed on screen |  | ||||||
| -- this should probably be input to a function |  | ||||||
| -- rather than hardcoded |  | ||||||
| instance Show CellState |  | ||||||
|   where |  | ||||||
|     show Alive = "█" |  | ||||||
|     show Dead = " " |  | ||||||
| 
 |  | ||||||
| -- a rule stating how a cell is determined |  | ||||||
| rule :: Space CellState -> CellState |  | ||||||
| rule (Space (l:_) _ (r:_)) |  | ||||||
|   | l == r = Dead |  | ||||||
|   | otherwise = Alive |  | ||||||
| 
 |  | ||||||
| -- a second rule for example |  | ||||||
| rule2 :: Space CellState -> CellState |  | ||||||
| rule2 (Space (l1:l2:_) m (r1:r2:_)) |  | ||||||
|   | m == Alive && numAlive == 1 = Dead |  | ||||||
|   | m == Alive && numAlive == 4 = Dead |  | ||||||
|   | m == Dead && numAlive == 3 = Alive |  | ||||||
|   | otherwise = m |  | ||||||
|   where |  | ||||||
|     ns = [l1, l2, r1, r2] |  | ||||||
|     numAlive = length $ filter (== Alive) ns |  | ||||||
| 
 |  | ||||||
| rule3 :: Space CellState -> CellState |  | ||||||
| rule3 (Space (l:_) m (r:_)) |  | ||||||
|   | l == r = m |  | ||||||
|   | otherwise = if m == Alive then Dead else Alive |  | ||||||
| 
 |  | ||||||
| -- take a space and a rule and |  | ||||||
| -- return the next space |  | ||||||
| step :: Comonad w => (w t -> t) -> w t -> w t |  | ||||||
| step f w = w =>> f |  | ||||||
| 
 | 
 | ||||||
| --------------- | --------------- | ||||||
| -- rng stuff -- | -- rng stuff -- | ||||||
|  | @ -134,74 +59,6 @@ ilobs rng = b : (ilobs r) | ||||||
|   where |   where | ||||||
|     (b,r) = random rng |     (b,r) = random rng | ||||||
| 
 | 
 | ||||||
| -- this is kinda gross but if it works it works |  | ||||||
| takeGive :: Int -> [a] -> ([a],[a]) |  | ||||||
| takeGive n as = ( (take n as), (drop n as) ) |  | ||||||
| 
 |  | ||||||
| -------------------------- |  | ||||||
| -- 2d cellular automata -- |  | ||||||
| -------------------------- |  | ||||||
| 
 |  | ||||||
| data Space2 t = |  | ||||||
|   Space2 [(Space t)] |  | ||||||
|           (Space t) |  | ||||||
|          [(Space t)] |  | ||||||
| 
 |  | ||||||
| instance Functor Space2 where |  | ||||||
|   fmap f (Space2 u m d) = |  | ||||||
|     Space2 (fmap (fmap f) u) (fmap f m) (fmap (fmap f) d) |  | ||||||
| 
 |  | ||||||
| instance Comonad Space2 where |  | ||||||
|   duplicate w = |  | ||||||
|     Space2 (tail $ iterate (f up2) dm) |  | ||||||
|            dm |  | ||||||
|            (tail $ iterate (f down2) dm) |  | ||||||
|     where |  | ||||||
|       f g (Space l m r) = Space (fmap g l) (g m) (fmap g r) |  | ||||||
|       dm = Space (tail $ iterate left2 w) w (tail $ iterate right2 w) |  | ||||||
|   extract (Space2 _ m _) = extract m |  | ||||||
| 
 |  | ||||||
| down2 :: Space2 t -> Space2 t |  | ||||||
| down2 w@(Space2 u m []) = w |  | ||||||
| down2 (Space2 u m (d:ds)) = Space2 (m:u) d ds |  | ||||||
| 
 |  | ||||||
| up2 :: Space2 t -> Space2 t |  | ||||||
| up2 w@(Space2 [] m d) = w |  | ||||||
| up2 (Space2 (u:us) m d) = Space2 us u (m:d) |  | ||||||
| 
 |  | ||||||
| left2 :: Space2 t -> Space2 t |  | ||||||
| left2 (Space2 u m d) = Space2 (fmap left u) (left m) (fmap left d) |  | ||||||
| 
 |  | ||||||
| right2 :: Space2 t -> Space2 t |  | ||||||
| right2 (Space2 u m d) = Space2 (fmap right u) (right m) (fmap right d) |  | ||||||
| 
 |  | ||||||
| bound2 :: Int -> Int -> Int -> Int -> Space2 t -> [[t]] |  | ||||||
| bound2 u d l r (Space2 uw mw dw) = (reverse (take u (map (bound l r) uw))) ++ ((bound l r mw):(take d (map (bound l r) dw))) |  | ||||||
| 
 |  | ||||||
| bound2w :: Int -> Int -> Space2 t -> [[t]] |  | ||||||
| bound2w x y = bound2 (r-q) r (n-m) n |  | ||||||
|   where |  | ||||||
|     o = if odd x then 1 else 0 |  | ||||||
|     m = if even x then 1 else 0 |  | ||||||
|     n = (x - o) `div` 2 |  | ||||||
|     p = if odd y then 1 else 0  |  | ||||||
|     q = if even y then 1 else 0 |  | ||||||
|     r = (y - p) `div` 2 |  | ||||||
| 
 |  | ||||||
| -------------- |  | ||||||
| -- 2d rules -- |  | ||||||
| -------------- |  | ||||||
| 
 |  | ||||||
| conway :: Space2 CellState -> CellState |  | ||||||
| conway (Space2 (u:_) m (d:_)) |  | ||||||
|   = case me of |  | ||||||
|       Alive -> if (length ns) == 2 || (length ns == 3) then Alive else Dead |  | ||||||
|       Dead -> if (length ns) == 3 then Alive else Dead |  | ||||||
|   where |  | ||||||
|     f b (Space (l:_) m (r:_)) = [l,r] ++ (if b then [m] else []) |  | ||||||
|     ns = filter (== Alive) $ concat [ (f True u), (f False m), (f True d) ] |  | ||||||
|     me = extract m |  | ||||||
| 
 |  | ||||||
| ----------------- | ----------------- | ||||||
| -- gross io bs -- | -- gross io bs -- | ||||||
| ----------------- | ----------------- | ||||||
|  | @ -262,47 +119,50 @@ parseArgs = do | ||||||
|         header = "Usage: " ++ progName ++ " [OPTION...]" |         header = "Usage: " ++ progName ++ " [OPTION...]" | ||||||
|         helpMessage = usageInfo header options |         helpMessage = usageInfo header options | ||||||
| 
 | 
 | ||||||
|  | initGame :: IO (Space2 CellState) | ||||||
|  | initGame = do | ||||||
|  |   rng <- getStdGen | ||||||
|  |   return $ createRandSpace2 rng | ||||||
|  | 
 | ||||||
| --------------- | --------------- | ||||||
| -- main loop -- | -- main loop -- | ||||||
| --------------- | --------------- | ||||||
| 
 | 
 | ||||||
| createRandSpace :: StdGen -> Space CellState |  | ||||||
| createRandSpace rng = |  | ||||||
|   Space (tail $ map snd $ iterate f (r1, Alive)) |  | ||||||
|         (fst (random rng)) |  | ||||||
|         (tail $ map snd $ iterate f (r2, Alive)) |  | ||||||
|   where |  | ||||||
|     f (r,b) = let (nb,nr) = (random r) in (nr,nb) |  | ||||||
|     (r1,r2) = split rng |  | ||||||
| 
 |  | ||||||
| createRandSpace2 :: StdGen -> Space2 CellState |  | ||||||
| createRandSpace2 rng =  |  | ||||||
|   Space2 (tail $ map snd $ iterate f (r1, (createRandSpace r1))) |  | ||||||
|          (createRandSpace rng) |  | ||||||
|          (tail $ map snd $ iterate f (r2, (createRandSpace r2))) |  | ||||||
|   where |  | ||||||
|     f (r,s) = let (nr1,nr2) = split r in (nr2, (createRandSpace nr1)) |  | ||||||
|     (r1,r2) = split rng |  | ||||||
| 
 |  | ||||||
| -- simply print the current space, then recurse to the next |  | ||||||
| --runAutomata :: Space2 CellState -> Int -> Int -> IO () |  | ||||||
| --runAutomata s 0 w = putStrLn $ concat $ map show $ boundw w s |  | ||||||
| --runAutomata s n w = do |  | ||||||
| --  mapM_ putStrLn $ map show $ concat $ bound2w w s |  | ||||||
| --  runAutomata (step conway s) (n - 1) w |  | ||||||
| 
 |  | ||||||
| main :: IO () | main :: IO () | ||||||
| main = do | main = do | ||||||
|   options <- parseArgs |   options <- parseArgs | ||||||
|   rng <- getStdGen |  | ||||||
|   let cs = map (\x -> if x then Alive else Dead) $ ilobs rng |  | ||||||
|   let w = (optWidth options) |   let w = (optWidth options) | ||||||
|   let h = (optHeight options) |   let h = (optHeight options) | ||||||
|   let g = (optGenerations options) |   chan <- newBChan 1 | ||||||
|   let s = createRandSpace2 rng |   forkIO $ forever $ do | ||||||
|   mapM_ (f w h) (loop conway g s) |     writeBChan chan Tick | ||||||
|  |     threadDelay 100000 | ||||||
|  |   g <- initGame | ||||||
|  |   let buildVty = V.mkVty V.defaultConfig | ||||||
|  |   initialVty <- buildVty | ||||||
|  |   void $ customMain initialVty buildVty (Just chan) (app h w) (clamp2cw w h g) | ||||||
|  | 
 | ||||||
|  | handleEvent :: (Space2 CellState) -> BrickEvent Name Tick -> EventM Name (Next (Space2 CellState)) | ||||||
|  | handleEvent g (AppEvent Tick) = continue $ step rps g | ||||||
|  | handleEvent g (VtyEvent (V.EvKey (V.KChar 'q') [])) = halt g | ||||||
|  | handleEvent g _ = continue g | ||||||
|  | 
 | ||||||
|  | drawUI :: Int -> Int -> Space2 CellState -> [Widget Name] | ||||||
|  | drawUI h w g = [ C.center $ drawGrid h w g ] | ||||||
|  | 
 | ||||||
|  | drawGrid :: Int -> Int -> Space2 CellState -> Widget Name | ||||||
|  | drawGrid h w g = vBox rows | ||||||
|   where |   where | ||||||
|     f w h s = do |     bw = bound2cw w h g    | ||||||
|       mapM_ putStrLn $ map (concat . (map show)) $ bound2w w h s |     rows = [ hBox $ cellsInRow r | r <- bw ] | ||||||
|       putStrLn (take w (repeat '-')) |     cellsInRow y = map drawCell y | ||||||
|     loop f n s = take n $ iterate (step f) s | 
 | ||||||
|  | drawCell :: CellState -> Widget Name | ||||||
|  | drawCell Paper = withAttr paperAttr $ str " " | ||||||
|  | drawCell Scissors = withAttr scissorsAttr $ str " " | ||||||
|  | drawCell Rock = withAttr rockAttr $ str " " | ||||||
|  | 
 | ||||||
|  | rockAttr, scissorsAttr, paperAttr :: AttrName | ||||||
|  | rockAttr = "rockAttr" | ||||||
|  | paperAttr = "paperAttr" | ||||||
|  | scissorsAttr = "scissorsAttr" | ||||||
|  |  | ||||||
							
								
								
									
										176
									
								
								src/Spaces.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/Spaces.hs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,176 @@ | ||||||
|  | {-# LANGUAGE BangPatterns #-} | ||||||
|  | {-# LANGUAGE DeriveGeneric #-} | ||||||
|  | 
 | ||||||
|  | module Spaces where | ||||||
|  | 
 | ||||||
|  | import Comonad | ||||||
|  | import System.Random | ||||||
|  | import Control.DeepSeq | ||||||
|  | import GHC.Generics | ||||||
|  | 
 | ||||||
|  | ------------ | ||||||
|  | -- spaces -- | ||||||
|  | ------------ | ||||||
|  | 
 | ||||||
|  | -- a locally focussed space | ||||||
|  | data Space t = Space [t] t [t] | ||||||
|  |   deriving (Generic, Generic1) | ||||||
|  | 
 | ||||||
|  | instance NFData a => NFData (Space a) | ||||||
|  | instance NFData1 Space | ||||||
|  | 
 | ||||||
|  | -- spaces are also functors | ||||||
|  | instance Functor Space where | ||||||
|  |   fmap f (Space l c r) = Space (map f l) (f c) (map f r) | ||||||
|  | 
 | ||||||
|  | -- our space is a comonad | ||||||
|  | instance Comonad Space where | ||||||
|  |   -- duplicate will create a new space where | ||||||
|  |   -- the focussed element is our original space | ||||||
|  |   -- and each side is increasingly shifted copies | ||||||
|  |   -- in that direction | ||||||
|  |   duplicate w = | ||||||
|  |     Space (tail $ iterate left w) | ||||||
|  |           w | ||||||
|  |           (tail $ iterate right w) | ||||||
|  |   -- extract simply returns the focussed element | ||||||
|  |   extract (Space _ c _) = c | ||||||
|  | 
 | ||||||
|  | -- functions for moving the point | ||||||
|  | -- of locality. | ||||||
|  | -- todo: question the empty list cases | ||||||
|  | -- most spaces should be infinite | ||||||
|  | right :: Space t -> Space t | ||||||
|  | right w@(Space l m []) = w | ||||||
|  | right (Space l c (r:rs)) = Space (c:l) r rs | ||||||
|  | 
 | ||||||
|  | left :: Space t -> Space t | ||||||
|  | left w@(Space [] m r) = w | ||||||
|  | left (Space (l:ls) c r) = Space ls l (c:r) | ||||||
|  | 
 | ||||||
|  | -- bound will take an infinite space | ||||||
|  | -- and bound it by i and j on each side | ||||||
|  | -- (not including the focus) and | ||||||
|  | -- turn it into a list for printing | ||||||
|  | bound :: Int -> Int -> Space t -> [t] | ||||||
|  | bound i j (Space l c r) = (reverse (take i l)) ++ (c:(take j r)) | ||||||
|  | 
 | ||||||
|  | -- boundw works as above, but the | ||||||
|  | -- entire list will be the size | ||||||
|  | -- given | ||||||
|  | boundw :: Int -> Space t -> [t] | ||||||
|  | boundw n = bound (x-m) x | ||||||
|  |   where | ||||||
|  |     o = if odd n then 1 else 0 | ||||||
|  |     m = if even n then 1 else 0 | ||||||
|  |     x = (n - o) `div` 2 | ||||||
|  | 
 | ||||||
|  | --------------- | ||||||
|  | -- 2d spaces -- | ||||||
|  | --------------- | ||||||
|  | 
 | ||||||
|  | data Space2 t = | ||||||
|  |   Space2 [(Space t)] | ||||||
|  |           (Space t) | ||||||
|  |          [(Space t)] | ||||||
|  |   deriving (Generic, Generic1) | ||||||
|  | 
 | ||||||
|  | instance NFData a => NFData (Space2 a) | ||||||
|  | instance NFData1 Space2 | ||||||
|  | 
 | ||||||
|  | instance Functor Space2 where | ||||||
|  |   fmap f (Space2 u m d) = | ||||||
|  |     Space2 (fmap (fmap f) u) (fmap f m) (fmap (fmap f) d) | ||||||
|  | 
 | ||||||
|  | instance Comonad Space2 where | ||||||
|  |   duplicate w = | ||||||
|  |     Space2 (tail $ iterate (f up2) dm) | ||||||
|  |            dm | ||||||
|  |            (tail $ iterate (f down2) dm) | ||||||
|  |     where | ||||||
|  |       f g (Space l m r) = Space (fmap g l) (g m) (fmap g r) | ||||||
|  |       dm = Space (tail $ iterate left2 w) w (tail $ iterate right2 w) | ||||||
|  |   extract (Space2 _ m _) = extract m | ||||||
|  | 
 | ||||||
|  | down2 :: Space2 t -> Space2 t | ||||||
|  | down2 w@(Space2 u m []) = w | ||||||
|  | down2 (Space2 u m (d:ds)) = Space2 (m:u) d ds | ||||||
|  | 
 | ||||||
|  | up2 :: Space2 t -> Space2 t | ||||||
|  | up2 w@(Space2 [] m d) = w | ||||||
|  | up2 (Space2 (u:us) m d) = Space2 us u (m:d) | ||||||
|  | 
 | ||||||
|  | left2 :: Space2 t -> Space2 t | ||||||
|  | left2 (Space2 u m d) = Space2 (fmap left u) (left m) (fmap left d) | ||||||
|  | 
 | ||||||
|  | right2 :: Space2 t -> Space2 t | ||||||
|  | right2 (Space2 u m d) = Space2 (fmap right u) (right m) (fmap right d) | ||||||
|  | 
 | ||||||
|  | bound2 :: Int -> Int -> Int -> Int -> Space2 t -> [[t]] | ||||||
|  | bound2 u d l r (Space2 uw mw dw) = (reverse (take u (map (bound l r) uw))) ++ ((bound l r mw):(take d (map (bound l r) dw))) | ||||||
|  | 
 | ||||||
|  | bound2w :: Int -> Int -> Space2 t -> [[t]] | ||||||
|  | bound2w x y = bound2 (r-q) r (n-m) n | ||||||
|  |   where | ||||||
|  |     o = if odd x then 1 else 0 | ||||||
|  |     m = if even x then 1 else 0 | ||||||
|  |     n = (x - o) `div` 2 | ||||||
|  |     p = if odd y then 1 else 0  | ||||||
|  |     q = if even y then 1 else 0 | ||||||
|  |     r = (y - p) `div` 2 | ||||||
|  | 
 | ||||||
|  | bound2cw :: NFData t => Int -> Int -> Space2 t -> [[t]] | ||||||
|  | bound2cw x y w = bound2 (r-q) r (n-m) n $ clamp2 (r-q+1) (r+1) (n-m+1) (n+1) w | ||||||
|  |   where | ||||||
|  |     o = if odd x then 1 else 0 | ||||||
|  |     m = if even x then 1 else 0 | ||||||
|  |     n = (x - o) `div` 2 | ||||||
|  |     p = if odd y then 1 else 0  | ||||||
|  |     q = if even y then 1 else 0 | ||||||
|  |     r = (y - p) `div` 2 | ||||||
|  | 
 | ||||||
|  | clamp2cw :: NFData t => Int -> Int -> Space2 t -> Space2 t | ||||||
|  | clamp2cw x y w = clamp2 (r-q+1) (r+1) (n-m+1) (n+1) w | ||||||
|  |   where | ||||||
|  |     o = if odd x then 1 else 0 | ||||||
|  |     m = if even x then 1 else 0 | ||||||
|  |     n = (x - o) `div` 2 | ||||||
|  |     p = if odd y then 1 else 0  | ||||||
|  |     q = if even y then 1 else 0 | ||||||
|  |     r = (y - p) `div` 2 | ||||||
|  | 
 | ||||||
|  | clamp2 :: NFData t => Int -> Int -> Int -> Int -> Space2 t -> Space2 t | ||||||
|  | clamp2 u d l r (Space2 uw mw dw) | ||||||
|  |   = force $ Space2 (take u $ fmap (clamp l r) uw) | ||||||
|  |            (clamp l r mw) | ||||||
|  |            (take d $ fmap (clamp l r) dw) | ||||||
|  | 
 | ||||||
|  | clamp :: NFData t => Int -> Int -> Space t -> Space t | ||||||
|  | clamp x y (Space l m r) = force $ Space (take x l) m (take y r) | ||||||
|  | 
 | ||||||
|  | -- take a space and a rule and | ||||||
|  | -- return the next space | ||||||
|  | step :: Comonad w => (w t -> t) -> w t -> w t | ||||||
|  | step f w = w =>> f | ||||||
|  | 
 | ||||||
|  | ------------------- | ||||||
|  | -- Random Spaces -- | ||||||
|  | ------------------- | ||||||
|  | 
 | ||||||
|  | createRandSpace :: Random a => StdGen -> Space a | ||||||
|  | createRandSpace rng = | ||||||
|  |   Space (tail $ map snd $ iterate f (r1, (fst (random rng)))) | ||||||
|  |         (fst (random rng)) | ||||||
|  |         (tail $ map snd $ iterate f (r2, (fst (random rng)))) | ||||||
|  |   where | ||||||
|  |     f (r,b) = let (nb,nr) = (random r) in (nr,nb) | ||||||
|  |     (r1,r2) = split rng | ||||||
|  | 
 | ||||||
|  | createRandSpace2 :: Random a => StdGen -> Space2 a | ||||||
|  | createRandSpace2 rng =  | ||||||
|  |   Space2 (tail $ map snd $ iterate f (r1, (createRandSpace r1))) | ||||||
|  |          (createRandSpace rng) | ||||||
|  |          (tail $ map snd $ iterate f (r2, (createRandSpace r2))) | ||||||
|  |   where | ||||||
|  |     f (r,s) = let (nr1,nr2) = split r in (nr2, (createRandSpace nr1)) | ||||||
|  |     (r1,r2) = split rng | ||||||
		Loading…
	
		Reference in a new issue
	
	 Thorn Avery
						Thorn Avery