working 2d animation, no memory leak

This commit is contained in:
Thorn Avery 2021-04-20 19:44:30 +12:00
parent d582c20af3
commit bdda683c42
5 changed files with 201 additions and 180 deletions

View File

@ -3,7 +3,8 @@
module Automata where module Automata where
import Comonad import Comonad
import Spaces import Spaces.Space1
import Spaces.Space2
import System.Random import System.Random
import GHC.Generics import GHC.Generics
import Control.DeepSeq import Control.DeepSeq

View File

@ -9,7 +9,8 @@ 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 Comonad
import Spaces import Spaces.Space2
import Spaces.Space1
import Automata import Automata
import Brick import Brick
import Brick.BChan (newBChan, writeBChan) import Brick.BChan (newBChan, writeBChan)
@ -140,7 +141,7 @@ main = do
g <- initGame g <- initGame
let buildVty = V.mkVty V.defaultConfig let buildVty = V.mkVty V.defaultConfig
initialVty <- buildVty initialVty <- buildVty
void $ customMain initialVty buildVty (Just chan) (app h w) (clamp2cw w h g) void $ customMain initialVty buildVty (Just chan) (app h w) (clamp2 w h g)
handleEvent :: (Space2 CellState) -> BrickEvent Name Tick -> EventM Name (Next (Space2 CellState)) handleEvent :: (Space2 CellState) -> BrickEvent Name Tick -> EventM Name (Next (Space2 CellState))
handleEvent g (AppEvent Tick) = continue $ step rps g handleEvent g (AppEvent Tick) = continue $ step rps g
@ -153,7 +154,7 @@ drawUI h w g = [ C.center $ drawGrid h w g ]
drawGrid :: Int -> Int -> Space2 CellState -> Widget Name drawGrid :: Int -> Int -> Space2 CellState -> Widget Name
drawGrid h w g = vBox rows drawGrid h w g = vBox rows
where where
bw = bound2cw w h g bw = mat2 g
rows = [ hBox $ cellsInRow r | r <- bw ] rows = [ hBox $ cellsInRow r | r <- bw ]
cellsInRow y = map drawCell y cellsInRow y = map drawCell y
@ -166,3 +167,21 @@ rockAttr, scissorsAttr, paperAttr :: AttrName
rockAttr = "rockAttr" rockAttr = "rockAttr"
paperAttr = "paperAttr" paperAttr = "paperAttr"
scissorsAttr = "scissorsAttr" scissorsAttr = "scissorsAttr"
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

View File

@ -1,176 +0,0 @@
{-# 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

67
src/Spaces/Space1.hs Normal file
View File

@ -0,0 +1,67 @@
{-# LANGUAGE DeriveGeneric #-}
module Spaces.Space1 where
import Comonad
import Control.DeepSeq
import GHC.Generics
-- a locally focussed space
data Space t = Space [t] t [t]
deriving (Generic, Generic1, Show)
-- allowing strict evaluation of a space
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)
-- moving a space focus right
right :: Space t -> Maybe (Space t)
right (Space _ _ []) = Nothing
right (Space l c (r:rs)) = Just $ Space (c:l) r rs
-- moving a space's focus left
left :: Space t -> Maybe (Space t)
left (Space [] _ _) = Nothing
left (Space (l:ls) c r) = Just $ Space ls l (c:r)
-- iterate until we reach an edge
finterate :: (a -> Maybe a) -> a -> [a]
finterate f x = case (f x) of
Nothing -> []
Just y -> y : finterate f y
-- our space is a comonad
instance Comonad Space where
-- duplicate creats a meta space
duplicate w =
Space (finterate left w)
w
(finterate right w)
-- extract simply returns the focussed element
extract (Space _ c _) = c
-- clamp an infinite space to a finite space
-- relative to center
clampRel :: Int -> Int -> Space t -> Space t
clampRel x y (Space l m r) = Space (take x l) m (take y r)
-- as above, but with a set width
-- if the width is even, we need to take one less from the left
clamp :: Int -> Space t -> Space t
clamp w (Space l m r) = Space (take ln l) m (take h r)
where
h = w `div` 2
ln = h - (if even w then 1 else 0)
-- materialises a space, will hang if infinite
mat :: Space t -> [t]
mat (Space l m r) = (reverse l) ++ (m:r)
-- as above, but clamps to a given size first
matn :: Int -> Space t -> [t]
matn n = mat . (clamp n)

110
src/Spaces/Space2.hs Normal file
View File

@ -0,0 +1,110 @@
{-# LANGUAGE DeriveGeneric #-}
module Spaces.Space2 where
import Comonad
import Data.Maybe
import Control.DeepSeq
import GHC.Generics
import Spaces.Space1
-- a nested space
data Space2 t = Space2 [(Space t)] (Space t) [(Space t)]
deriving (Generic, Generic1, Show)
-- generating strict data instances
instance NFData a => NFData (Space2 a)
instance NFData1 Space2
-- we can fmap into this structure by recursively fmapping
-- the inner spaces
instance Functor Space2 where
fmap f (Space2 u m d) =
Space2 (fmap (fmap f) u) (fmap f m) (fmap (fmap f) d)
-- map a partial function, converting to non maybe values
fintermap :: (a -> Maybe a) -> [a] -> [a]
fintermap _ [] = []
fintermap f (a:as) = case f a of
Nothing -> []
Just y -> y : fintermap f as
f :: ((Space2 a) -> Maybe (Space2 a)) -> Space (Space2 a) -> Maybe (Space (Space2 a))
f g (Space l m r) = case (g m) of
Nothing -> Nothing
Just y -> Just $ Space (fintermap g l) y (fintermap g r)
-- comonad instance for our 2d space
instance Comonad Space2 where
duplicate w =
Space2 (finterate (f up2) dm) dm (finterate (f down2) dm)
where
dm = Space (finterate left2 w) w (finterate right2 w)
-- to duplicate we must recursively duplicate in all directions
-- the focussed space becomes the whole space, with left and right
-- mapped to each side.
-- to do the up and down lists, each needs to be the middle space
-- mapped up and down as far as we can.
-- up2 and down2 will return Nothing when they cant go further
-- to extract we simply recursively extract
extract (Space2 _ m _) = extract m
-- directional moving of focus
up2 :: Space2 t -> Maybe (Space2 t)
up2 (Space2 [] _ _) = Nothing
up2 (Space2 (u:us) m d) = Just $ Space2 us u (m:d)
down2 :: Space2 t -> Maybe (Space2 t)
down2 (Space2 _ _ []) = Nothing
down2 (Space2 u m (d:ds)) = Just $ Space2 (m:u) d ds
noLeft :: Space t -> Bool
noLeft (Space [] _ _) = True
noLeft _ = False
noRight :: Space t -> Bool
noRight (Space _ _ []) = True
noRight _ = False
-- left and right require mapping further
-- we are assuming things are rectangular (maybe a bad idea?)
left2 :: Space2 t -> Maybe (Space2 t)
left2 (Space2 u m d) =
if check
then Nothing
else Just $ Space2 (fmap (f . left) u) (f $ left m) (fmap (f . left) d)
where
check = noLeft m
f l = fromJust l
right2 :: Space2 t -> Maybe (Space2 t)
right2 (Space2 u m d) =
if check
then Nothing
else Just $ Space2 (fmap (f . right) u) (f $ right m) (fmap (f . right) d)
where
check = noRight m
f l = fromJust l
-- clamp as we do in 1d Spaces
clampRel2 :: Int -> Int -> Int -> Int -> Space2 t -> Space2 t
clampRel2 w x y z (Space2 u m d) = Space2 (take w $ fmap f u) (f m) (take x $ fmap f d)
where
f = clampRel y z
clamp2 :: Int -> Int -> Space2 t -> Space2 t
clamp2 w h = clampRel2 nu nd nl nr
where
nu = h `div` 2
nd = nu - (if even h then 1 else 0)
nr = w `div` 2
nl = nr - (if even w then 1 else 0)
mat2 :: Space2 t -> [[t]]
mat2 (Space2 u m d) = (reverse (fmap mat u)) ++ ((mat m):(fmap mat d))
matn2 :: Int -> Int -> Space2 t -> [[t]]
matn2 w h = mat2 . (clamp2 w h)
step :: Comonad w => (w t -> t) -> w t -> w t
step f w = w =>> f