ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# angelikatyborska's solution

## to D&D Character in the Elixir Track

Published at Oct 28 2019 · 0 comments
Instructions
Test suite
Solution

For a game of Dungeons & Dragons, 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_character_test.exs

``````defmodule DndCharacterTest do
use ExUnit.Case

import DndCharacter, only: [modifier: 1, ability: 0, character: 0]

# canonical data version: 1.1.0

describe "ability modifier" do
# @tag :pending
test "for score 3 is -4" do
assert modifier(3) === -4
end

@tag :pending
test "for score 4 is -3" do
assert modifier(4) === -3
end

@tag :pending
test "for score 5 is -3" do
assert modifier(5) === -3
end

@tag :pending
test "for score 6 is -2" do
assert modifier(6) === -2
end

@tag :pending
test "for score 7 is -2" do
assert modifier(7) === -2
end

@tag :pending
test "for score 8 is -1" do
assert modifier(8) === -1
end

@tag :pending
test "for score 9 is -1" do
assert modifier(9) === -1
end

@tag :pending
test "for score 10 is 0" do
assert modifier(10) === 0
end

@tag :pending
test "for score 11 is 0" do
assert modifier(11) === 0
end

@tag :pending
test "for score 12 is +1" do
assert modifier(12) === 1
end

@tag :pending
test "for score 13 is +1" do
assert modifier(13) === 1
end

@tag :pending
test "for score 14 is +2" do
assert modifier(14) === 2
end

@tag :pending
test "for score 15 is +2" do
assert modifier(15) === 2
end

@tag :pending
test "for score 16 is +3" do
assert modifier(16) === 3
end

@tag :pending
test "for score 17 is +3" do
assert modifier(17) === 3
end

@tag :pending
test "for score 18 is +4" do
assert modifier(18) === 4
end
end

describe "random ability" do
@tag :pending
test "is within range" do
Enum.each(1..200, fn _ -> assert ability() in 3..18 end)
end
end

describe "random character" do
setup do
%{character: character()}
end

@tag :pending
test "has valid hitpoints", %{character: character} do
assert character.hitpoints === 10 + modifier(character.constitution)
end

@tag :pending
test "has each ability only calculated once", %{character: character} do
assert character.strength === character.strength
end
end
end``````

### test_helper.exs

``````ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)``````
``````defmodule DndCharacter do
@type t :: %__MODULE__{
strength: pos_integer(),
dexterity: pos_integer(),
constitution: pos_integer(),
intelligence: pos_integer(),
wisdom: pos_integer(),
charisma: pos_integer(),
hitpoints: pos_integer()
}

defstruct ~w[strength dexterity constitution intelligence wisdom charisma hitpoints]a

@spec modifier(pos_integer()) :: integer()
def modifier(score) do
Integer.floor_div(score - 10, 2)
end

@spec ability :: pos_integer()
def ability do
1..4
|> Enum.map(fn _ -> k6_roll() end)
|> Enum.sort()
|> Enum.take(-3)
|> Enum.sum()
end

@spec character :: t()
def character do
character = %__MODULE__{
strength: ability(),
dexterity: ability(),
constitution: ability(),
intelligence: ability(),
wisdom: ability(),
charisma: ability()
}

%{character | hitpoints: 10 + modifier(character.constitution)}
end

defp k6_roll() do
:rand.uniform(6)
end
end``````