parent
							
								
									e3dc99342a
								
							
						
					
					
						commit
						f435c644c2
					
				
				 13 changed files with 99566 additions and 0 deletions
			
			
		| @ -0,0 +1,8 @@ | |||||||
|  | # Chapter Exercises | ||||||
|  | ## Hangman game logic | ||||||
|  | see hangman/src/Main.hs | ||||||
|  | 
 | ||||||
|  | ## Modifying code | ||||||
|  | 1. see src/ciphers.hs | ||||||
|  | 2. see src/palindrome.hs | ||||||
|  | 3. see src/person.hs | ||||||
| @ -0,0 +1,8 @@ | |||||||
|  | # Intermission: Check your understanding | ||||||
|  | 1. `forever` and `when` | ||||||
|  | 2. `Data.Bits` and `Database.Blacktip.Types` | ||||||
|  | 3. The types used for the blacktip library | ||||||
|  | 4.  | ||||||
|  |     1. `Control.Concurrent.MVar`, `Filesystem.Path.CurrentOS` and `Control.Concurrent` | ||||||
|  |     2. `import qualifed Filesystem as FS` | ||||||
|  |     3. `import Control.Monad (forever, when)` | ||||||
| @ -0,0 +1,30 @@ | |||||||
|  | Copyright Author name here (c) 2017 | ||||||
|  | 
 | ||||||
|  | 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 Author name here 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. | ||||||
| @ -0,0 +1 @@ | |||||||
|  | # hangman | ||||||
| @ -0,0 +1,2 @@ | |||||||
|  | import Distribution.Simple | ||||||
|  | main = defaultMain | ||||||
									
										
											File diff suppressed because it is too large
											Load Diff
										
									
								
							
						| @ -0,0 +1,23 @@ | |||||||
|  | name:                hangman | ||||||
|  | version:             0.1.0.0 | ||||||
|  | -- synopsis: | ||||||
|  | -- description: | ||||||
|  | homepage:            https://github.com/githubuser/hangman#readme | ||||||
|  | license:             BSD3 | ||||||
|  | license-file:        LICENSE | ||||||
|  | author:              Gaël Depreeuw | ||||||
|  | maintainer:          example@example.com | ||||||
|  | copyright:           2017 Gaël Depreeuw | ||||||
|  | category:            Game | ||||||
|  | build-type:          Simple | ||||||
|  | cabal-version:       >=1.10 | ||||||
|  | extra-source-files:  README.md, | ||||||
|  |                      data/dict.txt | ||||||
|  | 
 | ||||||
|  | executable hangman | ||||||
|  |   hs-source-dirs:      src | ||||||
|  |   main-is:             Main.hs | ||||||
|  |   default-language:    Haskell2010 | ||||||
|  |   build-depends:       base >= 4.7 && < 5, | ||||||
|  |                        random, | ||||||
|  |                        split | ||||||
| @ -0,0 +1,123 @@ | |||||||
|  | module Main where | ||||||
|  | 
 | ||||||
|  | import Control.Monad (forever) | ||||||
|  | import Data.Char (toLower, isAlpha) | ||||||
|  | import Data.Maybe (isJust) | ||||||
|  | import Data.List (intersperse) | ||||||
|  | import System.Exit (exitSuccess) | ||||||
|  | import System.Random (randomRIO) | ||||||
|  | 
 | ||||||
|  | newtype WordList = WordList [String] deriving (Eq, Show) | ||||||
|  | 
 | ||||||
|  | allWords :: IO WordList | ||||||
|  | allWords = do | ||||||
|  |   dict <- readFile "data/dict.txt" | ||||||
|  |   return (WordList $ lines dict) | ||||||
|  | 
 | ||||||
|  | minWordLength :: Int | ||||||
|  | minWordLength = 5 | ||||||
|  | 
 | ||||||
|  | maxWordLength :: Int | ||||||
|  | maxWordLength = 9 | ||||||
|  | 
 | ||||||
|  | maxWrongGuesses :: Int | ||||||
|  | maxWrongGuesses = 10 | ||||||
|  | 
 | ||||||
|  | gameWords :: IO WordList | ||||||
|  | gameWords = do | ||||||
|  |   WordList aw <- allWords | ||||||
|  |   return $ WordList (filter gameLength aw) | ||||||
|  |   where gameLength w =  | ||||||
|  |           let l = length (w :: String) | ||||||
|  |           in l >= minWordLength && l < maxWordLength | ||||||
|  |              && all isAlpha w | ||||||
|  | 
 | ||||||
|  | randomWord :: WordList -> IO String | ||||||
|  | randomWord (WordList wl) = do | ||||||
|  |   randomIndex <- randomRIO (0, length wl - 1) | ||||||
|  |   return $ wl !! randomIndex | ||||||
|  | 
 | ||||||
|  | randomWord' :: IO String | ||||||
|  | randomWord' = gameWords >>= randomWord | ||||||
|  | 
 | ||||||
|  | data Puzzle = Puzzle String [Maybe Char] [Char] Int | ||||||
|  | instance Show Puzzle where | ||||||
|  |   show (Puzzle _ discovered guessed i) = | ||||||
|  |     (intersperse ' ' $ fmap renderPuzzleChar discovered) | ||||||
|  |     ++ " Guessed so far: " ++ guessed ++ "\n" | ||||||
|  |     ++ "Wrong guesses so far: " ++ show i | ||||||
|  | 
 | ||||||
|  | freshPuzzle :: String -> Puzzle | ||||||
|  | freshPuzzle s = Puzzle s l [] 0 | ||||||
|  |     where l = fmap (const Nothing) s | ||||||
|  | 
 | ||||||
|  | charInWord :: Puzzle -> Char -> Bool | ||||||
|  | charInWord (Puzzle s _ _ _) c = elem c s | ||||||
|  | 
 | ||||||
|  | alreadyGuessed :: Puzzle -> Char -> Bool | ||||||
|  | alreadyGuessed (Puzzle _ _ s _) c = elem c s | ||||||
|  | 
 | ||||||
|  | renderPuzzleChar :: Maybe Char -> Char | ||||||
|  | renderPuzzleChar Nothing = '_' | ||||||
|  | renderPuzzleChar (Just c) = c | ||||||
|  | 
 | ||||||
|  | fillInCharacter :: Puzzle -> Char -> Puzzle | ||||||
|  | fillInCharacter (Puzzle word filledInSoFar s i) c = | ||||||
|  |   Puzzle word newFilledInSoFar (c:s) i | ||||||
|  |   where newFilledInSoFar = zipWith (zipper c) word filledInSoFar | ||||||
|  |         zipper guessed wordChar guessChar = | ||||||
|  |           if wordChar == guessed | ||||||
|  |           then Just wordChar | ||||||
|  |           else guessChar | ||||||
|  |   | ||||||
|  | handleWrongGuess :: Puzzle -> Puzzle | ||||||
|  | handleWrongGuess (Puzzle w f g i) = | ||||||
|  |   Puzzle w f g (i+1) | ||||||
|  | 
 | ||||||
|  | handleGuess :: Puzzle -> Char -> IO Puzzle | ||||||
|  | handleGuess puzzle guess = do | ||||||
|  |   putStrLn $ "Your guess was: " ++ [guess] | ||||||
|  |   case (charInWord puzzle guess, alreadyGuessed puzzle guess) of | ||||||
|  |     (_, True) -> do | ||||||
|  |       putStrLn "You already guessed that character, pick something else!" | ||||||
|  |       return puzzle | ||||||
|  |     (True, _) -> do | ||||||
|  |       putStrLn "This character was in the word, filling in the word\ | ||||||
|  |                \ accordingly" | ||||||
|  |       return (fillInCharacter puzzle guess) | ||||||
|  |     (False, _) -> do | ||||||
|  |       putStrLn "This character wasn't in the word, try again." | ||||||
|  |       return $ handleWrongGuess (fillInCharacter puzzle guess) | ||||||
|  | 
 | ||||||
|  | gameOver :: Puzzle -> Int -> IO () | ||||||
|  | gameOver (Puzzle wordToGuess _ _ i) maxWrong = | ||||||
|  |   if i >= maxWrong then | ||||||
|  |     do putStrLn "You lose" | ||||||
|  |        putStrLn $ "The word was: " ++ wordToGuess | ||||||
|  |        exitSuccess | ||||||
|  |   else return () | ||||||
|  | 
 | ||||||
|  | gameWin :: Puzzle -> IO () | ||||||
|  | gameWin (Puzzle _ filledInSoFar _ _) = | ||||||
|  |   if all isJust filledInSoFar then | ||||||
|  |     do putStrLn "You win!" | ||||||
|  |        exitSuccess | ||||||
|  |   else return () | ||||||
|  | 
 | ||||||
|  | runGame :: Puzzle -> IO () | ||||||
|  | runGame puzzle = forever $ do | ||||||
|  |   gameOver puzzle maxWrongGuesses | ||||||
|  |   gameWin puzzle | ||||||
|  |   putStrLn $ "Current puzzle is: " ++ show puzzle | ||||||
|  |   putStr "Guess a letter: " | ||||||
|  |   guess <- getLine | ||||||
|  |   case guess of | ||||||
|  |     [c] -> handleGuess puzzle c >>= runGame | ||||||
|  |     _   -> putStrLn "Your guess must be a single character" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | main :: IO () | ||||||
|  | main = do | ||||||
|  |   word <- randomWord' | ||||||
|  |   let puzzle = freshPuzzle (fmap toLower word) | ||||||
|  |   runGame puzzle | ||||||
| @ -0,0 +1,66 @@ | |||||||
|  | # This file was automatically generated by 'stack init' | ||||||
|  | # | ||||||
|  | # Some commonly used options have been documented as comments in this file. | ||||||
|  | # For advanced use and comprehensive documentation of the format, please see: | ||||||
|  | # https://docs.haskellstack.org/en/stable/yaml_configuration/ | ||||||
|  | 
 | ||||||
|  | # Resolver to choose a 'specific' stackage snapshot or a compiler version. | ||||||
|  | # A snapshot resolver dictates the compiler version and the set of packages | ||||||
|  | # to be used for project dependencies. For example: | ||||||
|  | # | ||||||
|  | # resolver: lts-3.5 | ||||||
|  | # resolver: nightly-2015-09-21 | ||||||
|  | # resolver: ghc-7.10.2 | ||||||
|  | # resolver: ghcjs-0.1.0_ghc-7.10.2 | ||||||
|  | # resolver: | ||||||
|  | #  name: custom-snapshot | ||||||
|  | #  location: "./custom-snapshot.yaml" | ||||||
|  | resolver: lts-9.10 | ||||||
|  | 
 | ||||||
|  | # User packages to be built. | ||||||
|  | # Various formats can be used as shown in the example below. | ||||||
|  | # | ||||||
|  | # packages: | ||||||
|  | # - some-directory | ||||||
|  | # - https://example.com/foo/bar/baz-0.0.2.tar.gz | ||||||
|  | # - location: | ||||||
|  | #    git: https://github.com/commercialhaskell/stack.git | ||||||
|  | #    commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a | ||||||
|  | # - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a | ||||||
|  | #   extra-dep: true | ||||||
|  | #  subdirs: | ||||||
|  | #  - auto-update | ||||||
|  | #  - wai | ||||||
|  | # | ||||||
|  | # A package marked 'extra-dep: true' will only be built if demanded by a | ||||||
|  | # non-dependency (i.e. a user package), and its test suites and benchmarks | ||||||
|  | # will not be run. This is useful for tweaking upstream packages. | ||||||
|  | packages: | ||||||
|  | - . | ||||||
|  | # Dependency packages to be pulled from upstream that are not in the resolver | ||||||
|  | # (e.g., acme-missiles-0.3) | ||||||
|  | extra-deps: [] | ||||||
|  | 
 | ||||||
|  | # Override default flag values for local packages and extra-deps | ||||||
|  | flags: {} | ||||||
|  | 
 | ||||||
|  | # Extra package databases containing global packages | ||||||
|  | extra-package-dbs: [] | ||||||
|  | 
 | ||||||
|  | # Control whether we use the GHC we find on the path | ||||||
|  | # system-ghc: true | ||||||
|  | # | ||||||
|  | # Require a specific version of stack, using version ranges | ||||||
|  | # require-stack-version: -any # Default | ||||||
|  | # require-stack-version: ">=1.5" | ||||||
|  | # | ||||||
|  | # Override the architecture used by stack, especially useful on Windows | ||||||
|  | # arch: i386 | ||||||
|  | # arch: x86_64 | ||||||
|  | # | ||||||
|  | # Extra directories used by stack for building | ||||||
|  | # extra-include-dirs: [/path/to/dir] | ||||||
|  | # extra-lib-dirs: [/path/to/dir] | ||||||
|  | # | ||||||
|  | # Allow a newer minor version of GHC than the snapshot specifies | ||||||
|  | # compiler-check: newer-minor | ||||||
| @ -0,0 +1 @@ | |||||||
|  | Subproject commit dc97b6e0d15ae6d63c32f7b7fa35291270835552 | ||||||
| @ -0,0 +1,82 @@ | |||||||
|  | module Cipher where | ||||||
|  | 
 | ||||||
|  |     import Data.Char | ||||||
|  |      | ||||||
|  |     -- Chapter 9.12 | ||||||
|  |     caesar :: String -> Int -> String | ||||||
|  |     caesar [] _ = [] | ||||||
|  |     caesar (x:xs) k | ||||||
|  |         | isAlpha x = go x k ++ caesar xs k | ||||||
|  |         | otherwise = caesar xs k | ||||||
|  |         where go c key = [enc c (+) key] | ||||||
|  | 
 | ||||||
|  |     unCaesar :: String -> Int -> String | ||||||
|  |     unCaesar [] _ = [] | ||||||
|  |     unCaesar (x:xs) k | ||||||
|  |         | isAlpha x = go x k ++ unCaesar xs k | ||||||
|  |         | otherwise = caesar xs k | ||||||
|  |         where go c key = [enc c (-) key] | ||||||
|  |      | ||||||
|  |     enc :: Char -> (Int -> Int -> Int) -> Int -> Char | ||||||
|  |     enc c f k | ||||||
|  |         | elem c ['a'..'z'] = chr (i 'a') | ||||||
|  |         | elem c ['A'..'Z'] = chr (i 'A') | ||||||
|  |         | otherwise = c | ||||||
|  |         where i b  = (mod (ci b) r) + (ord b) | ||||||
|  |               ci b = f ((ord c) - (ord b)) k | ||||||
|  |               r = 26 | ||||||
|  | 
 | ||||||
|  |     alphaIndex :: Char -> Int | ||||||
|  |     alphaIndex c | ||||||
|  |         | elem c ['a'..'z'] = ord c - ord 'a' | ||||||
|  |         | elem c ['A'..'Z'] = ord c - ord 'A' | ||||||
|  |         | otherwise = 0 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     shift :: Char -> Char -> Char | ||||||
|  |     shift c k | ||||||
|  |         | elem c ['a'..'z'] = go c k 'a' | ||||||
|  |         | elem c ['A'..'Z'] = go c k 'A' | ||||||
|  |         | otherwise = c | ||||||
|  |         where go p key base = chr $ (mod rel r) + b | ||||||
|  |                 where rel = alphaIndex p + alphaIndex key | ||||||
|  |                       r = 26 | ||||||
|  |                       b = ord base | ||||||
|  | 
 | ||||||
|  |     -- nice solution, but maps keyword to non-alpha characters | ||||||
|  |     -- e.g. MEET_AT_DAWN  | ||||||
|  |     --      ALLYALLYALLY | ||||||
|  |     vigenere' :: [Char] -> [Char] -> [Char] | ||||||
|  |     vigenere' xs ys = zipWith shift xs ((concat . repeat) ys) | ||||||
|  | 
 | ||||||
|  |     -- wrote own zipWith variant which maps only when isAlpha | ||||||
|  |     vigenere :: [Char] -> [Char] -> [Char] | ||||||
|  |     vigenere xs [] = xs -- necessary to avoid bottom | ||||||
|  |     vigenere xs ys = myZipWith shift xs ys | ||||||
|  |         where myZipWith _ [] _  = [] | ||||||
|  |               myZipWith f s [] = myZipWith f s ys | ||||||
|  |               myZipWith f (a:as) k@(b:bs) = | ||||||
|  |                 if isAlpha a | ||||||
|  |                 then f a b : myZipWith f as bs | ||||||
|  |                 else     a : myZipWith f as k | ||||||
|  | 
 | ||||||
|  |     -- 1 | ||||||
|  |     -- Not a lot of error checking, but just testing | ||||||
|  |     -- reading the input | ||||||
|  |     callCaesar :: IO () | ||||||
|  |     callCaesar = do | ||||||
|  |         putStrLn "Enter the plain text:" | ||||||
|  |         s <- getLine | ||||||
|  |         putStr "Enter the key (Char): " | ||||||
|  |         c <- getChar | ||||||
|  |         putStr "\n" | ||||||
|  |         putStrLn $ caesar s $ alphaIndex c | ||||||
|  | 
 | ||||||
|  |     callVigenere :: IO () | ||||||
|  |     callVigenere = do | ||||||
|  |         putStrLn "Enter the plain text:" | ||||||
|  |         s <- getLine | ||||||
|  |         putStr "Enter the key (String): " | ||||||
|  |         k <- getLine | ||||||
|  |         putStr "\n" | ||||||
|  |         putStrLn $ vigenere s k | ||||||
| @ -0,0 +1,15 @@ | |||||||
|  | module Palindrome where | ||||||
|  | 
 | ||||||
|  | import Control.Monad | ||||||
|  | import Data.Char | ||||||
|  | 
 | ||||||
|  | palindrome :: IO () | ||||||
|  | palindrome = forever $ do | ||||||
|  |     line1 <- getLine | ||||||
|  |     case (pal line1) of | ||||||
|  |         True -> putStrLn "It's a palindrome!" | ||||||
|  |         False -> putStrLn "Nope!" | ||||||
|  |     where pal s =  | ||||||
|  |            let s' = map toLower $ filter isAlpha s | ||||||
|  |            in s' == reverse s' | ||||||
|  |          | ||||||
| @ -0,0 +1,36 @@ | |||||||
|  | module Person where | ||||||
|  | 
 | ||||||
|  | type Name = String | ||||||
|  | type Age = Integer | ||||||
|  | 
 | ||||||
|  | data Person = Person Name Age deriving Show | ||||||
|  | 
 | ||||||
|  | data PersonInvalid = | ||||||
|  |     NameEmpty | ||||||
|  |   | AgeTooLow | ||||||
|  |   | PersonInvalidUnknown String | ||||||
|  |   deriving (Show, Eq) | ||||||
|  | 
 | ||||||
|  | mkPerson :: Name -> Age -> Either PersonInvalid Person | ||||||
|  | mkPerson name age | ||||||
|  |     | name /= "" && age > 0 = Right $ Person name age | ||||||
|  |     | name == "" = Left NameEmpty | ||||||
|  |     | not (age > 0) = Left AgeTooLow | ||||||
|  |     | otherwise = | ||||||
|  |         Left $ PersonInvalidUnknown $ | ||||||
|  |             "Name was: " ++ show name ++ | ||||||
|  |             " Age was: " ++ show age | ||||||
|  | 
 | ||||||
|  | gimmePerson :: IO () | ||||||
|  | gimmePerson = do | ||||||
|  |     putStr "Provide a name: " | ||||||
|  |     name <- getLine | ||||||
|  |     putStr "Provide an age: " | ||||||
|  |     age <- getLine | ||||||
|  |     let p = mkPerson name ((read age) :: Integer) | ||||||
|  |     case go p of | ||||||
|  |         True  -> putStr "Yay! Succefully got a person: " | ||||||
|  |         False -> putStrLn "Error: " | ||||||
|  |     putStrLn $ show p | ||||||
|  |     where go (Right _) = True | ||||||
|  |           go (Left _)  = False | ||||||
					Loading…
					
					
				
		Reference in new issue