Improve structure using Polysemy

This commit is contained in:
Paul-Henri Froidmont 2019-12-10 02:06:09 +01:00 committed by Paul-Henri Froidmont
parent 0ddc766aea
commit 2c6fba8312
6 changed files with 133 additions and 53 deletions

View file

@ -7,10 +7,12 @@
module Main where module Main where
import Lib import Data.Function ((&))
import Minesweeper
import Options.Generic import Options.Generic
import System.Random import Polysemy
import Text.Read import Random
import Terminal
data Parameters w = data Parameters w =
Parameters Parameters
@ -27,36 +29,4 @@ deriving instance Show (Parameters Unwrapped)
main :: IO () main :: IO ()
main = do main = do
(Parameters boardWidth boardLength bombsCount) <- unwrapRecord "Minesweeper" (Parameters boardWidth boardLength bombsCount) <- unwrapRecord "Minesweeper"
generator <- newStdGen runMinesweeper boardWidth boardLength bombsCount & runTerminalIO & runRandomIO & runM
gameStep . createBoard boardWidth boardLength $
generateRandomCoordinates (boardWidth - 1) (boardLength - 1) bombsCount generator
gameStep :: Board -> IO ()
gameStep board = do
putStrLn . convertBoardToString $ board
coordinates <- getCoordinates
let nextBoard = revealTile coordinates board
if isGameLost nextBoard
then gameLost nextBoard
else if isGameWon nextBoard
then gameWon nextBoard
else gameStep nextBoard
getCoordinates :: IO (Int, Int)
getCoordinates = do
putStrLn "Enter the coordinates of the tile to reveal (X then Y, zero indexed):"
xString <- getLine
yString <- getLine
case (readMaybe xString, readMaybe yString) of
(Just x, Just y) -> return (x, y)
_ -> putStrLn "Invalid coordinates!" >> getCoordinates
gameLost :: Board -> IO ()
gameLost board = do
putStrLn . convertBoardToString . revealAll $ board
putStrLn "Boom! You lost!"
gameWon :: Board -> IO ()
gameWon board = do
putStrLn . convertBoardToString $ board
putStrLn "You won! All the bombs were found!"

View file

@ -4,7 +4,7 @@ cabal-version: 1.12
-- --
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
-- --
-- hash: ae0d0337fef94e96da9aaca377817a7cbca70851243db8fe2a6c435fe06f0cfa -- hash: e5b75ea7d51fb62c97c99a49ccb242a54d64422d7f1eeb101b67b94c40681c02
name: minesweeper name: minesweeper
version: 0.1.0.0 version: 0.1.0.0
@ -13,16 +13,22 @@ build-type: Simple
library library
exposed-modules: exposed-modules:
Lib Lib
Minesweeper
Random
Terminal
other-modules: other-modules:
Paths_minesweeper Paths_minesweeper
hs-source-dirs: hs-source-dirs:
src/ src/
ghc-options: -Wall default-extensions: DataKinds FlexibleContexts GADTs LambdaCase PolyKinds RankNTypes ScopedTypeVariables TypeApplications TypeOperators TypeFamilies
ghc-options: -Wall -O2 -flate-specialise -fspecialise-aggressively -fplugin=Polysemy.Plugin
build-depends: build-depends:
array array
, base , base
, containers , containers
, mtl , mtl
, polysemy
, polysemy-plugin
, random , random
, split , split
default-language: Haskell2010 default-language: Haskell2010
@ -33,12 +39,19 @@ executable minesweeper
Paths_minesweeper Paths_minesweeper
hs-source-dirs: hs-source-dirs:
app/ app/
ghc-options: -Wall default-extensions: DataKinds FlexibleContexts GADTs LambdaCase PolyKinds RankNTypes ScopedTypeVariables TypeApplications TypeOperators TypeFamilies
ghc-options: -Wall -O2 -flate-specialise -fspecialise-aggressively -fplugin=Polysemy.Plugin
build-depends: build-depends:
base array
, base
, containers
, minesweeper , minesweeper
, mtl
, optparse-generic , optparse-generic
, polysemy
, polysemy-plugin
, random , random
, split
default-language: Haskell2010 default-language: Haskell2010
test-suite minesweeper-test test-suite minesweeper-test
@ -48,10 +61,18 @@ test-suite minesweeper-test
Paths_minesweeper Paths_minesweeper
hs-source-dirs: hs-source-dirs:
test/ test/
ghc-options: -Wall default-extensions: DataKinds FlexibleContexts GADTs LambdaCase PolyKinds RankNTypes ScopedTypeVariables TypeApplications TypeOperators TypeFamilies
ghc-options: -Wall -O2 -flate-specialise -fspecialise-aggressively -fplugin=Polysemy.Plugin
build-depends: build-depends:
base array
, base
, containers
, minesweeper , minesweeper
, mtl
, polysemy
, polysemy-plugin
, random
, split
, tasty , tasty
, tasty-hunit , tasty-hunit
default-language: Haskell2010 default-language: Haskell2010

View file

@ -1,21 +1,32 @@
name: minesweeper name: minesweeper
version: 0.1.0.0 version: 0.1.0.0
ghc-options: -Wall ghc-options: -Wall -O2 -flate-specialise -fspecialise-aggressively -fplugin=Polysemy.Plugin
default-extensions:
- DataKinds
- FlexibleContexts
- GADTs
- LambdaCase
- PolyKinds
- RankNTypes
- ScopedTypeVariables
- TypeApplications
- TypeOperators
- TypeFamilies
dependencies: dependencies:
base - base
- polysemy-plugin
library: - polysemy
source-dirs: src/ - random
exposed-modules:
Lib
dependencies:
- containers - containers
- array - array
- split - split
- mtl - mtl
- random
library:
source-dirs: src/
executables: executables:
minesweeper: minesweeper:
@ -23,7 +34,6 @@ executables:
main: Main.hs main: Main.hs
dependencies: dependencies:
- minesweeper - minesweeper
- random
- optparse-generic - optparse-generic
tests: tests:

46
src/Minesweeper.hs Normal file
View file

@ -0,0 +1,46 @@
module Minesweeper where
import Lib
import Polysemy
import Random
import Terminal
import Text.Read
runMinesweeper ::
Member Terminal r
=> Member Random r =>
Int -> Int -> Int -> Sem r ()
runMinesweeper boardWidth boardLength bombsCount = do
generator <- newGenerator
gameStep . createBoard boardWidth boardLength $
generateRandomCoordinates (boardWidth - 1) (boardLength - 1) bombsCount generator
gameStep :: Member Terminal r => Board -> Sem r ()
gameStep board = do
printLine . convertBoardToString $ board
coordinates <- runGetCoordinates
let nextBoard = revealTile coordinates board
if isGameLost nextBoard
then runGameLost nextBoard
else if isGameWon nextBoard
then runGameWon nextBoard
else gameStep nextBoard
runGetCoordinates :: Member Terminal r => Sem r (Int, Int)
runGetCoordinates = do
printLine "Enter the coordinates of the tile to reveal (X then Y, zero indexed):"
xString <- readLine
yString <- readLine
case (readMaybe xString, readMaybe yString) of
(Just x, Just y) -> return (x, y)
_ -> printLine "Invalid coordinates!" >> runGetCoordinates
runGameLost :: Member Terminal r => Board -> Sem r ()
runGameLost board = do
printLine . convertBoardToString . revealAll $ board
printLine "Boom! You lost!"
runGameWon :: Member Terminal r => Board -> Sem r ()
runGameWon board = do
printLine . convertBoardToString $ board
printLine "You won! All the bombs were found!"

16
src/Random.hs Normal file
View file

@ -0,0 +1,16 @@
{-# LANGUAGE TemplateHaskell #-}
module Random where
import Polysemy
import qualified System.Random as R
data Random m a where
NewGenerator :: Random m R.StdGen
makeSem ''Random
runRandomIO :: Member (Embed IO) r => Sem (Random ': r) a -> Sem r a
runRandomIO =
interpret $ \case
NewGenerator -> embed R.newStdGen

17
src/Terminal.hs Normal file
View file

@ -0,0 +1,17 @@
{-# LANGUAGE TemplateHaskell #-}
module Terminal where
import Polysemy
data Terminal m a where
PrintLine :: String -> Terminal m ()
ReadLine :: Terminal m String
makeSem ''Terminal
runTerminalIO :: Member (Embed IO) r => Sem (Terminal ': r) a -> Sem r a
runTerminalIO =
interpret $ \case
PrintLine line -> embed $ putStrLn line
ReadLine -> embed getLine