packages feed

ansi-terminal-game 1.0.0.0 → 1.1.0.0

raw patch · 13 files changed

+218/−51 lines, 13 filesdep +unidecodedep ~QuickCheckdep ~ansi-terminaldep ~bytestringbinary-addedPVP ok

version bump matches the API change (PVP)

Dependencies added: unidecode

Dependency ranges changed: QuickCheck, ansi-terminal, bytestring, random

API changes (from Hackage documentation)

+ Terminal.Game: (===) :: Plane -> Plane -> Plane
+ Terminal.Game: (|||) :: Plane -> Plane -> Plane
+ Terminal.Game: displaySize :: IO (Width, Height)
+ Terminal.Game: hcat :: [Plane] -> Plane
+ Terminal.Game: infixl 6 ===
+ Terminal.Game: playGameS :: Game s -> IO s
+ Terminal.Game: subPlane :: Plane -> Coords -> Coords -> Plane
+ Terminal.Game: textBoxLiquid :: String -> Width -> Plane
+ Terminal.Game: vcat :: [Plane] -> Plane
+ Terminal.Game: word :: String -> Plane
- Terminal.Game: (&) :: () => a -> (a -> b) -> b
+ Terminal.Game: (&) :: a -> (a -> b) -> b
- Terminal.Game: creaTimer :: () => a -> a -> Integer -> Timed a
+ Terminal.Game: creaTimer :: a -> a -> Integer -> Timed a
- Terminal.Game: creaTimerLoop :: () => a -> a -> Integer -> Timed a
+ Terminal.Game: creaTimerLoop :: a -> a -> Integer -> Timed a
- Terminal.Game: fetchFrame :: () => Timed a -> a
+ Terminal.Game: fetchFrame :: Timed a -> a
- Terminal.Game: getFrames :: () => Timed a -> [(Integer, a)]
+ Terminal.Game: getFrames :: Timed a -> [(Integer, a)]
- Terminal.Game: getStdGen :: IO StdGen
+ Terminal.Game: getStdGen :: MonadIO m => m StdGen
- Terminal.Game: isExpired :: () => Timed a -> Bool
+ Terminal.Game: isExpired :: Timed a -> Bool
- Terminal.Game: lapse :: () => Timed a -> Timed a
+ Terminal.Game: lapse :: Timed a -> Timed a
- Terminal.Game: reset :: () => Timed a -> Timed a
+ Terminal.Game: reset :: Timed a -> Timed a
- Terminal.Game: tick :: () => Timed a -> Timed a
+ Terminal.Game: tick :: Timed a -> Timed a
- Terminal.Game: ticks :: () => Integer -> Timed a -> Timed a
+ Terminal.Game: ticks :: Integer -> Timed a -> Timed a

Files

ansi-terminal-game.cabal view
@@ -1,25 +1,26 @@ name:                ansi-terminal-game-version:             1.0.0.0+version:             1.1.0.0 synopsis:            sdl-like functions for terminal applications, based on                      ansi-terminal description:         Library which aims to replicate standard 2d game                      functions (blit, ticks, timers, etc.) in a terminal-                     setting.+                     setting; features double buffering to optimise+                     performance.                      Aims to be cross compatible (based on "ansi-terminal",                      no unix-only dependencies), practical.                      See @examples@ folder for some minimal programs.  A                      full game: <http://www.ariis.it/static/articles/venzone/page.html venzone>.-homepage:            none-yet+homepage:            http://www.ariis.it/static/articles/ansi-terminal-game/page.html license:             GPL-3 license-file:        LICENSE author:              Francesco Ariis maintainer:          fa-ml@ariis.it-copyright:           © 2017-2019 Francesco Ariis+copyright:           © 2017-2020 Francesco Ariis category:            Game build-type:          Simple extra-source-files:  README,                      changes.txt,-                     test/alone-recors-test.gr+                     test/alone-record-test.gr cabal-version:       >=1.10  flag examples@@ -41,25 +42,27 @@                        Terminal.Game.Layer.Object.Narrate,                        Terminal.Game.Layer.Object.Record,                        Terminal.Game.Layer.Object.Test,+                       Terminal.Game.Character,                        Terminal.Game.Draw,                        Terminal.Game.Utils,                        Terminal.Game.Plane,                        Terminal.Game.Random,                        Terminal.Game.Timer   build-depends:       base == 4.*,-                       ansi-terminal >= 0.8 && < 0.11,+                       ansi-terminal == 0.11.*,                        array == 0.5.*,-                       bytestring == 0.10.*,+                       bytestring >= 0.10 && < 0.12,                        cereal == 0.5.*,                        clock >= 0.7 && < 0.9,                        exceptions == 0.10.*,                        linebreak == 1.0.*,                        mintty == 0.1.*,                        mtl == 2.2.*,-                       QuickCheck == 2.13.*,-                       random >= 1.1 && < 1.2,+                       QuickCheck >= 2.13 && < 2.15,+                       random >= 1.1 && < 1.3,                        split == 0.2.*,                        terminal-size == 0.3.*,+                       unidecode >= 0.1.0 && < 0.2,                        timers-tick > 0.4.2 && < 0.5   hs-source-dirs:      src   default-language:    Haskell2010@@ -77,6 +80,7 @@   other-modules:       Alone,                        Terminal.Game,                        Terminal.Game.Animation,+                       Terminal.Game.Character,                        Terminal.Game.Draw,                        Terminal.Game.DrawSpec,                        Terminal.Game.Layer.Imperative,@@ -93,18 +97,19 @@                        Terminal.Game.Random,                        Terminal.Game.PlaneSpec   build-depends:       base == 4.*,-                       ansi-terminal >= 0.8 && < 0.11,+                       ansi-terminal == 0.11.*,                        array == 0.5.*,-                       bytestring == 0.10.*,+                       bytestring >= 0.10 && < 0.12,                        cereal == 0.5.*,                        clock >= 0.7 && < 0.9,                        exceptions == 0.10.*,                        linebreak == 1.0.*,                        mtl == 2.2.*,-                       QuickCheck == 2.13.*,-                       random >= 1.1 && < 1.2,+                       QuickCheck >= 2.13 && < 2.15,+                       random >= 1.1 && < 1.3,                        split == 0.2.*,                        terminal-size == 0.3.*,+                       unidecode >= 0.1.0 && < 0.2,                        timers-tick > 0.4.2 && < 0.5                        -- the above plus hspec                        , hspec
changes.txt view
@@ -1,3 +1,13 @@+1.1.0.0+-------++- Added Plane justapoxition functions (===, |||, vcat, hcat).+- Added `word` and and `textBoxLiquid` drawing functions.+- Added `subPlane`, `displaySize` Plane functions.+- Removed unused `trimPlane`.+- Sanitized non-ASCII chars on Win32 console.+- Wed 03 Feb 2021 18:41:20 CET+ 1.0.0.0 ------- 
platform-dep/windows/Terminal/Game/Utils.hs view
@@ -4,10 +4,10 @@ -------------------------------------------------------------------------------- {-# LANGUAGE ForeignFunctionInterface #-} --- see .cabal conditionals for more info- -- horrible horrible horrible hack to make unbuffered input -- work on Windows (and win32console check)+-- todo [windows] https://gitlab.haskell.org/ghc/ghc/-/issues/2189#note_290480+-- e anche i caratteri  module Terminal.Game.Utils (inputCharTerminal,                             isWin32Console )@@ -20,7 +20,7 @@ inputCharTerminal :: IO Char inputCharTerminal = getCharWindows --- no idea but unsafe breaks it+-- no idea why, but unsafe breaks it getCharWindows :: IO Char getCharWindows = fmap (C.chr . fromEnum) c_getch foreign import ccall safe "conio.h getch"
src/Terminal/Game.hs view
@@ -18,10 +18,15 @@ -- Only module to be imported.  -- todo test that handlers are closed in case of errr [test]+-- todo invert # invert (doppio) si elimina? [test] [design]+-- todo schermo piccolo, si centra verticalmente? [test] --- xxx timer in contrib o altro modulo?--- xxx qptain e jimreed, implement normal movement--- todo add readme+-- todo timer in contrib o altro modulo?+-- todo qptain e jimreed, implement normal movement+-- todo [design] javascript backend+-- todo controlla caratteri strani su windows, prova con morganw e ibispi+-- todo boxratio, blitcenter e blitTL etc in ansi-terminal-game?+-- todo autoresize terminal (or get-term-size?)  module Terminal.Game ( -- * Running                        FPS,@@ -33,7 +38,7 @@                        -- | Some convenient function dealing with                        -- Timers ('Timed') and 'Animation's.                        ---                       -- Usage of these is not mandatry: 'Game' is+                       -- Usage of these is not mandatory: 'Game' is                        -- parametrised over any state @s@, you are free                        -- to implement game logic as you prefer. @@ -63,6 +68,10 @@                        -- * Drawing                        -- | To get to the gist of drawing, check the                        -- documentation for '%'.+                       --+                       -- Blitting on screen is double-buffered and diff'd+                       -- (at each frame, only cells with changed character+                       -- will be redrawn).                         -- ** Plane                        Plane,@@ -79,9 +88,11 @@                         -- ** Draw                        Draw,-                       (%), (#), (&),+                       (%), (&), (#),+                       subPlane,                        mergePlanes,-                       cell, box, textBox,+                       (|||), (===), hcat, vcat,+                       cell, word, box, textBox, textBoxLiquid,                        Color(..), ColorIntensity(..),                        color, bold, invert, @@ -91,6 +102,10 @@                        recordGame,                        readRecord,                        narrateGame,+                       playGameS,++                       -- * Utility+                       Terminal.Game.displaySize,                        errorPress                         -- * Cross platform@@ -102,7 +117,7 @@ import Terminal.Game.Animation import Terminal.Game.Draw import Terminal.Game.Layer.Imperative-import Terminal.Game.Layer.Object+import Terminal.Game.Layer.Object as O import Terminal.Game.Plane import Terminal.Game.Random @@ -126,3 +141,7 @@ --   between vivid/dull; do not base your game mechanics on that --   difference. +-- | /Usable/ terminal display size (on Win32 console the last line is+-- set aside for input).+displaySize :: IO (Width, Height)+displaySize = O.displaySize
+ src/Terminal/Game/Character.hs view
@@ -0,0 +1,47 @@+module Terminal.Game.Character where++import Data.Char        as C+import Text.Unidecode   as D+import System.IO.Unsafe as U+++import Terminal.Game.Utils++-- TODO [test] this need to be tested on Win, both newer ones and older+--      versions (newer should display non-changed chars, old should not+--      crash).++-- Non ASCII character still cause crashes on Win32 console (see this+-- report: https://gitlab.haskell.org/ghc/ghc/issues/7593 ).+-- We provide a function to substitute them when playing on Win32+-- console, with another appropriate chatacter.++win32SafeChar :: Char -> Char+win32SafeChar c | areWeWin32 = toASCII c+                | otherwise  = c+    where+          areWeWin32 :: Bool+          areWeWin32 = unsafePerformIO isWin32Console++-- ANCILLARIES --++toASCII :: Char -> Char+toASCII c | C.isAscii c         = c+          | Just cm <- lu       = cm    -- hand-made substitution+          | [cu] <- unidecode c = cu    -- unidecode+          | otherwise           = '?'   -- all else failing+    where+          lu = lookup c subDictionary++subDictionary :: [(Char, Char)]+subDictionary = [ -- various open/close quotes+                  ('«', '<'),+                  ('»', '>'),+                  ('“', '\''),+                  ('”', '\''),+                  ('‘', '\''),+                  ('’', '\''),++                  -- typographical marks+                  ('—', '-') ]+
src/Terminal/Game/Draw.hs view
@@ -70,7 +70,33 @@           addPlane :: Plane -> (Coords, Plane) -> Plane           addPlane bp (cs, tp) = bp F.& cs % tp +-- | Place two 'Plane's side-by-side, horizontally.+(|||) :: Plane -> Plane -> Plane+(|||) a b = let (wa, ha) = planeSize a+                (wb, hb) = planeSize b+            in mergePlanes (blankPlane (wa + wb) (max ha hb))+                           [((1,1),    a),+                            ((1,wa+1), b)] +-- | Place two 'Plane's side-by-side, vertically.+(===) :: Plane -> Plane -> Plane+(===) a b = let (wa, ha) = planeSize a+                (wb, hb) = planeSize b+            in mergePlanes (blankPlane (max wa wb) (ha + hb))+                           [((1,1),    a),+                            ((ha+1,1), b)]++-- | Place a list of 'Plane's side-by-side, horizontally.+hcat :: [Plane] -> Plane+hcat ps = L.foldl' (|||) (blankPlane 0 0) ps++-- | Place a list of 'Plane's side-by-side, vertically.+vcat :: [Plane] -> Plane+vcat ps = L.foldl' (===) (blankPlane 0 0) ps++infixl 6 |||, ===++ ------------ -- STYLES -- ------------@@ -103,20 +129,32 @@ cell :: Char -> Plane cell ch = box ch 1 1 +-- | @1xn@ 'Plane' with a word in it. If you need to import multiline+-- ASCII art, check 'stringPlane' and 'stringPlaneTrans'.+word :: String -> Plane+word w = seqCellsDim (L.genericLength w) 1 cells+    where+          cells = zip (zip (repeat 1) [1..]) w+ -- opaque :: Plane -> Plane -- opaque p = pastePlane p (box ' ' White w h) (1, 1) --     where --           (w, h) = pSize p --- xxx li vogliamo davvero transparent?--- | A text-box. Assumes ' ' are transparent.+-- | A text-box. Assumes @' '@s are transparent. textBox :: String -> Width -> Height -> Plane-textBox cs w h = transparent+textBox cs w h = let pt = textBoxLiquid cs w+                 in  blankPlane w h F.& (1, 1) % pt++-- | Like 'textBox', but tall enough to fit @String@.+textBoxLiquid :: String -> Width -> Plane+textBoxLiquid cs w = transparent     where           -- hypenathion           hyp = Nothing -- Just english_GB           bf  = BreakFormat (fromIntegral w) 4 '-' hyp-          hcs = breakStringLn bf (take (fromIntegral $ w*h) cs)+          hcs = breakStringLn bf cs+          h   = L.genericLength hcs            f :: [String] -> [(Coords, Char)]           f css = concatMap (uncurry rf) (zip [1..] css)
src/Terminal/Game/Layer/Imperative.hs view
@@ -22,12 +22,16 @@  -- | Game definition datatype, parametrised on your gamestate. The two -- most important elements are the function dealing with logic and the--- drawing one. Check @alone@ (you can compile it with @cabal--- new-run -f examples alone@) to see a simple game in action.+-- drawing one. Check @alone@ (you can compile it with @cabal run -f+-- examples alone@) to see a simple game in action. data Game s = Game {-        gScreenWidth   :: Width,           -- ^Gamescreen size, width.-        gScreenHeight  :: Height,          -- ^Gamescreen size, height.-        gFPS           :: FPS,             -- ^Frames per second.+        gScreenWidth   :: Width,           -- ^Gamescreen width, in columns.+        gScreenHeight  :: Height,          -- ^Gamescreen height, in rows.+        gFPS           :: FPS,             -- ^Frames per second. Since the+                                           -- 2D «char canvas» is coarse, you+                                           -- do not need a high value (e.g.+                                           -- 13 FPS is enough for action+                                           -- games).         gInitState     :: s,               -- ^Initial state of the game.         gLogicFunction :: s -> Event -> s, -- ^Logic function.         gDrawFunction  :: s -> Plane,      -- ^Draw function.@@ -47,6 +51,10 @@ playGame :: Game s -> IO () playGame g = () <$ runGIO (runGameGeneral g) +-- | As 'playGame', but do not discard state.+playGameS :: Game s -> IO s+playGameS g = runGIO (runGameGeneral g)+ -- | Tests a game in a /pure/ environment. You can -- supply the 'Event's yourself or use 'recordGame' to obtain them. testGame :: Game s -> [Event] -> s@@ -62,7 +70,7 @@ -- 'testGame', the playthrough will be displayed on screen. Useful when a -- test fails and you want to see how. ----- See this in action with  @cabal new-run -f examples alone-playback@.+-- See this in action with  @cabal run -f examples alone-playback@. narrateGame :: Game s -> [Event] -> IO s narrateGame g e = runReplay (runGameGeneral g) e     -- xxx replaygame is very difficult to test@@ -112,8 +120,9 @@  -- | Wraps an @IO@ computation so that any 'error' gets displayed along -- with a @\<press any key to quit\>@ prompt.--- Some terminals shut-down immediately upon program end: 'errorPress'--- makes it easier to beta-test games on those terminals.+-- Some terminals shut-down immediately upon program end; adding+-- @errorPress@ to 'playGame' makes it easier to beta-test games on those+-- terminals. errorPress :: IO a -> IO a errorPress m = E.catch m errorDisplay     where
src/Terminal/Game/Plane.hs view
@@ -9,6 +9,8 @@ 
 module Terminal.Game.Plane where
 
+import Terminal.Game.Character
+
 import qualified Data.Array          as A
 import qualified Data.List           as L
 import qualified Data.List.Split     as LS
@@ -50,7 +52,9 @@ ----------
 
 creaCell :: Char -> Cell
-creaCell ch = CellChar ch False False Nothing
+creaCell ch = CellChar chm False False Nothing
+    where
+          chm = win32SafeChar ch
 
 colorCell :: CA.Color -> CA.ColorIntensity -> Cell -> Cell
 colorCell k i (CellChar c b r _) = CellChar c b r (Just (k, i))
@@ -113,14 +117,26 @@           solid (_, Transparent) = False
           solid _                = True
 
--- | Trims 'Plane' @p@ dimensions to @w@ and @h@, if needed.
-trimPlane :: Plane -> Width -> Height -> Plane
-trimPlane p wt ht = pastePlane p (blankPlane w h) (1, 1)
+-- | Cut out a plane by top-left and bottom-right coordinates.
+subPlane :: Plane -> Coords -> Coords -> Plane
+subPlane p (r1, c1) (r2, c2)
+        | r1 > r2 || c1 > c2 = err (r1, c1) (r2, c2)
+        | otherwise          =
+            let cs       = assocsPlane p
+                fs       = filter f cs
+                (pw, ph) = planeSize p
+                (w, h)   = (min pw (c2-c1+1), min ph (r2-r1+1))
+            in listPlane (h, w) (map snd fs)
     where
-          (wp, hp) = planeSize p
+          f ((rw, cw), _) = rw >= r1 && rw <= r2 &&
+                            cw >= c1 && cw <= c2
 
-          w = min wt wp
-          h = min ht hp
+          err p1 p2 = error ("subPlane: top-left point " ++ show p1 ++
+                             " > bottom-right point " ++ show p2 ++ ".")
+
+-- xxx crea modulo primitive in cui fai pastrocci ed esponi solo poche
+--     funzioni di base da quello [refactor]
+
 
 -------------
 -- INQUIRE --
test/Terminal/Game/DrawSpec.hs view
@@ -16,3 +16,13 @@       mergePlanes (stringPlane "aaa\naaa\naaa")                   [((2,2), cell 'b')] `shouldBe`                     stringPlane "aaa\naba\naaa"++  describe "textBox/textBoxLiquid" $ do+    let s  = "las rana in Spa"+        w  = 6+        ps = textBox s w 2+        pl = textBoxLiquid s w+    it "textBox follows specific size" $+      planeSize ps `shouldBe` (6, 2)+    it "textBoxLiquid fits the whole string" $+      planeSize pl `shouldBe` (6, 3)
test/Terminal/Game/Layer/ImperativeSpec.hs view
@@ -29,7 +29,7 @@       testGame g es `shouldBe` (3, True, 2)    describe "testGame" $ do-    r <- runIO $ readRecord "test/alone-recors-test.gr"+    r <- runIO $ readRecord "test/alone-record-test.gr"     it "tests a game" $         testGame aloneInARoom r `shouldBe` MyState (20, 66) Stop True     it "does not hang on empty/unclosed input" $
test/Terminal/Game/PlaneSpec.hs view
@@ -4,7 +4,9 @@ import Terminal.Game.Plane import Terminal.Game.Draw +import qualified Control.Exception as E + spec :: Spec spec = do @@ -22,14 +24,6 @@     it "pastes a simple plane onto another" $       pastePlane (cell 'a') (cell 'b') (1,1) `shouldBe` cell 'a' -  describe "trimPlane" $ do-    it "trims a plane" $-      trimPlane (stringPlane "ab\nc") 1 2 `shouldBe` stringPlane "a\nc"-    it "leaves it untouched on bigger dimensions" $-      trimPlane (stringPlane "ab\nc") 1 3 `shouldBe` stringPlane "a\nc"-    it "other dimesion too" $-      trimPlane (stringPlane "ab\nc") 8 9 `shouldBe` stringPlane "ab\nc"-   describe "stringPlane" $ do     it "creates plane from spec" $       stringPlane ".\n.." `shouldBe` testPlane@@ -44,3 +38,22 @@     it "updates a Plane" $       updatePlane ma [((1,1), creaCell 'x')] `shouldBe` mb +  describe "subPlane" $ do+    let pa = word "prova" === word "fol"+    it "cuts out a plane" $+      paperPlane (subPlane pa (1, 1) (2, 1)) `shouldBe` "p\nf\n"+    it "does not crash on OOB" $+      planeSize (subPlane pa (1, 1) (10, 10)) `shouldBe` (5, 2)+    it "errs on emptycell" $+      E.evaluate (subPlane pa (2, 3) (1, 1)) `shouldThrow`+        errorCall "subPlane: top-left point (2,3) > bottom-right point (1,1)."+    it "but not on a single cell" $+      subPlane pa (2, 3) (2, 3) `shouldBe` cell 'l'++  describe "hcat/vcat" $ do+    let pa = blankPlane 2 1+        pb = blankPlane 3 4+    it "concats planes horizontally with hcat" $+      planeSize (hcat [pa, pb]) `shouldBe` (5, 4)+    it "concats planes horizontally with vcat" $+      planeSize (vcat [pa, pb]) `shouldBe` (3, 5)
+ test/alone-record-test.gr view

binary file changed (absent → 194 bytes)

− test/alone-recors-test.gr

binary file changed (194 → absent bytes)