From 8be1ec30338bd06ad722fc43759c7e581ebea09b Mon Sep 17 00:00:00 2001
From: Erik Schierboom <erik_schierboom@hotmail.com>
Date: Sat, 18 Jan 2025 19:01:57 +0100
Subject: [PATCH] Add `food-chain` exercise

---
 config.json                                   |  12 ++
 .../practice/food-chain/.docs/instructions.md |  64 ++++++++
 .../practice/food-chain/.meta/config.json     |  19 +++
 .../practice/food-chain/.meta/example.clj     |  50 ++++++
 .../practice/food-chain/.meta/generator.tpl   |  12 ++
 .../practice/food-chain/.meta/tests.toml      |  40 +++++
 exercises/practice/food-chain/deps.edn        |   6 +
 exercises/practice/food-chain/project.clj     |   4 +
 .../practice/food-chain/src/food_chain.clj    |   7 +
 .../food-chain/test/food_chain_test.clj       | 150 ++++++++++++++++++
 10 files changed, 364 insertions(+)
 create mode 100644 exercises/practice/food-chain/.docs/instructions.md
 create mode 100644 exercises/practice/food-chain/.meta/config.json
 create mode 100644 exercises/practice/food-chain/.meta/example.clj
 create mode 100644 exercises/practice/food-chain/.meta/generator.tpl
 create mode 100644 exercises/practice/food-chain/.meta/tests.toml
 create mode 100644 exercises/practice/food-chain/deps.edn
 create mode 100644 exercises/practice/food-chain/project.clj
 create mode 100644 exercises/practice/food-chain/src/food_chain.clj
 create mode 100644 exercises/practice/food-chain/test/food_chain_test.clj

diff --git a/config.json b/config.json
index b109f13e6..86e782de4 100644
--- a/config.json
+++ b/config.json
@@ -1253,6 +1253,18 @@
           "time"
         ]
       },
+      {
+        "slug": "food-chain",
+        "name": "Food Chain",
+        "uuid": "188f12ad-e6ed-40d4-b515-d2b4d49cbb69",
+        "practices": [],
+        "prerequisites": [
+          "strings",
+          "conditionals",
+          "vectors"
+        ],
+        "difficulty": 5
+      },
       {
         "slug": "list-ops",
         "name": "List Ops",
diff --git a/exercises/practice/food-chain/.docs/instructions.md b/exercises/practice/food-chain/.docs/instructions.md
new file mode 100644
index 000000000..125820e32
--- /dev/null
+++ b/exercises/practice/food-chain/.docs/instructions.md
@@ -0,0 +1,64 @@
+# Instructions
+
+Generate the lyrics of the song 'I Know an Old Lady Who Swallowed a Fly'.
+
+While you could copy/paste the lyrics, or read them from a file, this problem is much more interesting if you approach it algorithmically.
+
+This is a [cumulative song][cumulative-song] of unknown origin.
+
+This is one of many common variants.
+
+```text
+I know an old lady who swallowed a fly.
+I don't know why she swallowed the fly. Perhaps she'll die.
+
+I know an old lady who swallowed a spider.
+It wriggled and jiggled and tickled inside her.
+She swallowed the spider to catch the fly.
+I don't know why she swallowed the fly. Perhaps she'll die.
+
+I know an old lady who swallowed a bird.
+How absurd to swallow a bird!
+She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
+She swallowed the spider to catch the fly.
+I don't know why she swallowed the fly. Perhaps she'll die.
+
+I know an old lady who swallowed a cat.
+Imagine that, to swallow a cat!
+She swallowed the cat to catch the bird.
+She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
+She swallowed the spider to catch the fly.
+I don't know why she swallowed the fly. Perhaps she'll die.
+
+I know an old lady who swallowed a dog.
+What a hog, to swallow a dog!
+She swallowed the dog to catch the cat.
+She swallowed the cat to catch the bird.
+She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
+She swallowed the spider to catch the fly.
+I don't know why she swallowed the fly. Perhaps she'll die.
+
+I know an old lady who swallowed a goat.
+Just opened her throat and swallowed a goat!
+She swallowed the goat to catch the dog.
+She swallowed the dog to catch the cat.
+She swallowed the cat to catch the bird.
+She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
+She swallowed the spider to catch the fly.
+I don't know why she swallowed the fly. Perhaps she'll die.
+
+I know an old lady who swallowed a cow.
+I don't know how she swallowed a cow!
+She swallowed the cow to catch the goat.
+She swallowed the goat to catch the dog.
+She swallowed the dog to catch the cat.
+She swallowed the cat to catch the bird.
+She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.
+She swallowed the spider to catch the fly.
+I don't know why she swallowed the fly. Perhaps she'll die.
+
+I know an old lady who swallowed a horse.
+She's dead, of course!
+```
+
+[cumulative-song]: https://en.wikipedia.org/wiki/Cumulative_song
diff --git a/exercises/practice/food-chain/.meta/config.json b/exercises/practice/food-chain/.meta/config.json
new file mode 100644
index 000000000..c53956222
--- /dev/null
+++ b/exercises/practice/food-chain/.meta/config.json
@@ -0,0 +1,19 @@
+{
+  "authors": [
+    "erikschierboom"
+  ],
+  "files": {
+    "solution": [
+      "src/food_chain.clj"
+    ],
+    "test": [
+      "test/food_chain_test.clj"
+    ],
+    "example": [
+      ".meta/example.clj"
+    ]
+  },
+  "blurb": "Generate the lyrics of the song 'I Know an Old Lady Who Swallowed a Fly'.",
+  "source": "Wikipedia",
+  "source_url": "https://en.wikipedia.org/wiki/There_Was_an_Old_Lady_Who_Swallowed_a_Fly"
+}
diff --git a/exercises/practice/food-chain/.meta/example.clj b/exercises/practice/food-chain/.meta/example.clj
new file mode 100644
index 000000000..9d1e8c94c
--- /dev/null
+++ b/exercises/practice/food-chain/.meta/example.clj
@@ -0,0 +1,50 @@
+(ns food-chain
+  (:require [clojure.string :as str]))
+
+(def animals ["fly" "spider" "bird" "cat" "dog" "goat" "cow" "horse"])
+
+(def phrases [nil
+              "It wriggled and jiggled and tickled inside her."
+              "How absurd to swallow a bird!"
+              "Imagine that, to swallow a cat!"
+              "What a hog, to swallow a dog!"
+              "Just opened her throat and swallowed a goat!"
+              "I don't know how she swallowed a cow!"
+              nil])
+
+(defn verse-begin [n]
+  (let [first (str "I know an old lady who swallowed a " (get animals n) ".")
+        second (get phrases n)]
+    (remove nil? [first second])))
+
+(defn verse-caught [caught]
+  (if (= "spider" caught)
+    (str caught " that wriggled and jiggled and tickled inside her")
+    caught))
+
+(defn verse-swallow [[swallowed caught]]
+  (str "She swallowed the " swallowed " to catch the " (verse-caught caught) "."))
+
+(defn verse-swallows [n]
+  (mapv verse-swallow (partition 2 1 (reverse (take (inc n) animals)))))
+
+(defn verse-middle [n]
+  (if (< 0 n 7)
+    (verse-swallows n)
+    []))
+
+(defn verse-end [n]
+  (if (< n 7)
+    "I don't know why she swallowed the fly. Perhaps she'll die."
+    "She's dead, of course!"))
+
+(defn verse [n]
+  (->> (cons (verse-begin n) (conj (verse-middle n) (verse-end n)))
+       (flatten)
+       (str/join "\n")))
+
+(defn recite [start-verse end-verse]
+  (->> (range (dec start-verse) end-verse)
+       (map verse)
+       (interpose "")
+       (str/join "\n")))
diff --git a/exercises/practice/food-chain/.meta/generator.tpl b/exercises/practice/food-chain/.meta/generator.tpl
new file mode 100644
index 000000000..610414ac6
--- /dev/null
+++ b/exercises/practice/food-chain/.meta/generator.tpl
@@ -0,0 +1,12 @@
+(ns food-chain-test
+  (:require [clojure.test :refer [deftest testing is]]
+            [clojure.string :as str]
+            food-chain))
+
+{{#test_cases.recite}}
+(deftest recite_test_{{idx}}
+  (testing {{description}}
+    (is (= (str/join "\n" [{{#expected~}}{{.}}{{#if @last}}]){{else}}
+                           {{/if}}{{/expected}}
+           (food-chain/recite {{input.startVerse}} {{input.endVerse}})))))
+{{/test_cases.recite}}
diff --git a/exercises/practice/food-chain/.meta/tests.toml b/exercises/practice/food-chain/.meta/tests.toml
new file mode 100644
index 000000000..30c5b980b
--- /dev/null
+++ b/exercises/practice/food-chain/.meta/tests.toml
@@ -0,0 +1,40 @@
+# 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.
+
+[751dce68-9412-496e-b6e8-855998c56166]
+description = "fly"
+
+[6c56f861-0c5e-4907-9a9d-b2efae389379]
+description = "spider"
+
+[3edf5f33-bef1-4e39-ae67-ca5eb79203fa]
+description = "bird"
+
+[e866a758-e1ff-400e-9f35-f27f28cc288f]
+description = "cat"
+
+[3f02c30e-496b-4b2a-8491-bc7e2953cafb]
+description = "dog"
+
+[4b3fd221-01ea-46e0-825b-5734634fbc59]
+description = "goat"
+
+[1b707da9-7001-4fac-941f-22ad9c7a65d4]
+description = "cow"
+
+[3cb10d46-ae4e-4d2c-9296-83c9ffc04cdc]
+description = "horse"
+
+[22b863d5-17e4-4d1e-93e4-617329a5c050]
+description = "multiple verses"
+
+[e626b32b-745c-4101-bcbd-3b13456893db]
+description = "full song"
diff --git a/exercises/practice/food-chain/deps.edn b/exercises/practice/food-chain/deps.edn
new file mode 100644
index 000000000..561c3e2da
--- /dev/null
+++ b/exercises/practice/food-chain/deps.edn
@@ -0,0 +1,6 @@
+{:aliases {:test {:extra-paths ["test"]
+                 :extra-deps {io.github.cognitect-labs/test-runner 
+                              {:git/url "https://github.com/cognitect-labs/test-runner.git"
+                               :sha "705ad25bbf0228b1c38d0244a36001c2987d7337"}}
+                 :main-opts ["-m" "cognitect.test-runner"]
+                 :exec-fn cognitect.test-runner.api/test}}}
\ No newline at end of file
diff --git a/exercises/practice/food-chain/project.clj b/exercises/practice/food-chain/project.clj
new file mode 100644
index 000000000..ab6f9b02f
--- /dev/null
+++ b/exercises/practice/food-chain/project.clj
@@ -0,0 +1,4 @@
+(defproject food-chain "0.1.0-SNAPSHOT"
+  :description "food-chain exercise."
+  :url "https://github.com/exercism/clojure/tree/main/exercises/practice/food-chain"
+  :dependencies [[org.clojure/clojure "1.11.1"]])
diff --git a/exercises/practice/food-chain/src/food_chain.clj b/exercises/practice/food-chain/src/food_chain.clj
new file mode 100644
index 000000000..6cee30796
--- /dev/null
+++ b/exercises/practice/food-chain/src/food_chain.clj
@@ -0,0 +1,7 @@
+(ns food-chain)
+
+(defn recite
+  "Returns the lyrics of the song: 'I Know an Old Lady Who Swallowed a Fly.'"
+  [start-verse end-verse]
+  ;; function body
+  )
diff --git a/exercises/practice/food-chain/test/food_chain_test.clj b/exercises/practice/food-chain/test/food_chain_test.clj
new file mode 100644
index 000000000..33224150a
--- /dev/null
+++ b/exercises/practice/food-chain/test/food_chain_test.clj
@@ -0,0 +1,150 @@
+(ns food-chain-test
+  (:require [clojure.test :refer [deftest testing is]]
+            [clojure.string :as str]
+            food-chain))
+
+(deftest recite_test_1
+  (testing "fly"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."])
+           (food-chain/recite 1 1)))))
+
+(deftest recite_test_2
+  (testing "spider"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a spider."
+                           "It wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."])
+           (food-chain/recite 2 2)))))
+
+(deftest recite_test_3
+  (testing "bird"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a bird."
+                           "How absurd to swallow a bird!"
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."])
+           (food-chain/recite 3 3)))))
+
+(deftest recite_test_4
+  (testing "cat"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a cat."
+                           "Imagine that, to swallow a cat!"
+                           "She swallowed the cat to catch the bird."
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."])
+           (food-chain/recite 4 4)))))
+
+(deftest recite_test_5
+  (testing "dog"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a dog."
+                           "What a hog, to swallow a dog!"
+                           "She swallowed the dog to catch the cat."
+                           "She swallowed the cat to catch the bird."
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."])
+           (food-chain/recite 5 5)))))
+
+(deftest recite_test_6
+  (testing "goat"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a goat."
+                           "Just opened her throat and swallowed a goat!"
+                           "She swallowed the goat to catch the dog."
+                           "She swallowed the dog to catch the cat."
+                           "She swallowed the cat to catch the bird."
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."])
+           (food-chain/recite 6 6)))))
+
+(deftest recite_test_7
+  (testing "cow"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a cow."
+                           "I don't know how she swallowed a cow!"
+                           "She swallowed the cow to catch the goat."
+                           "She swallowed the goat to catch the dog."
+                           "She swallowed the dog to catch the cat."
+                           "She swallowed the cat to catch the bird."
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."])
+           (food-chain/recite 7 7)))))
+
+(deftest recite_test_8
+  (testing "horse"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a horse."
+                           "She's dead, of course!"])
+           (food-chain/recite 8 8)))))
+
+(deftest recite_test_9
+  (testing "multiple verses"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."
+                           ""
+                           "I know an old lady who swallowed a spider."
+                           "It wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."
+                           ""
+                           "I know an old lady who swallowed a bird."
+                           "How absurd to swallow a bird!"
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."])
+           (food-chain/recite 1 3)))))
+
+(deftest recite_test_10
+  (testing "full song"
+    (is (= (str/join "\n" ["I know an old lady who swallowed a fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."
+                           ""
+                           "I know an old lady who swallowed a spider."
+                           "It wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."
+                           ""
+                           "I know an old lady who swallowed a bird."
+                           "How absurd to swallow a bird!"
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."
+                           ""
+                           "I know an old lady who swallowed a cat."
+                           "Imagine that, to swallow a cat!"
+                           "She swallowed the cat to catch the bird."
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."
+                           ""
+                           "I know an old lady who swallowed a dog."
+                           "What a hog, to swallow a dog!"
+                           "She swallowed the dog to catch the cat."
+                           "She swallowed the cat to catch the bird."
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."
+                           ""
+                           "I know an old lady who swallowed a goat."
+                           "Just opened her throat and swallowed a goat!"
+                           "She swallowed the goat to catch the dog."
+                           "She swallowed the dog to catch the cat."
+                           "She swallowed the cat to catch the bird."
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."
+                           ""
+                           "I know an old lady who swallowed a cow."
+                           "I don't know how she swallowed a cow!"
+                           "She swallowed the cow to catch the goat."
+                           "She swallowed the goat to catch the dog."
+                           "She swallowed the dog to catch the cat."
+                           "She swallowed the cat to catch the bird."
+                           "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her."
+                           "She swallowed the spider to catch the fly."
+                           "I don't know why she swallowed the fly. Perhaps she'll die."
+                           ""
+                           "I know an old lady who swallowed a horse."
+                           "She's dead, of course!"])
+           (food-chain/recite 1 8)))))