diff --git a/config.json b/config.json index 64cfaae5c..67bdc67cf 100644 --- a/config.json +++ b/config.json @@ -1307,6 +1307,22 @@ ], "difficulty": 3 }, + { + "slug": "bottle-song", + "name": "Bottle Song", + "uuid": "6c240d6c-d897-4c4e-a556-b595f45c2d3b", + "prerequisites": [ + "strings", + "multiple-clause-functions", + "ranges", + "enum", + "recursion" + ], + "practices": [ + "multiple-clause-functions" + ], + "difficulty": 3 + }, { "slug": "dnd-character", "name": "D&D Character", diff --git a/exercises/practice/bottle-song/.docs/instructions.md b/exercises/practice/bottle-song/.docs/instructions.md new file mode 100644 index 000000000..febdfc863 --- /dev/null +++ b/exercises/practice/bottle-song/.docs/instructions.md @@ -0,0 +1,57 @@ +# Instructions + +Recite the lyrics to that popular children's repetitive song: Ten Green Bottles. + +Note that not all verses are identical. + +```text +Ten green bottles hanging on the wall, +Ten green bottles hanging on the wall, +And if one green bottle should accidentally fall, +There'll be nine green bottles hanging on the wall. + +Nine green bottles hanging on the wall, +Nine green bottles hanging on the wall, +And if one green bottle should accidentally fall, +There'll be eight green bottles hanging on the wall. + +Eight green bottles hanging on the wall, +Eight green bottles hanging on the wall, +And if one green bottle should accidentally fall, +There'll be seven green bottles hanging on the wall. + +Seven green bottles hanging on the wall, +Seven green bottles hanging on the wall, +And if one green bottle should accidentally fall, +There'll be six green bottles hanging on the wall. + +Six green bottles hanging on the wall, +Six green bottles hanging on the wall, +And if one green bottle should accidentally fall, +There'll be five green bottles hanging on the wall. + +Five green bottles hanging on the wall, +Five green bottles hanging on the wall, +And if one green bottle should accidentally fall, +There'll be four green bottles hanging on the wall. + +Four green bottles hanging on the wall, +Four green bottles hanging on the wall, +And if one green bottle should accidentally fall, +There'll be three green bottles hanging on the wall. + +Three green bottles hanging on the wall, +Three green bottles hanging on the wall, +And if one green bottle should accidentally fall, +There'll be two green bottles hanging on the wall. + +Two green bottles hanging on the wall, +Two green bottles hanging on the wall, +And if one green bottle should accidentally fall, +There'll be one green bottle hanging on the wall. + +One green bottle hanging on the wall, +One green bottle hanging on the wall, +And if one green bottle should accidentally fall, +There'll be no green bottles hanging on the wall. +``` diff --git a/exercises/practice/bottle-song/.formatter.exs b/exercises/practice/bottle-song/.formatter.exs new file mode 100644 index 000000000..d2cda26ed --- /dev/null +++ b/exercises/practice/bottle-song/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/exercises/practice/bottle-song/.gitignore b/exercises/practice/bottle-song/.gitignore new file mode 100644 index 000000000..980151368 --- /dev/null +++ b/exercises/practice/bottle-song/.gitignore @@ -0,0 +1,26 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +bottle_song-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/exercises/practice/bottle-song/.meta/config.json b/exercises/practice/bottle-song/.meta/config.json new file mode 100644 index 000000000..a10e82923 --- /dev/null +++ b/exercises/practice/bottle-song/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "antoine-duchenet" + ], + "files": { + "solution": [ + "lib/bottle_song.ex" + ], + "test": [ + "test/bottle_song_test.exs" + ], + "example": [ + ".meta/example.ex" + ] + }, + "blurb": "Produce the lyrics to the popular children's repetitive song: Ten Green Bottles.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Ten_Green_Bottles" +} diff --git a/exercises/practice/bottle-song/.meta/design.md b/exercises/practice/bottle-song/.meta/design.md new file mode 100644 index 000000000..ab229d4a3 --- /dev/null +++ b/exercises/practice/bottle-song/.meta/design.md @@ -0,0 +1 @@ +# Design Decisions diff --git a/exercises/practice/bottle-song/.meta/example.ex b/exercises/practice/bottle-song/.meta/example.ex new file mode 100644 index 000000000..e60463696 --- /dev/null +++ b/exercises/practice/bottle-song/.meta/example.ex @@ -0,0 +1,51 @@ +defmodule BottleSong do + @moduledoc """ + Handles lyrics of the popular children song: Ten Green Bottles + """ + + @quantifiers %{ + 10 => "ten", + 9 => "nine", + 8 => "eight", + 7 => "seven", + 6 => "six", + 5 => "five", + 4 => "four", + 3 => "three", + 2 => "two", + 1 => "one", + 0 => "no" + } + + @spec recite(pos_integer, pos_integer) :: String.t() + def recite(start_bottle, take_down) do + 0..(take_down - 1) + |> Enum.map(&verse(start_bottle - &1)) + |> Enum.intersperse("") + |> List.flatten() + |> Enum.join("\n") + end + + @spec verse(pos_integer) :: Enum.t() + defp verse(before_the_fall) do + after_the_fall = before_the_fall - 1 + + [ + main_line(before_the_fall), + main_line(before_the_fall), + "And if one green bottle should accidentally fall,", + "There'll be #{quantifier(after_the_fall)} green #{bottle(after_the_fall)} hanging on the wall." + ] + end + + @spec main_line(pos_integer) :: String.t() + defp main_line(n), + do: "#{n |> quantifier() |> String.capitalize()} green #{bottle(n)} hanging on the wall," + + @spec quantifier(pos_integer) :: String.t() + defp quantifier(n), do: Map.get(@quantifiers, n) + + @spec bottle(pos_integer) :: String.t() + defp bottle(1), do: "bottle" + defp bottle(_), do: "bottles" +end diff --git a/exercises/practice/bottle-song/.meta/tests.toml b/exercises/practice/bottle-song/.meta/tests.toml new file mode 100644 index 000000000..1f6e40a37 --- /dev/null +++ b/exercises/practice/bottle-song/.meta/tests.toml @@ -0,0 +1,31 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[d4ccf8fc-01dc-48c0-a201-4fbeb30f2d03] +description = "verse -> single verse -> first generic verse" + +[0f0aded3-472a-4c64-b842-18d4f1f5f030] +description = "verse -> single verse -> last generic verse" + +[f61f3c97-131f-459e-b40a-7428f3ed99d9] +description = "verse -> single verse -> verse with 2 bottles" + +[05eadba9-5dbd-401e-a7e8-d17cc9baa8e0] +description = "verse -> single verse -> verse with 1 bottle" + +[a4a28170-83d6-4dc1-bd8b-319b6abb6a80] +description = "lyrics -> multiple verses -> first two verses" + +[3185d438-c5ac-4ce6-bcd3-02c9ff1ed8db] +description = "lyrics -> multiple verses -> last three verses" + +[28c1584a-0e51-4b65-9ae2-fbc0bf4bbb28] +description = "lyrics -> multiple verses -> all verses" diff --git a/exercises/practice/bottle-song/lib/bottle_song.ex b/exercises/practice/bottle-song/lib/bottle_song.ex new file mode 100644 index 000000000..476f6dc09 --- /dev/null +++ b/exercises/practice/bottle-song/lib/bottle_song.ex @@ -0,0 +1,9 @@ +defmodule BottleSong do + @moduledoc """ + Handles lyrics of the popular children song: Ten Green Bottles + """ + + @spec recite(pos_integer, pos_integer) :: String.t() + def recite(_start_bottle, _take_down) do + end +end diff --git a/exercises/practice/bottle-song/mix.exs b/exercises/practice/bottle-song/mix.exs new file mode 100644 index 000000000..b6cac602f --- /dev/null +++ b/exercises/practice/bottle-song/mix.exs @@ -0,0 +1,28 @@ +defmodule BottleSong.MixProject do + use Mix.Project + + def project do + [ + app: :bottle_song, + version: "1.0.0", + elixir: "~> 1.10", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/exercises/practice/bottle-song/test/bottle_song_test.exs b/exercises/practice/bottle-song/test/bottle_song_test.exs new file mode 100644 index 000000000..3a6c1cb6f --- /dev/null +++ b/exercises/practice/bottle-song/test/bottle_song_test.exs @@ -0,0 +1,144 @@ +defmodule BottleSongTest do + use ExUnit.Case + + describe "[Single verse]" do + # @tag :pending + test "First generic verse" do + assert BottleSong.recite(10, 1) == + """ + Ten green bottles hanging on the wall, + Ten green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be nine green bottles hanging on the wall.\ + """ + end + + @tag :pending + test "Last generic verse" do + assert BottleSong.recite(3, 1) == + """ + Three green bottles hanging on the wall, + Three green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be two green bottles hanging on the wall.\ + """ + end + + @tag :pendin + test "Verse with 2 bottles" do + assert BottleSong.recite(2, 1) == + """ + Two green bottles hanging on the wall, + Two green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be one green bottle hanging on the wall.\ + """ + end + + @tag :pending + test "Verse with 1 bottle" do + assert BottleSong.recite(1, 1) == + """ + One green bottle hanging on the wall, + One green bottle hanging on the wall, + And if one green bottle should accidentally fall, + There'll be no green bottles hanging on the wall.\ + """ + end + end + + describe "[Multiple verses]" do + @tag :pending + test "First two verses" do + assert BottleSong.recite(10, 2) == + """ + Ten green bottles hanging on the wall, + Ten green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be nine green bottles hanging on the wall. + + Nine green bottles hanging on the wall, + Nine green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be eight green bottles hanging on the wall.\ + """ + end + + @tag :pending + test "Last three verses" do + assert BottleSong.recite(3, 3) == + """ + Three green bottles hanging on the wall, + Three green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be two green bottles hanging on the wall. + + Two green bottles hanging on the wall, + Two green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be one green bottle hanging on the wall. + + One green bottle hanging on the wall, + One green bottle hanging on the wall, + And if one green bottle should accidentally fall, + There'll be no green bottles hanging on the wall.\ + """ + end + + @tag :pending + test "All verses" do + assert BottleSong.recite(10, 10) == + """ + Ten green bottles hanging on the wall, + Ten green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be nine green bottles hanging on the wall. + + Nine green bottles hanging on the wall, + Nine green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be eight green bottles hanging on the wall. + + Eight green bottles hanging on the wall, + Eight green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be seven green bottles hanging on the wall. + + Seven green bottles hanging on the wall, + Seven green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be six green bottles hanging on the wall. + + Six green bottles hanging on the wall, + Six green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be five green bottles hanging on the wall. + + Five green bottles hanging on the wall, + Five green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be four green bottles hanging on the wall. + + Four green bottles hanging on the wall, + Four green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be three green bottles hanging on the wall. + + Three green bottles hanging on the wall, + Three green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be two green bottles hanging on the wall. + + Two green bottles hanging on the wall, + Two green bottles hanging on the wall, + And if one green bottle should accidentally fall, + There'll be one green bottle hanging on the wall. + + One green bottle hanging on the wall, + One green bottle hanging on the wall, + And if one green bottle should accidentally fall, + There'll be no green bottles hanging on the wall.\ + """ + end + end +end diff --git a/exercises/practice/bottle-song/test/test_helper.exs b/exercises/practice/bottle-song/test/test_helper.exs new file mode 100644 index 000000000..35fc5bff8 --- /dev/null +++ b/exercises/practice/bottle-song/test/test_helper.exs @@ -0,0 +1,2 @@ +ExUnit.start() +ExUnit.configure(exclude: :pending, trace: true)