module DndCharacter | |||||
open System | |||||
let random = Random() | |||||
let diceRoll = (random.Next 5) + 1 | |||||
let modifier x = int (floor (float (x - 10) / 2.0)) | |||||
let ability () = | |||||
List.sum (List.take 3 (List.sortDescending [ for i in 1 .. 4 -> diceRoll ])) | |||||
type Character = | |||||
{ Strength: int | |||||
Dexterity: int | |||||
Constitution: int | |||||
Intelligence: int | |||||
Wisdom: int | |||||
Charisma: int | |||||
Hitpoints: int } | |||||
let createCharacter () = | |||||
let constitution = ability () | |||||
{ Strength = ability () | |||||
Dexterity = ability () | |||||
Constitution = constitution | |||||
Intelligence = ability () | |||||
Wisdom = ability () | |||||
Charisma = ability () | |||||
Hitpoints = (10 + modifier constitution) } |
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp3.0</TargetFramework> | |||||
<IsPackable>false</IsPackable> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<Compile Include="DndCharacter.fs" /> | |||||
<Compile Include="DndCharacterTests.fs" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> | |||||
<PackageReference Include="xunit" Version="2.4.1" /> | |||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> | |||||
<PackageReference Include="FsUnit.xUnit" Version="3.8.1" /> | |||||
</ItemGroup> | |||||
</Project> |
// This file was auto-generated based on version 1.1.0 of the canonical data. | |||||
module DndCharacterTests | |||||
open FsUnit.Xunit | |||||
open Xunit | |||||
open DndCharacter | |||||
[<Fact>] | |||||
let ``Ability modifier for score 3 is -4`` () = | |||||
modifier 3 |> should equal -4 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 4 is -3`` () = | |||||
modifier 4 |> should equal -3 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 5 is -3`` () = | |||||
modifier 5 |> should equal -3 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 6 is -2`` () = | |||||
modifier 6 |> should equal -2 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 7 is -2`` () = | |||||
modifier 7 |> should equal -2 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 8 is -1`` () = | |||||
modifier 8 |> should equal -1 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 9 is -1`` () = | |||||
modifier 9 |> should equal -1 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 10 is 0`` () = | |||||
modifier 10 |> should equal 0 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 11 is 0`` () = | |||||
modifier 11 |> should equal 0 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 12 is +1`` () = | |||||
modifier 12 |> should equal 1 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 13 is +1`` () = | |||||
modifier 13 |> should equal 1 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 14 is +2`` () = | |||||
modifier 14 |> should equal 2 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 15 is +2`` () = | |||||
modifier 15 |> should equal 2 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 16 is +3`` () = | |||||
modifier 16 |> should equal 3 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 17 is +3`` () = | |||||
modifier 17 |> should equal 3 | |||||
[<Fact>] | |||||
let ``Ability modifier for score 18 is +4`` () = | |||||
modifier 18 |> should equal 4 | |||||
[<Fact>] | |||||
let ``Random ability is within range`` () = | |||||
for i in 1 .. 10 do | |||||
let ability = ability() | |||||
ability |> should be (greaterThanOrEqualTo 3) | |||||
ability |> should be (lessThanOrEqualTo 18) | |||||
[<Fact>] | |||||
let ``Random character is valid`` () = | |||||
for i in 1 .. 10 do | |||||
let character = createCharacter() | |||||
character.Strength |> should be (greaterThanOrEqualTo 3) | |||||
character.Strength |> should be (lessThanOrEqualTo 18) | |||||
character.Dexterity |> should be (greaterThanOrEqualTo 3) | |||||
character.Dexterity |> should be (lessThanOrEqualTo 18) | |||||
character.Constitution |> should be (greaterThanOrEqualTo 3) | |||||
character.Constitution |> should be (lessThanOrEqualTo 18) | |||||
character.Intelligence |> should be (greaterThanOrEqualTo 3) | |||||
character.Intelligence |> should be (lessThanOrEqualTo 18) | |||||
character.Wisdom |> should be (greaterThanOrEqualTo 3) | |||||
character.Wisdom |> should be (lessThanOrEqualTo 18) | |||||
character.Charisma |> should be (greaterThanOrEqualTo 3) | |||||
character.Charisma |> should be (lessThanOrEqualTo 18) | |||||
character.Hitpoints |> should equal (10 + modifier(character.Constitution)) | |||||
[<Fact>] | |||||
let ``Each ability is only calculated once`` () = | |||||
for i in 1 .. 10 do | |||||
let character = createCharacter() | |||||
character.Strength |> should equal character.Strength | |||||
character.Dexterity |> should equal character.Dexterity | |||||
character.Constitution |> should equal character.Constitution | |||||
character.Intelligence |> should equal character.Intelligence | |||||
character.Wisdom |> should equal character.Wisdom | |||||
character.Charisma |> should equal character.Charisma | |||||
character.Hitpoints |> should equal character.Hitpoints | |||||
# 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/ | |||||
## Running the tests | |||||
To run the tests, run the command `dotnet test` from within the exercise directory. | |||||
## Autoformatting the code | |||||
F# source code can be formatted with the [Fantomas](https://github.com/fsprojects/fantomas) tool. | |||||
After installing it with `dotnet tool restore`, run `dotnet fantomas .` to format code within the current directory. | |||||
## Further information | |||||
For more detailed information about the F# track, including how to get help if | |||||
you're having trouble, please visit the exercism.io [F# language page](http://exercism.io/languages/fsharp/resources). | |||||
## 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) | |||||