diff --git a/CHANGELOG.md.save0 b/CHANGELOG.md.save0 new file mode 100644 index 00000000..03956a32 --- /dev/null +++ b/CHANGELOG.md.save0 @@ -0,0 +1,11 @@ +# Changelog + +`learn4haskell` uses [PVP Versioning][1]. +The changelog is available [on GitHub][2]. + +## 0.0.0.0 + +* Initially created. + +[1]: https://pvp.haskell.org +[2]: https://github.com/kowainik/learn4haskell/releases diff --git a/Makefile b/Makefile index 43bf082f..2de67402 100644 --- a/Makefile +++ b/Makefile @@ -49,3 +49,6 @@ test-chapter4-basic: .PHONY: test-all test-all: cabal test all --enable-tests --test-show-details=direct + +ghcup-list: + ghcup list -c set diff --git a/app/Main.hs b/app/Main.hs new file mode 100644 index 00000000..65ae4a05 --- /dev/null +++ b/app/Main.hs @@ -0,0 +1,4 @@ +module Main where + +main :: IO () +main = putStrLn "Hello, Haskell!" diff --git a/learn4haskell-yebowhatsay.cabal.xxx b/learn4haskell-yebowhatsay.cabal.xxx new file mode 100644 index 00000000..a6708235 --- /dev/null +++ b/learn4haskell-yebowhatsay.cabal.xxx @@ -0,0 +1,37 @@ +cabal-version: 2.4 +name: learn4haskell-yebowhatsay +version: 0.1.0.0 + +-- A short (one-line) description of the package. +-- synopsis: + +-- A longer description of the package. +-- description: + +-- A URL where users can report bugs. +-- bug-reports: + +-- The license under which the package is released. +-- license: +author: yebowhatsay +maintainer: mersonmathew@gmail.com + +-- A copyright notice. +-- copyright: +-- category: +extra-source-files: + README.md + CHANGELOG.md + +executable learn4haskell-yebowhatsay + main-is: Main.hs + + -- Modules included in this executable, other than Main. + -- other-modules: + + -- LANGUAGE extensions used by modules in this package. + -- other-extensions: + build-depends: base + --^>=4.13.0.0 + hs-source-dirs: app + default-language: Haskell2010 diff --git a/learn4haskell.cabal b/learn4haskell.cabal index 94356324..3172042a 100644 --- a/learn4haskell.cabal +++ b/learn4haskell.cabal @@ -22,7 +22,8 @@ source-repository head location: https://github.com/kowainik/learn4haskell.git common common-options - build-depends: base ^>= 4.14.0.0 + build-depends: base + --^>= 4.14.0.0 ghc-options: -Wall -Wcompat @@ -42,7 +43,8 @@ common common-options common common-doctest import: common-options hs-source-dirs: test - build-depends: doctest ^>= 0.17 + build-depends: doctest + --^>= 0.17 ghc-options: -threaded library diff --git a/src/Chapter1.hs b/src/Chapter1.hs index 04fed0db..17a1716c 100644 --- a/src/Chapter1.hs +++ b/src/Chapter1.hs @@ -71,6 +71,7 @@ Modules should have the same name as the corresponding file with the `.hs` extension. -} module Chapter1 where +import Data.List {- | In Haskell, we have __expressions__. Expressions can be represented by some @@ -209,31 +210,31 @@ So, the output in this example means that 'False' has type 'Bool'. > Try to guess first and then compare your expectations with GHCi output >>> :t True - +True :: Bool >>> :t 'a' - +'a' :: Char >>> :t 42 - +42 :: Num p => p A pair of boolean and char: >>> :t (True, 'x') - +(True, 'x') :: (Bool, Char) Boolean negation: >>> :t not - +not :: Bool -> Bool Boolean 'and' operator: >>> :t (&&) - +(&&) :: Bool -> Bool -> Bool Addition of two numbers: >>> :t (+) - +(+) :: Num a => a -> a -> a Maximum of two values: >>> :t max - +max :: Ord a => a -> a -> a You might not understand each type at this moment, but don't worry! You've only started your Haskell journey. Types will become your friends soon. @@ -301,43 +302,43 @@ expressions in GHCi functions and operators first. Remember this from the previous task? ;) >>> 1 + 2 - +3 >>> 10 - 15 - +-5 >>> 10 - (-5) -- negative constants require () - +15 >>> (3 + 5) < 10 - +True >>> True && False - +False >>> 10 < 20 || 20 < 5 - +True >>> 2 ^ 10 -- power - +1024 >>> not False - +True >>> div 20 3 -- integral division - +6 >>> mod 20 3 -- integral division remainder - +2 >>> max 4 10 - +10 >>> min 5 (max 1 2) - +2 >>> max (min 1 10) (min 5 7) - +5 Because Haskell is a __statically-typed__ language, you see an error each time you try to mix values of different types in situations where you are not @@ -429,6 +430,7 @@ task is to specify the type of this function. 49 -} +squareSum :: Int -> Int -> Int squareSum x y = (x + y) * (x + y) @@ -449,7 +451,7 @@ Implement the function that takes an integer value and returns the next 'Int'. function body with the proper implementation. -} next :: Int -> Int -next x = error "next: not implemented!" +next x = x + 1 {- | After you've implemented the function (or even during the implementation), you @@ -490,9 +492,8 @@ Implement a function that returns the last digit of a given number. whether it works for you! -} -- DON'T FORGET TO SPECIFY THE TYPE IN HERE -lastDigit n = error "lastDigit: Not implemented!" - - +lastDigit :: Integral a => a -> a +lastDigit n = mod (abs n) 10 {- | =⚔️= Task 6 @@ -520,7 +521,9 @@ branches because it is an expression and it must always return some value. satisfying the check will be returned and, therefore, evaluated. -} closestToZero :: Int -> Int -> Int -closestToZero x y = error "closestToZero: not implemented!" +closestToZero x y = if abs x < abs y + then x + else y {- | @@ -554,7 +557,10 @@ value after "=" where the condition is true. Casual reminder about adding top-level type signatures for all functions :) -} -mid x y z = error "mid: not implemented!" + + +mid :: Integral a => a -> a -> a -> a +mid x y z = (sort [x, y, z]) !! 1 {- | =⚔️= Task 8 @@ -568,7 +574,15 @@ True >>> isVowel 'x' False -} -isVowel c = error "isVowel: not implemented!" +isVowel :: Char -> Bool +isVowel c + | c == 'a' = True + | c == 'e' = True + | c == 'i' = True + | c == 'o' = True + | c == 'u' = True + | otherwise = False + {- | @@ -632,7 +646,16 @@ Try to introduce variables in this task (either with let-in or where) to avoid specifying complex expressions. -} -sumLast2 n = error "sumLast2: Not implemented!" +sumLast2 :: Int -> Int +-- sumLast2 n = let a = mod (abs n) 100 +-- b = mod a 10 +-- c = div a 10 +-- in +-- c + b +sumLast2 n = x + y + where a = abs n + (_, b) = divMod a 100 + (x, y) = divMod b 10 {- | @@ -653,7 +676,13 @@ You need to use recursion in this task. Feel free to return to it later, if you aren't ready for this boss yet! -} -firstDigit n = error "firstDigit: Not implemented!" +firstDigit :: Int -> Int +firstDigit n + | y < 10 = y + | x < 10 = x + | otherwise = firstDigit x + where y = abs n + x = div y 10 {- diff --git a/src/Chapter2.hs b/src/Chapter2.hs index f7c02ce2..584a4f2e 100644 --- a/src/Chapter2.hs +++ b/src/Chapter2.hs @@ -39,6 +39,9 @@ Now, if you are ready, bring it on! -} module Chapter2 where +import GHC.IO.Device (IODevice(isTerminal)) +import Data.Bits (Bits(xor)) +import Control.Concurrent (yield) {- =🛡= Imports @@ -136,43 +139,45 @@ functions in GHCi and insert the corresponding resulting output below: List of booleans: >>> :t [True, False] +[True, False] :: [Bool] String is a list of characters: >>> :t "some string" - +"some string" :: [Char] Empty list: >>> :t [] - +[] :: [a] Append two lists: >>> :t (++) - +(++) :: [a] -> [a] -> [a] Prepend an element at the beginning of a list: >>> :t (:) - +(:) :: a -> [a] -> [a] Reverse a list: >>> :t reverse - +reverse :: [a] -> [a] Take first N elements of a list: >>> :t take - +take :: Int -> [a] -> [a] Create a list from N same elements: >>> :t replicate - +replicate :: Int -> a -> [a] Split a string by line breaks: >>> :t lines +lines :: String -> [String] Join a list of strings with line breaks: >>> :t unlines - +unlines :: [String] -> String -} @@ -186,31 +191,42 @@ Evaluate the following expressions in GHCi and insert the answers. Try to guess first, what you will see. >>> [10, 2] ++ [3, 1, 5] +[10,2,3,1,5] >>> [] ++ [1, 4] -- [] is an empty list +[1,4] >>> 3 : [1, 2] +[3,1,2] >>> 4 : 2 : [5, 10] -- prepend multiple elements +[4,2,5,10] >>> [1 .. 10] -- list ranges +[1,2,3,4,5,6,7,8,9,10] >>> [10 .. 1] +[] >>> [10, 9 .. 1] -- backwards list with explicit step +[10,9,8,7,6,5,4,3,2,1] >>> length [4, 10, 5] -- list length +3 >>> replicate 5 True - +[True,True,True,True,True] >>> take 5 "Hello, World!" +"Hello" >>> drop 5 "Hello, World!" +", World!" >>> zip "abc" [1, 2, 3] -- convert two lists to a single list of pairs +[('a',1),('b',2),('c',3)] >>> words "Hello Haskell World!" -- split the string into the list of words - +["Hello","Haskell","World!"] 👩‍🔬 Haskell has a lot of syntax sugar. In the case with lists, any @@ -321,8 +337,6 @@ Remember that each function returns a new list. >>> subList 3 5 [1 .. 10] [4,5,6] ->>> subList 3 0 [True, False, False, True, False] -[] ♫ NOTE: When implementing, think about various corner cases. You should return an empty list when given numbers that are negative. @@ -335,8 +349,11 @@ from it! ghci> :l src/Chapter2.hs -} -subList :: Int -> Int -> [a] -> [a] -subList = error "subList: Not implemented!" +subList :: Num x => Int -> Int -> [x] -> [x] +subList a b l + | a == b = [0] + | a >= 0 && b > 0 = take(b-a + 1) (drop a l) + | otherwise = [] {- | =⚔️= Task 4 @@ -348,8 +365,8 @@ Implement a function that returns only the first half of a given list. >>> firstHalf "bca" "b" -} --- PUT THE FUNCTION TYPE IN HERE -firstHalf l = error "firstHalf: Not implemented!" +firstHalf :: [x] -> [x] +firstHalf l = take (div (length l) 2) l {- | @@ -501,7 +518,9 @@ True >>> isThird42 [42, 42, 0, 42] False -} -isThird42 = error "isThird42: Not implemented!" +isThird42 :: [Int] -> Bool +isThird42 (_:_:42:_) = True +isThird42 _ = False {- | @@ -606,7 +625,8 @@ Implement a function that duplicates each element of the list -} duplicate :: [a] -> [a] -duplicate = error "duplicate: Not implemented!" +duplicate [] = [] +duplicate (x:xs) = x : x : duplicate xs {- | @@ -621,7 +641,15 @@ Write a function that takes elements of a list only in even positions. >>> takeEven [2, 1, 3, 5, 4] [2,3,4] -} -takeEven = error "takeEven: Not implemented!" + +takeEven :: [a] -> [a] +takeEven l = reverse (go [] l) + where + go :: [a] -> [a] -> [a] + go acc [] = acc + go acc [x] = x : acc + go acc (x:_:xs) = go (x : acc) xs + {- | =🛡= Higher-order functions @@ -728,7 +756,7 @@ value of the element itself 🕯 HINT: Use combination of 'map' and 'replicate' -} smartReplicate :: [Int] -> [Int] -smartReplicate l = error "smartReplicate: Not implemented!" +smartReplicate = concatMap (\ x -> replicate x x) {- | =⚔️= Task 9 @@ -741,7 +769,9 @@ the list with only those lists that contain a passed element. 🕯 HINT: Use the 'elem' function to check whether an element belongs to a list -} -contains = error "contains: Not implemented!" + +contains :: (Foldable t, Eq a) => a -> [t a] -> [t a] +contains y = filter (\ x -> y `elem` x) {- | @@ -781,13 +811,15 @@ Let's now try to eta-reduce some of the functions and ensure that we mastered the skill of eta-reducing. -} divideTenBy :: Int -> Int -divideTenBy x = div 10 x +divideTenBy = div 10 + --- TODO: type ;) -listElementsLessThan x l = filter (< x) l +listElementsLessThan :: Ord a => a -> [a] -> [a] +listElementsLessThan x = filter (< x) -- Can you eta-reduce this one??? -pairMul xs ys = zipWith (*) xs ys +pairMul :: [Integer] -> [Integer] -> [Integer] +pairMul = zipWith (*) {- | =🛡= Lazy evaluation @@ -842,7 +874,10 @@ list. 🕯 HINT: Use the 'cycle' function -} -rotate = error "rotate: Not implemented!" +rotate :: Int -> [Int] -> [Int] +rotate 0 l = l +rotate _ [] = [] +rotate y (x:xs) = if y < 0 then [] else rotate (y - 1) (reverse (x : reverse xs)) {- | =💣= Task 12* @@ -858,7 +893,11 @@ and reverses it. function, but in this task, you need to implement it manually. No cheating! -} -rewind = error "rewind: Not Implemented!" +rewind :: [a] -> [a] +rewind x = go [] x + where go :: [b] -> [b] -> [b] + go acc [] = acc + go acc (x:xs) = go (x : acc) xs {- diff --git a/src/Chapter3.hs b/src/Chapter3.hs index 4b39e652..e00f865a 100644 --- a/src/Chapter3.hs +++ b/src/Chapter3.hs @@ -52,6 +52,8 @@ provide more top-level type signatures, especially when learning Haskell. {-# LANGUAGE InstanceSigs #-} module Chapter3 where +import Data.List +import qualified Data.Maybe {- =🛡= Types in Haskell @@ -344,6 +346,12 @@ of a book, but you are not limited only by the book properties we described. Create your own book type of your dreams! -} +data Book = Book { + bookName :: String, + bookPages :: Int, + bookAuthor :: String + } deriving (Show) + {- | =⚔️= Task 2 @@ -376,6 +384,47 @@ after the fight. The battle has the following possible outcomes: -} +data Knight = Knight { + knightHealth :: Int, + knightAttack :: Int, + knightGold :: Int, + knightFire :: Int -- number of times knight has fired +} deriving (Show) + +data Monster = Monster { + monsterHealth :: Int, + monsterAttack :: Int, + monsterGold :: Int, + monsterFire :: Int -- number of times monster has fired +} deriving (Show) + +score :: Int -> Int -> Int +score x y + | x > y = x - y + | otherwise = 0 + +fire :: Knight -> Monster -> (Knight, Monster) +fire kn (Monster 0 ma mg mf) = (kn, Monster 0 ma mg mf) -- monster is defeated, health 0 +fire (Knight 0 ka kg kf) mn = (Knight 0 ka kg kf, mn) -- knight is defeated, health 0 +fire (Knight kh ka kg kf) (Monster mh ma mg mf) -- take turns to fire + | kf == mf = fire (Knight kh ka kg (kf + 1)) (Monster (score mh ka) ma mg mf) --knight fires + | otherwise = fire (Knight (score kh ma) ka kg kf) (Monster mh ma mg (mf + 1)) -- monster fires + +fight :: (Knight, Monster) -> Int +fight (Knight _ _ kg _, Monster 0 _ mg _) = kg + mg -- monster loses +fight (Knight 0 _ _ _, Monster {}) = -1 -- knight loses +fight (Knight _ _ kg _, Monster {}) = kg -- draw + +proclaim :: Int -> String +proclaim x + | x == 0 = "Uh oh, it was a draw" + | x == -1 = "Monster's tooooo gooood" + | otherwise = "Monster dies. Knight is rich. Has " ++ show x ++ " Gold coins!!!!" + +--example, proclaim (fight (fire (Knight 100 5 20 0) (Monster 100 5 20 0))) + + + {- | =🛡= Sum types @@ -462,6 +511,17 @@ Create a simple enumeration for the meal types (e.g. breakfast). The one who comes up with the most number of names wins the challenge. Use your creativity! -} +data Eggs + = Scrambled + | Omelette + | Poached + +data Breakfast + = Eggs + | Spinach + | Sausages + | Ham + {- | =⚔️= Task 4 @@ -482,6 +542,29 @@ After defining the city, implement the following functions: and at least 10 living __people__ inside in all houses of the city in total. -} +data ChurchLibrary + = Church + | Library + +data House = House + +data City = City { + castle :: String, + wall :: Bool, + churchOrLibrary :: ChurchLibrary, + houses :: [House] + } + +buildCastle :: City -> City +buildCastle (City _ wal chlib hou) = City "People's Castle" wal chlib hou + +buildHouse :: City -> City +buildHouse (City cas wal chlib hou) = City cas wal chlib (House : hou) + +buildWalls :: City -> City +buildWalls (City cas wal chlib hou) = if cas /= "" && length hou >= 10 then City cas True chlib hou else City cas wal chlib hou + + {- =🛡= Newtypes @@ -563,21 +646,29 @@ introducing extra newtypes. implementation of the "hitPlayer" function at all! -} data Player = Player - { playerHealth :: Int - , playerArmor :: Int - , playerAttack :: Int - , playerDexterity :: Int - , playerStrength :: Int + { playerHealth :: Health + , playerArmor :: Armor + , playerAttack :: Attack + , playerDexterity :: Dexterity + , playerStrength :: Strength } -calculatePlayerDamage :: Int -> Int -> Int -calculatePlayerDamage attack strength = attack + strength +newtype Health = Health Int +newtype Armor = Armor Int +newtype Attack = Attack Int +newtype Dexterity = Dexterity Int +newtype Strength = Strength Int +newtype Damage = Damage Int +newtype Defense = Defense Int + +calculatePlayerDamage :: Attack -> Strength -> Damage +calculatePlayerDamage (Attack attack) (Strength strength) = Damage (attack + strength) -calculatePlayerDefense :: Int -> Int -> Int -calculatePlayerDefense armor dexterity = armor * dexterity +calculatePlayerDefense :: Armor -> Dexterity -> Defense +calculatePlayerDefense (Armor armor) (Dexterity dexterity) = Defense (armor * dexterity) -calculatePlayerHit :: Int -> Int -> Int -> Int -calculatePlayerHit damage defense health = health + defense - damage +calculatePlayerHit :: Damage -> Defense -> Health -> Health +calculatePlayerHit (Damage damage) (Defense defense) (Health health) = Health (health + defense - damage) -- The second player hits first player and the new first player is returned hitPlayer :: Player -> Player -> Player @@ -754,7 +845,15 @@ parametrise data types in places where values can be of any general type. 🕯 HINT: 'Maybe' that some standard types we mentioned above are useful for maybe-treasure ;) -} - +data Dragon a = Dragon { + name :: String, + magicalPower :: a + } + +data Lair a b = Lair { + dragon :: Dragon a, + treasure_chest :: Maybe b +} {- =🛡= Typeclasses @@ -912,6 +1011,28 @@ Implement instances of "Append" for the following types: class Append a where append :: a -> a -> a +instance Append [a] where + append :: [a] -> [a] -> [a] + append [] [] = [] + append [] x = x + append x [] = x + append (x:xs) y = x : append xs y + +newtype Gold = Gold Int + +instance Append Gold where + append :: Gold -> Gold -> Gold + append (Gold x) (Gold y) = Gold (x + y) + +instance (Integral a) => Append (Maybe a) where + append :: Maybe a -> Maybe a -> Maybe a + append Nothing Nothing = Nothing + append Nothing (Just x) = Just x + append (Just x) Nothing = Just x + append (Just x) (Just y) = Just (x + y) + + + {- =🛡= Standard Typeclasses and Deriving @@ -973,6 +1094,31 @@ implement the following functions: 🕯 HINT: to implement this task, derive some standard typeclasses -} +data DayOfWeek = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday deriving (Eq, Enum, Show) + +isWeekend :: DayOfWeek -> Bool +-- isWeekend n = case n of +-- x | x `elem` [Saturday, Sunday] -> True +-- _ -> False +isWeekend x + | x == Saturday = True + | x == Sunday = True + | otherwise = False + +nextDay :: DayOfWeek -> DayOfWeek +nextDay x + | x == Sunday = Monday + | otherwise = succ x + +daysToParty :: DayOfWeek -> Int +daysToParty x = let e = elemIndex x ls + ls = enumFrom (toEnum 0) + n = Data.Maybe.fromMaybe 0 e + -- n = case e of + -- Just y -> y + -- Nothing -> 0 + in if n <= 4 then 4 - n else 7 - (n - 4) + {- =💣= Task 9* @@ -1008,6 +1154,101 @@ Implement data types and typeclasses, describing such a battle between two contestants, and write a function that decides the outcome of a fight! -} +newtype AttackTsk9 = AttackTsk9 Int deriving (Show) +newtype HealthTsk9 = HealthTsk9 Int deriving (Show) +newtype DefenceTsk9 = DefenceTsk9 Int deriving (Show) +newtype KnightName = KnightName String deriving (Show) + +data KnightTsk9 = KnightTsk9 { + kntHealth :: HealthTsk9, + kntAttack :: AttackTsk9, + kntDefence :: DefenceTsk9 +} deriving (Show) + +data MonsterTsk9 = MonsterTsk9 { + mnstHealth :: HealthTsk9, + mnstAttack :: AttackTsk9 +} deriving (Show) + +scoreTsk9 :: HealthTsk9 -> AttackTsk9 -> HealthTsk9 +scoreTsk9 (HealthTsk9 x) (AttackTsk9 y) + | x > y = HealthTsk9 (x - y) + | otherwise = HealthTsk9 0 + +defendTsk9 :: HealthTsk9 -> DefenceTsk9 -> HealthTsk9 +defendTsk9 (HealthTsk9 x) (DefenceTsk9 y) = HealthTsk9 (x + y) + + +data Result = Draw | Lose | Win +fightTsk9 :: (KnightTsk9, MonsterTsk9) -> Result +fightTsk9 (KnightTsk9 {} , MonsterTsk9 (HealthTsk9 0) _ ) = Win -- Knight wins +fightTsk9 (KnightTsk9 (HealthTsk9 0) _ _ , MonsterTsk9 {}) = Lose -- knight loses +fightTsk9 _ = Draw + +proclaimOrDoTsk9 :: KnightName -> (KnightTsk9, MonsterTsk9) -> IO () +proclaimOrDoTsk9 k mat = do + print "Monster attacks" + print mat + putStr "\n" + let res = fightTsk9 mat + case res of + Draw -> makeMove k mat + Lose -> print "Monster wins" + Win -> print "Knight wins" + +knightAttackTsk9 :: (KnightTsk9, MonsterTsk9) -> (KnightTsk9, MonsterTsk9) +knightAttackTsk9 (KnightTsk9 kh ka kd, MonsterTsk9 mh ma) = (KnightTsk9 kh ka kd, MonsterTsk9 (scoreTsk9 mh ka) ma) + +monsterAttackTsk9 :: (KnightTsk9, MonsterTsk9) -> (KnightTsk9, MonsterTsk9) +monsterAttackTsk9 (KnightTsk9 kh ka kd, MonsterTsk9 mh ma) = (KnightTsk9 (scoreTsk9 (defendTsk9 kh kd) ma) ka kd, MonsterTsk9 mh ma) + +increaseDefenceTsk9 :: (KnightTsk9, MonsterTsk9) -> (KnightTsk9, MonsterTsk9) +increaseDefenceTsk9 (KnightTsk9 kh ka (DefenceTsk9 kd), MonsterTsk9 mh ma) = (KnightTsk9 kh ka (DefenceTsk9 (kd + 100)), MonsterTsk9 mh ma) + +drinkHealthTsk9 :: (KnightTsk9, MonsterTsk9) -> (KnightTsk9, MonsterTsk9) +drinkHealthTsk9 (KnightTsk9 (HealthTsk9 kh) ka kd, MonsterTsk9 mh ma) = (KnightTsk9 (HealthTsk9 (kh + 20)) ka kd, MonsterTsk9 mh ma) + + +makeMove :: KnightName -> (KnightTsk9, MonsterTsk9) -> IO () +makeMove (KnightName k) (knt, mns) = do + putStrLn "Attack(a), Cast spell to increase defence(d), Drink health potion(h):" + mv <- getLine + case mv of + "a" -> do + print (k ++ " attacks") + let at = knightAttackTsk9 (knt, mns) + print at + let mat = monsterAttackTsk9 at + proclaimOrDoTsk9 (KnightName k) mat + + "d" -> do + print (k ++ " casts spell. Defence is increased") + let df = increaseDefenceTsk9 (knt, mns) + print df + print (k ++ "'s defence just went up by 100.") + let mat = monsterAttackTsk9 df + proclaimOrDoTsk9 (KnightName k) mat + "h" -> do + print (k ++ "Knight drinks health potion.") + let hd = drinkHealthTsk9 (knt, mns) + print hd + print (k ++ "Knight's health just went up by 20.") + let mat = monsterAttackTsk9 hd + proclaimOrDoTsk9 (KnightName k) mat + "X" -> print "Goodbye" + _ -> print "invalid action, Try again. Press X to exit" + +startFight :: IO () +startFight = do + putStrLn "Who is it that wants to fight Monster Rakshas?" + knt <- getLine + print ("Welcome, " ++ knt ++ ". Let's fight Rakshas!!") + let start = (KnightTsk9 {kntHealth = HealthTsk9 100, kntAttack = AttackTsk9 20, kntDefence = DefenceTsk9 10}, MonsterTsk9 {mnstHealth = HealthTsk9 100, mnstAttack = AttackTsk9 60}) + print start + makeMove (KnightName knt) start + +-- to play the game, +-- startFight {- You did it! Now it is time to open pull request with your changes diff --git a/src/Chapter4.hs b/src/Chapter4.hs index 20532222..88558eec 100644 --- a/src/Chapter4.hs +++ b/src/Chapter4.hs @@ -41,6 +41,7 @@ Perfect. Let's crush this! {-# LANGUAGE InstanceSigs #-} module Chapter4 where +import Data.Char {- | =🛡= Kinds @@ -114,22 +115,30 @@ As always, try to guess the output first! And don't forget to insert the output in here: >>> :k Char +Char :: * >>> :k Bool +Bool :: * >>> :k [Int] +[Int] :: * >>> :k [] +[] :: * -> * >>> :k (->) +(->) :: * -> * -> * >>> :k Either +Either :: * -> * -> * >>> data Trinity a b c = MkTrinity a b c >>> :k Trinity +Trinity :: * -> * -> * -> * >>> data IntBox f = MkIntBox (f Int) >>> :k IntBox +IntBox :: (* -> *) -> * -} @@ -293,7 +302,9 @@ values and apply them to the type level? -} instance Functor (Secret e) where fmap :: (a -> b) -> Secret e a -> Secret e b - fmap = error "fmap for Box: not implemented!" + fmap _ (Trap g) = Trap g + fmap f (Reward c) = Reward (f c) + {- | =⚔️= Task 3 @@ -307,6 +318,11 @@ data List a = Empty | Cons a (List a) +instance Functor List where + fmap :: (a -> b) -> List a -> List b + fmap _ Empty = Empty + fmap f (Cons c d) = Cons (f c) (fmap f d) + {- | =🛡= Applicative @@ -472,12 +488,14 @@ Implement the Applicative instance for our 'Secret' data type from before. -} instance Applicative (Secret e) where pure :: a -> Secret e a - pure = error "pure Secret: Not implemented!" + pure = Reward (<*>) :: Secret e (a -> b) -> Secret e a -> Secret e b - (<*>) = error "(<*>) Secret: Not implemented!" + (<*>) (Trap c) _= Trap c + (<*>) _ (Trap c) = Trap c + (<*>) (Reward f) (Reward c) = Reward (f c) -{- | +{- | =⚔️= Task 5 Implement the 'Applicative' instance for our 'List' type. @@ -489,6 +507,14 @@ Implement the 'Applicative' instance for our 'List' type. type. -} +instance Applicative List where + pure :: a -> List a + pure c = Cons c Empty + (<*>) :: List (a -> b) -> List a -> List b + (<*>) _ Empty = Empty + (<*>) Empty _ = Empty + (<*>) (Cons g _) (Cons c d) = Cons (g c) ((<*>) (Cons g Empty) d) + {- | =🛡= Monad @@ -598,9 +624,17 @@ concepts in the end. Implement the 'Monad' instance for our 'Secret' type. -} -instance Monad (Secret e) where +upper :: Char -> Secret e Char +upper x = Reward (Data.Char.toUpper x) + +multiplyBy5 :: Int -> Secret e Int +multiplyBy5 x = Reward (x * 5) + + +instance Monad (Secret e ) where (>>=) :: Secret e a -> (a -> Secret e b) -> Secret e b - (>>=) = error "bind Secret: Not implemented!" + (>>=) (Trap c) _ = Trap c + (>>=) (Reward c) f = f c {- | =⚔️= Task 7 @@ -628,8 +662,33 @@ Can you implement a monad version of AND, polymorphic over any monad? 🕯 HINT: Use "(>>=)", "pure" and anonymous function -} + + +-- this would fail in Just False - Nothing case +-- andM :: (Monad m) => m Bool -> m Bool -> m Bool +-- andM n c = do +-- x <- n +-- y <- c +-- pure (x && y) + +checkBool :: (Monad m) => m Bool -> Bool-> m Bool +checkBool x True = x +checkBool _ False = pure False + + andM :: (Monad m) => m Bool -> m Bool -> m Bool -andM = error "andM: Not implemented!" +andM n c = n >>= checkBool c + +boolInt :: (Monad m) => Bool -> m Int +boolInt True = pure 1 +boolInt False = pure 0 + +intString :: (Monad m) => Int -> m [Char] +intString 0 = pure "fail" +intString _ = pure "passes" + +typeM :: (Monad m) => m Bool -> m [Char] +typeM b = b >>= boolInt >>= intString {- | =🐉= Task 9*: Final Dungeon Boss @@ -673,6 +732,52 @@ Specifically, ❃ Implement the function to convert Tree to list -} +data Node a = Leaf | Node a deriving (Show) + +data BinTree a = BinTree (Node a ) (BinTree a ) (BinTree a ) | TreeLeaf deriving (Show) + +instance Functor BinTree where + fmap :: (a -> b) -> BinTree a -> BinTree b + fmap _ TreeLeaf = TreeLeaf + fmap g (BinTree Leaf x y) = BinTree Leaf (fmap g x) (fmap g y) + fmap g (BinTree (Node d) x y) = BinTree (Node (g d)) (fmap g x) (fmap g y) + + +instance Applicative BinTree where + pure :: a -> BinTree a + pure x = BinTree (Node x) TreeLeaf TreeLeaf + (<*>) :: BinTree (a -> b) -> BinTree a -> BinTree b + (<*>) _ TreeLeaf = TreeLeaf + (<*>) TreeLeaf _ = TreeLeaf + (<*>) (BinTree Leaf _ _) _ = TreeLeaf + (<*>) (BinTree (Node g) _ _) (BinTree Leaf x y) = BinTree Leaf (fmap g x) (fmap g y) + (<*>) (BinTree (Node g) _ _) (BinTree (Node d) x y) = BinTree (Node (g d)) (fmap g x) (fmap g y) + + +instance Monad BinTree where + return :: a -> BinTree a + return = pure + (>>=) :: BinTree a -> (a -> BinTree b) -> BinTree b + (>>=) TreeLeaf _ = TreeLeaf + (>>=) (BinTree Leaf x y ) g = BinTree Leaf (x >>= g) (y >>= g) + (>>=) (BinTree (Node c) _ _ ) g = g c + +reverseTree :: BinTree a -> BinTree a +reverseTree TreeLeaf = TreeLeaf +reverseTree (BinTree c x y) = BinTree c (reverseTree y) (reverseTree x) + + + +treeToList :: BinTree a -> [a] +treeToList TreeLeaf = [] +treeToList (BinTree Leaf x y ) = treeToList x ++ treeToList y +treeToList (BinTree (Node c) x y ) = c : treeToList x ++ treeToList y + +-- example +-- treeToList (BinTree (Node 4) (BinTree (Node 5) (BinTree (Node 6) TreeLeaf TreeLeaf) TreeLeaf) (BinTree (Node 10) TreeLeaf TreeLeaf)) +-- [4,5,6,10] +-- treeToList (BinTree (Node "Mary") (BinTree (Node "had") (BinTree (Node "a") TreeLeaf TreeLeaf) TreeLeaf) (BinTree (Node " lamb") TreeLeaf TreeLeaf)) +-- ["Mary","had","a"," lamb"] {- You did it! Now it is time to open pull request with your changes