diff --git a/bin/add-exercise b/bin/add-exercise index 025184b5..5a7bf800 100755 --- a/bin/add-exercise +++ b/bin/add-exercise @@ -19,7 +19,6 @@ required_tool curl [[ -f ./bin/fetch-configlet ]] || die "run this script from the repo's root directory." - slug="${1}" name=$(echo "${slug}" | sed 's/\b\w/\u&/g; s/-/ /g') # assumes GNU sed @@ -58,18 +57,22 @@ jq --arg slug "${slug}" \ --docs \ --exercise "${slug}" -cp lib/test-words.8th "exercises/practice/${slug}" +mkdir -p "exercises/practice/${slug}/libs/exercism" +cp ./libs/exercism/test "exercises/practice/${slug}/libs/exercism/test" cat << END_TEST > "exercises/practice/${slug}/test.8th" -"test-words.8th" dup . cr f:include -"${slug}.8th" dup . cr f:include -"${slug}-tests.8th" dup . cr f:include -bye +"${slug}.8th" f:include +needs exercism/test + +N tests + +$(curl --silent "https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/${slug}/canonical-data.json") + +end-of-tests END_TEST touch "exercises/practice/${slug}/${slug}.8th" touch "exercises/practice/${slug}/.meta/example.8th" -curl --silent "https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/${slug}/canonical-data.json" > "exercises/practice/${slug}/${slug}-tests.8th" echo read -rp 'Your github username: ' author @@ -80,19 +83,19 @@ jq --arg slug "${slug}" \ .authors = [$author] | .files = { solution: [$slug + ".8th"], - test: [$slug + "-tests.8th"], + test: ["test.8th"], example: [".meta/example.8th"] } ' "${conf}" > "${conf}.tmp" \ && mv "${conf}.tmp" "${conf}" echo -ls -laR "exercises/practice/${slug}" +find "exercises/practice/${slug}" -type f -ls cat << NEXT_STEPS Your next steps are: -- Create the test suite in 'exercises/practice/${slug}/${slug}-tests.8th' +- Create the test suite in 'exercises/practice/${slug}/test.8th' based on the canonical data at 'https://github.com/exercism/problem-specifications/blob/main/exercises/${slug}/canonical-data.json' - Any test cases you don't implement, mark them in 'exercises/practice/${slug}/.meta/tests.toml' with "include = false" - Create the example solution in 'exercises/practice/${slug}/.meta/example.8th' diff --git a/config.json b/config.json index 901bfc4f..43574bc6 100644 --- a/config.json +++ b/config.json @@ -242,6 +242,14 @@ "practices": [], "prerequisites": [], "difficulty": 8 + }, + { + "slug": "etl", + "name": "ETL", + "uuid": "07d2a60c-1ac4-407d-b273-6505b175e017", + "practices": [], + "prerequisites": [], + "difficulty": 5 } ] }, diff --git a/exercises/practice/etl/.docs/instructions.md b/exercises/practice/etl/.docs/instructions.md new file mode 100644 index 00000000..802863b5 --- /dev/null +++ b/exercises/practice/etl/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Your task is to change the data format of letters and their point values in the game. + +Currently, letters are stored in groups based on their score, in a one-to-many mapping. + +- 1 point: "A", "E", "I", "O", "U", "L", "N", "R", "S", "T", +- 2 points: "D", "G", +- 3 points: "B", "C", "M", "P", +- 4 points: "F", "H", "V", "W", "Y", +- 5 points: "K", +- 8 points: "J", "X", +- 10 points: "Q", "Z", + +This needs to be changed to store each individual letter with its score in a one-to-one mapping. + +- "a" is worth 1 point. +- "b" is worth 3 points. +- "c" is worth 3 points. +- "d" is worth 2 points. +- etc. + +As part of this change, the team has also decided to change the letters to be lower-case rather than upper-case. + +~~~~exercism/note +If you want to look at how the data was previously structured and how it needs to change, take a look at the examples in the test suite. +~~~~ diff --git a/exercises/practice/etl/.docs/introduction.md b/exercises/practice/etl/.docs/introduction.md new file mode 100644 index 00000000..5be65147 --- /dev/null +++ b/exercises/practice/etl/.docs/introduction.md @@ -0,0 +1,16 @@ +# Introduction + +You work for a company that makes an online multiplayer game called Lexiconia. + +To play the game, each player is given 13 letters, which they must rearrange to create words. +Different letters have different point values, since it's easier to create words with some letters than others. + +The game was originally launched in English, but it is very popular, and now the company wants to expand to other languages as well. + +Different languages need to support different point values for letters. +The point values are determined by how often letters are used, compared to other letters in that language. + +For example, the letter 'C' is quite common in English, and is only worth 3 points. +But in Norwegian it's a very rare letter, and is worth 10 points. + +To make it easier to add new languages, your team needs to change the way letters and their point values are stored in the game. diff --git a/exercises/practice/etl/.meta/config.json b/exercises/practice/etl/.meta/config.json new file mode 100644 index 00000000..298a4b96 --- /dev/null +++ b/exercises/practice/etl/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "etl.8th" + ], + "test": [ + "test.8th" + ], + "example": [ + ".meta/example.8th" + ] + }, + "blurb": "Change the data format for scoring a game to more easily add other languages.", + "source": "Based on an 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/etl/.meta/example.8th b/exercises/practice/etl/.meta/example.8th new file mode 100644 index 00000000..4b0db43b --- /dev/null +++ b/exercises/practice/etl/.meta/example.8th @@ -0,0 +1,12 @@ +: transform \ m -- m + m:new swap \ transformed input + ( swap >n swap \ transformed score [letters] + ( s:lc \ transformed score letter + _swap third \ score transformed letter score + m:! \ score transformed + swap \ transformed score + ) a:each! \ transformed score [letters] + 2drop + ) m:each \ transformed input + drop +; diff --git a/exercises/practice/etl/.meta/tests.toml b/exercises/practice/etl/.meta/tests.toml new file mode 100644 index 00000000..e9371078 --- /dev/null +++ b/exercises/practice/etl/.meta/tests.toml @@ -0,0 +1,22 @@ +# 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. + +[78a7a9f9-4490-4a47-8ee9-5a38bb47d28f] +description = "single letter" + +[60dbd000-451d-44c7-bdbb-97c73ac1f497] +description = "single score with multiple letters" + +[f5c5de0c-301f-4fdd-a0e5-df97d4214f54] +description = "multiple scores with multiple letters" + +[5db8ea89-ecb4-4dcd-902f-2b418cc87b9d] +description = "multiple scores with differing numbers of letters" diff --git a/exercises/practice/etl/etl.8th b/exercises/practice/etl/etl.8th new file mode 100644 index 00000000..0b9626f9 --- /dev/null +++ b/exercises/practice/etl/etl.8th @@ -0,0 +1,3 @@ +: transform \ m -- m + +; diff --git a/exercises/practice/etl/libs/exercism/test b/exercises/practice/etl/libs/exercism/test new file mode 100644 index 00000000..c2e2f1a7 --- /dev/null +++ b/exercises/practice/etl/libs/exercism/test @@ -0,0 +1,137 @@ +needs console/loaded + +\ ----------------------------------------------------------------- + +ns: test + +-1 var, test-count +var tests-passed +var tests-failed +var tests-skipped +true var, run-test + +\ Some utility words +: test-passed \ s x x -- \\ test name, expected value, actual value + 2drop + 1 tests-passed n:+! + con:green con:onBlack . space " ... OK" . con:white con:onBlack cr +; + +: test-skipped \ s -- + 1 tests-skipped n:+! + con:cyan con:onBlack . space " ... SKIPPED" . con:white con:onBlack cr +; + +: test-failed \ s x x -- \\ test name, expected value, actual value + 1 tests-failed n:+! + rot + con:red con:onBlack . space " ... FAIL" . con:white con:onBlack cr + " Actual: «" . . "»" . cr + " Expected: «" . . "»" . cr cr +; + +: isword? \ x -- x f + dup >kind ns:w n:= +; + +: run-test? \ -- T + run-test @ if true else "RUN_ALL_TESTS" getenv n:>bool then +; + +\ Num passed + num skipped + num failed should == num tests +: all-tests-run? \ -- T + tests-passed @ tests-skipped @ tests-failed @ n:+ n:+ + test-count @ n:= +; + +\ returns true if x is a date, false otherwise +: date? \ x -- x T + dup >kind ns:d n:= +; + +\ adapted from 8th forum -- https://8th-dev.com/forum/index.php/topic,2745.0.html +: eq? \ x x -- T + \ are the items the same kind? + 2dup >kind swap >kind n:= + !if 2drop false ;then + + \ same kind: try different comparators + number? if n:= ;then + string? if s:= ;then + array? if ' eq? a:= 2nip ;then + map? if ' eq? m:= 2nip ;then + date? if d:= ;then + + \ otherwise fall back to 'lazy evaluation' + l: = +; + +\ ----------------------------------------------------------------- + +\ status report at end of run +( all-tests-run? + !if con:red con:onBlack "... FAIL - not all tests completed" . con:white con:onBlack cr then +) onexit + +\ Print a summary of the tests run +( con:white con:onBlack + test-count @ . space "tests planned - " . + tests-passed @ . space "passed - " . + tests-skipped @ . space "skipped - " . + tests-failed @ . space "failed" . cr +) onexit + +\ ----------------------------------------------------------------- +\ The public-facing words +\ ----------------------------------------------------------------- + +: equal? \ s x w -- | s w x -- + run-test? !if 2drop test-skipped ;; then + isword? !if swap then + w:exec + 2dup \ so test-failed can show actual and expected + eq? if test-passed else test-failed then +; + +: true? \ s w -- + run-test? !if drop test-skipped ;; then + w:exec + true swap dup \ so test-failed can show actual and expected + if test-passed else test-failed then +; + +: false? \ s w -- + run-test? !if drop test-skipped ;; then + w:exec + false swap dup \ so test-failed can show actual and expected + !if test-passed else test-failed then +; + +: null? \ s w -- + run-test? !if drop test-skipped ;; then + w:exec + null swap dup \ so test-failed can show actual and expected + G:null? nip if test-passed else test-failed then +; + +: SKIP-REST-OF-TESTS false run-test ! ; + +: tests \ n -- + test-count ! +; + +\ Set the exit status: +\ 0 = all OK +\ 1 = not all tests were run (some error occurred) +\ 2 = some tests failed +: end-of-tests \ -- + all-tests-run? + if + tests-failed @ 0 n:= if 0 else 2 then + else + 1 + then + die +; + +with: test diff --git a/exercises/practice/etl/test.8th b/exercises/practice/etl/test.8th new file mode 100644 index 00000000..49562551 --- /dev/null +++ b/exercises/practice/etl/test.8th @@ -0,0 +1,62 @@ +"etl.8th" f:include +needs exercism/test + +4 tests + +"single letter" + {"a": 1} + ( {"1": ["A"]} transform ) + equal? + +SKIP-REST-OF-TESTS + +"single score with multiple letters" + {"a": 1, "e": 1, "i": 1, "o": 1, "u": 1} + ( {"1": ["A", "E", "I", "O", "U"]} transform ) + equal? + +"multiple scores with multiple letters" + {"a": 1, "d": 2, "e": 1, "g": 2} + ( {"1": ["A", "E"], + "2": ["D", "G"]} + transform ) + equal? + +"multiple scores with differing numbers of letters" + {"a": 1, + "b": 3, + "c": 3, + "d": 2, + "e": 1, + "f": 4, + "g": 2, + "h": 4, + "i": 1, + "j": 8, + "k": 5, + "l": 1, + "m": 3, + "n": 1, + "o": 1, + "p": 3, + "q": 10, + "r": 1, + "s": 1, + "t": 1, + "u": 1, + "v": 4, + "w": 4, + "x": 8, + "y": 4, + "z": 10} + ( {"1": ["A", "E", "I", "O", "U", "L", "N", "R", "S", "T"], + "2": ["D", "G"], + "3": ["B", "C", "M", "P"], + "4": ["F", "H", "V", "W", "Y"], + "5": ["K"], + "8": ["J", "X"], + "10": ["Q", "Z"]} + transform ) + equal? + +end-of-tests