packages feed

timers-tick 0.2.0.0 → 0.3.0.0

raw patch · 4 files changed

+123/−70 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Control.Timer.Tick: data TimedRes a
- Control.Timer.Tick: instance GHC.Base.Functor Control.Timer.Tick.TimedRes
- Control.Timer.Tick: instance GHC.Classes.Eq a => GHC.Classes.Eq (Control.Timer.Tick.TimedRes a)
- Control.Timer.Tick: instance GHC.Generics.Generic (Control.Timer.Tick.TimedRes a)
- Control.Timer.Tick: instance GHC.Show.Show a => GHC.Show.Show (Control.Timer.Tick.TimedRes a)
- Control.Timer.Tick: type Timer = TimedRes ()
+ Control.Timer.Tick: Elapse :: ExpBehaviour
+ Control.Timer.Tick: Reach :: ExpBehaviour
+ Control.Timer.Tick: creaBoolTimer :: Integer -> Timed Bool
+ Control.Timer.Tick: data ExpBehaviour
+ Control.Timer.Tick: data Timed a
+ Control.Timer.Tick: instance GHC.Base.Functor Control.Timer.Tick.Timed
+ Control.Timer.Tick: instance GHC.Classes.Eq Control.Timer.Tick.ExpBehaviour
+ Control.Timer.Tick: instance GHC.Classes.Eq a => GHC.Classes.Eq (Control.Timer.Tick.Timed a)
+ Control.Timer.Tick: instance GHC.Generics.Generic (Control.Timer.Tick.Timed a)
+ Control.Timer.Tick: instance GHC.Generics.Generic Control.Timer.Tick.ExpBehaviour
+ Control.Timer.Tick: instance GHC.Show.Show Control.Timer.Tick.ExpBehaviour
+ Control.Timer.Tick: instance GHC.Show.Show a => GHC.Show.Show (Control.Timer.Tick.Timed a)
- Control.Timer.Tick: Times :: Integer -> Loop
+ Control.Timer.Tick: Times :: Integer -> ExpBehaviour -> Loop
- Control.Timer.Tick: creaTimedRes :: Loop -> [(Integer, a)] -> TimedRes a
+ Control.Timer.Tick: creaTimedRes :: Loop -> [(Integer, a)] -> Timed a
- Control.Timer.Tick: creaTimer :: Integer -> Timer
+ Control.Timer.Tick: creaTimer :: a -> a -> Integer -> Timed a
- Control.Timer.Tick: fetchFrame :: TimedRes a -> a
+ Control.Timer.Tick: fetchFrame :: Timed a -> a
- Control.Timer.Tick: getFrames :: TimedRes a -> [(Integer, a)]
+ Control.Timer.Tick: getFrames :: Timed a -> [(Integer, a)]
- Control.Timer.Tick: isExpired :: TimedRes a -> Bool
+ Control.Timer.Tick: isExpired :: Timed a -> Bool
- Control.Timer.Tick: isLive :: TimedRes a -> Bool
+ Control.Timer.Tick: isLive :: Timed a -> Bool
- Control.Timer.Tick: reset :: TimedRes a -> TimedRes a
+ Control.Timer.Tick: reset :: Timed a -> Timed a
- Control.Timer.Tick: tick :: TimedRes a -> TimedRes a
+ Control.Timer.Tick: tick :: Timed a -> Timed a
- Control.Timer.Tick: ticks :: Integer -> TimedRes a -> TimedRes a
+ Control.Timer.Tick: ticks :: Integer -> Timed a -> Timed a

Files

changes.txt view
@@ -1,10 +1,16 @@+0.3.0.0+-------++- Reworked the API, added Reach ending, fixed bugs.+- Released on Thu 15 Mar 2018 08:49:30 CET.+ 0.2.0.0 -------  - Renamed `fetch` to `fetchFrame`. - Added `getFrames`. - Added Functor instance-- Released Thu 15 Mar 2018 03:33:20 CET+- Released on Thu 15 Mar 2018 03:33:20 CET  0.1.2.0 -------
src/Control/Timer/Tick.hs view
@@ -17,11 +17,12 @@  module Control.Timer.Tick ( -- * Simple timers                             creaTimer,-                            Timer,+                            creaBoolTimer,                             -- * Timed Resources-                            TimedRes,+                            Timed,                             creaTimedRes,                             Loop(..),+                            ExpBehaviour(..),                             -- * Use                             tick,                             ticks,@@ -48,77 +49,100 @@ -- Example: -- -- @--- run = creaTimedRes (Times 1) [(2, "a "), (1, "b "), (2, "c ")]--- main = count run---     where---           count t | isExpired t = putStrLn "\nOver!"---                   | otherwise   = do putStr (fetch t)---                                      count (tick t)---    -- λ> main---    -- a a b c c---    -- Over!+-- timer = creaTimedRes (Times 1 Elapse) [(2, "a "), (1, "b "), (2, "c ")]+-- test t | isExpired t = putStrLn "Fine."+--        | otherwise   = do putStr (fetchFrame t)+--                           test (tick t)+--+--    -- λ> test timer+--    -- a a b c c Fine. -- @-data TimedRes a = TimedRes { -- init-                             tSteps    :: [TimerStep a],-                             tLoop     :: Loop,-                             tOrigLoop :: Loop,+data Timed a = TimedRes { -- init+                          tSteps    :: [TimerStep a],+                          tLoop     :: Loop,+                          tOrigLoop :: Loop, -                             -- convenience-                             tMaxTicks :: Integer,+                          -- convenience+                          tMaxTicks :: Maybe Integer, -                             --  curr-                             tCurrTick :: Integer,-                             tExpired  :: Bool-                           }+                          --  curr+                          tCurrTick :: Integer,+                          tExpired  :: Bool+                        }         deriving (Show, Eq, Generic)  type TimerStep a = (Integer, a)  -- | Number of times to repeat the animation.-data Loop = Times Integer -- currentLoop and maxLoop-          | AlwaysLoop+data Loop = -- | Loops forever, never expires.+            AlwaysLoop+            -- | Repeats the cycle for a fixed number of times.+          | Times Integer ExpBehaviour      deriving (Show, Eq, Generic) +-- | Expire behaviour.+data ExpBehaviour =+        -- | Expires upon __reaching__ last frame.+          Reach+        -- | Expires when last frame is __over__.+        | Elapse+    deriving (Show, Eq, Generic)+ -- todo Monoid (or semigroup) <> for timers [2.0] --- | For easy mapping on frames.-instance Functor TimedRes where+-- | Mapping on frames.+instance Functor Timed where     fmap f t = t { tSteps = fmap (\(i, a) -> (i, f a))                                  (tSteps t) } +-- todo [release] modifice esempi per renderli one liner o ghci+--      firnedly+ ------------ -- CREATE -- ------------ -type Timer = TimedRes ()+-- todo 0-length frames [release]+-- todo inverted timer (expires on Nothing) [release] --- | Creates a 'Timer' expiring in @x@ ticks.+-- | A simple off/on timer expiring in fixed number of ticks. -- -- Example: -- -- @--- main = count (creaTimer 4)---     where---           count t | isExpired t = putStrLn "Over!"---                   | otherwise   = do putStrLn "Ticking."---                                      count (tick t)+-- timer = creaTimer Nothing (Just "Over!") 4+-- test t | isExpired t = print (fetchFrame t)+--        | otherwise   = do print (fetchFrame t)+--                           test (tick t) -----    -- λ> main---    -- Ticking.---    -- Ticking.---    -- Ticking.---    -- Ticking.---    -- Over!+--    -- λ> test timer+--    -- Nothing+--    -- Nothing+--    -- Nothing+--    -- Nothing+--    -- Just \"Over\"! -- @-creaTimer :: Integer -> Timer-creaTimer c = creaTimedRes (Times 1) [(c, ())]+creaTimer :: a -> a -> Integer -> Timed a+creaTimer off on i = creaTimedRes (Times 1 Reach) [(i, off), (1, on)] --- | Creates a time-based resource, like an animation.-creaTimedRes :: Loop -> [(Integer, a)] -> TimedRes a+-- | Shorthand for: @'creaTimer' False True i@.+creaBoolTimer :: Integer -> Timed Bool+creaBoolTimer i = creaTimer False True i++-- | Most generic way to create a time-based resource (like an animation).+-- 'Loop' controls the expiring behaviour, @[(Integer, a)]@ is a list of+-- frames and their duration.+creaTimedRes :: Loop -> [(Integer, a)] -> Timed a creaTimedRes _ [] = error "Cannot create an empty TimedRes" creaTimedRes l ss = TimedRes ss l l-                                  (sum . map fst $ ss)-                                  0 False+                             maxTricks+                             0 False+    where+          maxTricks :: Maybe Integer+          maxTricks = case l of+                        AlwaysLoop     -> Nothing+                        Times _ Elapse -> Just (sum . map fst $ ss)+                        Times _ Reach  -> Just (sum . map fst $ init ss)   -------------@@ -126,52 +150,65 @@ -------------  -- | Ticks the timer (one step).-tick :: TimedRes a -> TimedRes a+tick :: Timed a -> Timed a tick t | isExpired t = t        | otherwise   =             let t' = t { tCurrTick = succ (tCurrTick t) } in -            if tCurrTick t' == tMaxTicks t'-              then maxed t'+            if Just (tCurrTick t') == tMaxTicks t'+              then expire t'               else t'++expire :: Timed a -> Timed a+expire tm = -- need this as last tick on Elapse is OOB+            if isElB (tLoop tm)+              then expx { tCurrTick = tCurrTick tm - 1 }+              else expx     where-          maxed :: TimedRes a -> TimedRes a-          maxed tm = case tLoop tm of-                       Times 1    -> tm { tLoop = Times 0,-                                          tExpired = True }-                       AlwaysLoop -> tm { tCurrTick = 0 }-                       Times n    -> tm { tLoop = Times (n-1),-                                          tCurrTick = 0 }+          expx = case tLoop tm of+                   -- infinite loop+                   AlwaysLoop -> tm { tCurrTick = 0 }+                   -- last loop of a timed resource+                   Times 1 eb  -> tm { tLoop = Times 0 eb,+                                       tExpired = True }+                   -- not-last loop of a timed resource+                   Times n eb -> tm { tLoop = Times (n-1) eb,+                                      tCurrTick = 0 } +          isElB (Times _ Elapse) = True+          isElB _                = False+ -- | Ticks the timer (multiple steps).-ticks :: Integer -> TimedRes a -> TimedRes a+ticks :: Integer -> Timed a -> Timed a ticks 1 t = tick t ticks n t | n < 1     = error "negative number passed to `ticks`"           | otherwise = ticks (n-1) (tick t) --- | Equal to @not isExpired@.-isLive :: TimedRes a -> Bool+-- | Antonym of 'isExpired'.+--+-- > isLive = not isExpired+isLive :: Timed a -> Bool isLive t = not $ tExpired t  -- | Checks wheter the timer is expired (an expired timer will not -- respond to 'tick').-isExpired :: TimedRes a -> Bool+isExpired :: Timed a -> Bool isExpired t = tExpired t  -- | Fetches the current resource of the timer.-fetchFrame :: TimedRes a -> a+fetchFrame :: Timed a -> a fetchFrame t = bl !! (fromIntegral $ tCurrTick t)     where           bl = concatMap (\(c, a) -> replicate (fromIntegral c) a) $ tSteps t --- | Return a list of all frames plus the display timelenght.-getFrames :: TimedRes a -> [(Integer, a)]+-- | Return a list of all frames plus their duration.+getFrames :: Timed a -> [(Integer, a)] getFrames t = tSteps t  -- todo having another input apart from []? maybe a function?  -- | Resets the timer to its original state.-reset :: TimedRes a -> TimedRes a+reset :: Timed a -> Timed a reset t = t { tCurrTick = 0,               tExpired = False,               tLoop = tOrigLoop t }
test/Control/Timer/TickSpec.hs view
@@ -12,6 +12,11 @@ spec :: Spec spec = do +  describe "creaTimer" $ do+    it "should get off with the right resource" $+      let st = creaTimer 'a' 'b' 1 in+      fetchFrame (tick st) `shouldBe` 'b'+   describe "creaTimedRes" $ do     it "does not allow creation of empty timed resources" $       E.evaluate (creaTimedRes AlwaysLoop [])@@ -19,11 +24,11 @@    describe "tick" $ do     it "ticks a simple timer" $-      let st = creaTimer 1 in+      let st = creaBoolTimer 1 in       isExpired (tick st) `shouldBe` True    describe "ticks" $ do-    let t = creaTimer 10+    let t = creaBoolTimer 10     it "fails on negative integer" $       E.evaluate (ticks (-3) t) `shouldThrow` anyException     it "performs a number of ticks" $@@ -34,7 +39,7 @@       isExpired (ticks 80 t) `shouldBe` True    describe "loops" $ do-    let t  = creaTimedRes (Times 2)  [(1,())]+    let t  = creaTimedRes (Times 2 Elapse)  [(1,())]     let ta = creaTimedRes AlwaysLoop [(1,())]     it "loops appropriately" $       isExpired (ticks 2 t) `shouldBe` True@@ -44,19 +49,24 @@       isExpired (ticks 1 t) `shouldBe` False    describe "isExpired" $ do-    let st = creaTimer 10+    let st = creaBoolTimer 10     it "checks if a timer is expired" $       isExpired (ticks 100 st) `shouldBe` True     it "complements isLive" $       isExpired (ticks 100 st) `shouldBe` not (isLive (ticks 100 st))+    it "expires on last frame with Reach" $+      let st1 = creaTimedRes (Times 1 Reach) [(1, 'a'), (1, 'b')] in+      isExpired (ticks 1 st1) `shouldBe` True    describe "fetchFrame" $ do-    let ta = creaTimedRes (Times 1) [(1,'a'), (2, 'b'), (1, 'c')]+    let ta = creaTimedRes (Times 1 Elapse) [(1,'a'), (2, 'b'), (1, 'c')]     it "gets the underlying resoure" $       fetchFrame (ticks 3 ta) `shouldBe` 'c'+    it "does not choke on last tick" $+      fetchFrame (ticks 10 ta) `shouldBe` 'c'    describe "reset" $ do-    let t = creaTimer 10+    let t = creaBoolTimer 10     it "resets the timer" $       reset (ticks 30 t) `shouldBe` t 
timers-tick.cabal view
@@ -1,5 +1,5 @@ name:                timers-tick-version:             0.2.0.0+version:             0.3.0.0 synopsis:            tick based timers description:         Tick-based timers and utilities, for games and                      discrete-time programs.