2021-04-19 22:25:18 -04:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
2021-04-17 21:29:09 -04:00
|
|
|
module Main where
|
|
|
|
|
|
|
|
import Control.Monad
|
|
|
|
import System.Process
|
|
|
|
import System.Random
|
|
|
|
import System.Console.GetOpt
|
|
|
|
import System.Environment(getArgs, getProgName)
|
|
|
|
import Data.Maybe (fromMaybe)
|
2021-04-19 22:25:18 -04:00
|
|
|
import Comonad
|
2021-04-20 03:44:30 -04:00
|
|
|
import Spaces.Space2
|
|
|
|
import Spaces.Space1
|
2021-04-19 22:25:18 -04:00
|
|
|
import Automata
|
|
|
|
import Brick
|
|
|
|
import Brick.BChan (newBChan, writeBChan)
|
|
|
|
import qualified Brick.Widgets.Border as B
|
|
|
|
import qualified Brick.Widgets.Border.Style as BS
|
|
|
|
import qualified Brick.Widgets.Center as C
|
|
|
|
import qualified Graphics.Vty as V
|
|
|
|
import Control.Applicative
|
|
|
|
import Control.Monad.IO.Class
|
|
|
|
import Control.Concurrent
|
|
|
|
import Control.DeepSeq
|
2021-04-17 21:29:09 -04:00
|
|
|
|
2021-04-19 22:25:18 -04:00
|
|
|
-----------------
|
|
|
|
-- brick stuff --
|
|
|
|
-----------------
|
2021-04-17 21:29:09 -04:00
|
|
|
|
2021-04-19 22:25:18 -04:00
|
|
|
data Tick = Tick
|
|
|
|
type Name = ()
|
2021-04-19 04:17:34 -04:00
|
|
|
|
2021-04-19 22:25:18 -04:00
|
|
|
-- App definition
|
2021-04-17 21:29:09 -04:00
|
|
|
|
2021-04-19 22:25:18 -04:00
|
|
|
app :: Int -> Int -> App (Space2 CellState) Tick Name
|
|
|
|
app h w = App { appDraw = drawUI h w
|
|
|
|
, appChooseCursor = neverShowCursor
|
|
|
|
, appHandleEvent = handleEvent
|
|
|
|
, appStartEvent = return
|
|
|
|
, appAttrMap = const theMap
|
|
|
|
}
|
2021-04-17 21:29:09 -04:00
|
|
|
|
2021-04-19 22:25:18 -04:00
|
|
|
-- Handling events
|
2021-04-17 21:29:09 -04:00
|
|
|
|
2021-04-19 22:25:18 -04:00
|
|
|
theMap :: AttrMap
|
|
|
|
theMap = attrMap V.defAttr
|
|
|
|
[ (rockAttr, V.red `on` V.blue)
|
|
|
|
, (scissorsAttr, V.green `on` V.red)
|
|
|
|
, (paperAttr, V.blue `on` V.green)
|
|
|
|
]
|
2021-04-17 21:29:09 -04:00
|
|
|
|
|
|
|
---------------
|
|
|
|
-- rng stuff --
|
|
|
|
---------------
|
|
|
|
|
|
|
|
-- takes a generator and returns
|
|
|
|
-- an infinite list of bools
|
|
|
|
ilobs :: StdGen -> [Bool]
|
|
|
|
ilobs rng = b : (ilobs r)
|
|
|
|
where
|
|
|
|
(b,r) = random rng
|
|
|
|
|
|
|
|
-----------------
|
|
|
|
-- gross io bs --
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
-- everything below this line deals with
|
|
|
|
-- input/output, and is therefore gross
|
|
|
|
-- i will clean this up one day, but it
|
|
|
|
-- hurts my soul.
|
|
|
|
|
|
|
|
------------------------
|
|
|
|
-- command line flags --
|
|
|
|
------------------------
|
|
|
|
|
|
|
|
-- structure containing the programs options
|
|
|
|
data Options = Options
|
|
|
|
{ optWidth :: Int
|
|
|
|
, optGenerations :: Int
|
2021-04-19 04:17:34 -04:00
|
|
|
, optHeight :: Int
|
2021-04-17 21:29:09 -04:00
|
|
|
} deriving Show
|
|
|
|
|
|
|
|
-- the default options for the program
|
|
|
|
-- the width and generations are injected
|
|
|
|
-- and intended to be gotten at runtime
|
|
|
|
-- to match the window dimensions
|
|
|
|
defaultOptions :: Int -> Int -> Options
|
|
|
|
defaultOptions w h = Options
|
|
|
|
{ optWidth = w
|
2021-04-19 04:17:34 -04:00
|
|
|
, optGenerations = 40
|
|
|
|
, optHeight = h
|
2021-04-17 21:29:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
-- the avaliable options
|
|
|
|
options :: [OptDescr (Options -> Options)]
|
|
|
|
options =
|
|
|
|
[ Option ['w'] ["width"]
|
|
|
|
(ReqArg (\w opts -> opts { optWidth = (read w) }) "WIDTH")
|
|
|
|
"term width"
|
|
|
|
, Option ['g'] ["generations"]
|
|
|
|
(ReqArg (\t opts -> opts { optGenerations = (read t) }) "GENERATIONS")
|
|
|
|
"time steps to simulate"
|
2021-04-19 04:17:34 -04:00
|
|
|
, Option ['h'] ["height"]
|
|
|
|
(ReqArg (\t opts -> opts { optHeight = (read t) }) "HEIGHT")
|
|
|
|
"term height"
|
2021-04-17 21:29:09 -04:00
|
|
|
]
|
|
|
|
|
|
|
|
-- parse the options into the structure
|
|
|
|
-- erroring if encountering a flag not known to us
|
|
|
|
parseArgs :: IO Options
|
|
|
|
parseArgs = do
|
|
|
|
argv <- getArgs
|
|
|
|
progName <- getProgName
|
|
|
|
tw <- readProcess "tput" [ "cols" ] ""
|
|
|
|
th <- readProcess "tput" [ "lines" ] ""
|
|
|
|
case getOpt RequireOrder options argv of
|
|
|
|
(opts, [], []) -> return (foldl (flip id) (defaultOptions (read tw) (read th)) opts)
|
|
|
|
(_, _, errs) -> ioError (userError (concat errs ++ helpMessage))
|
|
|
|
where
|
|
|
|
header = "Usage: " ++ progName ++ " [OPTION...]"
|
|
|
|
helpMessage = usageInfo header options
|
|
|
|
|
2021-04-19 22:25:18 -04:00
|
|
|
initGame :: IO (Space2 CellState)
|
|
|
|
initGame = do
|
|
|
|
rng <- getStdGen
|
|
|
|
return $ createRandSpace2 rng
|
|
|
|
|
2021-04-17 21:29:09 -04:00
|
|
|
---------------
|
|
|
|
-- main loop --
|
|
|
|
---------------
|
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
main = do
|
|
|
|
options <- parseArgs
|
|
|
|
let w = (optWidth options)
|
2021-04-19 04:17:34 -04:00
|
|
|
let h = (optHeight options)
|
2021-04-19 22:25:18 -04:00
|
|
|
chan <- newBChan 1
|
|
|
|
forkIO $ forever $ do
|
|
|
|
writeBChan chan Tick
|
|
|
|
threadDelay 100000
|
|
|
|
g <- initGame
|
|
|
|
let buildVty = V.mkVty V.defaultConfig
|
|
|
|
initialVty <- buildVty
|
2021-04-20 03:44:30 -04:00
|
|
|
void $ customMain initialVty buildVty (Just chan) (app h w) (clamp2 w h g)
|
2021-04-19 22:25:18 -04:00
|
|
|
|
|
|
|
handleEvent :: (Space2 CellState) -> BrickEvent Name Tick -> EventM Name (Next (Space2 CellState))
|
|
|
|
handleEvent g (AppEvent Tick) = continue $ step rps g
|
|
|
|
handleEvent g (VtyEvent (V.EvKey (V.KChar 'q') [])) = halt g
|
|
|
|
handleEvent g _ = continue g
|
|
|
|
|
|
|
|
drawUI :: Int -> Int -> Space2 CellState -> [Widget Name]
|
|
|
|
drawUI h w g = [ C.center $ drawGrid h w g ]
|
|
|
|
|
|
|
|
drawGrid :: Int -> Int -> Space2 CellState -> Widget Name
|
|
|
|
drawGrid h w g = vBox rows
|
2021-04-19 04:17:34 -04:00
|
|
|
where
|
2021-04-20 03:44:30 -04:00
|
|
|
bw = mat2 g
|
2021-04-19 22:25:18 -04:00
|
|
|
rows = [ hBox $ cellsInRow r | r <- bw ]
|
|
|
|
cellsInRow y = map drawCell y
|
|
|
|
|
|
|
|
drawCell :: CellState -> Widget Name
|
|
|
|
drawCell Paper = withAttr paperAttr $ str " "
|
|
|
|
drawCell Scissors = withAttr scissorsAttr $ str " "
|
|
|
|
drawCell Rock = withAttr rockAttr $ str " "
|
|
|
|
|
|
|
|
rockAttr, scissorsAttr, paperAttr :: AttrName
|
|
|
|
rockAttr = "rockAttr"
|
|
|
|
paperAttr = "paperAttr"
|
|
|
|
scissorsAttr = "scissorsAttr"
|
2021-04-20 03:44:30 -04:00
|
|
|
|
|
|
|
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
|