a one dimensional cellular automata, using comonads
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

188 Zeilen
5.0KB

  1. {-# LANGUAGE OverloadedStrings #-}
  2. module Main where
  3. import Control.Monad
  4. import System.Process
  5. import System.Random
  6. import System.Console.GetOpt
  7. import System.Environment(getArgs, getProgName)
  8. import Data.Maybe (fromMaybe)
  9. import Comonad
  10. import Spaces.Space2
  11. import Spaces.Space1
  12. import Automata
  13. import Brick
  14. import Brick.BChan (newBChan, writeBChan)
  15. import qualified Brick.Widgets.Border as B
  16. import qualified Brick.Widgets.Border.Style as BS
  17. import qualified Brick.Widgets.Center as C
  18. import qualified Graphics.Vty as V
  19. import Control.Applicative
  20. import Control.Monad.IO.Class
  21. import Control.Concurrent
  22. import Control.DeepSeq
  23. -----------------
  24. -- brick stuff --
  25. -----------------
  26. data Tick = Tick
  27. type Name = ()
  28. -- App definition
  29. app :: Int -> Int -> App (Space2 CellState) Tick Name
  30. app h w = App { appDraw = drawUI h w
  31. , appChooseCursor = neverShowCursor
  32. , appHandleEvent = handleEvent
  33. , appStartEvent = return
  34. , appAttrMap = const theMap
  35. }
  36. -- Handling events
  37. theMap :: AttrMap
  38. theMap = attrMap V.defAttr
  39. [ (rockAttr, V.red `on` V.blue)
  40. , (scissorsAttr, V.green `on` V.red)
  41. , (paperAttr, V.blue `on` V.green)
  42. ]
  43. ---------------
  44. -- rng stuff --
  45. ---------------
  46. -- takes a generator and returns
  47. -- an infinite list of bools
  48. ilobs :: StdGen -> [Bool]
  49. ilobs rng = b : (ilobs r)
  50. where
  51. (b,r) = random rng
  52. -----------------
  53. -- gross io bs --
  54. -----------------
  55. -- everything below this line deals with
  56. -- input/output, and is therefore gross
  57. -- i will clean this up one day, but it
  58. -- hurts my soul.
  59. ------------------------
  60. -- command line flags --
  61. ------------------------
  62. -- structure containing the programs options
  63. data Options = Options
  64. { optWidth :: Int
  65. , optGenerations :: Int
  66. , optHeight :: Int
  67. } deriving Show
  68. -- the default options for the program
  69. -- the width and generations are injected
  70. -- and intended to be gotten at runtime
  71. -- to match the window dimensions
  72. defaultOptions :: Int -> Int -> Options
  73. defaultOptions w h = Options
  74. { optWidth = w
  75. , optGenerations = 40
  76. , optHeight = h
  77. }
  78. -- the avaliable options
  79. options :: [OptDescr (Options -> Options)]
  80. options =
  81. [ Option ['w'] ["width"]
  82. (ReqArg (\w opts -> opts { optWidth = (read w) }) "WIDTH")
  83. "term width"
  84. , Option ['g'] ["generations"]
  85. (ReqArg (\t opts -> opts { optGenerations = (read t) }) "GENERATIONS")
  86. "time steps to simulate"
  87. , Option ['h'] ["height"]
  88. (ReqArg (\t opts -> opts { optHeight = (read t) }) "HEIGHT")
  89. "term height"
  90. ]
  91. -- parse the options into the structure
  92. -- erroring if encountering a flag not known to us
  93. parseArgs :: IO Options
  94. parseArgs = do
  95. argv <- getArgs
  96. progName <- getProgName
  97. tw <- readProcess "tput" [ "cols" ] ""
  98. th <- readProcess "tput" [ "lines" ] ""
  99. case getOpt RequireOrder options argv of
  100. (opts, [], []) -> return (foldl (flip id) (defaultOptions (read tw) (read th)) opts)
  101. (_, _, errs) -> ioError (userError (concat errs ++ helpMessage))
  102. where
  103. header = "Usage: " ++ progName ++ " [OPTION...]"
  104. helpMessage = usageInfo header options
  105. initGame :: IO (Space2 CellState)
  106. initGame = do
  107. rng <- getStdGen
  108. return $ createRandSpace2 rng
  109. ---------------
  110. -- main loop --
  111. ---------------
  112. main :: IO ()
  113. main = do
  114. options <- parseArgs
  115. let w = (optWidth options)
  116. let h = (optHeight options)
  117. chan <- newBChan 1
  118. forkIO $ forever $ do
  119. writeBChan chan Tick
  120. threadDelay 100000
  121. g <- initGame
  122. let buildVty = V.mkVty V.defaultConfig
  123. initialVty <- buildVty
  124. void $ customMain initialVty buildVty (Just chan) (app h w) (clamp2 w h g)
  125. handleEvent :: (Space2 CellState) -> BrickEvent Name Tick -> EventM Name (Next (Space2 CellState))
  126. handleEvent g (AppEvent Tick) = continue $ step rps g
  127. handleEvent g (VtyEvent (V.EvKey (V.KChar 'q') [])) = halt g
  128. handleEvent g _ = continue g
  129. drawUI :: Int -> Int -> Space2 CellState -> [Widget Name]
  130. drawUI h w g = [ C.center $ drawGrid h w g ]
  131. drawGrid :: Int -> Int -> Space2 CellState -> Widget Name
  132. drawGrid h w g = vBox rows
  133. where
  134. bw = mat2 g
  135. rows = [ hBox $ cellsInRow r | r <- bw ]
  136. cellsInRow y = map drawCell y
  137. drawCell :: CellState -> Widget Name
  138. drawCell Paper = withAttr paperAttr $ str " "
  139. drawCell Scissors = withAttr scissorsAttr $ str " "
  140. drawCell Rock = withAttr rockAttr $ str " "
  141. rockAttr, scissorsAttr, paperAttr :: AttrName
  142. rockAttr = "rockAttr"
  143. paperAttr = "paperAttr"
  144. scissorsAttr = "scissorsAttr"
  145. createRandSpace :: Random a => StdGen -> Space a
  146. createRandSpace rng =
  147. Space (tail $ map snd $ iterate f (r1, (fst (random rng))))
  148. (fst (random rng))
  149. (tail $ map snd $ iterate f (r2, (fst (random rng))))
  150. where
  151. f (r,b) = let (nb,nr) = (random r) in (nr,nb)
  152. (r1,r2) = split rng
  153. createRandSpace2 :: Random a => StdGen -> Space2 a
  154. createRandSpace2 rng =
  155. Space2 (tail $ map snd $ iterate f (r1, (createRandSpace r1)))
  156. (createRandSpace rng)
  157. (tail $ map snd $ iterate f (r2, (createRandSpace r2)))
  158. where
  159. f (r,s) = let (nr1,nr2) = split r in (nr2, (createRandSpace nr1))
  160. (r1,r2) = split rng