-
-
Notifications
You must be signed in to change notification settings - Fork 194
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dnd-character: add new exercise (#782)
This exercise was created in exercism/problem-specifications#1397. It is a basic exercise in random generators, so the idea I had for the Haskell track was to practice making QuickCheck generators rather than simply use the standard random generator. For this reason, QuickCheck is added as a root dependency. The 1.1.0 test case added in exercism/problem-specifications#1416 does not apply to QuickCheck generators.
- Loading branch information
Showing
9 changed files
with
292 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
## Hints | ||
|
||
This exercise uses QuickCheck generators. Here are some resources to get started: | ||
|
||
- [A QuickCheck Tutorial: Generators](https://www.stackbuilders.com/news/a-quickcheck-tutorial-generators) | ||
- The documentation for [Test.QuickCheck.Gen](http://hackage.haskell.org/package/QuickCheck-2.12.6.1/docs/Test-QuickCheck-Gen.html) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# D&D Character | ||
|
||
For a game of [Dungeons & Dragons][DND], each player starts by generating a | ||
character they can play with. This character has, among other things, six | ||
abilities; strength, dexterity, constitution, intelligence, wisdom and | ||
charisma. These six abilities have scores that are determined randomly. You | ||
do this by rolling four 6-sided dice and record the sum of the largest three | ||
dice. You do this six times, once for each ability. | ||
|
||
Your character's initial hitpoints are 10 + your character's constitution | ||
modifier. You find your character's constitution modifier by subtracting 10 | ||
from your character's constitution, divide by 2 and round down. | ||
|
||
Write a random character generator that follows the rules above. | ||
|
||
For example, the six throws of four dice may look like: | ||
|
||
* 5, 3, 1, 6: You discard the 1 and sum 5 + 3 + 6 = 14, which you assign to strength. | ||
* 3, 2, 5, 3: You discard the 2 and sum 3 + 5 + 3 = 11, which you assign to dexterity. | ||
* 1, 1, 1, 1: You discard the 1 and sum 1 + 1 + 1 = 3, which you assign to constitution. | ||
* 2, 1, 6, 6: You discard the 1 and sum 2 + 6 + 6 = 14, which you assign to intelligence. | ||
* 3, 5, 3, 4: You discard the 3 and sum 5 + 3 + 4 = 12, which you assign to wisdom. | ||
* 6, 6, 6, 6: You discard the 6 and sum 6 + 6 + 6 = 18, which you assign to charisma. | ||
|
||
Because constitution is 3, the constitution modifier is -4 and the hitpoints are 6. | ||
|
||
## Notes | ||
|
||
Most programming languages feature (pseudo-)random generators, but few | ||
programming languages are designed to roll dice. One such language is [Troll]. | ||
|
||
[DND]: https://en.wikipedia.org/wiki/Dungeons_%26_Dragons | ||
[Troll]: http://hjemmesider.diku.dk/~torbenm/Troll/ | ||
|
||
## Hints | ||
|
||
This exercise uses QuickCheck generators. Here are some resources to get started: | ||
|
||
- [A QuickCheck Tutorial: Generators](https://www.stackbuilders.com/news/a-quickcheck-tutorial-generators) | ||
- The documentation for [Test.QuickCheck.Gen](http://hackage.haskell.org/package/QuickCheck-2.12.6.1/docs/Test-QuickCheck-Gen.html) | ||
|
||
|
||
|
||
## Getting Started | ||
|
||
For installation and learning resources, refer to the | ||
[exercism help page](http://exercism.io/languages/haskell). | ||
|
||
## Running the tests | ||
|
||
To run the test suite, execute the following command: | ||
|
||
```bash | ||
stack test | ||
``` | ||
|
||
#### If you get an error message like this... | ||
|
||
``` | ||
No .cabal file found in directory | ||
``` | ||
|
||
You are probably running an old stack version and need | ||
to upgrade it. | ||
|
||
#### Otherwise, if you get an error message like this... | ||
|
||
``` | ||
No compiler found, expected minor version match with... | ||
Try running "stack setup" to install the correct GHC... | ||
``` | ||
|
||
Just do as it says and it will download and install | ||
the correct compiler version: | ||
|
||
```bash | ||
stack setup | ||
``` | ||
|
||
## Running *GHCi* | ||
|
||
If you want to play with your solution in GHCi, just run the command: | ||
|
||
```bash | ||
stack ghci | ||
``` | ||
|
||
## Feedback, Issues, Pull Requests | ||
|
||
The [exercism/haskell](https://github.com/exercism/haskell) repository on | ||
GitHub is the home for all of the Haskell exercises. | ||
|
||
If you have feedback about an exercise, or want to help implementing a new | ||
one, head over there and create an issue. We'll do our best to help you! | ||
|
||
## Source | ||
|
||
Simon Shine, Erik Schierboom [https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945](https://github.com/exercism/problem-specifications/issues/616#issuecomment-437358945) | ||
|
||
## Submitting Incomplete Solutions | ||
It's possible to submit an incomplete solution so you can see how others have completed the exercise. |
17 changes: 17 additions & 0 deletions
17
exercises/dnd-character/examples/success-standard/package.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
name: dnd-character | ||
|
||
dependencies: | ||
- base | ||
- QuickCheck | ||
|
||
library: | ||
exposed-modules: DND | ||
source-dirs: src | ||
|
||
tests: | ||
test: | ||
main: Tests.hs | ||
source-dirs: test | ||
dependencies: | ||
- dnd-character | ||
- hspec |
37 changes: 37 additions & 0 deletions
37
exercises/dnd-character/examples/success-standard/src/DND.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{-# LANGUAGE RecordWildCards #-} | ||
module DND (Character(..), ability, modifier, character) where | ||
|
||
import Control.Monad (replicateM) | ||
import Test.QuickCheck.Gen (Gen, choose) | ||
|
||
data Character = Character | ||
{ name :: String | ||
, strength :: Int | ||
, dexterity :: Int | ||
, constitution :: Int | ||
, intelligence :: Int | ||
, wisdom :: Int | ||
, charisma :: Int | ||
, hitpoints :: Int | ||
} | ||
deriving (Show, Eq) | ||
|
||
modifier :: Int -> Int | ||
modifier a = (a - 10) `div` 2 | ||
|
||
ability :: Gen Int | ||
ability = do | ||
ds <- replicateM 4 (choose (1, 6)) | ||
return (sum ds - minimum ds) | ||
|
||
character :: Gen Character | ||
character = do | ||
let name = "Bob" | ||
strength <- ability | ||
dexterity <- ability | ||
constitution <- ability | ||
intelligence <- ability | ||
wisdom <- ability | ||
charisma <- ability | ||
let hitpoints = 10 + modifier constitution | ||
return Character{..} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
name: dnd-character | ||
version: 1.1.0.0 | ||
|
||
dependencies: | ||
- base | ||
- QuickCheck | ||
|
||
library: | ||
exposed-modules: DND | ||
source-dirs: src | ||
ghc-options: -Wall | ||
# dependencies: | ||
# - foo # List here the packages you | ||
# - bar # want to use in your solution. | ||
|
||
tests: | ||
test: | ||
main: Tests.hs | ||
source-dirs: test | ||
dependencies: | ||
- dnd-character | ||
- hspec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
module DND ( Character(..) | ||
, ability | ||
, modifier | ||
, character | ||
) where | ||
|
||
import Test.QuickCheck (Gen) | ||
|
||
data Character = Character | ||
{ name :: String | ||
, strength :: Int | ||
, dexterity :: Int | ||
, constitution :: Int | ||
, intelligence :: Int | ||
, wisdom :: Int | ||
, charisma :: Int | ||
, hitpoints :: Int | ||
} | ||
deriving (Show, Eq) | ||
|
||
modifier :: Int -> Int | ||
modifier = | ||
error "You need to implement this function." | ||
|
||
ability :: Gen Int | ||
ability = | ||
error "You need to implement this generator." | ||
|
||
character :: Gen Character | ||
character = | ||
error "You need to implement this generator." |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
resolver: lts-12.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
{-# OPTIONS_GHC -fno-warn-type-defaults #-} | ||
{-# LANGUAGE RecordWildCards #-} | ||
|
||
import Data.Foldable (for_) | ||
import Test.Hspec (Spec, describe, it, shouldBe) | ||
import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith) | ||
import Test.QuickCheck | ||
import Text.Printf (printf) | ||
|
||
import DND (Character(..), ability, modifier, character) | ||
|
||
data Case = Case { input :: Int | ||
, expected :: Int | ||
} | ||
|
||
type Ability = Character -> Int | ||
|
||
main :: IO () | ||
main = hspecWith defaultConfig {configFastFail = True} specs | ||
|
||
specs :: Spec | ||
specs = do | ||
describe "modifier" $ for_ cases test | ||
|
||
describe "ability" $ it "generates values within range" $ | ||
property (forAll ability abilityWithinRange) | ||
|
||
describe "character" $ it "generates valid characters" $ | ||
property (forAll character characterIsValid) | ||
where | ||
test Case{..} = it description assertion | ||
where | ||
description = printf "computes the modifier for %2d to be %2d" input expected | ||
assertion = modifier input `shouldBe` expected | ||
|
||
cases :: [Case] | ||
cases = map (uncurry Case) | ||
[ (18, 4), (17, 3), (16, 3), (15, 2), (14, 2) | ||
, (13, 1), (12, 1), (11, 0), (10, 0), (9, -1) | ||
, (8, -1), (7, -2), (6, -2), (5, -3), (4, -3) | ||
, (3, -4) ] | ||
|
||
abilityWithinRange :: Int -> Bool | ||
abilityWithinRange ability' = ability' >= 3 && ability' <= 18 | ||
|
||
characterIsValid :: Character -> Property | ||
characterIsValid character' = | ||
conjoin $ | ||
hitpointsAddUp character' : | ||
map (abilityWithinRange' character') abilities | ||
where | ||
abilities = [ ("strength", strength) | ||
, ("dexterity", dexterity) | ||
, ("constitution", constitution) | ||
, ("intelligence", intelligence) | ||
, ("wisdom", wisdom) | ||
, ("charisma", charisma) ] | ||
|
||
abilityWithinRange' :: Character -> (String, Ability) -> Property | ||
abilityWithinRange' character' (name, ability') = | ||
counterexample msg (abilityWithinRange (ability' character')) | ||
where msg = "The '" ++ name ++ "' ability is out of range" | ||
|
||
hitpointsAddUp :: Character -> Property | ||
hitpointsAddUp Character{..} = | ||
counterexample msg (hitpoints === 10 + modifier constitution) | ||
where msg = "The 'hitpoints' are not 10 + constitution modifier" |