timers-tick (empty) → 0.1.0.0
raw patch · 7 files changed
+308/−0 lines, 7 filesdep +basedep +hspecsetup-changed
Dependencies added: base, hspec
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- changes.txt +5/−0
- src/Control/Timer/Tick.hs +166/−0
- test/Control/Timer/TickSpec.hs +62/−0
- test/Tests.hs +1/−0
- timers-tick.cabal +42/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2018, Francesco Ariis++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Francesco Ariis nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ changes.txt view
@@ -0,0 +1,5 @@+0.1.0.0+-------++- Added basic animation/timer functionality.+- Released on: Wed 14 Mar 2018 13:36:06 CET
+ src/Control/Timer/Tick.hs view
@@ -0,0 +1,166 @@+--------------------------------------------------------------------------------+-- |+-- Module : Control.Timer.Tick+-- Copyright : (C) 2018 Francesco Ariis+-- License : BSD3 (see LICENSE file)+--+-- Maintainer : Francesco Ariis <fa-ml@ariis.it>+-- Stability : provisional+-- Portability : portable+--+-- Timers and timed resources (animations, etc.) utilities for tick-based+-- programs.+--+--------------------------------------------------------------------------------+++module Control.Timer.Tick ( -- * Simple timers+ creaTimer,+ Timer,+ -- * Timed Resources+ TimedRes,+ creaTimedRes,+ Loop(..),+ -- * Use+ tick,+ ticks,+ reset,+ -- * Query+ isLive,+ isExpired,+ fetch+ )+++ where++-----------+-- TYPES --+-----------++-- | A timed resource is a timer which, at any given moment, points to+-- a specific item (like an animation).+--+-- 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!+-- @+data TimedRes a = TimedRes { -- init+ tSteps :: [TimerStep a],+ tLoop :: Loop,+ tOrigLoop :: Loop,++ -- convenience+ tMaxTicks :: Integer,++ -- curr+ tCurrTick :: Integer,+ tExpired :: Bool+ }+ deriving (Show, Eq)++type TimerStep a = (Integer, a)++-- | Number of times to repeat the animation.+data Loop = Times Integer -- currentLoop and maxLoop+ | AlwaysLoop+ deriving (Show, Eq)++-- todo Monoid (or semigroup) <> for timers [2.0]++------------+-- CREATE --+------------++type Timer = TimedRes ()++-- | Creates a 'Timer' expiring in @x@ ticks.+--+-- Example:+--+-- @+-- main = count (creaTimer 4)+-- where+-- count t | isExpired t = putStrLn "Over!"+-- | otherwise = do putStrLn "Ticking."+-- count (tick t)+--+-- -- λ> main+-- -- Ticking.+-- -- Ticking.+-- -- Ticking.+-- -- Ticking.+-- -- Over!+-- @+creaTimer :: Integer -> Timer+creaTimer c = creaTimedRes (Times 1) [(c, ())]++-- | Creates a time-based resource, like an animation.+creaTimedRes :: Loop -> [(Integer, a)] -> TimedRes a+creaTimedRes _ [] = error "Cannot create an empty TimedRes"+creaTimedRes l ss = TimedRes ss l l+ (sum . map fst $ ss)+ 0 False+++-------------+-- OPERATE --+-------------++-- | Ticks the timer (one step).+tick :: TimedRes a -> TimedRes a+tick t | isExpired t = t+ | otherwise =+ let t' = t { tCurrTick = succ (tCurrTick t) } in++ if tCurrTick t' == tMaxTicks t'+ then maxed t'+ else t'+ 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 }++-- | Ticks the timer (multiple steps).+ticks :: Integer -> TimedRes a -> TimedRes 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+isLive t = not $ tExpired t++-- | Checks wheter the timer is expired (an expired timer will not+-- respond to 'tick').+isExpired :: TimedRes a -> Bool+isExpired t = tExpired t++-- | Fetches the current resource of the timer.+fetch :: TimedRes a -> a+fetch t = bl !! (fromIntegral $ tCurrTick t)+ where+ bl = concatMap (\(c, a) -> replicate (fromIntegral c) a) $ tSteps t++-- todo having another input apart from []? maybe a function?++-- | Resets the timer to its original state.+reset :: TimedRes a -> TimedRes a+reset t = t { tCurrTick = 0,+ tExpired = False,+ tLoop = tOrigLoop t }++-- todo elapsed time? ticking time?
+ test/Control/Timer/TickSpec.hs view
@@ -0,0 +1,62 @@+module Control.Timer.TickSpec where++import Control.Timer.Tick++import Test.Hspec+import qualified Control.Exception as E+++main :: IO ()+main = hspec spec++spec :: Spec+spec = do++ describe "creaTimedResource" $ do+ it "does not allow creation of empty timed resources" $+ E.evaluate (creaTimedResource AlwaysLoop [])+ `shouldThrow` anyException++ describe "tick" $ do+ it "ticks a simple timer" $+ let st = creaTimer 1 in+ isExpired (tick st) `shouldBe` True++ describe "ticks" $ do+ let t = creaTimer 10+ it "fails on negative integer" $+ E.evaluate (ticks (-3) t) `shouldThrow` anyException+ it "performs a number of ticks" $+ isExpired (ticks 10 t) `shouldBe` True+ it "does not overtick" $+ isExpired (ticks 9 t) `shouldBe` False+ it "does not choke on extra tick" $+ isExpired (ticks 80 t) `shouldBe` True++ describe "loops" $ do+ let t = creaTimedResource (Times 2) [(1,())]+ let ta = creaTimedResource AlwaysLoop [(1,())]+ it "loops appropriately" $+ isExpired (ticks 2 t) `shouldBe` True+ it "loops appropriately (forever)" $+ isExpired (ticks 20 ta) `shouldBe` False+ it "expires appropriately" $+ isExpired (ticks 1 t) `shouldBe` False++ describe "isExpired" $ do+ let st = creaTimer 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))++ describe "fetch" $ do+ let ta = creaTimedResource (Times 1) [(1,'a'), (2, 'b'), (1, 'c')]+ it "gets the underlying resoure" $+ fetch (ticks 3 ta) `shouldBe` 'c'++ describe "reset" $ do+ let t = creaTimer 10+ it "resets the timer" $+ reset (ticks 30 t) `shouldBe` t+
+ test/Tests.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ timers-tick.cabal view
@@ -0,0 +1,42 @@+name: timers-tick+version: 0.1.0.0+synopsis: tick based timers+description: Tick-based timers and utilities, for games and+ discrete-time programs.+ Includes types and functions to work with sequence-based+ resources (e.g. animations, frames).+license: BSD3+license-file: LICENSE+author: Francesco Ariis+maintainer: fa-ml@ariis.it+copyright: © 2018 Francesco Ariis+category: Control+build-type: Simple+extra-source-files: changes.txt+cabal-version: >=1.10++flag developer+ description: developer mode - warnings let compilation fail+ manual: True+ default: False++library+ default-language: Haskell2010+ exposed-modules: Control.Timer.Tick+ build-depends: base == 4.*+ hs-source-dirs: src++test-suite test+ default-language: Haskell2010+ if flag(developer)+ ghc-options: -Wall -Werror+ else+ ghc-options: -Wall+ HS-Source-Dirs: test, src+ main-is: Tests.hs+ build-depends: base == 4.*+ , hspec == 2.4.*+ other-modules: Control.Timer.Tick,+ Control.Timer.TickSpec+ type: exitcode-stdio-1.0+