From 95d1eacf95def39ccfce95eaf2d1f288e72e9a8f Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Fri, 19 Jan 2024 20:52:51 +1100 Subject: [PATCH] Add kindergarten-garden exercise (#270) --- config.json | 9 + .../kindergarten-garden/.docs/instructions.md | 58 ++++++ .../kindergarten-garden/.meta/config.json | 19 ++ .../kindergarten-garden/.meta/example.sml | 22 +++ .../kindergarten-garden/.meta/tests.toml | 61 +++++++ .../kindergarten-garden.sml | 2 + .../practice/kindergarten-garden/test.sml | 171 ++++++++++++++++++ .../practice/kindergarten-garden/testlib.sml | 160 ++++++++++++++++ 8 files changed, 502 insertions(+) create mode 100644 exercises/practice/kindergarten-garden/.docs/instructions.md create mode 100644 exercises/practice/kindergarten-garden/.meta/config.json create mode 100644 exercises/practice/kindergarten-garden/.meta/example.sml create mode 100644 exercises/practice/kindergarten-garden/.meta/tests.toml create mode 100644 exercises/practice/kindergarten-garden/kindergarten-garden.sml create mode 100644 exercises/practice/kindergarten-garden/test.sml create mode 100644 exercises/practice/kindergarten-garden/testlib.sml diff --git a/config.json b/config.json index 0e7fb22..8393787 100644 --- a/config.json +++ b/config.json @@ -123,6 +123,15 @@ "difficulty": 3, "topics": [] }, + { + "slug": "kindergarten-garden", + "name": "Kindergarten Garden", + "uuid": "7d74175b-a2c6-4e77-a908-ebcbcf1e29b6", + "practices": [], + "prerequisites": [], + "difficulty": 3, + "topics": [] + }, { "slug": "matching-brackets", "name": "Matching Brackets", diff --git a/exercises/practice/kindergarten-garden/.docs/instructions.md b/exercises/practice/kindergarten-garden/.docs/instructions.md new file mode 100644 index 0000000..472ee26 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.docs/instructions.md @@ -0,0 +1,58 @@ +# Instructions + +Given a diagram, determine which plants each child in the kindergarten class is +responsible for. + +The kindergarten class is learning about growing plants. +The teacher thought it would be a good idea to give them actual seeds, plant them in actual dirt, and grow actual plants. + +They've chosen to grow grass, clover, radishes, and violets. + +To this end, the children have put little cups along the window sills, and +planted one type of plant in each cup, choosing randomly from the available +types of seeds. + +```text +[window][window][window] +........................ # each dot represents a cup +........................ +``` + +There are 12 children in the class: + +- Alice, Bob, Charlie, David, +- Eve, Fred, Ginny, Harriet, +- Ileana, Joseph, Kincaid, and Larry. + +Each child gets 4 cups, two on each row. +Their teacher assigns cups to the children alphabetically by their names. + +The following diagram represents Alice's plants: + +```text +[window][window][window] +VR...................... +RG...................... +``` + +In the first row, nearest the windows, she has a violet and a radish. +In the second row she has a radish and some grass. + +Your program will be given the plants from left-to-right starting with the row nearest the windows. +From this, it should be able to determine which plants belong to each student. + +For example, if it's told that the garden looks like so: + +```text +[window][window][window] +VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV +``` + +Then if asked for Alice's plants, it should provide: + +- Violets, radishes, violets, radishes + +While asking for Bob's plants would yield: + +- Clover, grass, clover, clover diff --git a/exercises/practice/kindergarten-garden/.meta/config.json b/exercises/practice/kindergarten-garden/.meta/config.json new file mode 100644 index 0000000..710f7cd --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "kindergarten-garden.sml" + ], + "test": [ + "test.sml" + ], + "example": [ + ".meta/example.sml" + ] + }, + "blurb": "Given a diagram, determine which plants each child in the kindergarten class is responsible for.", + "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://turing.edu" +} diff --git a/exercises/practice/kindergarten-garden/.meta/example.sml b/exercises/practice/kindergarten-garden/.meta/example.sml new file mode 100644 index 0000000..4da180d --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/example.sml @@ -0,0 +1,22 @@ +fun plants (diagram: string) (student: string): string list = + let + val index = ord (String.sub (student, 0)) - ord #"A" + + val first = 2 * index + val second = 2 * index + 1 + val third = (1 + size diagram) div 2 + 2 * index + val fourth = (1 + size diagram) div 2 + 2 * index + 1 + + fun plant (place: int): string = + let + val c = String.sub (diagram, place) + in + if c = #"G" then "grass" + else if c = #"C" then "clover" + else if c = #"R" then "radishes" + else if c = #"V" then "violets" + else raise Fail "unknown plant" + end + in + [plant first, plant second, plant third, plant fourth] + end diff --git a/exercises/practice/kindergarten-garden/.meta/tests.toml b/exercises/practice/kindergarten-garden/.meta/tests.toml new file mode 100644 index 0000000..0cdd9ad --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/tests.toml @@ -0,0 +1,61 @@ +# 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. + +[1fc316ed-17ab-4fba-88ef-3ae78296b692] +description = "partial garden -> garden with single student" + +[acd19dc1-2200-4317-bc2a-08f021276b40] +description = "partial garden -> different garden with single student" + +[c376fcc8-349c-446c-94b0-903947315757] +description = "partial garden -> garden with two students" + +[2d620f45-9617-4924-9d27-751c80d17db9] +description = "partial garden -> multiple students for the same garden with three students -> second student's garden" + +[57712331-4896-4364-89f8-576421d69c44] +description = "partial garden -> multiple students for the same garden with three students -> third student's garden" + +[149b4290-58e1-40f2-8ae4-8b87c46e765b] +description = "full garden -> for Alice, first student's garden" + +[ba25dbbc-10bd-4a37-b18e-f89ecd098a5e] +description = "full garden -> for Bob, second student's garden" + +[566b621b-f18e-4c5f-873e-be30544b838c] +description = "full garden -> for Charlie" + +[3ad3df57-dd98-46fc-9269-1877abf612aa] +description = "full garden -> for David" + +[0f0a55d1-9710-46ed-a0eb-399ba8c72db2] +description = "full garden -> for Eve" + +[a7e80c90-b140-4ea1-aee3-f4625365c9a4] +description = "full garden -> for Fred" + +[9d94b273-2933-471b-86e8-dba68694c615] +description = "full garden -> for Ginny" + +[f55bc6c2-ade8-4844-87c4-87196f1b7258] +description = "full garden -> for Harriet" + +[759070a3-1bb1-4dd4-be2c-7cce1d7679ae] +description = "full garden -> for Ileana" + +[78578123-2755-4d4a-9c7d-e985b8dda1c6] +description = "full garden -> for Joseph" + +[6bb66df7-f433-41ab-aec2-3ead6e99f65b] +description = "full garden -> for Kincaid, second to last student's garden" + +[d7edec11-6488-418a-94e6-ed509e0fa7eb] +description = "full garden -> for Larry, last student's garden" diff --git a/exercises/practice/kindergarten-garden/kindergarten-garden.sml b/exercises/practice/kindergarten-garden/kindergarten-garden.sml new file mode 100644 index 0000000..c79d7bd --- /dev/null +++ b/exercises/practice/kindergarten-garden/kindergarten-garden.sml @@ -0,0 +1,2 @@ +fun plants (diagram: string) (student: string): string list = + raise Fail "'plants' is not implemented" diff --git a/exercises/practice/kindergarten-garden/test.sml b/exercises/practice/kindergarten-garden/test.sml new file mode 100644 index 0000000..416980f --- /dev/null +++ b/exercises/practice/kindergarten-garden/test.sml @@ -0,0 +1,171 @@ +(* version 1.0.0 *) + +use "testlib.sml"; +use "kindergarten-garden.sml"; + +infixr |> +fun x |> f = f x + +val testsuite = + describe "kindergarten-garden" [ + describe "partial garden" [ + test "garden with single student" + (fn _ => let + val diagram = "RC\nGG" + val student = "Alice" + val expected = ["radishes", "clover", "grass", "grass"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "different garden with single student" + (fn _ => let + val diagram = "VC\nRC" + val student = "Alice" + val expected = ["violets", "clover", "radishes", "clover"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "garden with two students" + (fn _ => let + val diagram = "VVCG\nVVRC" + val student = "Bob" + val expected = ["clover", "grass", "radishes", "clover"] + in + plants diagram student |> Expect.equalTo expected + end), + + describe "multiple students for the same garden with three students" [ + test "second student's garden" + (fn _ => let + val diagram = "VVCCGG\nVVCCGG" + val student = "Bob" + val expected = ["clover", "clover", "clover", "clover"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "third student's garden" + (fn _ => let + val diagram = "VVCCGG\nVVCCGG" + val student = "Charlie" + val expected = ["grass", "grass", "grass", "grass"] + in + plants diagram student |> Expect.equalTo expected + end) + ] + ], + + describe "full garden" [ + test "for Alice, first student's garden" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Alice" + val expected = ["violets", "radishes", "violets", "radishes"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Bob, second student's garden" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Bob" + val expected = ["clover", "grass", "clover", "clover"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Charlie" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Charlie" + val expected = ["violets", "violets", "clover", "grass"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for David" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "David" + val expected = ["radishes", "violets", "clover", "radishes"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Eve" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Eve" + val expected = ["clover", "grass", "radishes", "grass"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Fred" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Fred" + val expected = ["grass", "clover", "violets", "clover"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Ginny" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Ginny" + val expected = ["clover", "grass", "grass", "clover"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Harriet" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Harriet" + val expected = ["violets", "radishes", "radishes", "violets"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Ileana" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Ileana" + val expected = ["grass", "clover", "violets", "clover"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Joseph" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Joseph" + val expected = ["violets", "clover", "violets", "grass"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Kincaid, second to last student's garden" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Kincaid" + val expected = ["grass", "clover", "clover", "grass"] + in + plants diagram student |> Expect.equalTo expected + end), + + test "for Larry, last student's garden" + (fn _ => let + val diagram = "VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV" + val student = "Larry" + val expected = ["grass", "violets", "clover", "violets"] + in + plants diagram student |> Expect.equalTo expected + end) + ] + ] + +val _ = Test.run testsuite diff --git a/exercises/practice/kindergarten-garden/testlib.sml b/exercises/practice/kindergarten-garden/testlib.sml new file mode 100644 index 0000000..0c8370c --- /dev/null +++ b/exercises/practice/kindergarten-garden/testlib.sml @@ -0,0 +1,160 @@ +structure Expect = +struct + datatype expectation = Pass | Fail of string * string + + local + fun failEq b a = + Fail ("Expected: " ^ b, "Got: " ^ a) + + fun failExn b a = + Fail ("Expected: " ^ b, "Raised: " ^ a) + + fun exnName (e: exn): string = General.exnName e + in + fun truthy a = + if a + then Pass + else failEq "true" "false" + + fun falsy a = + if a + then failEq "false" "true" + else Pass + + fun equalTo b a = + if a = b + then Pass + else failEq (PolyML.makestring b) (PolyML.makestring a) + + fun nearTo delta b a = + if Real.abs (a - b) <= delta * Real.abs a orelse + Real.abs (a - b) <= delta * Real.abs b + then Pass + else failEq (Real.toString b ^ " +/- " ^ Real.toString delta) (Real.toString a) + + fun anyError f = + ( + f (); + failExn "an exception" "Nothing" + ) handle _ => Pass + + fun error e f = + ( + f (); + failExn (exnName e) "Nothing" + ) handle e' => if exnMessage e' = exnMessage e + then Pass + else failExn (exnMessage e) (exnMessage e') + end +end + +structure TermColor = +struct + datatype color = Red | Green | Yellow | Normal + + fun f Red = "\027[31m" + | f Green = "\027[32m" + | f Yellow = "\027[33m" + | f Normal = "\027[0m" + + fun colorize color s = (f color) ^ s ^ (f Normal) + + val redit = colorize Red + + val greenit = colorize Green + + val yellowit = colorize Yellow +end + +structure Test = +struct + datatype testnode = TestGroup of string * testnode list + | Test of string * (unit -> Expect.expectation) + + local + datatype evaluation = Success of string + | Failure of string * string * string + | Error of string * string + + fun indent n s = (implode (List.tabulate (n, fn _ => #" "))) ^ s + + fun fmt indentlvl ev = + let + val check = TermColor.greenit "\226\156\148 " (* ✔ *) + val cross = TermColor.redit "\226\156\150 " (* ✖ *) + val indentlvl = indentlvl * 2 + in + case ev of + Success descr => indent indentlvl (check ^ descr) + | Failure (descr, exp, got) => + String.concatWith "\n" [indent indentlvl (cross ^ descr), + indent (indentlvl + 2) exp, + indent (indentlvl + 2) got] + | Error (descr, reason) => + String.concatWith "\n" [indent indentlvl (cross ^ descr), + indent (indentlvl + 2) (TermColor.redit reason)] + end + + fun eval (TestGroup _) = raise Fail "Only a 'Test' can be evaluated" + | eval (Test (descr, thunk)) = + ( + case thunk () of + Expect.Pass => ((1, 0, 0), Success descr) + | Expect.Fail (s, s') => ((0, 1, 0), Failure (descr, s, s')) + ) + handle e => ((0, 0, 1), Error (descr, "Unexpected error: " ^ exnMessage e)) + + fun flatten depth testnode = + let + fun sum (x, y, z) (a, b, c) = (x + a, y + b, z + c) + + fun aux (t, (counter, acc)) = + let + val (counter', texts) = flatten (depth + 1) t + in + (sum counter' counter, texts :: acc) + end + in + case testnode of + TestGroup (descr, ts) => + let + val (counter, texts) = foldr aux ((0, 0, 0), []) ts + in + (counter, (indent (depth * 2) descr) :: List.concat texts) + end + | Test _ => + let + val (counter, evaluation) = eval testnode + in + (counter, [fmt depth evaluation]) + end + end + + fun println s = print (s ^ "\n") + in + fun run suite = + let + val ((succeeded, failed, errored), texts) = flatten 0 suite + + val summary = String.concatWith ", " [ + TermColor.greenit ((Int.toString succeeded) ^ " passed"), + TermColor.redit ((Int.toString failed) ^ " failed"), + TermColor.redit ((Int.toString errored) ^ " errored"), + (Int.toString (succeeded + failed + errored)) ^ " total" + ] + + val status = if failed = 0 andalso errored = 0 + then OS.Process.success + else OS.Process.failure + + in + List.app println texts; + println ""; + println ("Tests: " ^ summary); + OS.Process.exit status + end + end +end + +fun describe description tests = Test.TestGroup (description, tests) +fun test description thunk = Test.Test (description, thunk)