Skip to content

Commit c645312

Browse files
pwadsworthpetertseng
authored andcommitted
Add guessing-game exercise
1 parent d2174e4 commit c645312

File tree

13 files changed

+256
-0
lines changed

13 files changed

+256
-0
lines changed

config.json

+12
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@
6868
"concepts": [
6969
"booleans"
7070
]
71+
},
72+
{
73+
"slug": "guessing-game",
74+
"name": "Guessing Game",
75+
"uuid": "fb9868eb-ce4f-4e6b-aa0d-b47d3451070f",
76+
"prerequisites": [
77+
"numbers"
78+
],
79+
"status": "beta",
80+
"concepts": [
81+
"pattern-matching-literals"
82+
]
7183
}
7284
],
7385
"practice": [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Hints
2+
3+
## General
4+
5+
- Learn You A Haskell has a nice [introduction to pattern matching in Haskell][pattern-matching].
6+
7+
## 1. Reply to a correct guess
8+
9+
- You can use a constant pattern to match on a specific number.
10+
11+
## 2. Reply to a close guess
12+
13+
- You can use a constant pattern to match on a specific number.
14+
15+
## 3. Reply to too low guesses
16+
17+
- You can use a combination of a variable pattern and [guards][guards].
18+
19+
## 4. Reply to too high guesses
20+
21+
- You can use a combination of a variable pattern and [guards][guards].
22+
23+
24+
[pattern-matching]: https://learnyouahaskell.github.io/syntax-in-functions#pattern-matching
25+
[guards]: https://learnyouahaskell.github.io/syntax-in-functions#guards-guards
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Instructions
2+
3+
In this exercise, you are playing a number guessing game with a friend.
4+
The rules are simple: you secretly choose a number between `1` and `100` and your friend tries to guess what number you've chosen.
5+
To help your friend, you respond differently depending on how close the guess was to the number you've chosen (`42`).
6+
These are the rules for the different possible inputs:
7+
8+
- If the guess is `42`: "Correct"
9+
- If the guess is `41` or `43`: "So close"
10+
- If the guess is less than `41`: "Too low"
11+
- If the guess is greater than `43`: "Too high"
12+
13+
You have four tasks to encode the replies to the guesses.
14+
15+
## 1. Reply to a correct guess
16+
17+
Implement the `reply` function to reply to a correct guess:
18+
19+
```haskell
20+
reply 42
21+
-- -> "Correct"
22+
```
23+
24+
## 2. Reply to a close guess
25+
26+
Modify the `reply` function to reply to close guesses:
27+
28+
```haskell
29+
reply 41
30+
-- -> "So close"
31+
```
32+
33+
## 3. Reply to too low guesses
34+
35+
Modify the `reply` function to reply to too low guesses:
36+
37+
```haskell
38+
reply 25
39+
-- -> "Too low"
40+
```
41+
42+
## 4. Reply to too high guesses
43+
44+
Modify the `reply` function to reply to too high guesses:
45+
46+
```haskell
47+
reply 88
48+
-- -> "Too high"
49+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Introduction
2+
3+
## Pattern Matching Literals
4+
5+
While `if/else` expressions can be used to execute conditional logic, Haskell also has a more powerful way to execute conditional logic: [pattern matching][pattern-matching].
6+
With pattern matching, a value can be tested against one or more _patterns_.
7+
An example of such a pattern is the _constant pattern_, which matches a value against a constant (e.g. `1` or `"hello"`).
8+
9+
When defining functions, you can define separate function bodies for different patterns.
10+
This leads to clean code that is simple and readable.
11+
You can pattern match on any data type — numbers, characters, lists, tuples, etc.
12+
13+
For example, a trivial function that takes a whole number (`Int`) and makes it _1_ closer to _0_ could be expressed like this:
14+
15+
```haskell
16+
closerToZero :: Int -> Int
17+
closerToZero 0 = 0
18+
closerToZero 1 = 0
19+
```
20+
21+
Pattern matching starts to shine when used together with other patterns, for example the _variable pattern_:
22+
23+
```haskell
24+
closerToZero :: Int -> Int
25+
closerToZero 0 = 0
26+
closerToZero n = n - 1
27+
```
28+
29+
The above example treats all inputs other than _0_ the same, and would produce incorrect results for negative numbers.
30+
This can be solved using conditional patterns, known as _guards_, which are expressed with the `|` symbol:
31+
32+
```haskell
33+
closerToZero :: Int -> Int
34+
closerToZero n
35+
| n < 0 = n + 1
36+
| n > 0 = n - 1
37+
```
38+
39+
In the above examples not all possible inputs have a matching pattern.
40+
The compiler will detect this and output a warning.
41+
This is a very useful feature of Haskell that helps ensure all possible paths are covered to avoid run-time errors.
42+
It is known as _exhaustive checking_.
43+
To solve the warning, you have to handle all cases.
44+
Within _guards_ you can use the expression `otherwise` as syntactic sugar for `True` to catch all remaining patterns.
45+
46+
```haskell
47+
closerToZero :: Int -> Int
48+
closerToZero n
49+
| n < 0 = n + 1
50+
| n > 0 = n - 1
51+
| otherwise = 0
52+
```
53+
54+
Pattern matching will test a value against each pattern from top to bottom, until it finds a matching pattern and executes the logic associated with that pattern.
55+
**The order of patterns matters!**
56+
57+
[pattern-matching]: https://learnyouahaskell.github.io/syntax-in-functions#pattern-matching
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Introduction
2+
3+
%{concept:pattern-matching-literals}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"blurb": "Learn about pattern matching by implementing a simple secret number guessing game",
3+
"icon": "guessing-game",
4+
"authors": [
5+
"pwadsworth"
6+
],
7+
"files": {
8+
"solution": [
9+
"src/GuessingGame.hs",
10+
"package.yaml"
11+
],
12+
"test": [
13+
"test/Tests.hs"
14+
],
15+
"exemplar": [
16+
".meta/exemplar/src/GuessingGame.hs"
17+
]
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Design
2+
3+
## Learning objectives
4+
5+
- Know what [pattern matching][pattern-matching] is.
6+
- Know about constant, variable and wildcard patterns.
7+
- Know about guards.
8+
- Know about exhaustiveness checking in pattern matching.
9+
10+
## Out of scope
11+
12+
- Other patterns besides the constant and wildcard patterns.
13+
- Lazy pattern matching.
14+
- `case` statements.
15+
16+
## Concepts
17+
18+
- `pattern-matching`: know what [pattern matching][pattern-matching] is; know about constant, variable and wildcard patterns; know how to use guards; know about exhaustiveness checking in pattern matching.
19+
20+
## Prerequisites
21+
22+
- `basics`: function definitions
23+
- `numbers`: numeric types and operators
24+
25+
[pattern-matching]: https://learnyouahaskell.github.io/syntax-in-functions#pattern-matching
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: guessing-game
2+
version: 1.0.0.0
3+
4+
dependencies:
5+
- base
6+
7+
library:
8+
exposed-modules: GuessingGame
9+
source-dirs: src
10+
ghc-options: -Wall
11+
12+
tests:
13+
test:
14+
main: Tests.hs
15+
source-dirs: test
16+
dependencies:
17+
- guessing-game
18+
- hspec
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module GuessingGame (reply) where
2+
3+
reply :: Int -> String
4+
reply 41 = "So close"
5+
reply 42 = "Correct"
6+
reply 43 = "So close"
7+
reply guess
8+
| guess < 41 = "Too low"
9+
| otherwise = "Too high"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: guessing-game
2+
version: 1.0.0.0
3+
4+
dependencies:
5+
- base
6+
7+
library:
8+
exposed-modules: GuessingGame
9+
source-dirs: src
10+
ghc-options: -Wall
11+
12+
tests:
13+
test:
14+
main: Tests.hs
15+
source-dirs: test
16+
dependencies:
17+
- guessing-game
18+
- hspec
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module GuessingGame (reply) where
2+
3+
reply :: Int -> String
4+
reply guess = error "Implement this function."
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
resolver: lts-18.14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import GuessingGame (reply)
2+
import Test.Hspec (describe, hspec, it, shouldBe)
3+
4+
main :: IO ()
5+
main = hspec $
6+
describe "reply" $ do
7+
it "1 should be 'Too low'" $
8+
reply 1 `shouldBe` "Too low"
9+
it "100 should be 'Too high'" $
10+
reply 100 `shouldBe` "Too high"
11+
it "41 should be 'So close'" $
12+
reply 41 `shouldBe` "So close"
13+
it "43 should be 'So close'" $
14+
reply 43 `shouldBe` "So close"
15+
it "42 should be 'Correct'" $
16+
reply 42 `shouldBe` "Correct"

0 commit comments

Comments
 (0)