From bae22dbe682f08aae97232186553b93398aa91b6 Mon Sep 17 00:00:00 2001 From: Eric Willigers Date: Mon, 27 Apr 2026 09:13:19 +1000 Subject: [PATCH] eleven practice exercises --- config.json | 187 +++++++++++++++++- .../character-study/.docs/instructions.md | 12 +- .../character-study/.docs/introduction.md | 19 ++ .../concept/character-study/.meta/config.json | 2 +- .../binary-search/.docs/instructions.md | 29 +++ .../binary-search/.docs/introduction.md | 13 ++ .../practice/binary-search/.meta/config.json | 19 ++ .../binary-search/.meta/example.factor | 18 ++ .../practice/binary-search/.meta/generator.jl | 13 ++ .../practice/binary-search/.meta/tests.toml | 43 ++++ .../binary-search/binary-search-tests.factor | 47 +++++ .../binary-search/binary-search.factor | 7 + .../flatten-array/.docs/instructions.md | 16 ++ .../flatten-array/.docs/introduction.md | 7 + .../practice/flatten-array/.meta/config.json | 19 ++ .../flatten-array/.meta/example.factor | 14 ++ .../practice/flatten-array/.meta/generator.jl | 22 +++ .../practice/flatten-array/.meta/tests.toml | 63 ++++++ .../flatten-array/flatten-array-tests.factor | 52 +++++ .../flatten-array/flatten-array.factor | 5 + .../high-scores/.docs/instructions.md | 6 + .../practice/high-scores/.meta/config.json | 18 ++ .../practice/high-scores/.meta/example.factor | 19 ++ .../practice/high-scores/.meta/generator.jl | 31 +++ .../practice/high-scores/.meta/tests.toml | 46 +++++ .../high-scores/high-scores-tests.factor | 56 ++++++ .../high-scores/high-scores.factor | 19 ++ .../practice/knapsack/.docs/instructions.md | 25 +++ .../practice/knapsack/.docs/introduction.md | 10 + exercises/practice/knapsack/.meta/config.json | 19 ++ .../practice/knapsack/.meta/example.factor | 16 ++ .../practice/knapsack/.meta/generator.jl | 12 ++ exercises/practice/knapsack/.meta/tests.toml | 36 ++++ .../knapsack/knapsack/knapsack-tests.factor | 36 ++++ .../knapsack/knapsack/knapsack.factor | 7 + .../.docs/instructions.md | 26 +++ .../.docs/introduction.md | 5 + .../largest-series-product/.meta/config.json | 19 ++ .../.meta/example.factor | 13 ++ .../largest-series-product/.meta/generator.jl | 13 ++ .../largest-series-product/.meta/tests.toml | 70 +++++++ .../largest-series-product-tests.factor | 64 ++++++ .../largest-series-product.factor | 7 + .../practice/micro-blog/.docs/instructions.md | 37 ++++ .../practice/micro-blog/.meta/config.json | 17 ++ .../practice/micro-blog/.meta/example.factor | 5 + .../practice/micro-blog/.meta/generator.jl | 9 + .../practice/micro-blog/.meta/tests.toml | 46 +++++ .../micro-blog/micro-blog-tests.factor | 56 ++++++ .../micro-blog/micro-blog/micro-blog.factor | 5 + .../nucleotide-count/.docs/instructions.md | 23 +++ .../nucleotide-count/.meta/config.json | 19 ++ .../nucleotide-count/.meta/example.factor | 9 + .../nucleotide-count/.meta/generator.jl | 14 ++ .../nucleotide-count/.meta/tests.toml | 25 +++ .../nucleotide-count-tests.factor | 27 +++ .../nucleotide-count/nucleotide-count.factor | 7 + .../palindrome-products/.meta/example.factor | 44 ++--- .../palindrome-products/.meta/generator.jl | 2 +- .../palindrome-products-tests.factor | 26 +-- .../palindrome-products.factor | 4 +- .../pascals-triangle/.docs/instructions.md | 35 ++++ .../pascals-triangle/.docs/introduction.md | 22 +++ .../pascals-triangle/.meta/config.json | 19 ++ .../pascals-triangle/.meta/example.factor | 10 + .../pascals-triangle/.meta/generator.jl | 11 ++ .../pascals-triangle/.meta/tests.toml | 34 ++++ .../pascals-triangle-tests.factor | 40 ++++ .../pascals-triangle/pascals-triangle.factor | 5 + .../phone-number/.docs/instructions.md | 34 ++++ .../phone-number/.docs/introduction.md | 12 ++ .../practice/phone-number/.meta/config.json | 19 ++ .../phone-number/.meta/example.factor | 27 +++ .../practice/phone-number/.meta/generator.jl | 12 ++ .../practice/phone-number/.meta/tests.toml | 84 ++++++++ .../phone-number/phone-number-tests.factor | 67 +++++++ .../phone-number/phone-number.factor | 7 + .../robot-simulator/.meta/example.factor | 50 +++-- .../robot-simulator/.meta/generator.jl | 6 +- .../robot-simulator-tests.factor | 72 +++---- .../robot-simulator/robot-simulator.factor | 5 +- .../practice/satellite/.docs/instructions.md | 27 +++ .../practice/satellite/.meta/config.json | 17 ++ .../practice/satellite/.meta/example.factor | 25 +++ .../practice/satellite/.meta/generator.jl | 24 +++ exercises/practice/satellite/.meta/tests.toml | 37 ++++ .../satellite/satellite-tests.factor | 41 ++++ .../satellite/satellite/satellite.factor | 9 + .../scrabble-score/.docs/instructions.md | 25 +++ .../scrabble-score/.docs/introduction.md | 7 + .../practice/scrabble-score/.meta/config.json | 19 ++ .../scrabble-score/.meta/example.factor | 15 ++ .../scrabble-score/.meta/generator.jl | 9 + .../practice/scrabble-score/.meta/tests.toml | 43 ++++ .../scrabble-score-tests.factor | 52 +++++ .../scrabble-score/scrabble-score.factor | 5 + .../practice/space-age/.meta/example.factor | 28 ++- .../practice/space-age/.meta/generator.jl | 16 +- .../space-age/space-age-tests.factor | 16 +- .../space-age/space-age/space-age.factor | 24 +-- .../practice/triangle/.meta/example.factor | 25 ++- .../practice/triangle/.meta/generator.jl | 2 +- .../triangle/triangle/triangle-tests.factor | 42 ++-- .../triangle/triangle/triangle.factor | 11 +- .../practice/two-fer/.meta/example.factor | 2 +- 105 files changed, 2449 insertions(+), 198 deletions(-) create mode 100644 exercises/practice/binary-search/.docs/instructions.md create mode 100644 exercises/practice/binary-search/.docs/introduction.md create mode 100644 exercises/practice/binary-search/.meta/config.json create mode 100644 exercises/practice/binary-search/.meta/example.factor create mode 100644 exercises/practice/binary-search/.meta/generator.jl create mode 100644 exercises/practice/binary-search/.meta/tests.toml create mode 100644 exercises/practice/binary-search/binary-search/binary-search-tests.factor create mode 100644 exercises/practice/binary-search/binary-search/binary-search.factor create mode 100644 exercises/practice/flatten-array/.docs/instructions.md create mode 100644 exercises/practice/flatten-array/.docs/introduction.md create mode 100644 exercises/practice/flatten-array/.meta/config.json create mode 100644 exercises/practice/flatten-array/.meta/example.factor create mode 100644 exercises/practice/flatten-array/.meta/generator.jl create mode 100644 exercises/practice/flatten-array/.meta/tests.toml create mode 100644 exercises/practice/flatten-array/flatten-array/flatten-array-tests.factor create mode 100644 exercises/practice/flatten-array/flatten-array/flatten-array.factor create mode 100644 exercises/practice/high-scores/.docs/instructions.md create mode 100644 exercises/practice/high-scores/.meta/config.json create mode 100644 exercises/practice/high-scores/.meta/example.factor create mode 100644 exercises/practice/high-scores/.meta/generator.jl create mode 100644 exercises/practice/high-scores/.meta/tests.toml create mode 100644 exercises/practice/high-scores/high-scores/high-scores-tests.factor create mode 100644 exercises/practice/high-scores/high-scores/high-scores.factor create mode 100644 exercises/practice/knapsack/.docs/instructions.md create mode 100644 exercises/practice/knapsack/.docs/introduction.md create mode 100644 exercises/practice/knapsack/.meta/config.json create mode 100644 exercises/practice/knapsack/.meta/example.factor create mode 100644 exercises/practice/knapsack/.meta/generator.jl create mode 100644 exercises/practice/knapsack/.meta/tests.toml create mode 100644 exercises/practice/knapsack/knapsack/knapsack-tests.factor create mode 100644 exercises/practice/knapsack/knapsack/knapsack.factor create mode 100644 exercises/practice/largest-series-product/.docs/instructions.md create mode 100644 exercises/practice/largest-series-product/.docs/introduction.md create mode 100644 exercises/practice/largest-series-product/.meta/config.json create mode 100644 exercises/practice/largest-series-product/.meta/example.factor create mode 100644 exercises/practice/largest-series-product/.meta/generator.jl create mode 100644 exercises/practice/largest-series-product/.meta/tests.toml create mode 100644 exercises/practice/largest-series-product/largest-series-product/largest-series-product-tests.factor create mode 100644 exercises/practice/largest-series-product/largest-series-product/largest-series-product.factor create mode 100644 exercises/practice/micro-blog/.docs/instructions.md create mode 100644 exercises/practice/micro-blog/.meta/config.json create mode 100644 exercises/practice/micro-blog/.meta/example.factor create mode 100644 exercises/practice/micro-blog/.meta/generator.jl create mode 100644 exercises/practice/micro-blog/.meta/tests.toml create mode 100644 exercises/practice/micro-blog/micro-blog/micro-blog-tests.factor create mode 100644 exercises/practice/micro-blog/micro-blog/micro-blog.factor create mode 100644 exercises/practice/nucleotide-count/.docs/instructions.md create mode 100644 exercises/practice/nucleotide-count/.meta/config.json create mode 100644 exercises/practice/nucleotide-count/.meta/example.factor create mode 100644 exercises/practice/nucleotide-count/.meta/generator.jl create mode 100644 exercises/practice/nucleotide-count/.meta/tests.toml create mode 100644 exercises/practice/nucleotide-count/nucleotide-count/nucleotide-count-tests.factor create mode 100644 exercises/practice/nucleotide-count/nucleotide-count/nucleotide-count.factor create mode 100644 exercises/practice/pascals-triangle/.docs/instructions.md create mode 100644 exercises/practice/pascals-triangle/.docs/introduction.md create mode 100644 exercises/practice/pascals-triangle/.meta/config.json create mode 100644 exercises/practice/pascals-triangle/.meta/example.factor create mode 100644 exercises/practice/pascals-triangle/.meta/generator.jl create mode 100644 exercises/practice/pascals-triangle/.meta/tests.toml create mode 100644 exercises/practice/pascals-triangle/pascals-triangle/pascals-triangle-tests.factor create mode 100644 exercises/practice/pascals-triangle/pascals-triangle/pascals-triangle.factor create mode 100644 exercises/practice/phone-number/.docs/instructions.md create mode 100644 exercises/practice/phone-number/.docs/introduction.md create mode 100644 exercises/practice/phone-number/.meta/config.json create mode 100644 exercises/practice/phone-number/.meta/example.factor create mode 100644 exercises/practice/phone-number/.meta/generator.jl create mode 100644 exercises/practice/phone-number/.meta/tests.toml create mode 100644 exercises/practice/phone-number/phone-number/phone-number-tests.factor create mode 100644 exercises/practice/phone-number/phone-number/phone-number.factor create mode 100644 exercises/practice/satellite/.docs/instructions.md create mode 100644 exercises/practice/satellite/.meta/config.json create mode 100644 exercises/practice/satellite/.meta/example.factor create mode 100644 exercises/practice/satellite/.meta/generator.jl create mode 100644 exercises/practice/satellite/.meta/tests.toml create mode 100644 exercises/practice/satellite/satellite/satellite-tests.factor create mode 100644 exercises/practice/satellite/satellite/satellite.factor create mode 100644 exercises/practice/scrabble-score/.docs/instructions.md create mode 100644 exercises/practice/scrabble-score/.docs/introduction.md create mode 100644 exercises/practice/scrabble-score/.meta/config.json create mode 100644 exercises/practice/scrabble-score/.meta/example.factor create mode 100644 exercises/practice/scrabble-score/.meta/generator.jl create mode 100644 exercises/practice/scrabble-score/.meta/tests.toml create mode 100644 exercises/practice/scrabble-score/scrabble-score/scrabble-score-tests.factor create mode 100644 exercises/practice/scrabble-score/scrabble-score/scrabble-score.factor diff --git a/config.json b/config.json index d73dfed4..8d5d2d7c 100644 --- a/config.json +++ b/config.json @@ -386,6 +386,47 @@ ], "difficulty": 2 }, + { + "slug": "high-scores", + "name": "High Scores", + "uuid": "81e1ea19-1c58-4b34-b3c4-d05e029032aa", + "practices": [ + "tuples" + ], + "prerequisites": [ + "sequences", + "higher-order-sequences", + "tuples" + ], + "difficulty": 2 + }, + { + "slug": "micro-blog", + "name": "Micro Blog", + "uuid": "5cef0fa1-06b4-4212-9a07-3b9ecb1c4249", + "practices": [ + "unicode" + ], + "prerequisites": [ + "strings", + "unicode" + ], + "difficulty": 2 + }, + { + "slug": "nucleotide-count", + "name": "Nucleotide Count", + "uuid": "23b64c13-1b0e-4179-bbed-71369c70a7c1", + "practices": [ + "assocs" + ], + "prerequisites": [ + "strings", + "errors", + "assocs" + ], + "difficulty": 2 + }, { "slug": "pangram", "name": "Pangram", @@ -434,7 +475,10 @@ ], "prerequisites": [ "conditionals", - "booleans" + "booleans", + "combinators", + "tuples", + "sequences" ], "difficulty": 2 }, @@ -478,6 +522,21 @@ ], "difficulty": 3 }, + { + "slug": "binary-search", + "name": "Binary Search", + "uuid": "ea3a619c-0d03-4688-821a-22379470d7e1", + "practices": [ + "recursion" + ], + "prerequisites": [ + "sequences", + "conditionals", + "locals", + "recursion" + ], + "difficulty": 3 + }, { "slug": "bob", "name": "Bob", @@ -516,7 +575,8 @@ "prerequisites": [ "strings", "numbers", - "unicode" + "unicode", + "sequences" ], "difficulty": 3 }, @@ -545,6 +605,21 @@ ], "difficulty": 3 }, + { + "slug": "phone-number", + "name": "Phone Number", + "uuid": "7fa72ba9-5942-47aa-ae62-eba877d18905", + "practices": [ + "strings" + ], + "prerequisites": [ + "strings", + "conditionals", + "higher-order-sequences", + "errors" + ], + "difficulty": 3 + }, { "slug": "prime-factors", "name": "Prime Factors", @@ -567,7 +642,8 @@ ], "prerequisites": [ "conditionals", - "strings" + "strings", + "higher-order-sequences" ], "difficulty": 3 }, @@ -599,6 +675,21 @@ ], "difficulty": 3 }, + { + "slug": "scrabble-score", + "name": "Scrabble Score", + "uuid": "9fafa9b3-2ca6-44ac-af1c-f202572f94c8", + "practices": [ + "assocs" + ], + "prerequisites": [ + "strings", + "unicode", + "higher-order-sequences", + "assocs" + ], + "difficulty": 3 + }, { "slug": "sum-of-multiples", "name": "Sum of Multiples", @@ -629,6 +720,35 @@ ], "difficulty": 4 }, + { + "slug": "flatten-array", + "name": "Flatten Array", + "uuid": "27cb8df3-ed88-4b60-990e-eb0422123260", + "practices": [ + "recursion" + ], + "prerequisites": [ + "sequences", + "conditionals", + "recursion" + ], + "difficulty": 4 + }, + { + "slug": "largest-series-product", + "name": "Largest Series Product", + "uuid": "d6d49ae0-5999-435c-902f-beb35e72aac9", + "practices": [ + "higher-order-sequences" + ], + "prerequisites": [ + "strings", + "numbers", + "higher-order-sequences", + "errors" + ], + "difficulty": 4 + }, { "slug": "luhn", "name": "Luhn", @@ -638,7 +758,23 @@ ], "prerequisites": [ "strings", - "numbers" + "numbers", + "higher-order-sequences", + "unicode", + "locals" + ], + "difficulty": 4 + }, + { + "slug": "pascals-triangle", + "name": "Pascal's Triangle", + "uuid": "95dc30b1-af57-4c4c-af4d-64c53cfea619", + "practices": [ + "higher-order-sequences" + ], + "prerequisites": [ + "sequences", + "higher-order-sequences" ], "difficulty": 4 }, @@ -653,7 +789,8 @@ "case", "combinators", "sequences", - "dynamic-variables" + "dynamic-variables", + "tuples" ], "difficulty": 4 }, @@ -679,7 +816,8 @@ ], "prerequisites": [ "numbers", - "tuples" + "case", + "dynamic-variables" ], "difficulty": 4 }, @@ -758,7 +896,26 @@ "strings" ], "prerequisites": [ - "strings" + "strings", + "higher-order-sequences", + "unicode", + "locals" + ], + "difficulty": 5 + }, + { + "slug": "knapsack", + "name": "Knapsack", + "uuid": "0c991751-e9a9-4291-a0fd-65af4263a65e", + "practices": [ + "recursion" + ], + "prerequisites": [ + "tuples", + "sequences", + "conditionals", + "locals", + "recursion" ], "difficulty": 5 }, @@ -943,6 +1100,22 @@ ], "difficulty": 7 }, + { + "slug": "satellite", + "name": "Satellite", + "uuid": "65d091e3-0d2f-4f69-a088-069f799d0109", + "practices": [ + "recursion" + ], + "prerequisites": [ + "sequences", + "errors", + "tuples", + "locals", + "recursion" + ], + "difficulty": 7 + }, { "slug": "nth-prime", "name": "Nth Prime", diff --git a/exercises/concept/character-study/.docs/instructions.md b/exercises/concept/character-study/.docs/instructions.md index 03878aaf..5328e0f8 100644 --- a/exercises/concept/character-study/.docs/instructions.md +++ b/exercises/concept/character-study/.docs/instructions.md @@ -1,8 +1,8 @@ # Instructions -Lewis, a young Lisp Alien, needs to get some work done on their -Human alphabets project and asks if you can help them program some -words they will need. +Heidi, a young Forth Alien, needs to get some work done on her +Human alphabets project and asks if you can help her program some +words she will need. ## 1. Compare two characters @@ -20,7 +20,7 @@ CHAR: A CHAR: a compare-chars . ! => less ## 2. Determine the size of a character -Lewis needs to know whether a character is "big" (uppercase), +Heidi needs to know whether a character is "big" (uppercase), "small" (lowercase), or has no size. Define `size-of-char` to return `big`, `small`, or `no-size`. @@ -33,7 +33,7 @@ CHAR: space size-of-char . ! => no-size ## 3. Change the size of a character -Lewis sometimes needs to flip a character to a different size. +Heidi sometimes needs to flip a character to a different size. Define `change-size-of-char` to take a character and a desired size (`big` or `small`) and return the corresponding-case character. @@ -45,7 +45,7 @@ CHAR: A small change-size-of-char . ! => 97 (CHAR: a) ## 4. Determine the type of a character -Lewis also needs to know what *kind* of character it is. Define +Heidi also needs to know what *kind* of character it is. Define `type-of-char` to return: - `alpha` for any letter (upper or lower) diff --git a/exercises/concept/character-study/.docs/introduction.md b/exercises/concept/character-study/.docs/introduction.md index 8950f610..3902a975 100644 --- a/exercises/concept/character-study/.docs/introduction.md +++ b/exercises/concept/character-study/.docs/introduction.md @@ -44,4 +44,23 @@ CHAR: space . ! => 32 CHAR: \n . ! => 10 ``` +## Symbols + +When a word needs to return a tag rather than a value — say, +`yes`/`no`/`maybe` instead of a boolean — declare each tag as a +*symbol*. A symbol is a word that pushes itself when called, and +two symbols compare equal only when they are the same name. + +```factor +USING: kernel ; + +SYMBOLS: yes no maybe ; + +yes . ! => yes +yes yes = . ! => t +yes no = . ! => f +``` + +`SYMBOL: name` declares one; `SYMBOLS: a b c ;` declares several. + [unicode]: https://docs.factorcode.org/content/vocab-unicode.html diff --git a/exercises/concept/character-study/.meta/config.json b/exercises/concept/character-study/.meta/config.json index f26e931c..dd9e6a9b 100644 --- a/exercises/concept/character-study/.meta/config.json +++ b/exercises/concept/character-study/.meta/config.json @@ -16,5 +16,5 @@ "forked_from": [ "common-lisp/character-study" ], - "blurb": "Learn about Unicode-aware character handling by helping a Lisp Alien with their alphabets project." + "blurb": "Learn about Unicode-aware character handling by helping a Forth Alien with her alphabets project." } diff --git a/exercises/practice/binary-search/.docs/instructions.md b/exercises/practice/binary-search/.docs/instructions.md new file mode 100644 index 00000000..12f4358e --- /dev/null +++ b/exercises/practice/binary-search/.docs/instructions.md @@ -0,0 +1,29 @@ +# Instructions + +Your task is to implement a binary search algorithm. + +A binary search algorithm finds an item in a list by repeatedly splitting it in half, only keeping the half which contains the item we're looking for. +It allows us to quickly narrow down the possible locations of our item until we find it, or until we've eliminated all possible locations. + +~~~~exercism/caution +Binary search only works when a list has been sorted. +~~~~ + +The algorithm looks like this: + +- Find the middle element of a _sorted_ list and compare it with the item we're looking for. +- If the middle element is our item, then we're done! +- If the middle element is greater than our item, we can eliminate that element and all the elements **after** it. +- If the middle element is less than our item, we can eliminate that element and all the elements **before** it. +- If every element of the list has been eliminated then the item is not in the list. +- Otherwise, repeat the process on the part of the list that has not been eliminated. + +Here's an example: + +Let's say we're looking for the number 23 in the following sorted list: `[4, 8, 12, 16, 23, 28, 32]`. + +- We start by comparing 23 with the middle element, 16. +- Since 23 is greater than 16, we can eliminate the left half of the list, leaving us with `[23, 28, 32]`. +- We then compare 23 with the new middle element, 28. +- Since 23 is less than 28, we can eliminate the right half of the list: `[23]`. +- We've found our item. diff --git a/exercises/practice/binary-search/.docs/introduction.md b/exercises/practice/binary-search/.docs/introduction.md new file mode 100644 index 00000000..03496599 --- /dev/null +++ b/exercises/practice/binary-search/.docs/introduction.md @@ -0,0 +1,13 @@ +# Introduction + +You have stumbled upon a group of mathematicians who are also singer-songwriters. +They have written a song for each of their favorite numbers, and, as you can imagine, they have a lot of favorite numbers (like [0][zero] or [73][seventy-three] or [6174][kaprekars-constant]). + +You are curious to hear the song for your favorite number, but with so many songs to wade through, finding the right song could take a while. +Fortunately, they have organized their songs in a playlist sorted by the title — which is simply the number that the song is about. + +You realize that you can use a binary search algorithm to quickly find a song given the title. + +[zero]: https://en.wikipedia.org/wiki/0 +[seventy-three]: https://en.wikipedia.org/wiki/73_(number) +[kaprekars-constant]: https://en.wikipedia.org/wiki/6174_(number) diff --git a/exercises/practice/binary-search/.meta/config.json b/exercises/practice/binary-search/.meta/config.json new file mode 100644 index 00000000..76d95f13 --- /dev/null +++ b/exercises/practice/binary-search/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "binary-search/binary-search.factor" + ], + "test": [ + "binary-search/binary-search-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Implement a binary search algorithm.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Binary_search_algorithm" +} diff --git a/exercises/practice/binary-search/.meta/example.factor b/exercises/practice/binary-search/.meta/example.factor new file mode 100644 index 00000000..fed4cd41 --- /dev/null +++ b/exercises/practice/binary-search/.meta/example.factor @@ -0,0 +1,18 @@ +USING: combinators kernel locals math sequences ; +IN: binary-search + +ERROR: value-not-in-array ; + +:: search-range ( array value low high -- index ) + low high > [ value-not-in-array ] [ + low high + 2/ :> mid + mid array nth :> probe + { + { [ probe value = ] [ mid ] } + { [ probe value < ] [ array value mid 1 + high search-range ] } + [ array value low mid 1 - search-range ] + } cond + ] if ; + +: find ( array value -- index ) + over length 1 - [ 0 ] dip search-range ; diff --git a/exercises/practice/binary-search/.meta/generator.jl b/exercises/practice/binary-search/.meta/generator.jl new file mode 100644 index 00000000..9d30f269 --- /dev/null +++ b/exercises/practice/binary-search/.meta/generator.jl @@ -0,0 +1,13 @@ +module BinarySearch + +function gen_test_case(case) + array = format_int_array(case["input"]["array"]) + value = to_int_str(case["input"]["value"]) + expected = case["expected"] + if expected isa AbstractDict && haskey(expected, "error") + return """[ $(array) $(value) find ] [ value-not-in-array? ] must-fail-with""" + end + return "{ $(to_int_str(expected)) }\n[ $(array) $(value) find ] unit-test" +end + +end diff --git a/exercises/practice/binary-search/.meta/tests.toml b/exercises/practice/binary-search/.meta/tests.toml new file mode 100644 index 00000000..61e2b068 --- /dev/null +++ b/exercises/practice/binary-search/.meta/tests.toml @@ -0,0 +1,43 @@ +# 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. + +[b55c24a9-a98d-4379-a08c-2adcf8ebeee8] +description = "finds a value in an array with one element" + +[73469346-b0a0-4011-89bf-989e443d503d] +description = "finds a value in the middle of an array" + +[327bc482-ab85-424e-a724-fb4658e66ddb] +description = "finds a value at the beginning of an array" + +[f9f94b16-fe5e-472c-85ea-c513804c7d59] +description = "finds a value at the end of an array" + +[f0068905-26e3-4342-856d-ad153cadb338] +description = "finds a value in an array of odd length" + +[fc316b12-c8b3-4f5e-9e89-532b3389de8c] +description = "finds a value in an array of even length" + +[da7db20a-354f-49f7-a6a1-650a54998aa6] +description = "identifies that a value is not included in the array" + +[95d869ff-3daf-4c79-b622-6e805c675f97] +description = "a value smaller than the array's smallest value is not found" + +[8b24ef45-6e51-4a94-9eac-c2bf38fdb0ba] +description = "a value larger than the array's largest value is not found" + +[f439a0fa-cf42-4262-8ad1-64bf41ce566a] +description = "nothing is found in an empty array" + +[2c353967-b56d-40b8-acff-ce43115eed64] +description = "nothing is found when the left and right bounds cross" diff --git a/exercises/practice/binary-search/binary-search/binary-search-tests.factor b/exercises/practice/binary-search/binary-search/binary-search-tests.factor new file mode 100644 index 00000000..f12a0b43 --- /dev/null +++ b/exercises/practice/binary-search/binary-search/binary-search-tests.factor @@ -0,0 +1,47 @@ +USING: binary-search io kernel lexer tools.test unicode ; +IN: binary-search.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Binary Search:" print + +"finds a value in an array with one element" print +{ 0 } +[ { 6 } 6 find ] unit-test + +STOP-HERE + +"finds a value in the middle of an array" print +{ 3 } +[ { 1 3 4 6 8 9 11 } 6 find ] unit-test + +"finds a value at the beginning of an array" print +{ 0 } +[ { 1 3 4 6 8 9 11 } 1 find ] unit-test + +"finds a value at the end of an array" print +{ 6 } +[ { 1 3 4 6 8 9 11 } 11 find ] unit-test + +"finds a value in an array of odd length" print +{ 9 } +[ { 1 3 5 8 13 21 34 55 89 144 233 377 634 } 144 find ] unit-test + +"finds a value in an array of even length" print +{ 5 } +[ { 1 3 5 8 13 21 34 55 89 144 233 377 } 21 find ] unit-test + +"identifies that a value is not included in the array" print +[ { 1 3 4 6 8 9 11 } 7 find ] [ value-not-in-array? ] must-fail-with + +"a value smaller than the array's smallest value is not found" print +[ { 1 3 4 6 8 9 11 } 0 find ] [ value-not-in-array? ] must-fail-with + +"a value larger than the array's largest value is not found" print +[ { 1 3 4 6 8 9 11 } 13 find ] [ value-not-in-array? ] must-fail-with + +"nothing is found in an empty array" print +[ { } 1 find ] [ value-not-in-array? ] must-fail-with + +"nothing is found when the left and right bounds cross" print +[ { 1 2 } 0 find ] [ value-not-in-array? ] must-fail-with diff --git a/exercises/practice/binary-search/binary-search/binary-search.factor b/exercises/practice/binary-search/binary-search/binary-search.factor new file mode 100644 index 00000000..b1b55a98 --- /dev/null +++ b/exercises/practice/binary-search/binary-search/binary-search.factor @@ -0,0 +1,7 @@ +USING: kernel ; +IN: binary-search + +ERROR: value-not-in-array ; + +: find ( array value -- index ) + "unimplemented" throw ; diff --git a/exercises/practice/flatten-array/.docs/instructions.md b/exercises/practice/flatten-array/.docs/instructions.md new file mode 100644 index 00000000..b5b82713 --- /dev/null +++ b/exercises/practice/flatten-array/.docs/instructions.md @@ -0,0 +1,16 @@ +# Instructions + +Take a nested array of any depth and return a fully flattened array. + +Note that some language tracks may include null-like values in the input array, and the way these values are represented varies by track. +Such values should be excluded from the flattened array. + +Additionally, the input may be of a different data type and contain different types, depending on the track. + +Check the test suite for details. + +## Example + +input: `[1, [2, 6, null], [[null, [4]], 5]]` + +output: `[1, 2, 6, 4, 5]` diff --git a/exercises/practice/flatten-array/.docs/introduction.md b/exercises/practice/flatten-array/.docs/introduction.md new file mode 100644 index 00000000..a3148574 --- /dev/null +++ b/exercises/practice/flatten-array/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +A shipment of emergency supplies has arrived, but there's a problem. +To protect from damage, the items — flashlights, first-aid kits, blankets — are packed inside boxes, and some of those boxes are nested several layers deep inside other boxes! + +To be prepared for an emergency, everything must be easily accessible in one box. +Can you unpack all the supplies and place them into a single box, so they're ready when needed most? diff --git a/exercises/practice/flatten-array/.meta/config.json b/exercises/practice/flatten-array/.meta/config.json new file mode 100644 index 00000000..660ec9b5 --- /dev/null +++ b/exercises/practice/flatten-array/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "flatten-array/flatten-array.factor" + ], + "test": [ + "flatten-array/flatten-array-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Take a nested list and return a single list with all values except nil/null.", + "source": "Interview Question", + "source_url": "https://reference.wolfram.com/language/ref/Flatten.html" +} diff --git a/exercises/practice/flatten-array/.meta/example.factor b/exercises/practice/flatten-array/.meta/example.factor new file mode 100644 index 00000000..1cb29b4b --- /dev/null +++ b/exercises/practice/flatten-array/.meta/example.factor @@ -0,0 +1,14 @@ +USING: arrays combinators kernel sequences ; +IN: flatten-array + +DEFER: flatten + +: flatten-elt ( elt -- seq ) + { + { [ dup not ] [ drop { } ] } + { [ dup array? ] [ flatten ] } + [ 1array ] + } cond ; + +: flatten ( array -- flat ) + [ flatten-elt ] map concat ; diff --git a/exercises/practice/flatten-array/.meta/generator.jl b/exercises/practice/flatten-array/.meta/generator.jl new file mode 100644 index 00000000..7ed68923 --- /dev/null +++ b/exercises/practice/flatten-array/.meta/generator.jl @@ -0,0 +1,22 @@ +module FlattenArray + +function format_nullable_nested(arr) + parts = map(arr) do x + if x === nothing + "f" + elseif x isa AbstractVector + format_nullable_nested(x) + else + to_int_str(x) + end + end + return "{ $(join(parts, " ")) }" +end + +function gen_test_case(case) + input = format_nullable_nested(case["input"]["array"]) + expected = format_nullable_nested(case["expected"]) + return "{ $(expected) }\n[ $(input) flatten ] unit-test" +end + +end diff --git a/exercises/practice/flatten-array/.meta/tests.toml b/exercises/practice/flatten-array/.meta/tests.toml new file mode 100644 index 00000000..44acf175 --- /dev/null +++ b/exercises/practice/flatten-array/.meta/tests.toml @@ -0,0 +1,63 @@ +# 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. + +[8c71dabd-da60-422d-a290-4a571471fb14] +description = "empty" + +[d268b919-963c-442d-9f07-82b93f1b518c] +description = "no nesting" + +[3f15bede-c856-479e-bb71-1684b20c6a30] +description = "flattens a nested array" + +[c84440cc-bb3a-48a6-862c-94cf23f2815d] +description = "flattens array with just integers present" + +[d3d99d39-6be5-44f5-a31d-6037d92ba34f] +description = "5 level nesting" + +[d572bdba-c127-43ed-bdcd-6222ac83d9f7] +description = "6 level nesting" + +[0705a8e5-dc86-4cec-8909-150c5e54fa9c] +description = "null values are omitted from the final result" + +[c6cf26de-8ccd-4410-84bd-b9efd88fd2bc] +description = "consecutive null values at the front of the list are omitted from the final result" +include = false + +[bc72da10-5f55-4ada-baf3-50e4da02ec8e] +description = "consecutive null values at the front of the array are omitted from the final result" +reimplements = "c6cf26de-8ccd-4410-84bd-b9efd88fd2bc" + +[382c5242-587e-4577-b8ce-a5fb51e385a1] +description = "consecutive null values in the middle of the list are omitted from the final result" +include = false + +[6991836d-0d9b-4703-80a0-3f1f23eb5981] +description = "consecutive null values in the middle of the array are omitted from the final result" +reimplements = "382c5242-587e-4577-b8ce-a5fb51e385a1" + +[ef1d4790-1b1e-4939-a179-51ace0829dbd] +description = "6 level nest list with null values" +include = false + +[dc90a09c-5376-449c-a7b3-c2d20d540069] +description = "6 level nested array with null values" +reimplements = "ef1d4790-1b1e-4939-a179-51ace0829dbd" + +[85721643-705a-4150-93ab-7ae398e2942d] +description = "all values in nested list are null" +include = false + +[51f5d9af-8f7f-4fb5-a156-69e8282cb275] +description = "all values in nested array are null" +reimplements = "85721643-705a-4150-93ab-7ae398e2942d" diff --git a/exercises/practice/flatten-array/flatten-array/flatten-array-tests.factor b/exercises/practice/flatten-array/flatten-array/flatten-array-tests.factor new file mode 100644 index 00000000..4e409e53 --- /dev/null +++ b/exercises/practice/flatten-array/flatten-array/flatten-array-tests.factor @@ -0,0 +1,52 @@ +USING: flatten-array io kernel lexer tools.test unicode ; +IN: flatten-array.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Flatten Array:" print + +"empty" print +{ { } } +[ { } flatten ] unit-test + +STOP-HERE + +"no nesting" print +{ { 0 1 2 } } +[ { 0 1 2 } flatten ] unit-test + +"flattens a nested array" print +{ { } } +[ { { { } } } flatten ] unit-test + +"flattens array with just integers present" print +{ { 1 2 3 4 5 6 7 8 } } +[ { 1 { 2 3 4 5 6 7 } 8 } flatten ] unit-test + +"5 level nesting" print +{ { 0 2 2 3 8 100 4 50 -2 } } +[ { 0 2 { { 2 3 } 8 100 4 { { { 50 } } } } -2 } flatten ] unit-test + +"6 level nesting" print +{ { 1 2 3 4 5 6 7 8 } } +[ { 1 { 2 { { 3 } } { 4 { { 5 } } } 6 7 } 8 } flatten ] unit-test + +"null values are omitted from the final result" print +{ { 1 2 } } +[ { 1 2 f } flatten ] unit-test + +"consecutive null values at the front of the array are omitted from the final result" print +{ { 3 } } +[ { f f 3 } flatten ] unit-test + +"consecutive null values in the middle of the array are omitted from the final result" print +{ { 1 4 } } +[ { 1 f f 4 } flatten ] unit-test + +"6 level nested array with null values" print +{ { 0 2 2 3 8 100 -2 } } +[ { 0 2 { { 2 3 } 8 { { 100 } } f { { f } } } -2 } flatten ] unit-test + +"all values in nested array are null" print +{ { } } +[ { f { { { f } } } f f { { f f } f } f } flatten ] unit-test diff --git a/exercises/practice/flatten-array/flatten-array/flatten-array.factor b/exercises/practice/flatten-array/flatten-array/flatten-array.factor new file mode 100644 index 00000000..b4b5939f --- /dev/null +++ b/exercises/practice/flatten-array/flatten-array/flatten-array.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: flatten-array + +: flatten ( array -- flat ) + "unimplemented" throw ; diff --git a/exercises/practice/high-scores/.docs/instructions.md b/exercises/practice/high-scores/.docs/instructions.md new file mode 100644 index 00000000..55802488 --- /dev/null +++ b/exercises/practice/high-scores/.docs/instructions.md @@ -0,0 +1,6 @@ +# Instructions + +Manage a game player's High Score list. + +Your task is to build a high-score component of the classic Frogger game, one of the highest selling and most addictive games of all time, and a classic of the arcade era. +Your task is to write methods that return the highest score from the list, the last added score and the three highest scores. diff --git a/exercises/practice/high-scores/.meta/config.json b/exercises/practice/high-scores/.meta/config.json new file mode 100644 index 00000000..73698c0e --- /dev/null +++ b/exercises/practice/high-scores/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "high-scores/high-scores.factor" + ], + "test": [ + "high-scores/high-scores-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Manage a player's High Score list.", + "source": "Tribute to the eighties' arcade game Frogger" +} diff --git a/exercises/practice/high-scores/.meta/example.factor b/exercises/practice/high-scores/.meta/example.factor new file mode 100644 index 00000000..b9c340bd --- /dev/null +++ b/exercises/practice/high-scores/.meta/example.factor @@ -0,0 +1,19 @@ +USING: accessors kernel math math.order math.statistics sequences sorting ; +IN: high-scores + +TUPLE: high-scores scores ; + +: ( scores -- hs ) + high-scores boa ; + +: scores ( hs -- scores ) + scores>> ; + +: latest ( hs -- score ) + scores>> last ; + +: personal-best ( hs -- score ) + scores>> supremum ; + +: personal-top-three ( hs -- top-three ) + scores>> sort reverse 3 over length min head ; diff --git a/exercises/practice/high-scores/.meta/generator.jl b/exercises/practice/high-scores/.meta/generator.jl new file mode 100644 index 00000000..40e43b0f --- /dev/null +++ b/exercises/practice/high-scores/.meta/generator.jl @@ -0,0 +1,31 @@ +module HighScores + +function gen_test_case(case) + scores = format_int_array(case["input"]["scores"]) + expected = case["expected"] + expected_str = expected isa AbstractVector ? format_int_array(expected) : to_int_str(expected) + property = case["property"] + setup = "$(scores) " + call = if property == "scores" + "$(setup) scores" + elseif property == "latest" + "$(setup) latest" + elseif property == "personalBest" + "$(setup) personal-best" + elseif property == "personalTopThree" + "$(setup) personal-top-three" + elseif property == "latestAfterTopThree" + "$(setup) dup personal-top-three drop latest" + elseif property == "scoresAfterTopThree" + "$(setup) dup personal-top-three drop scores" + elseif property == "latestAfterBest" + "$(setup) dup personal-best drop latest" + elseif property == "scoresAfterBest" + "$(setup) dup personal-best drop scores" + else + error("Unknown property: $(property)") + end + return "{ $(expected_str) }\n[ $(call) ] unit-test" +end + +end diff --git a/exercises/practice/high-scores/.meta/tests.toml b/exercises/practice/high-scores/.meta/tests.toml new file mode 100644 index 00000000..7c946338 --- /dev/null +++ b/exercises/practice/high-scores/.meta/tests.toml @@ -0,0 +1,46 @@ +# 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. + +[1035eb93-2208-4c22-bab8-fef06769a73c] +description = "List of scores" + +[6aa5dbf5-78fa-4375-b22c-ffaa989732d2] +description = "Latest score" + +[b661a2e1-aebf-4f50-9139-0fb817dd12c6] +description = "Personal best" + +[3d996a97-c81c-4642-9afc-80b80dc14015] +description = "Top 3 scores -> Personal top three from a list of scores" + +[1084ecb5-3eb4-46fe-a816-e40331a4e83a] +description = "Top 3 scores -> Personal top highest to lowest" + +[e6465b6b-5a11-4936-bfe3-35241c4f4f16] +description = "Top 3 scores -> Personal top when there is a tie" + +[f73b02af-c8fd-41c9-91b9-c86eaa86bce2] +description = "Top 3 scores -> Personal top when there are less than 3" + +[16608eae-f60f-4a88-800e-aabce5df2865] +description = "Top 3 scores -> Personal top when there is only one" + +[2df075f9-fec9-4756-8f40-98c52a11504f] +description = "Top 3 scores -> Latest score after personal top scores" + +[809c4058-7eb1-4206-b01e-79238b9b71bc] +description = "Top 3 scores -> Scores after personal top scores" + +[ddb0efc0-9a86-4f82-bc30-21ae0bdc6418] +description = "Top 3 scores -> Latest score after personal best" + +[6a0fd2d1-4cc4-46b9-a5bb-2fb667ca2364] +description = "Top 3 scores -> Scores after personal best" diff --git a/exercises/practice/high-scores/high-scores/high-scores-tests.factor b/exercises/practice/high-scores/high-scores/high-scores-tests.factor new file mode 100644 index 00000000..cf5128a9 --- /dev/null +++ b/exercises/practice/high-scores/high-scores/high-scores-tests.factor @@ -0,0 +1,56 @@ +USING: high-scores io kernel lexer tools.test unicode ; +IN: high-scores.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"High Scores:" print + +"List of scores" print +{ { 30 50 20 70 } } +[ { 30 50 20 70 } scores ] unit-test + +STOP-HERE + +"Latest score" print +{ 30 } +[ { 100 0 90 30 } latest ] unit-test + +"Personal best" print +{ 100 } +[ { 40 100 70 } personal-best ] unit-test + +"Personal top three from a list of scores" print +{ { 100 90 70 } } +[ { 10 30 90 30 100 20 10 0 30 40 40 70 70 } personal-top-three ] unit-test + +"Personal top highest to lowest" print +{ { 30 20 10 } } +[ { 20 10 30 } personal-top-three ] unit-test + +"Personal top when there is a tie" print +{ { 40 40 30 } } +[ { 40 20 40 30 } personal-top-three ] unit-test + +"Personal top when there are less than 3" print +{ { 70 30 } } +[ { 30 70 } personal-top-three ] unit-test + +"Personal top when there is only one" print +{ { 40 } } +[ { 40 } personal-top-three ] unit-test + +"Latest score after personal top scores" print +{ 30 } +[ { 70 50 20 30 } dup personal-top-three drop latest ] unit-test + +"Scores after personal top scores" print +{ { 30 50 20 70 } } +[ { 30 50 20 70 } dup personal-top-three drop scores ] unit-test + +"Latest score after personal best" print +{ 30 } +[ { 20 70 15 25 30 } dup personal-best drop latest ] unit-test + +"Scores after personal best" print +{ { 20 70 15 25 30 } } +[ { 20 70 15 25 30 } dup personal-best drop scores ] unit-test diff --git a/exercises/practice/high-scores/high-scores/high-scores.factor b/exercises/practice/high-scores/high-scores/high-scores.factor new file mode 100644 index 00000000..1dbe19fa --- /dev/null +++ b/exercises/practice/high-scores/high-scores/high-scores.factor @@ -0,0 +1,19 @@ +USING: kernel ; +IN: high-scores + +TUPLE: high-scores scores ; + +: ( scores -- hs ) + "unimplemented" throw ; + +: scores ( hs -- scores ) + "unimplemented" throw ; + +: latest ( hs -- score ) + "unimplemented" throw ; + +: personal-best ( hs -- score ) + "unimplemented" throw ; + +: personal-top-three ( hs -- top-three ) + "unimplemented" throw ; diff --git a/exercises/practice/knapsack/.docs/instructions.md b/exercises/practice/knapsack/.docs/instructions.md new file mode 100644 index 00000000..0ebf7914 --- /dev/null +++ b/exercises/practice/knapsack/.docs/instructions.md @@ -0,0 +1,25 @@ +# Instructions + +Your task is to determine which items to take so that the total value of her selection is maximized, taking into account the knapsack's carrying capacity. + +Items will be represented as a list of items. +Each item will have a weight and value. +All values given will be strictly positive. +Lhakpa can take only one of each item. + +For example: + +```text +Items: [ + { "weight": 5, "value": 10 }, + { "weight": 4, "value": 40 }, + { "weight": 6, "value": 30 }, + { "weight": 4, "value": 50 } +] + +Knapsack Maximum Weight: 10 +``` + +For the above, the first item has weight 5 and value 10, the second item has weight 4 and value 40, and so on. +In this example, Lhakpa should take the second and fourth item to maximize her value, which, in this case, is 90. +She cannot get more than 90 as her knapsack has a weight limit of 10. diff --git a/exercises/practice/knapsack/.docs/introduction.md b/exercises/practice/knapsack/.docs/introduction.md new file mode 100644 index 00000000..9ac9df59 --- /dev/null +++ b/exercises/practice/knapsack/.docs/introduction.md @@ -0,0 +1,10 @@ +# Introduction + +Lhakpa is a [Sherpa][sherpa] mountain guide and porter. +After months of careful planning, the expedition Lhakpa works for is about to leave. +She will be paid the value she carried to the base camp. + +In front of her are many items, each with a value and weight. +Lhakpa would gladly take all of the items, but her knapsack can only hold so much weight. + +[sherpa]: https://en.wikipedia.org/wiki/Sherpa_people#Mountaineering diff --git a/exercises/practice/knapsack/.meta/config.json b/exercises/practice/knapsack/.meta/config.json new file mode 100644 index 00000000..89715870 --- /dev/null +++ b/exercises/practice/knapsack/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "knapsack/knapsack.factor" + ], + "test": [ + "knapsack/knapsack-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a knapsack that can only carry a certain weight, determine which items to put in the knapsack in order to maximize their combined value.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Knapsack_problem" +} diff --git a/exercises/practice/knapsack/.meta/example.factor b/exercises/practice/knapsack/.meta/example.factor new file mode 100644 index 00000000..dbb6cc9a --- /dev/null +++ b/exercises/practice/knapsack/.meta/example.factor @@ -0,0 +1,16 @@ +USING: accessors kernel locals math math.order sequences ; +IN: knapsack + +TUPLE: item weight value ; + +:: maximum-value ( max-weight items -- value ) + items empty? [ 0 ] [ + items unclip :> first :> rest + first weight>> max-weight > [ + max-weight rest maximum-value + ] [ + max-weight rest maximum-value + max-weight first weight>> - rest maximum-value first value>> + + max + ] if + ] if ; diff --git a/exercises/practice/knapsack/.meta/generator.jl b/exercises/practice/knapsack/.meta/generator.jl new file mode 100644 index 00000000..23244ffa --- /dev/null +++ b/exercises/practice/knapsack/.meta/generator.jl @@ -0,0 +1,12 @@ +module Knapsack + +function gen_test_case(case) + max_weight = to_int_str(case["input"]["maximumWeight"]) + items = case["input"]["items"] + item_strs = ["T{ item { weight $(to_int_str(it["weight"])) } { value $(to_int_str(it["value"])) } }" for it in items] + items_str = "{ $(join(item_strs, " ")) }" + expected = to_int_str(case["expected"]) + return "{ $(expected) }\n[ $(max_weight) $(items_str) maximum-value ] unit-test" +end + +end diff --git a/exercises/practice/knapsack/.meta/tests.toml b/exercises/practice/knapsack/.meta/tests.toml new file mode 100644 index 00000000..8e013ef1 --- /dev/null +++ b/exercises/practice/knapsack/.meta/tests.toml @@ -0,0 +1,36 @@ +# 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. + +[a4d7d2f0-ad8a-460c-86f3-88ba709d41a7] +description = "no items" +include = false + +[3993a824-c20e-493d-b3c9-ee8a7753ee59] +description = "no items" +reimplements = "a4d7d2f0-ad8a-460c-86f3-88ba709d41a7" + +[1d39e98c-6249-4a8b-912f-87cb12e506b0] +description = "one item, too heavy" + +[833ea310-6323-44f2-9d27-a278740ffbd8] +description = "five items (cannot be greedy by weight)" + +[277cdc52-f835-4c7d-872b-bff17bab2456] +description = "five items (cannot be greedy by value)" + +[81d8e679-442b-4f7a-8a59-7278083916c9] +description = "example knapsack" + +[f23a2449-d67c-4c26-bf3e-cde020f27ecc] +description = "8 items" + +[7c682ae9-c385-4241-a197-d2fa02c81a11] +description = "15 items" diff --git a/exercises/practice/knapsack/knapsack/knapsack-tests.factor b/exercises/practice/knapsack/knapsack/knapsack-tests.factor new file mode 100644 index 00000000..2f190e3b --- /dev/null +++ b/exercises/practice/knapsack/knapsack/knapsack-tests.factor @@ -0,0 +1,36 @@ +USING: io kernel knapsack lexer tools.test unicode ; +IN: knapsack.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Knapsack:" print + +"no items" print +{ 0 } +[ 100 { } maximum-value ] unit-test + +STOP-HERE + +"one item, too heavy" print +{ 0 } +[ 10 { T{ item { weight 100 } { value 1 } } } maximum-value ] unit-test + +"five items (cannot be greedy by weight)" print +{ 21 } +[ 10 { T{ item { weight 2 } { value 5 } } T{ item { weight 2 } { value 5 } } T{ item { weight 2 } { value 5 } } T{ item { weight 2 } { value 5 } } T{ item { weight 10 } { value 21 } } } maximum-value ] unit-test + +"five items (cannot be greedy by value)" print +{ 80 } +[ 10 { T{ item { weight 2 } { value 20 } } T{ item { weight 2 } { value 20 } } T{ item { weight 2 } { value 20 } } T{ item { weight 2 } { value 20 } } T{ item { weight 10 } { value 50 } } } maximum-value ] unit-test + +"example knapsack" print +{ 90 } +[ 10 { T{ item { weight 5 } { value 10 } } T{ item { weight 4 } { value 40 } } T{ item { weight 6 } { value 30 } } T{ item { weight 4 } { value 50 } } } maximum-value ] unit-test + +"8 items" print +{ 900 } +[ 104 { T{ item { weight 25 } { value 350 } } T{ item { weight 35 } { value 400 } } T{ item { weight 45 } { value 450 } } T{ item { weight 5 } { value 20 } } T{ item { weight 25 } { value 70 } } T{ item { weight 3 } { value 8 } } T{ item { weight 2 } { value 5 } } T{ item { weight 2 } { value 5 } } } maximum-value ] unit-test + +"15 items" print +{ 1458 } +[ 750 { T{ item { weight 70 } { value 135 } } T{ item { weight 73 } { value 139 } } T{ item { weight 77 } { value 149 } } T{ item { weight 80 } { value 150 } } T{ item { weight 82 } { value 156 } } T{ item { weight 87 } { value 163 } } T{ item { weight 90 } { value 173 } } T{ item { weight 94 } { value 184 } } T{ item { weight 98 } { value 192 } } T{ item { weight 106 } { value 201 } } T{ item { weight 110 } { value 210 } } T{ item { weight 113 } { value 214 } } T{ item { weight 115 } { value 221 } } T{ item { weight 118 } { value 229 } } T{ item { weight 120 } { value 240 } } } maximum-value ] unit-test diff --git a/exercises/practice/knapsack/knapsack/knapsack.factor b/exercises/practice/knapsack/knapsack/knapsack.factor new file mode 100644 index 00000000..766a6f2c --- /dev/null +++ b/exercises/practice/knapsack/knapsack/knapsack.factor @@ -0,0 +1,7 @@ +USING: kernel ; +IN: knapsack + +TUPLE: item weight value ; + +: maximum-value ( max-weight items -- value ) + "unimplemented" throw ; diff --git a/exercises/practice/largest-series-product/.docs/instructions.md b/exercises/practice/largest-series-product/.docs/instructions.md new file mode 100644 index 00000000..f297b57f --- /dev/null +++ b/exercises/practice/largest-series-product/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your task is to look for patterns in the long sequence of digits in the encrypted signal. + +The technique you're going to use here is called the largest series product. + +Let's define a few terms, first. + +- **input**: the sequence of digits that you need to analyze +- **series**: a sequence of adjacent digits (those that are next to each other) that is contained within the input +- **span**: how many digits long each series is +- **product**: what you get when you multiply numbers together + +Let's work through an example, with the input `"63915"`. + +- To form a series, take adjacent digits in the original input. +- If you are working with a span of `3`, there will be three possible series: + - `"639"` + - `"391"` + - `"915"` +- Then we need to calculate the product of each series: + - The product of the series `"639"` is 162 (`6 × 3 × 9 = 162`) + - The product of the series `"391"` is 27 (`3 × 9 × 1 = 27`) + - The product of the series `"915"` is 45 (`9 × 1 × 5 = 45`) +- 162 is bigger than both 27 and 45, so the largest series product of `"63915"` is from the series `"639"`. + So the answer is **162**. diff --git a/exercises/practice/largest-series-product/.docs/introduction.md b/exercises/practice/largest-series-product/.docs/introduction.md new file mode 100644 index 00000000..597bb5fa --- /dev/null +++ b/exercises/practice/largest-series-product/.docs/introduction.md @@ -0,0 +1,5 @@ +# Introduction + +You work for a government agency that has intercepted a series of encrypted communication signals from a group of bank robbers. +The signals contain a long sequence of digits. +Your team needs to use various digital signal processing techniques to analyze the signals and identify any patterns that may indicate the planning of a heist. diff --git a/exercises/practice/largest-series-product/.meta/config.json b/exercises/practice/largest-series-product/.meta/config.json new file mode 100644 index 00000000..a29e525c --- /dev/null +++ b/exercises/practice/largest-series-product/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "largest-series-product/largest-series-product.factor" + ], + "test": [ + "largest-series-product/largest-series-product-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a string of digits, calculate the largest product for a contiguous substring of digits of length n.", + "source": "A variation on Problem 8 at Project Euler", + "source_url": "https://projecteuler.net/problem=8" +} diff --git a/exercises/practice/largest-series-product/.meta/example.factor b/exercises/practice/largest-series-product/.meta/example.factor new file mode 100644 index 00000000..9b8577bb --- /dev/null +++ b/exercises/practice/largest-series-product/.meta/example.factor @@ -0,0 +1,13 @@ +USING: combinators grouping kernel math math.parser math.statistics sequences strings unicode ; +IN: largest-series-product + +ERROR: invalid-input ; + +: largest-product ( digits span -- product ) + { + { [ dup 0 < ] [ 2drop invalid-input ] } + { [ 2dup [ length ] dip < ] [ 2drop invalid-input ] } + { [ over [ digit? ] all? not ] [ 2drop invalid-input ] } + { [ dup 0 = ] [ 2drop 1 ] } + [ clump [ [ digit> ] map product ] map supremum ] + } cond ; diff --git a/exercises/practice/largest-series-product/.meta/generator.jl b/exercises/practice/largest-series-product/.meta/generator.jl new file mode 100644 index 00000000..0b4bec41 --- /dev/null +++ b/exercises/practice/largest-series-product/.meta/generator.jl @@ -0,0 +1,13 @@ +module LargestSeriesProduct + +function gen_test_case(case) + digits = escape_factor(case["input"]["digits"]) + span = to_int_str(case["input"]["span"]) + expected = case["expected"] + if expected isa AbstractDict && haskey(expected, "error") + return """[ "$(digits)" $(span) largest-product ] [ invalid-input? ] must-fail-with""" + end + return """{ $(to_int_str(expected)) }\n[ "$(digits)" $(span) largest-product ] unit-test""" +end + +end diff --git a/exercises/practice/largest-series-product/.meta/tests.toml b/exercises/practice/largest-series-product/.meta/tests.toml new file mode 100644 index 00000000..5a62d619 --- /dev/null +++ b/exercises/practice/largest-series-product/.meta/tests.toml @@ -0,0 +1,70 @@ +# 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. + +[7c82f8b7-e347-48ee-8a22-f672323324d4] +description = "finds the largest product if span equals length" + +[88523f65-21ba-4458-a76a-b4aaf6e4cb5e] +description = "can find the largest product of 2 with numbers in order" + +[f1376b48-1157-419d-92c2-1d7e36a70b8a] +description = "can find the largest product of 2" + +[46356a67-7e02-489e-8fea-321c2fa7b4a4] +description = "can find the largest product of 3 with numbers in order" + +[a2dcb54b-2b8f-4993-92dd-5ce56dece64a] +description = "can find the largest product of 3" + +[673210a3-33cd-4708-940b-c482d7a88f9d] +description = "can find the largest product of 5 with numbers in order" + +[02acd5a6-3bbf-46df-8282-8b313a80a7c9] +description = "can get the largest product of a big number" + +[76dcc407-21e9-424c-a98e-609f269622b5] +description = "reports zero if the only digits are zero" + +[6ef0df9f-52d4-4a5d-b210-f6fae5f20e19] +description = "reports zero if all spans include zero" + +[5d81aaf7-4f67-4125-bf33-11493cc7eab7] +description = "rejects span longer than string length" +include = false + +[0ae1ce53-d9ba-41bb-827f-2fceb64f058b] +description = "rejects span longer than string length" +reimplements = "5d81aaf7-4f67-4125-bf33-11493cc7eab7" + +[06bc8b90-0c51-4c54-ac22-3ec3893a079e] +description = "reports 1 for empty string and empty product (0 span)" + +[3ec0d92e-f2e2-4090-a380-70afee02f4c0] +description = "reports 1 for nonempty string and empty product (0 span)" + +[6d96c691-4374-4404-80ee-2ea8f3613dd4] +description = "rejects empty string and nonzero span" +include = false + +[6cf66098-a6af-4223-aab1-26aeeefc7402] +description = "rejects empty string and nonzero span" +reimplements = "6d96c691-4374-4404-80ee-2ea8f3613dd4" + +[7a38f2d6-3c35-45f6-8d6f-12e6e32d4d74] +description = "rejects invalid character in digits" + +[5fe3c0e5-a945-49f2-b584-f0814b4dd1ef] +description = "rejects negative span" +include = false + +[c859f34a-9bfe-4897-9c2f-6d7f8598e7f0] +description = "rejects negative span" +reimplements = "5fe3c0e5-a945-49f2-b584-f0814b4dd1ef" diff --git a/exercises/practice/largest-series-product/largest-series-product/largest-series-product-tests.factor b/exercises/practice/largest-series-product/largest-series-product/largest-series-product-tests.factor new file mode 100644 index 00000000..3a47d994 --- /dev/null +++ b/exercises/practice/largest-series-product/largest-series-product/largest-series-product-tests.factor @@ -0,0 +1,64 @@ +USING: io kernel largest-series-product lexer tools.test unicode ; +IN: largest-series-product.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Largest Series Product:" print + +"finds the largest product if span equals length" print +{ 18 } +[ "29" 2 largest-product ] unit-test + +STOP-HERE + +"can find the largest product of 2 with numbers in order" print +{ 72 } +[ "0123456789" 2 largest-product ] unit-test + +"can find the largest product of 2" print +{ 48 } +[ "576802143" 2 largest-product ] unit-test + +"can find the largest product of 3 with numbers in order" print +{ 504 } +[ "0123456789" 3 largest-product ] unit-test + +"can find the largest product of 3" print +{ 270 } +[ "1027839564" 3 largest-product ] unit-test + +"can find the largest product of 5 with numbers in order" print +{ 15120 } +[ "0123456789" 5 largest-product ] unit-test + +"can get the largest product of a big number" print +{ 23520 } +[ "73167176531330624919225119674426574742355349194934" 6 largest-product ] unit-test + +"reports zero if the only digits are zero" print +{ 0 } +[ "0000" 2 largest-product ] unit-test + +"reports zero if all spans include zero" print +{ 0 } +[ "99099" 3 largest-product ] unit-test + +"rejects span longer than string length" print +[ "123" 4 largest-product ] [ invalid-input? ] must-fail-with + +"reports 1 for empty string and empty product (0 span)" print +{ 1 } +[ "" 0 largest-product ] unit-test + +"reports 1 for nonempty string and empty product (0 span)" print +{ 1 } +[ "123" 0 largest-product ] unit-test + +"rejects empty string and nonzero span" print +[ "" 1 largest-product ] [ invalid-input? ] must-fail-with + +"rejects invalid character in digits" print +[ "1234a5" 2 largest-product ] [ invalid-input? ] must-fail-with + +"rejects negative span" print +[ "12345" -1 largest-product ] [ invalid-input? ] must-fail-with diff --git a/exercises/practice/largest-series-product/largest-series-product/largest-series-product.factor b/exercises/practice/largest-series-product/largest-series-product/largest-series-product.factor new file mode 100644 index 00000000..73420ed5 --- /dev/null +++ b/exercises/practice/largest-series-product/largest-series-product/largest-series-product.factor @@ -0,0 +1,7 @@ +USING: kernel ; +IN: largest-series-product + +ERROR: invalid-input ; + +: largest-product ( digits span -- product ) + "unimplemented" throw ; diff --git a/exercises/practice/micro-blog/.docs/instructions.md b/exercises/practice/micro-blog/.docs/instructions.md new file mode 100644 index 00000000..d6c6cf65 --- /dev/null +++ b/exercises/practice/micro-blog/.docs/instructions.md @@ -0,0 +1,37 @@ +# Instructions + +You have identified a gap in the social media market for very very short posts. +Now that Twitter allows 280 character posts, people wanting quick social media updates aren't being served. +You decide to create your own social media network. + +To make your product noteworthy, you make it extreme and only allow posts of 5 or less characters. +Any posts of more than 5 characters should be truncated to 5. + +To allow your users to express themselves fully, you allow Emoji and other Unicode. + +The task is to truncate input strings to 5 characters. + +## Text Encodings + +Text stored digitally has to be converted to a series of bytes. +There are 3 ways to map characters to bytes in common use. + +- **ASCII** can encode English language characters. + All characters are precisely 1 byte long. +- **UTF-8** is a Unicode text encoding. + Characters take between 1 and 4 bytes. +- **UTF-16** is a Unicode text encoding. + Characters are either 2 or 4 bytes long. + +UTF-8 and UTF-16 are both Unicode encodings which means they're capable of representing a massive range of characters including: + +- Text in most of the world's languages and scripts +- Historic text +- Emoji + +UTF-8 and UTF-16 are both variable length encodings, which means that different characters take up different amounts of space. + +Consider the letter 'a' and the emoji '😛'. +In UTF-16 the letter takes 2 bytes but the emoji takes 4 bytes. + +The trick to this exercise is to use APIs designed around Unicode characters (codepoints) instead of Unicode codeunits. diff --git a/exercises/practice/micro-blog/.meta/config.json b/exercises/practice/micro-blog/.meta/config.json new file mode 100644 index 00000000..50e732fc --- /dev/null +++ b/exercises/practice/micro-blog/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "micro-blog/micro-blog.factor" + ], + "test": [ + "micro-blog/micro-blog-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given an input string, truncate it to 5 characters." +} diff --git a/exercises/practice/micro-blog/.meta/example.factor b/exercises/practice/micro-blog/.meta/example.factor new file mode 100644 index 00000000..9f594362 --- /dev/null +++ b/exercises/practice/micro-blog/.meta/example.factor @@ -0,0 +1,5 @@ +USING: kernel math math.order sequences ; +IN: micro-blog + +: truncate ( str -- str' ) + 5 over length min head ; diff --git a/exercises/practice/micro-blog/.meta/generator.jl b/exercises/practice/micro-blog/.meta/generator.jl new file mode 100644 index 00000000..db0e9a06 --- /dev/null +++ b/exercises/practice/micro-blog/.meta/generator.jl @@ -0,0 +1,9 @@ +module MicroBlog + +function gen_test_case(case) + phrase = escape_factor(case["input"]["phrase"]) + expected = escape_factor(case["expected"]) + return """{ "$(expected)" }\n[ "$(phrase)" truncate ] unit-test""" +end + +end diff --git a/exercises/practice/micro-blog/.meta/tests.toml b/exercises/practice/micro-blog/.meta/tests.toml new file mode 100644 index 00000000..f23ff0bc --- /dev/null +++ b/exercises/practice/micro-blog/.meta/tests.toml @@ -0,0 +1,46 @@ +# 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. + +[b927b57f-7c98-42fd-8f33-fae091dc1efc] +description = "English language short" + +[a3fcdc5b-0ed4-4f49-80f5-b1a293eac2a0] +description = "English language long" + +[01910864-8e15-4007-9c7c-ac956c686e60] +description = "German language short (broth)" + +[f263e488-aefb-478f-a671-b6ba99722543] +description = "German language long (bear carpet → beards)" + +[0916e8f1-41d7-4402-a110-b08aa000342c] +description = "Bulgarian language short (good)" + +[bed6b89c-03df-4154-98e6-a61a74f61b7d] +description = "Greek language short (health)" + +[485a6a70-2edb-424d-b999-5529dbc8e002] +description = "Maths short" + +[8b4b7b51-8f48-4fbe-964e-6e4e6438be28] +description = "Maths long" + +[71f4a192-0566-4402-a512-fe12878be523] +description = "English and emoji short" + +[6f0f71f3-9806-4759-a844-fa182f7bc203] +description = "Emoji short" + +[ce71fb92-5214-46d0-a7f8-d5ba56b4cc6e] +description = "Emoji long" + +[5dee98d2-d56e-468a-a1f2-121c3f7c5a0b] +description = "Royal Flush?" diff --git a/exercises/practice/micro-blog/micro-blog/micro-blog-tests.factor b/exercises/practice/micro-blog/micro-blog/micro-blog-tests.factor new file mode 100644 index 00000000..1f9a7078 --- /dev/null +++ b/exercises/practice/micro-blog/micro-blog/micro-blog-tests.factor @@ -0,0 +1,56 @@ +USING: io kernel lexer micro-blog tools.test unicode ; +IN: micro-blog.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Micro Blog:" print + +"English language short" print +{ "Hi" } +[ "Hi" truncate ] unit-test + +STOP-HERE + +"English language long" print +{ "Hello" } +[ "Hello there" truncate ] unit-test + +"German language short (broth)" print +{ "brühe" } +[ "brühe" truncate ] unit-test + +"German language long (bear carpet → beards)" print +{ "Bärte" } +[ "Bärteppich" truncate ] unit-test + +"Bulgarian language short (good)" print +{ "Добър" } +[ "Добър" truncate ] unit-test + +"Greek language short (health)" print +{ "υγειά" } +[ "υγειά" truncate ] unit-test + +"Maths short" print +{ "a=πr²" } +[ "a=πr²" truncate ] unit-test + +"Maths long" print +{ "∅⊊ℕ⊊ℤ" } +[ "∅⊊ℕ⊊ℤ⊊ℚ⊊ℝ⊊ℂ" truncate ] unit-test + +"English and emoji short" print +{ "Fly 🛫" } +[ "Fly 🛫" truncate ] unit-test + +"Emoji short" print +{ "💇" } +[ "💇" truncate ] unit-test + +"Emoji long" print +{ "❄🌡🤧🤒🏥" } +[ "❄🌡🤧🤒🏥🕰😀" truncate ] unit-test + +"Royal Flush?" print +{ "🃎🂸🃅🃋🃍" } +[ "🃎🂸🃅🃋🃍🃁🃊" truncate ] unit-test diff --git a/exercises/practice/micro-blog/micro-blog/micro-blog.factor b/exercises/practice/micro-blog/micro-blog/micro-blog.factor new file mode 100644 index 00000000..0474c840 --- /dev/null +++ b/exercises/practice/micro-blog/micro-blog/micro-blog.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: micro-blog + +: truncate ( str -- str' ) + "unimplemented" throw ; diff --git a/exercises/practice/nucleotide-count/.docs/instructions.md b/exercises/practice/nucleotide-count/.docs/instructions.md new file mode 100644 index 00000000..548d9ba5 --- /dev/null +++ b/exercises/practice/nucleotide-count/.docs/instructions.md @@ -0,0 +1,23 @@ +# Instructions + +Each of us inherits from our biological parents a set of chemical instructions known as DNA that influence how our bodies are constructed. +All known life depends on DNA! + +> Note: You do not need to understand anything about nucleotides or DNA to complete this exercise. + +DNA is a long chain of other chemicals and the most important are the four nucleotides, adenine, cytosine, guanine and thymine. +A single DNA chain can contain billions of these four nucleotides and the order in which they occur is important! +We call the order of these nucleotides in a bit of DNA a "DNA sequence". + +We represent a DNA sequence as an ordered collection of these four nucleotides and a common way to do that is with a string of characters such as "ATTACG" for a DNA sequence of 6 nucleotides. +'A' for adenine, 'C' for cytosine, 'G' for guanine, and 'T' for thymine. + +Given a string representing a DNA sequence, count how many of each nucleotide is present. +If the string contains characters that aren't A, C, G, or T then it is invalid and you should signal an error. + +For example: + +```text +"GATTACA" -> 'A': 3, 'C': 1, 'G': 1, 'T': 2 +"INVALID" -> error +``` diff --git a/exercises/practice/nucleotide-count/.meta/config.json b/exercises/practice/nucleotide-count/.meta/config.json new file mode 100644 index 00000000..62a29ce7 --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "nucleotide-count/nucleotide-count.factor" + ], + "test": [ + "nucleotide-count/nucleotide-count-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a DNA string, compute how many times each nucleotide occurs in the string.", + "source": "The Calculating DNA Nucleotides_problem at Rosalind", + "source_url": "https://rosalind.info/problems/dna/" +} diff --git a/exercises/practice/nucleotide-count/.meta/example.factor b/exercises/practice/nucleotide-count/.meta/example.factor new file mode 100644 index 00000000..bb7c3b2c --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/example.factor @@ -0,0 +1,9 @@ +USING: assocs kernel math sequences strings ; +IN: nucleotide-count + +ERROR: invalid-nucleotide ; + +: nucleotide-counts ( strand -- counts ) + dup [ "ACGT" member? ] all? [ invalid-nucleotide ] unless + H{ { "A" 0 } { "C" 0 } { "G" 0 } { "T" 0 } } clone + swap [ 1string over [ 1 + ] change-at ] each ; diff --git a/exercises/practice/nucleotide-count/.meta/generator.jl b/exercises/practice/nucleotide-count/.meta/generator.jl new file mode 100644 index 00000000..2ec166ee --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/generator.jl @@ -0,0 +1,14 @@ +module NucleotideCount + +function gen_test_case(case) + strand = escape_factor(case["input"]["strand"]) + expected = case["expected"] + if expected isa AbstractDict && haskey(expected, "error") + return """[ "$(strand)" nucleotide-counts ] [ invalid-nucleotide? ] must-fail-with""" + end + pairs = ["""{ "$(k)" $(to_int_str(v)) }""" for (k, v) in expected] + expected_str = "H{ $(join(pairs, " ")) }" + return """{ $(expected_str) }\n[ "$(strand)" nucleotide-counts ] unit-test""" +end + +end diff --git a/exercises/practice/nucleotide-count/.meta/tests.toml b/exercises/practice/nucleotide-count/.meta/tests.toml new file mode 100644 index 00000000..7c55e53f --- /dev/null +++ b/exercises/practice/nucleotide-count/.meta/tests.toml @@ -0,0 +1,25 @@ +# 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. + +[3e5c30a8-87e2-4845-a815-a49671ade970] +description = "empty strand" + +[a0ea42a6-06d9-4ac6-828c-7ccaccf98fec] +description = "can count one nucleotide in single-character input" + +[eca0d565-ed8c-43e7-9033-6cefbf5115b5] +description = "strand with repeated nucleotide" + +[40a45eac-c83f-4740-901a-20b22d15a39f] +description = "strand with multiple nucleotides" + +[b4c47851-ee9e-4b0a-be70-a86e343bd851] +description = "strand with invalid nucleotides" diff --git a/exercises/practice/nucleotide-count/nucleotide-count/nucleotide-count-tests.factor b/exercises/practice/nucleotide-count/nucleotide-count/nucleotide-count-tests.factor new file mode 100644 index 00000000..4185b946 --- /dev/null +++ b/exercises/practice/nucleotide-count/nucleotide-count/nucleotide-count-tests.factor @@ -0,0 +1,27 @@ +USING: io kernel lexer nucleotide-count tools.test unicode ; +IN: nucleotide-count.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Nucleotide Count:" print + +"empty strand" print +{ H{ { "A" 0 } { "T" 0 } { "C" 0 } { "G" 0 } } } +[ "" nucleotide-counts ] unit-test + +STOP-HERE + +"can count one nucleotide in single-character input" print +{ H{ { "A" 0 } { "T" 0 } { "C" 0 } { "G" 1 } } } +[ "G" nucleotide-counts ] unit-test + +"strand with repeated nucleotide" print +{ H{ { "A" 0 } { "T" 0 } { "C" 0 } { "G" 7 } } } +[ "GGGGGGG" nucleotide-counts ] unit-test + +"strand with multiple nucleotides" print +{ H{ { "A" 20 } { "T" 21 } { "C" 12 } { "G" 17 } } } +[ "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC" nucleotide-counts ] unit-test + +"strand with invalid nucleotides" print +[ "AGXXACT" nucleotide-counts ] [ invalid-nucleotide? ] must-fail-with diff --git a/exercises/practice/nucleotide-count/nucleotide-count/nucleotide-count.factor b/exercises/practice/nucleotide-count/nucleotide-count/nucleotide-count.factor new file mode 100644 index 00000000..3f28e672 --- /dev/null +++ b/exercises/practice/nucleotide-count/nucleotide-count/nucleotide-count.factor @@ -0,0 +1,7 @@ +USING: kernel ; +IN: nucleotide-count + +ERROR: invalid-nucleotide ; + +: nucleotide-counts ( strand -- counts ) + "unimplemented" throw ; diff --git a/exercises/practice/palindrome-products/.meta/example.factor b/exercises/practice/palindrome-products/.meta/example.factor index 55c278b3..298ef33e 100644 --- a/exercises/practice/palindrome-products/.meta/example.factor +++ b/exercises/practice/palindrome-products/.meta/example.factor @@ -4,23 +4,23 @@ IN: palindrome-products : palindrome? ( n -- ? ) number>string dup reverse sequence= ; -:: smallest ( mn mx -- result ) +:: smallest ( mn mx -- value factors ) mn mx > [ "min must be <= max" throw ] when - f :> value! - V{ } clone :> factors + f :> v! + V{ } clone :> facs mn :> first! [ first mx <= ] [ first :> second! - [ second mx <= value [ first second * < ] [ f ] if* not and ] [ + [ second mx <= v [ first second * < ] [ f ] if* not and ] [ first second * :> product product palindrome? [ - value [ product > ] [ t ] if* [ - product value! - factors delete-all - first second 2array factors push + v [ product > ] [ t ] if* [ + product v! + facs delete-all + first second 2array facs push ] [ - product value = [ - first second 2array factors push + product v = [ + first second 2array facs push ] when ] if ] when @@ -28,25 +28,25 @@ IN: palindrome-products ] while first 1 + first! ] while - value factors >array 2array ; + v facs >array ; -:: largest ( mn mx -- result ) +:: largest ( mn mx -- value factors ) mn mx > [ "min must be <= max" throw ] when - f :> value! - V{ } clone :> factors + f :> v! + V{ } clone :> facs mx :> second! [ second mn >= ] [ second :> first! - [ first mn >= value [ first second * > ] [ f ] if* not and ] [ + [ first mn >= v [ first second * > ] [ f ] if* not and ] [ first second * :> product product palindrome? [ - value [ product < ] [ t ] if* [ - product value! - factors delete-all - first second 2array factors push + v [ product < ] [ t ] if* [ + product v! + facs delete-all + first second 2array facs push ] [ - product value = [ - first second 2array factors push + product v = [ + first second 2array facs push ] when ] if ] when @@ -54,4 +54,4 @@ IN: palindrome-products ] while second 1 - second! ] while - value factors >array 2array ; + v facs >array ; diff --git a/exercises/practice/palindrome-products/.meta/generator.jl b/exercises/practice/palindrome-products/.meta/generator.jl index 5d2ee482..c0e47250 100644 --- a/exercises/practice/palindrome-products/.meta/generator.jl +++ b/exercises/practice/palindrome-products/.meta/generator.jl @@ -18,7 +18,7 @@ function gen_test_case(case) factors = expected["factors"] val_str = isnothing(val) ? "f" : to_int_str(val) fac_str = format_factors(factors) - return "{ { $(val_str) $(fac_str) } }\n[ $(mn) $(mx) $(prop) ] unit-test" + return "{ $(val_str) $(fac_str) }\n[ $(mn) $(mx) $(prop) ] unit-test" end end diff --git a/exercises/practice/palindrome-products/palindrome-products/palindrome-products-tests.factor b/exercises/practice/palindrome-products/palindrome-products/palindrome-products-tests.factor index 8c65f3bd..272bd9ef 100644 --- a/exercises/practice/palindrome-products/palindrome-products/palindrome-products-tests.factor +++ b/exercises/practice/palindrome-products/palindrome-products/palindrome-products-tests.factor @@ -6,45 +6,45 @@ IN: palindrome-products.tests "Palindrome Products:" print "find the smallest palindrome from single digit factors" print -{ { 1 { { 1 1 } } } } +{ 1 { { 1 1 } } } [ 1 9 smallest ] unit-test STOP-HERE "find the largest palindrome from single digit factors" print -{ { 9 { { 1 9 } { 3 3 } } } } +{ 9 { { 1 9 } { 3 3 } } } [ 1 9 largest ] unit-test "find the smallest palindrome from double digit factors" print -{ { 121 { { 11 11 } } } } +{ 121 { { 11 11 } } } [ 10 99 smallest ] unit-test "find the largest palindrome from double digit factors" print -{ { 9009 { { 91 99 } } } } +{ 9009 { { 91 99 } } } [ 10 99 largest ] unit-test "find the smallest palindrome from triple digit factors" print -{ { 10201 { { 101 101 } } } } +{ 10201 { { 101 101 } } } [ 100 999 smallest ] unit-test "find the largest palindrome from triple digit factors" print -{ { 906609 { { 913 993 } } } } +{ 906609 { { 913 993 } } } [ 100 999 largest ] unit-test "find the smallest palindrome from four digit factors" print -{ { 1002001 { { 1001 1001 } } } } +{ 1002001 { { 1001 1001 } } } [ 1000 9999 smallest ] unit-test "find the largest palindrome from four digit factors" print -{ { 99000099 { { 9901 9999 } } } } +{ 99000099 { { 9901 9999 } } } [ 1000 9999 largest ] unit-test "empty result for smallest if no palindrome in the range" print -{ { f { } } } +{ f { } } [ 1002 1003 smallest ] unit-test "empty result for largest if no palindrome in the range" print -{ { f { } } } +{ f { } } [ 15 15 largest ] unit-test "error result for smallest if min is more than max" print @@ -56,13 +56,13 @@ STOP-HERE [ "min must be <= max" = ] must-fail-with "smallest product does not use the smallest factor" print -{ { 10988901 { { 3297 3333 } } } } +{ 10988901 { { 3297 3333 } } } [ 3215 4000 smallest ] unit-test "find the smallest palindrome from six digit factors" print -{ { 50067176005 { { 223619 223895 } } } } +{ 50067176005 { { 223619 223895 } } } [ 223617 244818 smallest ] unit-test "find the largest palindrome from six digit factors" print -{ { 59842824895 { { 244445 244811 } } } } +{ 59842824895 { { 244445 244811 } } } [ 223617 244818 largest ] unit-test diff --git a/exercises/practice/palindrome-products/palindrome-products/palindrome-products.factor b/exercises/practice/palindrome-products/palindrome-products/palindrome-products.factor index c92c27f8..e10cb952 100644 --- a/exercises/practice/palindrome-products/palindrome-products/palindrome-products.factor +++ b/exercises/practice/palindrome-products/palindrome-products/palindrome-products.factor @@ -1,8 +1,8 @@ USING: kernel ; IN: palindrome-products -: smallest ( mn mx -- result ) +: smallest ( mn mx -- value factors ) "unimplemented" throw ; -: largest ( mn mx -- result ) +: largest ( mn mx -- value factors ) "unimplemented" throw ; diff --git a/exercises/practice/pascals-triangle/.docs/instructions.md b/exercises/practice/pascals-triangle/.docs/instructions.md new file mode 100644 index 00000000..0f58f006 --- /dev/null +++ b/exercises/practice/pascals-triangle/.docs/instructions.md @@ -0,0 +1,35 @@ +# Instructions + +Your task is to output the first N rows of Pascal's triangle. + +[Pascal's triangle][wikipedia] is a triangular array of positive integers. + +In Pascal's triangle, the number of values in a row is equal to its row number (which starts at one). +Therefore, the first row has one value, the second row has two values, and so on. + +The first (topmost) row has a single value: `1`. +Subsequent rows' values are computed by adding the numbers directly to the right and left of the current position in the previous row. + +If the previous row does _not_ have a value to the left or right of the current position (which only happens for the leftmost and rightmost positions), treat that position's value as zero (effectively "ignoring" it in the summation). + +## Example + +Let's look at the first 5 rows of Pascal's Triangle: + +```text + 1 + 1 1 + 1 2 1 + 1 3 3 1 +1 4 6 4 1 +``` + +The topmost row has one value, which is `1`. + +The leftmost and rightmost values have only one preceding position to consider, which is the position to its right respectively to its left. +With the topmost value being `1`, it follows from this that all the leftmost and rightmost values are also `1`. + +The other values all have two positions to consider. +For example, the fifth row's (`1 4 6 4 1`) middle value is `6`, as the values to its left and right in the preceding row are `3` and `3`: + +[wikipedia]: https://en.wikipedia.org/wiki/Pascal%27s_triangle diff --git a/exercises/practice/pascals-triangle/.docs/introduction.md b/exercises/practice/pascals-triangle/.docs/introduction.md new file mode 100644 index 00000000..eab454e5 --- /dev/null +++ b/exercises/practice/pascals-triangle/.docs/introduction.md @@ -0,0 +1,22 @@ +# Introduction + +With the weather being great, you're not looking forward to spending an hour in a classroom. +Annoyed, you enter the class room, where you notice a strangely satisfying triangle shape on the blackboard. +Whilst waiting for your math teacher to arrive, you can't help but notice some patterns in the triangle: the outer values are all ones, each subsequent row has one more value than its previous row and the triangle is symmetrical. +Weird! + +Not long after you sit down, your teacher enters the room and explains that this triangle is the famous [Pascal's triangle][wikipedia]. + +Over the next hour, your teacher reveals some amazing things hidden in this triangle: + +- It can be used to compute how many ways you can pick K elements from N values. +- It contains the Fibonacci sequence. +- If you color odd and even numbers differently, you get a beautiful pattern called the [Sierpiński triangle][wikipedia-sierpinski-triangle]. + +The teacher implores you and your classmates to look up other uses, and assures you that there are lots more! +At that moment, the school bell rings. +You realize that for the past hour, you were completely absorbed in learning about Pascal's triangle. +You quickly grab your laptop from your bag and go outside, ready to enjoy both the sunshine _and_ the wonders of Pascal's triangle. + +[wikipedia]: https://en.wikipedia.org/wiki/Pascal%27s_triangle +[wikipedia-sierpinski-triangle]: https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle diff --git a/exercises/practice/pascals-triangle/.meta/config.json b/exercises/practice/pascals-triangle/.meta/config.json new file mode 100644 index 00000000..36a5bd5b --- /dev/null +++ b/exercises/practice/pascals-triangle/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "pascals-triangle/pascals-triangle.factor" + ], + "test": [ + "pascals-triangle/pascals-triangle-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Compute Pascal's triangle up to a given number of rows.", + "source": "Pascal's Triangle at Wolfram Math World", + "source_url": "https://www.wolframalpha.com/input/?i=Pascal%27s+triangle" +} diff --git a/exercises/practice/pascals-triangle/.meta/example.factor b/exercises/practice/pascals-triangle/.meta/example.factor new file mode 100644 index 00000000..c130859a --- /dev/null +++ b/exercises/practice/pascals-triangle/.meta/example.factor @@ -0,0 +1,10 @@ +USING: arrays kernel math sequences ; +IN: pascals-triangle + +: next-row ( row -- next ) + dup 0 prefix swap 0 suffix [ + ] 2map ; + +: rows ( count -- triangle ) + dup 0 = [ drop { } ] [ + [ { 1 } 1array ] dip 1 - [ dup last next-row suffix ] times + ] if ; diff --git a/exercises/practice/pascals-triangle/.meta/generator.jl b/exercises/practice/pascals-triangle/.meta/generator.jl new file mode 100644 index 00000000..7f8028f2 --- /dev/null +++ b/exercises/practice/pascals-triangle/.meta/generator.jl @@ -0,0 +1,11 @@ +module PascalsTriangle + +function gen_test_case(case) + count = to_int_str(case["input"]["count"]) + expected = case["expected"] + rows = ["{ " * join(map(to_int_str, row), " ") * " }" for row in expected] + expected_str = "{ $(join(rows, " ")) }" + return "{ $(expected_str) }\n[ $(count) rows ] unit-test" +end + +end diff --git a/exercises/practice/pascals-triangle/.meta/tests.toml b/exercises/practice/pascals-triangle/.meta/tests.toml new file mode 100644 index 00000000..2db0ee52 --- /dev/null +++ b/exercises/practice/pascals-triangle/.meta/tests.toml @@ -0,0 +1,34 @@ +# 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. + +[9920ce55-9629-46d5-85d6-4201f4a4234d] +description = "zero rows" + +[70d643ce-a46d-4e93-af58-12d88dd01f21] +description = "single row" + +[a6e5a2a2-fc9a-4b47-9f4f-ed9ad9fbe4bd] +description = "two rows" + +[97206a99-79ba-4b04-b1c5-3c0fa1e16925] +description = "three rows" + +[565a0431-c797-417c-a2c8-2935e01ce306] +description = "four rows" + +[06f9ea50-9f51-4eb2-b9a9-c00975686c27] +description = "five rows" + +[c3912965-ddb4-46a9-848e-3363e6b00b13] +description = "six rows" + +[6cb26c66-7b57-4161-962c-81ec8c99f16b] +description = "ten rows" diff --git a/exercises/practice/pascals-triangle/pascals-triangle/pascals-triangle-tests.factor b/exercises/practice/pascals-triangle/pascals-triangle/pascals-triangle-tests.factor new file mode 100644 index 00000000..b238b990 --- /dev/null +++ b/exercises/practice/pascals-triangle/pascals-triangle/pascals-triangle-tests.factor @@ -0,0 +1,40 @@ +USING: io kernel lexer pascals-triangle tools.test unicode ; +IN: pascals-triangle.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Pascals Triangle:" print + +"zero rows" print +{ { } } +[ 0 rows ] unit-test + +STOP-HERE + +"single row" print +{ { { 1 } } } +[ 1 rows ] unit-test + +"two rows" print +{ { { 1 } { 1 1 } } } +[ 2 rows ] unit-test + +"three rows" print +{ { { 1 } { 1 1 } { 1 2 1 } } } +[ 3 rows ] unit-test + +"four rows" print +{ { { 1 } { 1 1 } { 1 2 1 } { 1 3 3 1 } } } +[ 4 rows ] unit-test + +"five rows" print +{ { { 1 } { 1 1 } { 1 2 1 } { 1 3 3 1 } { 1 4 6 4 1 } } } +[ 5 rows ] unit-test + +"six rows" print +{ { { 1 } { 1 1 } { 1 2 1 } { 1 3 3 1 } { 1 4 6 4 1 } { 1 5 10 10 5 1 } } } +[ 6 rows ] unit-test + +"ten rows" print +{ { { 1 } { 1 1 } { 1 2 1 } { 1 3 3 1 } { 1 4 6 4 1 } { 1 5 10 10 5 1 } { 1 6 15 20 15 6 1 } { 1 7 21 35 35 21 7 1 } { 1 8 28 56 70 56 28 8 1 } { 1 9 36 84 126 126 84 36 9 1 } } } +[ 10 rows ] unit-test diff --git a/exercises/practice/pascals-triangle/pascals-triangle/pascals-triangle.factor b/exercises/practice/pascals-triangle/pascals-triangle/pascals-triangle.factor new file mode 100644 index 00000000..afc1a5c0 --- /dev/null +++ b/exercises/practice/pascals-triangle/pascals-triangle/pascals-triangle.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: pascals-triangle + +: rows ( count -- triangle ) + "unimplemented" throw ; diff --git a/exercises/practice/phone-number/.docs/instructions.md b/exercises/practice/phone-number/.docs/instructions.md new file mode 100644 index 00000000..5d4d3739 --- /dev/null +++ b/exercises/practice/phone-number/.docs/instructions.md @@ -0,0 +1,34 @@ +# Instructions + +Clean up phone numbers so that they can be sent SMS messages. + +The **North American Numbering Plan (NANP)** is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda. +All NANP-countries share the same international country code: `1`. + +NANP numbers are ten-digit numbers consisting of a three-digit Numbering Plan Area code, commonly known as _area code_, followed by a seven-digit local number. +The first three digits of the local number represent the _exchange code_, followed by the unique four-digit number which is the _subscriber number_. + +The format is usually represented as + +```text +NXX NXX-XXXX +``` + +where `N` is any digit from 2 through 9 and `X` is any digit from 0 through 9. + +Sometimes they also have the country code (represented as `1` or `+1`) prefixed. + +Your task is to clean up differently formatted telephone numbers by removing punctuation and the country code if present. + +For example, the inputs + +- `+1 (613)-995-0253` +- `613-995-0253` +- `1 613 995 0253` +- `613.995.0253` + +should all produce the output + +`6139950253` + +**Note:** As this exercise only deals with telephone numbers used in NANP-countries, only 1 is considered a valid country code. diff --git a/exercises/practice/phone-number/.docs/introduction.md b/exercises/practice/phone-number/.docs/introduction.md new file mode 100644 index 00000000..c4142c5a --- /dev/null +++ b/exercises/practice/phone-number/.docs/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +You've joined LinkLine, a leading communications company working to ensure reliable connections for everyone. +The team faces a big challenge: users submit phone numbers in all sorts of formats — dashes, spaces, dots, parentheses, and even prefixes. +Some numbers are valid, while others are impossible to use. + +Your mission is to turn this chaos into order. +You'll clean up valid numbers, formatting them appropriately for use in the system. +At the same time, you'll identify and filter out any invalid entries. + +The success of LinkLine's operations depends on your ability to separate the useful from the unusable. +Are you ready to take on the challenge and keep the connections running smoothly? diff --git a/exercises/practice/phone-number/.meta/config.json b/exercises/practice/phone-number/.meta/config.json new file mode 100644 index 00000000..4af46002 --- /dev/null +++ b/exercises/practice/phone-number/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "phone-number/phone-number.factor" + ], + "test": [ + "phone-number/phone-number-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Clean up user-entered phone numbers so that they can be sent SMS messages.", + "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://www.turing.edu/" +} diff --git a/exercises/practice/phone-number/.meta/example.factor b/exercises/practice/phone-number/.meta/example.factor new file mode 100644 index 00000000..d8bb1ac4 --- /dev/null +++ b/exercises/practice/phone-number/.meta/example.factor @@ -0,0 +1,27 @@ +USING: combinators kernel math sequences strings unicode ; +IN: phone-number + +ERROR: invalid-phone-number ; + +: strip-punctuation ( phrase -- candidates ) + [ "()-. +" member? ] reject ; + +: validate-chars ( candidates -- candidates ) + dup [ digit? ] all? [ invalid-phone-number ] unless ; + +: strip-country-code ( digits -- digits' ) + { + { [ dup length 11 = ] [ unclip CHAR: 1 = [ invalid-phone-number ] unless ] } + { [ dup length 10 = ] [ ] } + [ invalid-phone-number ] + } cond ; + +: validate-area-exchange ( digits -- digits ) + dup [ first ] [ fourth ] bi + [ CHAR: 2 < ] either? [ invalid-phone-number ] when ; + +: clean ( phrase -- digits ) + strip-punctuation + validate-chars + strip-country-code + validate-area-exchange ; diff --git a/exercises/practice/phone-number/.meta/generator.jl b/exercises/practice/phone-number/.meta/generator.jl new file mode 100644 index 00000000..d4815a54 --- /dev/null +++ b/exercises/practice/phone-number/.meta/generator.jl @@ -0,0 +1,12 @@ +module PhoneNumber + +function gen_test_case(case) + phrase = escape_factor(case["input"]["phrase"]) + expected = case["expected"] + if expected isa AbstractDict && haskey(expected, "error") + return """[ "$(phrase)" clean ] [ invalid-phone-number? ] must-fail-with""" + end + return """{ "$(escape_factor(expected))" }\n[ "$(phrase)" clean ] unit-test""" +end + +end diff --git a/exercises/practice/phone-number/.meta/tests.toml b/exercises/practice/phone-number/.meta/tests.toml new file mode 100644 index 00000000..24dbf07a --- /dev/null +++ b/exercises/practice/phone-number/.meta/tests.toml @@ -0,0 +1,84 @@ +# 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. + +[79666dce-e0f1-46de-95a1-563802913c35] +description = "cleans the number" + +[c360451f-549f-43e4-8aba-fdf6cb0bf83f] +description = "cleans numbers with dots" + +[08f94c34-9a37-46a2-a123-2a8e9727395d] +description = "cleans numbers with multiple spaces" + +[598d8432-0659-4019-a78b-1c6a73691d21] +description = "invalid when 9 digits" +include = false + +[2de74156-f646-42b5-8638-0ef1d8b58bc2] +description = "invalid when 9 digits" +reimplements = "598d8432-0659-4019-a78b-1c6a73691d21" + +[57061c72-07b5-431f-9766-d97da7c4399d] +description = "invalid when 11 digits does not start with a 1" + +[9962cbf3-97bb-4118-ba9b-38ff49c64430] +description = "valid when 11 digits and starting with 1" + +[fa724fbf-054c-4d91-95da-f65ab5b6dbca] +description = "valid when 11 digits and starting with 1 even with punctuation" + +[c6a5f007-895a-4fc5-90bc-a7e70f9b5cad] +description = "invalid when more than 11 digits" +include = false + +[4a1509b7-8953-4eec-981b-c483358ff531] +description = "invalid when more than 11 digits" +reimplements = "c6a5f007-895a-4fc5-90bc-a7e70f9b5cad" + +[63f38f37-53f6-4a5f-bd86-e9b404f10a60] +description = "invalid with letters" +include = false + +[eb8a1fc0-64e5-46d3-b0c6-33184208e28a] +description = "invalid with letters" +reimplements = "63f38f37-53f6-4a5f-bd86-e9b404f10a60" + +[4bd97d90-52fd-45d3-b0db-06ab95b1244e] +description = "invalid with punctuations" +include = false + +[065f6363-8394-4759-b080-e6c8c351dd1f] +description = "invalid with punctuations" +reimplements = "4bd97d90-52fd-45d3-b0db-06ab95b1244e" + +[d77d07f8-873c-4b17-8978-5f66139bf7d7] +description = "invalid if area code starts with 0" + +[c7485cfb-1e7b-4081-8e96-8cdb3b77f15e] +description = "invalid if area code starts with 1" + +[4d622293-6976-413d-b8bf-dd8a94d4e2ac] +description = "invalid if exchange code starts with 0" + +[4cef57b4-7d8e-43aa-8328-1e1b89001262] +description = "invalid if exchange code starts with 1" + +[9925b09c-1a0d-4960-a197-5d163cbe308c] +description = "invalid if area code starts with 0 on valid 11-digit number" + +[3f809d37-40f3-44b5-ad90-535838b1a816] +description = "invalid if area code starts with 1 on valid 11-digit number" + +[e08e5532-d621-40d4-b0cc-96c159276b65] +description = "invalid if exchange code starts with 0 on valid 11-digit number" + +[57b32f3d-696a-455c-8bf1-137b6d171cdf] +description = "invalid if exchange code starts with 1 on valid 11-digit number" diff --git a/exercises/practice/phone-number/phone-number/phone-number-tests.factor b/exercises/practice/phone-number/phone-number/phone-number-tests.factor new file mode 100644 index 00000000..c167a0b2 --- /dev/null +++ b/exercises/practice/phone-number/phone-number/phone-number-tests.factor @@ -0,0 +1,67 @@ +USING: io kernel lexer phone-number tools.test unicode ; +IN: phone-number.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Phone Number:" print + +"cleans the number" print +{ "2234567890" } +[ "(223) 456-7890" clean ] unit-test + +STOP-HERE + +"cleans numbers with dots" print +{ "2234567890" } +[ "223.456.7890" clean ] unit-test + +"cleans numbers with multiple spaces" print +{ "2234567890" } +[ "223 456 7890 " clean ] unit-test + +"invalid when 9 digits" print +[ "123456789" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid when 11 digits does not start with a 1" print +[ "22234567890" clean ] [ invalid-phone-number? ] must-fail-with + +"valid when 11 digits and starting with 1" print +{ "2234567890" } +[ "12234567890" clean ] unit-test + +"valid when 11 digits and starting with 1 even with punctuation" print +{ "2234567890" } +[ "+1 (223) 456-7890" clean ] unit-test + +"invalid when more than 11 digits" print +[ "321234567890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid with letters" print +[ "523-abc-7890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid with punctuations" print +[ "523-@:!-7890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid if area code starts with 0" print +[ "(023) 456-7890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid if area code starts with 1" print +[ "(123) 456-7890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid if exchange code starts with 0" print +[ "(223) 056-7890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid if exchange code starts with 1" print +[ "(223) 156-7890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid if area code starts with 0 on valid 11-digit number" print +[ "1 (023) 456-7890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid if area code starts with 1 on valid 11-digit number" print +[ "1 (123) 456-7890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid if exchange code starts with 0 on valid 11-digit number" print +[ "1 (223) 056-7890" clean ] [ invalid-phone-number? ] must-fail-with + +"invalid if exchange code starts with 1 on valid 11-digit number" print +[ "1 (223) 156-7890" clean ] [ invalid-phone-number? ] must-fail-with diff --git a/exercises/practice/phone-number/phone-number/phone-number.factor b/exercises/practice/phone-number/phone-number/phone-number.factor new file mode 100644 index 00000000..cebe007f --- /dev/null +++ b/exercises/practice/phone-number/phone-number/phone-number.factor @@ -0,0 +1,7 @@ +USING: kernel ; +IN: phone-number + +ERROR: invalid-phone-number ; + +: clean ( phrase -- digits ) + "unimplemented" throw ; diff --git a/exercises/practice/robot-simulator/.meta/example.factor b/exercises/practice/robot-simulator/.meta/example.factor index 6e0e3530..a425f336 100644 --- a/exercises/practice/robot-simulator/.meta/example.factor +++ b/exercises/practice/robot-simulator/.meta/example.factor @@ -1,26 +1,42 @@ -USING: arrays combinators kernel locals math sequences ; +USING: accessors combinators kernel math sequences ; IN: robot-simulator -: ( x y dir -- robot ) 3array ; +SYMBOLS: north east south west ; -: turn ( robot offset -- robot ) - [ dup third { "north" "east" "south" "west" } index ] dip - + 4 + 4 mod { "north" "east" "south" "west" } nth - [ but-last ] dip suffix ; +TUPLE: robot x y direction ; -:: advance ( robot -- robot ) - robot first3 :> ( x y dir ) - dir { - { "north" [ x y 1 + ] } - { "east" [ x 1 + y ] } - { "south" [ x y 1 - ] } - { "west" [ x 1 - y ] } - } case dir ; +: ( x y direction -- robot ) robot boa ; + +: turn-right ( direction -- direction' ) + { + { north [ east ] } + { east [ south ] } + { south [ west ] } + { west [ north ] } + } case ; + +: turn-left ( direction -- direction' ) + { + { north [ west ] } + { east [ north ] } + { south [ east ] } + { west [ south ] } + } case ; + +: advance ( robot -- robot ) + dup direction>> { + { north [ [ 1 + ] change-y ] } + { east [ [ 1 + ] change-x ] } + { south [ [ 1 - ] change-y ] } + { west [ [ 1 - ] change-x ] } + } case ; : step ( robot char -- robot ) - { { CHAR: R [ 1 turn ] } - { CHAR: L [ -1 turn ] } - { CHAR: A [ advance ] } } case ; + { + { CHAR: R [ [ turn-right ] change-direction ] } + { CHAR: L [ [ turn-left ] change-direction ] } + { CHAR: A [ advance ] } + } case ; : move ( robot instructions -- robot ) [ step ] each ; diff --git a/exercises/practice/robot-simulator/.meta/generator.jl b/exercises/practice/robot-simulator/.meta/generator.jl index 08389919..ed61a08c 100644 --- a/exercises/practice/robot-simulator/.meta/generator.jl +++ b/exercises/practice/robot-simulator/.meta/generator.jl @@ -4,7 +4,7 @@ function format_robot(obj) x = Int(obj["position"]["x"]) y = Int(obj["position"]["y"]) dir = obj["direction"] - return """{ $(x) $(y) "$(dir)" }""" + return "T{ robot { x $(x) } { y $(y) } { direction $(dir) } }" end function gen_test_case(case) @@ -13,10 +13,10 @@ function gen_test_case(case) y = Int(case["input"]["position"]["y"]) dir = case["input"]["direction"] if case["property"] == "create" - return """{ $(expected) }\n[ $(x) $(y) "$(dir)" ] unit-test""" + return "{ $(expected) }\n[ $(x) $(y) $(dir) ] unit-test" else instructions = case["input"]["instructions"] - return """{ $(expected) }\n[ $(x) $(y) "$(dir)" "$(instructions)" move ] unit-test""" + return """{ $(expected) }\n[ $(x) $(y) $(dir) "$(instructions)" move ] unit-test""" end end diff --git a/exercises/practice/robot-simulator/robot-simulator/robot-simulator-tests.factor b/exercises/practice/robot-simulator/robot-simulator/robot-simulator-tests.factor index f6af4dc6..d06112ca 100644 --- a/exercises/practice/robot-simulator/robot-simulator/robot-simulator-tests.factor +++ b/exercises/practice/robot-simulator/robot-simulator/robot-simulator-tests.factor @@ -6,75 +6,75 @@ IN: robot-simulator.tests "Robot Simulator:" print "at origin facing north" print -{ { 0 0 "north" } } -[ 0 0 "north" ] unit-test +{ T{ robot { x 0 } { y 0 } { direction north } } } +[ 0 0 north ] unit-test STOP-HERE "at negative position facing south" print -{ { -1 -1 "south" } } -[ -1 -1 "south" ] unit-test +{ T{ robot { x -1 } { y -1 } { direction south } } } +[ -1 -1 south ] unit-test "changes north to east" print -{ { 0 0 "east" } } -[ 0 0 "north" "R" move ] unit-test +{ T{ robot { x 0 } { y 0 } { direction east } } } +[ 0 0 north "R" move ] unit-test "changes east to south" print -{ { 0 0 "south" } } -[ 0 0 "east" "R" move ] unit-test +{ T{ robot { x 0 } { y 0 } { direction south } } } +[ 0 0 east "R" move ] unit-test "changes south to west" print -{ { 0 0 "west" } } -[ 0 0 "south" "R" move ] unit-test +{ T{ robot { x 0 } { y 0 } { direction west } } } +[ 0 0 south "R" move ] unit-test "changes west to north" print -{ { 0 0 "north" } } -[ 0 0 "west" "R" move ] unit-test +{ T{ robot { x 0 } { y 0 } { direction north } } } +[ 0 0 west "R" move ] unit-test "changes north to west" print -{ { 0 0 "west" } } -[ 0 0 "north" "L" move ] unit-test +{ T{ robot { x 0 } { y 0 } { direction west } } } +[ 0 0 north "L" move ] unit-test "changes west to south" print -{ { 0 0 "south" } } -[ 0 0 "west" "L" move ] unit-test +{ T{ robot { x 0 } { y 0 } { direction south } } } +[ 0 0 west "L" move ] unit-test "changes south to east" print -{ { 0 0 "east" } } -[ 0 0 "south" "L" move ] unit-test +{ T{ robot { x 0 } { y 0 } { direction east } } } +[ 0 0 south "L" move ] unit-test "changes east to north" print -{ { 0 0 "north" } } -[ 0 0 "east" "L" move ] unit-test +{ T{ robot { x 0 } { y 0 } { direction north } } } +[ 0 0 east "L" move ] unit-test "facing north increments Y" print -{ { 0 1 "north" } } -[ 0 0 "north" "A" move ] unit-test +{ T{ robot { x 0 } { y 1 } { direction north } } } +[ 0 0 north "A" move ] unit-test "facing south decrements Y" print -{ { 0 -1 "south" } } -[ 0 0 "south" "A" move ] unit-test +{ T{ robot { x 0 } { y -1 } { direction south } } } +[ 0 0 south "A" move ] unit-test "facing east increments X" print -{ { 1 0 "east" } } -[ 0 0 "east" "A" move ] unit-test +{ T{ robot { x 1 } { y 0 } { direction east } } } +[ 0 0 east "A" move ] unit-test "facing west decrements X" print -{ { -1 0 "west" } } -[ 0 0 "west" "A" move ] unit-test +{ T{ robot { x -1 } { y 0 } { direction west } } } +[ 0 0 west "A" move ] unit-test "moving east and north from README" print -{ { 9 4 "west" } } -[ 7 3 "north" "RAALAL" move ] unit-test +{ T{ robot { x 9 } { y 4 } { direction west } } } +[ 7 3 north "RAALAL" move ] unit-test "moving west and north" print -{ { -4 1 "west" } } -[ 0 0 "north" "LAAARALA" move ] unit-test +{ T{ robot { x -4 } { y 1 } { direction west } } } +[ 0 0 north "LAAARALA" move ] unit-test "moving west and south" print -{ { -3 -8 "south" } } -[ 2 -7 "east" "RRAAAAALA" move ] unit-test +{ T{ robot { x -3 } { y -8 } { direction south } } } +[ 2 -7 east "RRAAAAALA" move ] unit-test "moving east and north" print -{ { 11 5 "north" } } -[ 8 4 "south" "LAAARRRALLLL" move ] unit-test +{ T{ robot { x 11 } { y 5 } { direction north } } } +[ 8 4 south "LAAARRRALLLL" move ] unit-test diff --git a/exercises/practice/robot-simulator/robot-simulator/robot-simulator.factor b/exercises/practice/robot-simulator/robot-simulator/robot-simulator.factor index 87cf640c..dd569364 100644 --- a/exercises/practice/robot-simulator/robot-simulator/robot-simulator.factor +++ b/exercises/practice/robot-simulator/robot-simulator/robot-simulator.factor @@ -1,7 +1,10 @@ USING: kernel ; IN: robot-simulator -: ( x y dir -- robot ) +! Declare a symbol per direction (north, east, south, west) and a +! `robot` tuple so the tests can refer to them by name. + +: ( x y direction -- robot ) "unimplemented" throw ; : move ( robot instructions -- robot ) diff --git a/exercises/practice/satellite/.docs/instructions.md b/exercises/practice/satellite/.docs/instructions.md new file mode 100644 index 00000000..fbbf14f4 --- /dev/null +++ b/exercises/practice/satellite/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Imagine you need to transmit a binary tree to a satellite approaching Alpha Centauri and you have limited bandwidth. +Since the tree has no repeating items it can be uniquely represented by its [pre-order and in-order traversals][wiki]. + +Write the software for the satellite to rebuild the tree from the traversals. + +A pre-order traversal reads the value of the current node before (hence "pre") reading the left subtree in pre-order. +Afterwards the right subtree is read in pre-order. + +An in-order traversal reads the left subtree in-order then the current node and finally the right subtree in-order. +So in order from left to right. + +For example the pre-order traversal of this tree is [a, i, x, f, r]. +The in-order traversal of this tree is [i, a, f, x, r] + +```text + a + / \ +i x + / \ + f r +``` + +Note: the first item in the pre-order traversal is always the root. + +[wiki]: https://en.wikipedia.org/wiki/Tree_traversal diff --git a/exercises/practice/satellite/.meta/config.json b/exercises/practice/satellite/.meta/config.json new file mode 100644 index 00000000..1b21835b --- /dev/null +++ b/exercises/practice/satellite/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "satellite/satellite.factor" + ], + "test": [ + "satellite/satellite-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Rebuild binary trees from pre-order and in-order traversals." +} diff --git a/exercises/practice/satellite/.meta/example.factor b/exercises/practice/satellite/.meta/example.factor new file mode 100644 index 00000000..e0ceca63 --- /dev/null +++ b/exercises/practice/satellite/.meta/example.factor @@ -0,0 +1,25 @@ +USING: kernel locals math sequences sets sorting ; +IN: satellite + +TUPLE: tree value left right ; + +ERROR: invalid-traversals ; + +:: build ( preorder inorder -- tree ) + preorder empty? [ f ] [ + preorder first :> root + inorder [ root = ] find drop :> idx + preorder rest idx head + inorder idx head + build :> left + preorder rest idx tail + inorder idx 1 + tail + build :> right + root left right tree boa + ] if ; + +:: tree-from-traversals ( preorder inorder -- tree ) + preorder length inorder length = [ invalid-traversals ] unless + preorder all-unique? [ invalid-traversals ] unless + preorder natural-sort inorder natural-sort = [ invalid-traversals ] unless + preorder inorder build ; diff --git a/exercises/practice/satellite/.meta/generator.jl b/exercises/practice/satellite/.meta/generator.jl new file mode 100644 index 00000000..ca1cb963 --- /dev/null +++ b/exercises/practice/satellite/.meta/generator.jl @@ -0,0 +1,24 @@ +module Satellite + +function format_tree(node) + if isempty(node) + return "f" + end + v = escape_factor(node["v"]) + l = format_tree(node["l"]) + r = format_tree(node["r"]) + return """T{ tree { value "$(v)" } { left $(l) } { right $(r) } }""" +end + +function gen_test_case(case) + preorder = format_string_array(case["input"]["preorder"]) + inorder = format_string_array(case["input"]["inorder"]) + expected = case["expected"] + if expected isa AbstractDict && haskey(expected, "error") + return """[ $(preorder) $(inorder) tree-from-traversals ] [ invalid-traversals? ] must-fail-with""" + end + expected_str = format_tree(expected) + return "{ $(expected_str) }\n[ $(preorder) $(inorder) tree-from-traversals ] unit-test" +end + +end diff --git a/exercises/practice/satellite/.meta/tests.toml b/exercises/practice/satellite/.meta/tests.toml new file mode 100644 index 00000000..d0ed5b6a --- /dev/null +++ b/exercises/practice/satellite/.meta/tests.toml @@ -0,0 +1,37 @@ +# 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. + +[8df3fa26-811a-4165-9286-ff9ac0850d19] +description = "Empty tree" + +[f945ccfc-05e3-47d7-825b-0270559d43ad] +description = "Tree with one item" + +[a0121d5f-37b0-48dd-9c64-cba4c4464135] +description = "Tree with many items" + +[6074041f-4891-4d81-a128-401050c2a3b0] +description = "Reject traversals of different length" + +[27916ce4-45f3-4d8b-8528-496fedc157ca] +description = "Reject inconsistent traversals of same length" + +[d86a3d72-76a9-43b5-9d3a-e64cb1216035] +description = "Reject traversals with repeated items" + +[af31ae02-7e5b-4452-a990-bccb3fca9148] +description = "A degenerate binary tree" + +[ee54463d-a719-4aae-ade4-190d30ce7320] +description = "Another degenerate binary tree" + +[87123c08-c155-4486-90a4-e2f75b0f3e8f] +description = "Tree with many more items" diff --git a/exercises/practice/satellite/satellite/satellite-tests.factor b/exercises/practice/satellite/satellite/satellite-tests.factor new file mode 100644 index 00000000..981ee414 --- /dev/null +++ b/exercises/practice/satellite/satellite/satellite-tests.factor @@ -0,0 +1,41 @@ +USING: io kernel lexer satellite tools.test unicode ; +IN: satellite.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Satellite:" print + +"Empty tree" print +{ f } +[ { } { } tree-from-traversals ] unit-test + +STOP-HERE + +"Tree with one item" print +{ T{ tree { value "a" } { left f } { right f } } } +[ { "a" } { "a" } tree-from-traversals ] unit-test + +"Tree with many items" print +{ T{ tree { value "a" } { left T{ tree { value "i" } { left f } { right f } } } { right T{ tree { value "x" } { left T{ tree { value "f" } { left f } { right f } } } { right T{ tree { value "r" } { left f } { right f } } } } } } } +[ { "a" "i" "x" "f" "r" } { "i" "a" "f" "x" "r" } tree-from-traversals ] unit-test + +"Reject traversals of different length" print +[ { "a" "b" } { "b" "a" "r" } tree-from-traversals ] [ invalid-traversals? ] must-fail-with + +"Reject inconsistent traversals of same length" print +[ { "x" "y" "z" } { "a" "b" "c" } tree-from-traversals ] [ invalid-traversals? ] must-fail-with + +"Reject traversals with repeated items" print +[ { "a" "b" "a" } { "b" "a" "a" } tree-from-traversals ] [ invalid-traversals? ] must-fail-with + +"A degenerate binary tree" print +{ T{ tree { value "a" } { left T{ tree { value "b" } { left T{ tree { value "c" } { left T{ tree { value "d" } { left f } { right f } } } { right f } } } { right f } } } { right f } } } +[ { "a" "b" "c" "d" } { "d" "c" "b" "a" } tree-from-traversals ] unit-test + +"Another degenerate binary tree" print +{ T{ tree { value "a" } { left f } { right T{ tree { value "b" } { left f } { right T{ tree { value "c" } { left f } { right T{ tree { value "d" } { left f } { right f } } } } } } } } } +[ { "a" "b" "c" "d" } { "a" "b" "c" "d" } tree-from-traversals ] unit-test + +"Tree with many more items" print +{ T{ tree { value "a" } { left T{ tree { value "b" } { left T{ tree { value "d" } { left T{ tree { value "g" } { left f } { right f } } } { right T{ tree { value "h" } { left f } { right f } } } } } { right f } } } { right T{ tree { value "c" } { left T{ tree { value "e" } { left f } { right f } } } { right T{ tree { value "f" } { left T{ tree { value "i" } { left f } { right f } } } { right f } } } } } } } +[ { "a" "b" "d" "g" "h" "c" "e" "f" "i" } { "g" "d" "h" "b" "a" "e" "c" "i" "f" } tree-from-traversals ] unit-test diff --git a/exercises/practice/satellite/satellite/satellite.factor b/exercises/practice/satellite/satellite/satellite.factor new file mode 100644 index 00000000..bfbfd134 --- /dev/null +++ b/exercises/practice/satellite/satellite/satellite.factor @@ -0,0 +1,9 @@ +USING: kernel ; +IN: satellite + +TUPLE: tree value left right ; + +ERROR: invalid-traversals ; + +: tree-from-traversals ( preorder inorder -- tree ) + "unimplemented" throw ; diff --git a/exercises/practice/scrabble-score/.docs/instructions.md b/exercises/practice/scrabble-score/.docs/instructions.md new file mode 100644 index 00000000..738f928c --- /dev/null +++ b/exercises/practice/scrabble-score/.docs/instructions.md @@ -0,0 +1,25 @@ +# Instructions + +Your task is to compute a word's Scrabble score by summing the values of its letters. + +The letters are valued as follows: + +| Letter | Value | +| ---------------------------- | ----- | +| A, E, I, O, U, L, N, R, S, T | 1 | +| D, G | 2 | +| B, C, M, P | 3 | +| F, H, V, W, Y | 4 | +| K | 5 | +| J, X | 8 | +| Q, Z | 10 | + +For example, the word "cabbage" is worth 14 points: + +- 3 points for C +- 1 point for A +- 3 points for B +- 3 points for B +- 1 point for A +- 2 points for G +- 1 point for E diff --git a/exercises/practice/scrabble-score/.docs/introduction.md b/exercises/practice/scrabble-score/.docs/introduction.md new file mode 100644 index 00000000..8821f240 --- /dev/null +++ b/exercises/practice/scrabble-score/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +[Scrabble][wikipedia] is a word game where players place letter tiles on a board to form words. +Each letter has a value. +A word's score is the sum of its letters' values. + +[wikipedia]: https://en.wikipedia.org/wiki/Scrabble diff --git a/exercises/practice/scrabble-score/.meta/config.json b/exercises/practice/scrabble-score/.meta/config.json new file mode 100644 index 00000000..00cbadcb --- /dev/null +++ b/exercises/practice/scrabble-score/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "scrabble-score/scrabble-score.factor" + ], + "test": [ + "scrabble-score/scrabble-score-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a word, compute the Scrabble score for that word.", + "source": "Inspired by the Extreme Startup game", + "source_url": "https://github.com/rchatley/extreme_startup" +} diff --git a/exercises/practice/scrabble-score/.meta/example.factor b/exercises/practice/scrabble-score/.meta/example.factor new file mode 100644 index 00000000..609b43f6 --- /dev/null +++ b/exercises/practice/scrabble-score/.meta/example.factor @@ -0,0 +1,15 @@ +USING: assocs kernel math.statistics sequences unicode ; +IN: scrabble-score + +CONSTANT: letter-scores H{ + { CHAR: A 1 } { CHAR: B 3 } { CHAR: C 3 } { CHAR: D 2 } + { CHAR: E 1 } { CHAR: F 4 } { CHAR: G 2 } { CHAR: H 4 } + { CHAR: I 1 } { CHAR: J 8 } { CHAR: K 5 } { CHAR: L 1 } + { CHAR: M 3 } { CHAR: N 1 } { CHAR: O 1 } { CHAR: P 3 } + { CHAR: Q 10 } { CHAR: R 1 } { CHAR: S 1 } { CHAR: T 1 } + { CHAR: U 1 } { CHAR: V 4 } { CHAR: W 4 } { CHAR: X 8 } + { CHAR: Y 4 } { CHAR: Z 10 } +} + +: score ( word -- n ) + >upper [ letter-scores at ] map-sum ; diff --git a/exercises/practice/scrabble-score/.meta/generator.jl b/exercises/practice/scrabble-score/.meta/generator.jl new file mode 100644 index 00000000..3ca818d7 --- /dev/null +++ b/exercises/practice/scrabble-score/.meta/generator.jl @@ -0,0 +1,9 @@ +module ScrabbleScore + +function gen_test_case(case) + word = escape_factor(case["input"]["word"]) + expected = to_int_str(case["expected"]) + return """{ $(expected) }\n[ "$(word)" score ] unit-test""" +end + +end diff --git a/exercises/practice/scrabble-score/.meta/tests.toml b/exercises/practice/scrabble-score/.meta/tests.toml new file mode 100644 index 00000000..33a873c0 --- /dev/null +++ b/exercises/practice/scrabble-score/.meta/tests.toml @@ -0,0 +1,43 @@ +# 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. + +[f46cda29-1ca5-4ef2-bd45-388a767e3db2] +description = "lowercase letter" + +[f7794b49-f13e-45d1-a933-4e48459b2201] +description = "uppercase letter" + +[eaba9c76-f9fa-49c9-a1b0-d1ba3a5b31fa] +description = "valuable letter" + +[f3c8c94e-bb48-4da2-b09f-e832e103151e] +description = "short word" + +[71e3d8fa-900d-4548-930e-68e7067c4615] +description = "short, valuable word" + +[d3088ad9-570c-4b51-8764-c75d5a430e99] +description = "medium word" + +[fa20c572-ad86-400a-8511-64512daac352] +description = "medium, valuable word" + +[9336f0ba-9c2b-4fa0-bd1c-2e2d328cf967] +description = "long, mixed-case word" + +[1e34e2c3-e444-4ea7-b598-3c2b46fd2c10] +description = "english-like word" + +[4efe3169-b3b6-4334-8bae-ff4ef24a7e4f] +description = "empty input" + +[3b305c1c-f260-4e15-a5b5-cb7d3ea7c3d7] +description = "entire alphabet available" diff --git a/exercises/practice/scrabble-score/scrabble-score/scrabble-score-tests.factor b/exercises/practice/scrabble-score/scrabble-score/scrabble-score-tests.factor new file mode 100644 index 00000000..2b489b97 --- /dev/null +++ b/exercises/practice/scrabble-score/scrabble-score/scrabble-score-tests.factor @@ -0,0 +1,52 @@ +USING: io kernel lexer scrabble-score tools.test unicode ; +IN: scrabble-score.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Scrabble Score:" print + +"lowercase letter" print +{ 1 } +[ "a" score ] unit-test + +STOP-HERE + +"uppercase letter" print +{ 1 } +[ "A" score ] unit-test + +"valuable letter" print +{ 4 } +[ "f" score ] unit-test + +"short word" print +{ 2 } +[ "at" score ] unit-test + +"short, valuable word" print +{ 12 } +[ "zoo" score ] unit-test + +"medium word" print +{ 6 } +[ "street" score ] unit-test + +"medium, valuable word" print +{ 22 } +[ "quirky" score ] unit-test + +"long, mixed-case word" print +{ 41 } +[ "OxyphenButazone" score ] unit-test + +"english-like word" print +{ 8 } +[ "pinata" score ] unit-test + +"empty input" print +{ 0 } +[ "" score ] unit-test + +"entire alphabet available" print +{ 87 } +[ "abcdefghijklmnopqrstuvwxyz" score ] unit-test diff --git a/exercises/practice/scrabble-score/scrabble-score/scrabble-score.factor b/exercises/practice/scrabble-score/scrabble-score/scrabble-score.factor new file mode 100644 index 00000000..bd674cbb --- /dev/null +++ b/exercises/practice/scrabble-score/scrabble-score/scrabble-score.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: scrabble-score + +: score ( word -- n ) + "unimplemented" throw ; diff --git a/exercises/practice/space-age/.meta/example.factor b/exercises/practice/space-age/.meta/example.factor index f1a5c30d..54c836e2 100644 --- a/exercises/practice/space-age/.meta/example.factor +++ b/exercises/practice/space-age/.meta/example.factor @@ -1,13 +1,21 @@ -USING: kernel math ; +USING: combinators kernel math ; IN: space-age -: earth-year ( -- seconds ) 31557600.0 ; +SYMBOLS: mercury venus earth mars jupiter saturn uranus neptune ; -: on-earth ( seconds -- years ) earth-year / ; -: on-mercury ( seconds -- years ) on-earth 0.2408467 / ; -: on-venus ( seconds -- years ) on-earth 0.61519726 / ; -: on-mars ( seconds -- years ) on-earth 1.8808158 / ; -: on-jupiter ( seconds -- years ) on-earth 11.862615 / ; -: on-saturn ( seconds -- years ) on-earth 29.447498 / ; -: on-uranus ( seconds -- years ) on-earth 84.016846 / ; -: on-neptune ( seconds -- years ) on-earth 164.79132 / ; +CONSTANT: earth-year-seconds 31557600.0 + +: orbital-period ( planet -- period ) + { + { mercury [ 0.2408467 ] } + { venus [ 0.61519726 ] } + { earth [ 1 ] } + { mars [ 1.8808158 ] } + { jupiter [ 11.862615 ] } + { saturn [ 29.447498 ] } + { uranus [ 84.016846 ] } + { neptune [ 164.79132 ] } + } case ; + +: on-planet ( seconds planet -- years ) + orbital-period earth-year-seconds * / ; diff --git a/exercises/practice/space-age/.meta/generator.jl b/exercises/practice/space-age/.meta/generator.jl index 078b3d62..2fd220a4 100644 --- a/exercises/practice/space-age/.meta/generator.jl +++ b/exercises/practice/space-age/.meta/generator.jl @@ -1,22 +1,10 @@ module SpaceAge -const PLANETS = Dict( - "Earth" => "on-earth", - "Mercury" => "on-mercury", - "Venus" => "on-venus", - "Mars" => "on-mars", - "Jupiter" => "on-jupiter", - "Saturn" => "on-saturn", - "Uranus" => "on-uranus", - "Neptune" => "on-neptune", -) - function gen_test_case(case) - planet = case["input"]["planet"] + planet = lowercase(case["input"]["planet"]) seconds = case["input"]["seconds"] expected = case["expected"] - word = PLANETS[planet] - return "{ $(expected) 0.005 } [ $(seconds) $(word) ] unit-test~" + return "{ $(expected) 0.005 } [ $(seconds) $(planet) on-planet ] unit-test~" end end diff --git a/exercises/practice/space-age/space-age/space-age-tests.factor b/exercises/practice/space-age/space-age/space-age-tests.factor index 0f89a5e2..8d6262ff 100644 --- a/exercises/practice/space-age/space-age/space-age-tests.factor +++ b/exercises/practice/space-age/space-age/space-age-tests.factor @@ -6,27 +6,27 @@ IN: space-age.tests "Space Age:" print "age on Earth" print -{ 31.69 0.005 } [ 1000000000 on-earth ] unit-test~ +{ 31.69 0.005 } [ 1000000000 earth on-planet ] unit-test~ STOP-HERE "age on Mercury" print -{ 280.88 0.005 } [ 2134835688 on-mercury ] unit-test~ +{ 280.88 0.005 } [ 2134835688 mercury on-planet ] unit-test~ "age on Venus" print -{ 9.78 0.005 } [ 189839836 on-venus ] unit-test~ +{ 9.78 0.005 } [ 189839836 venus on-planet ] unit-test~ "age on Mars" print -{ 35.88 0.005 } [ 2129871239 on-mars ] unit-test~ +{ 35.88 0.005 } [ 2129871239 mars on-planet ] unit-test~ "age on Jupiter" print -{ 2.41 0.005 } [ 901876382 on-jupiter ] unit-test~ +{ 2.41 0.005 } [ 901876382 jupiter on-planet ] unit-test~ "age on Saturn" print -{ 2.15 0.005 } [ 2000000000 on-saturn ] unit-test~ +{ 2.15 0.005 } [ 2000000000 saturn on-planet ] unit-test~ "age on Uranus" print -{ 0.46 0.005 } [ 1210123456 on-uranus ] unit-test~ +{ 0.46 0.005 } [ 1210123456 uranus on-planet ] unit-test~ "age on Neptune" print -{ 0.35 0.005 } [ 1821023456 on-neptune ] unit-test~ +{ 0.35 0.005 } [ 1821023456 neptune on-planet ] unit-test~ diff --git a/exercises/practice/space-age/space-age/space-age.factor b/exercises/practice/space-age/space-age/space-age.factor index d9b42101..491965cb 100644 --- a/exercises/practice/space-age/space-age/space-age.factor +++ b/exercises/practice/space-age/space-age/space-age.factor @@ -1,26 +1,8 @@ USING: kernel ; IN: space-age -: on-earth ( seconds -- years ) - "unimplemented" throw ; - -: on-mercury ( seconds -- years ) - "unimplemented" throw ; - -: on-venus ( seconds -- years ) - "unimplemented" throw ; - -: on-mars ( seconds -- years ) - "unimplemented" throw ; - -: on-jupiter ( seconds -- years ) - "unimplemented" throw ; - -: on-saturn ( seconds -- years ) - "unimplemented" throw ; - -: on-uranus ( seconds -- years ) - "unimplemented" throw ; +! Declare a symbol per planet (mercury, venus, earth, mars, jupiter, +! saturn, uranus, neptune) so the tests can refer to them by name. -: on-neptune ( seconds -- years ) +: on-planet ( seconds planet -- years ) "unimplemented" throw ; diff --git a/exercises/practice/triangle/.meta/example.factor b/exercises/practice/triangle/.meta/example.factor index 614c064c..17dd8735 100644 --- a/exercises/practice/triangle/.meta/example.factor +++ b/exercises/practice/triangle/.meta/example.factor @@ -1,17 +1,22 @@ -USING: arrays grouping kernel math sequences sets sorting ; +USING: accessors arrays combinators kernel math sequences sets sorting ; IN: triangle -: sides ( a b c -- arr ) 3array ; +TUPLE: triangle a b c ; -: valid? ( arr -- ? ) - dup [ 0 > ] all? +: ( a b c -- triangle ) triangle boa ; + +: sides ( triangle -- arr ) + [ a>> ] [ b>> ] [ c>> ] tri 3array ; + +: valid? ( triangle -- ? ) + sides dup [ 0 > ] all? [ sort first3 [ + ] dip > ] [ drop f ] if ; -: equilateral? ( a b c -- ? ) - sides dup valid? [ all-equal? ] [ drop f ] if ; +: equilateral? ( triangle -- ? ) + dup valid? [ sides all-equal? ] [ drop f ] if ; -: isosceles? ( a b c -- ? ) - sides dup valid? [ duplicates length 0 > ] [ drop f ] if ; +: isosceles? ( triangle -- ? ) + dup valid? [ sides duplicates length 0 > ] [ drop f ] if ; -: scalene? ( a b c -- ? ) - sides dup valid? [ all-unique? ] [ drop f ] if ; +: scalene? ( triangle -- ? ) + dup valid? [ sides all-unique? ] [ drop f ] if ; diff --git a/exercises/practice/triangle/.meta/generator.jl b/exercises/practice/triangle/.meta/generator.jl index f7473354..8acf63c4 100644 --- a/exercises/practice/triangle/.meta/generator.jl +++ b/exercises/practice/triangle/.meta/generator.jl @@ -11,7 +11,7 @@ function gen_test_case(case) sides = format_sides(case["input"]["sides"]) prop = case["property"] expected = case["expected"] ? "t" : "f" - return "{ $(expected) } [ $(sides) $(prop)? ] unit-test" + return "{ $(expected) } [ $(sides) $(prop)? ] unit-test" end end diff --git a/exercises/practice/triangle/triangle/triangle-tests.factor b/exercises/practice/triangle/triangle/triangle-tests.factor index deb5ff10..58178b4a 100644 --- a/exercises/practice/triangle/triangle/triangle-tests.factor +++ b/exercises/practice/triangle/triangle/triangle-tests.factor @@ -6,66 +6,66 @@ IN: triangle.tests "Triangle:" print "all sides are equal" print -{ t } [ 2 2 2 equilateral? ] unit-test +{ t } [ 2 2 2 equilateral? ] unit-test STOP-HERE "any side is unequal" print -{ f } [ 2 3 2 equilateral? ] unit-test +{ f } [ 2 3 2 equilateral? ] unit-test "no sides are equal" print -{ f } [ 5 4 6 equilateral? ] unit-test +{ f } [ 5 4 6 equilateral? ] unit-test "all zero sides is not a triangle" print -{ f } [ 0 0 0 equilateral? ] unit-test +{ f } [ 0 0 0 equilateral? ] unit-test "sides may be floats" print -{ t } [ 0.5 0.5 0.5 equilateral? ] unit-test +{ t } [ 0.5 0.5 0.5 equilateral? ] unit-test "last two sides are equal" print -{ t } [ 3 4 4 isosceles? ] unit-test +{ t } [ 3 4 4 isosceles? ] unit-test "first two sides are equal" print -{ t } [ 4 4 3 isosceles? ] unit-test +{ t } [ 4 4 3 isosceles? ] unit-test "first and last sides are equal" print -{ t } [ 4 3 4 isosceles? ] unit-test +{ t } [ 4 3 4 isosceles? ] unit-test "equilateral triangles are also isosceles" print -{ t } [ 4 4 4 isosceles? ] unit-test +{ t } [ 4 4 4 isosceles? ] unit-test "no sides are equal" print -{ f } [ 2 3 4 isosceles? ] unit-test +{ f } [ 2 3 4 isosceles? ] unit-test "first triangle inequality violation" print -{ f } [ 1 1 3 isosceles? ] unit-test +{ f } [ 1 1 3 isosceles? ] unit-test "second triangle inequality violation" print -{ f } [ 1 3 1 isosceles? ] unit-test +{ f } [ 1 3 1 isosceles? ] unit-test "third triangle inequality violation" print -{ f } [ 3 1 1 isosceles? ] unit-test +{ f } [ 3 1 1 isosceles? ] unit-test "sides may be floats" print -{ t } [ 0.5 0.4 0.5 isosceles? ] unit-test +{ t } [ 0.5 0.4 0.5 isosceles? ] unit-test "no sides are equal" print -{ t } [ 5 4 6 scalene? ] unit-test +{ t } [ 5 4 6 scalene? ] unit-test "all sides are equal" print -{ f } [ 4 4 4 scalene? ] unit-test +{ f } [ 4 4 4 scalene? ] unit-test "first and second sides are equal" print -{ f } [ 4 4 3 scalene? ] unit-test +{ f } [ 4 4 3 scalene? ] unit-test "first and third sides are equal" print -{ f } [ 3 4 3 scalene? ] unit-test +{ f } [ 3 4 3 scalene? ] unit-test "second and third sides are equal" print -{ f } [ 4 3 3 scalene? ] unit-test +{ f } [ 4 3 3 scalene? ] unit-test "may not violate triangle inequality" print -{ f } [ 7 3 2 scalene? ] unit-test +{ f } [ 7 3 2 scalene? ] unit-test "sides may be floats" print -{ t } [ 0.5 0.4 0.6 scalene? ] unit-test +{ t } [ 0.5 0.4 0.6 scalene? ] unit-test diff --git a/exercises/practice/triangle/triangle/triangle.factor b/exercises/practice/triangle/triangle/triangle.factor index c3da7580..770e71a0 100644 --- a/exercises/practice/triangle/triangle/triangle.factor +++ b/exercises/practice/triangle/triangle/triangle.factor @@ -1,11 +1,16 @@ USING: kernel ; IN: triangle -: equilateral? ( a b c -- ? ) +! Declare a `triangle` tuple with slots a, b, and c. + +: ( a b c -- triangle ) + "unimplemented" throw ; + +: equilateral? ( triangle -- ? ) "unimplemented" throw ; -: isosceles? ( a b c -- ? ) +: isosceles? ( triangle -- ? ) "unimplemented" throw ; -: scalene? ( a b c -- ? ) +: scalene? ( triangle -- ? ) "unimplemented" throw ; diff --git a/exercises/practice/two-fer/.meta/example.factor b/exercises/practice/two-fer/.meta/example.factor index 3563d0c2..404a9a29 100644 --- a/exercises/practice/two-fer/.meta/example.factor +++ b/exercises/practice/two-fer/.meta/example.factor @@ -1,6 +1,6 @@ USING: formatting kernel sequences ; IN: two-fer -: 2-for-1 ( name -- string ) +: 2-for-1 ( name -- str ) dup empty? [ drop "you" ] when "One for %s, one for me." sprintf ;