diff --git a/config.json b/config.json index a9f86de9..ca4ce236 100644 --- a/config.json +++ b/config.json @@ -299,10 +299,91 @@ ], "difficulty": 4 }, + { + "slug": "luhn", + "name": "Luhn", + "uuid": "e83e333e-612a-4ec8-8d9f-da387327acdc", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, + { + "slug": "robot-simulator", + "name": "Robot Simulator", + "uuid": "69868e6a-7e87-4cff-8f36-dd24c042e879", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, + { + "slug": "sieve", + "name": "Sieve", + "uuid": "8b1fc515-71c2-436c-98dc-8789d25c7ddb", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, + { + "slug": "space-age", + "name": "Space Age", + "uuid": "92fc6937-1b33-49f1-b66e-7ec7e3786906", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, + { + "slug": "word-count", + "name": "Word Count", + "uuid": "f0a2b8f5-b244-4c35-86a7-9bf86843093c", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "accumulate", "name": "Accumulate", "uuid": "8a373305-98c9-4b19-9011-085abe19cfed", + "practices": [], + "prerequisites": [], + "difficulty": 5, + "status": "deprecated" + }, + { + "slug": "affine-cipher", + "name": "Affine Cipher", + "uuid": "3e018380-7d24-403e-a951-b6f33ccc8afd", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "anagram", + "name": "Anagram", + "uuid": "ec107dbf-5123-4405-934e-3e0846433fd0", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "clock", + "name": "Clock", + "uuid": "2a606909-644b-4fd3-bd6c-bf0bb605a48d", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "crypto-square", + "name": "Crypto Square", + "uuid": "c4b23be2-92e7-40ee-ad93-19f9f79eebc9", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "list-ops", + "name": "List Ops", + "uuid": "9eb1ed19-7c5a-4c8f-98e3-7d99826deb18", "practices": [ "quotations", "collections", @@ -315,6 +396,38 @@ ], "difficulty": 5 }, + { + "slug": "matching-brackets", + "name": "Matching Brackets", + "uuid": "afef364d-12dd-42fd-bbc8-f2c6228f0f53", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "run-length-encoding", + "name": "Run-Length Encoding", + "uuid": "009090ef-d7fd-4ba5-9942-c6e686069e12", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "square-root", + "name": "Square Root", + "uuid": "53cef38b-ad56-4462-bf37-48902fd3745c", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, + { + "slug": "yacht", + "name": "Yacht", + "uuid": "c94bbdbb-cdfe-4b53-913c-fc7f705cc0d0", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "all-your-base", "name": "All Your Base", @@ -336,6 +449,62 @@ "topics": [ "math" ] + }, + { + "slug": "game-of-life", + "name": "Conway's Game of Life", + "uuid": "36771f7d-aee3-44bd-a718-60efeaca3a8a", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "ocr-numbers", + "name": "OCR Numbers", + "uuid": "725a35a9-b2ae-4c71-8dc9-a801a8699bd2", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "rail-fence-cipher", + "name": "Rail Fence Cipher", + "uuid": "7f404c18-968e-41de-8067-7be5596a87ef", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "say", + "name": "Say", + "uuid": "613a30e5-d545-40c7-b41e-f5ccb2c4ec88", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, + { + "slug": "dominoes", + "name": "Dominoes", + "uuid": "cbbd1c5f-587b-4274-9889-1332d9446c63", + "practices": [], + "prerequisites": [], + "difficulty": 7 + }, + { + "slug": "perfect-numbers", + "name": "Perfect Numbers", + "uuid": "0420eb1a-c607-4a65-8acb-aa3f60c01302", + "practices": [], + "prerequisites": [], + "difficulty": 7 + }, + { + "slug": "rectangles", + "name": "Rectangles", + "uuid": "bfcd7856-38df-48e5-9bf1-438962ac44f8", + "practices": [], + "prerequisites": [], + "difficulty": 7 } ] }, diff --git a/exercises/practice/accumulate/.meta/example.factor b/exercises/practice/accumulate/.meta/example.factor index 1a7483a4..e23bc3d3 100644 --- a/exercises/practice/accumulate/.meta/example.factor +++ b/exercises/practice/accumulate/.meta/example.factor @@ -1,13 +1,5 @@ -USING: arrays kernel locals sequences sequences.extras ; +USING: kernel locals make sequences ; IN: accumulate :: accum ( seq quot: ( x -- y ) -- newseq ) - seq >resizable :> seq - seq length seq new-resizable :> newseq - - [ seq empty? ] [ - seq pop quot call - newseq push - ] until - - newseq reverse >array ; inline + [ seq [ quot call , ] each ] { } make ; inline diff --git a/exercises/practice/accumulate/accumulate/accumulate.factor b/exercises/practice/accumulate/accumulate/accumulate.factor index 8d6786c0..1ee6a4ac 100644 --- a/exercises/practice/accumulate/accumulate/accumulate.factor +++ b/exercises/practice/accumulate/accumulate/accumulate.factor @@ -2,4 +2,4 @@ USING: kernel ; IN: accumulate :: accum ( seq quot: ( x -- y ) -- newseq ) - "unimplemented" throw ; + "unimplemented" throw ; inline diff --git a/exercises/practice/affine-cipher/.docs/instructions.md b/exercises/practice/affine-cipher/.docs/instructions.md new file mode 100644 index 00000000..1603dbbc --- /dev/null +++ b/exercises/practice/affine-cipher/.docs/instructions.md @@ -0,0 +1,74 @@ +# Instructions + +Create an implementation of the affine cipher, an ancient encryption system created in the Middle East. + +The affine cipher is a type of monoalphabetic substitution cipher. +Each character is mapped to its numeric equivalent, encrypted with a mathematical function and then converted to the letter relating to its new numeric value. +Although all monoalphabetic ciphers are weak, the affine cipher is much stronger than the Atbash cipher, because it has many more keys. + +[//]: # " monoalphabetic as spelled by Merriam-Webster, compare to polyalphabetic " + +## Encryption + +The encryption function is: + +```text +E(x) = (ai + b) mod m +``` + +Where: + +- `i` is the letter's index from `0` to the length of the alphabet - 1. +- `m` is the length of the alphabet. + For the Latin alphabet `m` is `26`. +- `a` and `b` are integers which make up the encryption key. + +Values `a` and `m` must be _coprime_ (or, _relatively prime_) for automatic decryption to succeed, i.e., they have number `1` as their only common factor (more information can be found in the [Wikipedia article about coprime integers][coprime-integers]). +In case `a` is not coprime to `m`, your program should indicate that this is an error. +Otherwise it should encrypt or decrypt with the provided key. + +For the purpose of this exercise, digits are valid input but they are not encrypted. +Spaces and punctuation characters are excluded. +Ciphertext is written out in groups of fixed length separated by space, the traditional group size being `5` letters. +This is to make it harder to guess encrypted text based on word boundaries. + +## Decryption + +The decryption function is: + +```text +D(y) = (a^-1)(y - b) mod m +``` + +Where: + +- `y` is the numeric value of an encrypted letter, i.e., `y = E(x)` +- it is important to note that `a^-1` is the modular multiplicative inverse (MMI) of `a mod m` +- the modular multiplicative inverse only exists if `a` and `m` are coprime. + +The MMI of `a` is `x` such that the remainder after dividing `ax` by `m` is `1`: + +```text +ax mod m = 1 +``` + +More information regarding how to find a Modular Multiplicative Inverse and what it means can be found in the [related Wikipedia article][mmi]. + +## General Examples + +- Encrypting `"test"` gives `"ybty"` with the key `a = 5`, `b = 7` +- Decrypting `"ybty"` gives `"test"` with the key `a = 5`, `b = 7` +- Decrypting `"ybty"` gives `"lqul"` with the wrong key `a = 11`, `b = 7` +- Decrypting `"kqlfd jzvgy tpaet icdhm rtwly kqlon ubstx"` gives `"thequickbrownfoxjumpsoverthelazydog"` with the key `a = 19`, `b = 13` +- Encrypting `"test"` with the key `a = 18`, `b = 13` is an error because `18` and `26` are not coprime + +## Example of finding a Modular Multiplicative Inverse (MMI) + +Finding MMI for `a = 15`: + +- `(15 * x) mod 26 = 1` +- `(15 * 7) mod 26 = 1`, ie. `105 mod 26 = 1` +- `7` is the MMI of `15 mod 26` + +[mmi]: https://en.wikipedia.org/wiki/Modular_multiplicative_inverse +[coprime-integers]: https://en.wikipedia.org/wiki/Coprime_integers diff --git a/exercises/practice/affine-cipher/.meta/config.json b/exercises/practice/affine-cipher/.meta/config.json new file mode 100644 index 00000000..26e39c3e --- /dev/null +++ b/exercises/practice/affine-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "affine-cipher/affine-cipher.factor" + ], + "test": [ + "affine-cipher/affine-cipher-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Create an implementation of the Affine cipher, an ancient encryption algorithm from the Middle East.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Affine_cipher" +} diff --git a/exercises/practice/affine-cipher/.meta/example.factor b/exercises/practice/affine-cipher/.meta/example.factor new file mode 100644 index 00000000..8a8c0381 --- /dev/null +++ b/exercises/practice/affine-cipher/.meta/example.factor @@ -0,0 +1,30 @@ +USING: combinators grouping kernel locals math math.functions +ranges sequences sets strings unicode ; +IN: affine-cipher + +: check-coprime ( a -- a ) + dup 26 gcd nip 1 = [ "a and m must be coprime." throw ] unless ; + +: mmi ( a -- a^-1 ) + 26 [1..b] [ over * 26 mod 1 = ] find nip nip ; + +: group5 ( str -- str ) 5 group " " join ; + +:: encode ( phrase a b -- cipher ) + a check-coprime drop + phrase >lower + [ [ Letter? ] [ digit? ] bi or ] filter + [| ch | + ch digit? + [ ch ] + [ ch CHAR: a - a * b + 26 mod CHAR: a + ] if + ] map group5 ; + +:: decode ( phrase a b -- plain ) + a check-coprime mmi :> a-inv + phrase " " without + [| ch | + ch digit? + [ ch ] + [ ch CHAR: a - b - a-inv * 26 mod dup 0 < [ 26 + ] when CHAR: a + ] if + ] map ; diff --git a/exercises/practice/affine-cipher/.meta/generator.jl b/exercises/practice/affine-cipher/.meta/generator.jl new file mode 100644 index 00000000..79d38ec1 --- /dev/null +++ b/exercises/practice/affine-cipher/.meta/generator.jl @@ -0,0 +1,17 @@ +module AffineCipher + +function gen_test_case(case) + phrase = escape_factor(case["input"]["phrase"]) + a = Int(case["input"]["key"]["a"]) + b = Int(case["input"]["key"]["b"]) + prop = case["property"] + expected = case["expected"] + if expected isa Dict + msg = expected["error"] + return """[ "$(phrase)" $(a) $(b) $(prop) ]\n[ "$(msg)" = ] must-fail-with""" + else + return """{ "$(expected)" }\n[ "$(phrase)" $(a) $(b) $(prop) ] unit-test""" + end +end + +end diff --git a/exercises/practice/affine-cipher/.meta/supplements.json b/exercises/practice/affine-cipher/.meta/supplements.json new file mode 100644 index 00000000..f259d869 --- /dev/null +++ b/exercises/practice/affine-cipher/.meta/supplements.json @@ -0,0 +1,8 @@ +[ + { + "description": "encode boundary characters", + "property": "encode", + "input": {"phrase": "/09:@AMNZ[`amnz{", "key": {"a": 25, "b": 12}}, + "expected": "09maz nmazn" + } +] diff --git a/exercises/practice/affine-cipher/.meta/tests.toml b/exercises/practice/affine-cipher/.meta/tests.toml new file mode 100644 index 00000000..07cce7c7 --- /dev/null +++ b/exercises/practice/affine-cipher/.meta/tests.toml @@ -0,0 +1,58 @@ +# 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. + +[2ee1d9af-1c43-416c-b41b-cefd7d4d2b2a] +description = "encode -> encode yes" + +[785bade9-e98b-4d4f-a5b0-087ba3d7de4b] +description = "encode -> encode no" + +[2854851c-48fb-40d8-9bf6-8f192ed25054] +description = "encode -> encode OMG" + +[bc0c1244-b544-49dd-9777-13a770be1bad] +description = "encode -> encode O M G" + +[381a1a20-b74a-46ce-9277-3778625c9e27] +description = "encode -> encode mindblowingly" + +[6686f4e2-753b-47d4-9715-876fdc59029d] +description = "encode -> encode numbers" + +[ae23d5bd-30a8-44b6-afbe-23c8c0c7faa3] +description = "encode -> encode deep thought" + +[c93a8a4d-426c-42ef-9610-76ded6f7ef57] +description = "encode -> encode all the letters" + +[0673638a-4375-40bd-871c-fb6a2c28effb] +description = "encode -> encode with a not coprime to m" + +[3f0ac7e2-ec0e-4a79-949e-95e414953438] +description = "decode -> decode exercism" + +[241ee64d-5a47-4092-a5d7-7939d259e077] +description = "decode -> decode a sentence" + +[33fb16a1-765a-496f-907f-12e644837f5e] +description = "decode -> decode numbers" + +[20bc9dce-c5ec-4db6-a3f1-845c776bcbf7] +description = "decode -> decode all the letters" + +[623e78c0-922d-49c5-8702-227a3e8eaf81] +description = "decode -> decode with no spaces in input" + +[58fd5c2a-1fd9-4563-a80a-71cff200f26f] +description = "decode -> decode with too many spaces" + +[b004626f-c186-4af9-a3f4-58f74cdb86d5] +description = "decode -> decode with a not coprime to m" diff --git a/exercises/practice/affine-cipher/affine-cipher/affine-cipher-tests.factor b/exercises/practice/affine-cipher/affine-cipher/affine-cipher-tests.factor new file mode 100644 index 00000000..516ed24d --- /dev/null +++ b/exercises/practice/affine-cipher/affine-cipher/affine-cipher-tests.factor @@ -0,0 +1,76 @@ +USING: affine-cipher io kernel lexer tools.test unicode ; +IN: affine-cipher.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Affine Cipher:" print + +"encode yes" print +{ "xbt" } +[ "yes" 5 7 encode ] unit-test + +STOP-HERE + +"encode no" print +{ "fu" } +[ "no" 15 18 encode ] unit-test + +"encode OMG" print +{ "lvz" } +[ "OMG" 21 3 encode ] unit-test + +"encode O M G" print +{ "hjp" } +[ "O M G" 25 47 encode ] unit-test + +"encode mindblowingly" print +{ "rzcwa gnxzc dgt" } +[ "mindblowingly" 11 15 encode ] unit-test + +"encode numbers" print +{ "jqgjc rw123 jqgjc rw" } +[ "Testing,1 2 3, testing." 3 4 encode ] unit-test + +"encode deep thought" print +{ "iynia fdqfb ifje" } +[ "Truth is fiction." 5 17 encode ] unit-test + +"encode all the letters" print +{ "swxtj npvyk lruol iejdc blaxk swxmh qzglf" } +[ "The quick brown fox jumps over the lazy dog." 17 33 encode ] unit-test + +"encode with a not coprime to m" print +[ "This is a test." 6 17 encode ] +[ "a and m must be coprime." = ] must-fail-with + +"decode exercism" print +{ "exercism" } +[ "tytgn fjr" 3 7 decode ] unit-test + +"decode a sentence" print +{ "anobstacleisoftenasteppingstone" } +[ "qdwju nqcro muwhn odqun oppmd aunwd o" 19 16 decode ] unit-test + +"decode numbers" print +{ "testing123testing" } +[ "odpoz ub123 odpoz ub" 25 7 decode ] unit-test + +"decode all the letters" print +{ "thequickbrownfoxjumpsoverthelazydog" } +[ "swxtj npvyk lruol iejdc blaxk swxmh qzglf" 17 33 decode ] unit-test + +"decode with no spaces in input" print +{ "thequickbrownfoxjumpsoverthelazydog" } +[ "swxtjnpvyklruoliejdcblaxkswxmhqzglf" 17 33 decode ] unit-test + +"decode with too many spaces" print +{ "jollygreengiant" } +[ "vszzm cly yd cg qdp" 15 16 decode ] unit-test + +"decode with a not coprime to m" print +[ "Test" 13 5 decode ] +[ "a and m must be coprime." = ] must-fail-with + +"encode boundary characters" print +{ "09maz nmazn" } +[ "/09:@AMNZ[`amnz{" 25 12 encode ] unit-test diff --git a/exercises/practice/affine-cipher/affine-cipher/affine-cipher.factor b/exercises/practice/affine-cipher/affine-cipher/affine-cipher.factor new file mode 100644 index 00000000..28d31158 --- /dev/null +++ b/exercises/practice/affine-cipher/affine-cipher/affine-cipher.factor @@ -0,0 +1,8 @@ +USING: kernel ; +IN: affine-cipher + +: encode ( phrase a b -- cipher ) + "unimplemented" throw ; + +: decode ( phrase a b -- plain ) + "unimplemented" throw ; diff --git a/exercises/practice/anagram/.docs/instructions.md b/exercises/practice/anagram/.docs/instructions.md new file mode 100644 index 00000000..dca24f52 --- /dev/null +++ b/exercises/practice/anagram/.docs/instructions.md @@ -0,0 +1,12 @@ +# Instructions + +Given a target word and one or more candidate words, your task is to find the candidates that are anagrams of the target. + +An anagram is a rearrangement of letters to form a new word: for example `"owns"` is an anagram of `"snow"`. +A word is _not_ its own anagram: for example, `"stop"` is not an anagram of `"stop"`. + +The target word and candidate words are made up of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`). +Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `"StoP"` is not an anagram of `"sTOp"`. +The words you need to find should be taken from the candidate words, using the same letter case. + +Given the target `"stone"` and the candidate words `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, and `"Seton"`, the anagram words you need to find are `"tones"`, `"notes"`, and `"Seton"`. diff --git a/exercises/practice/anagram/.docs/introduction.md b/exercises/practice/anagram/.docs/introduction.md new file mode 100644 index 00000000..1acbdf00 --- /dev/null +++ b/exercises/practice/anagram/.docs/introduction.md @@ -0,0 +1,12 @@ +# Introduction + +At a garage sale, you find a lovely vintage typewriter at a bargain price! +Excitedly, you rush home, insert a sheet of paper, and start typing away. +However, your excitement wanes when you examine the output: all words are garbled! +For example, it prints "stop" instead of "post" and "least" instead of "stale." +Carefully, you try again, but now it prints "spot" and "slate." +After some experimentation, you find there is a random delay before each letter is printed, which messes up the order. +You now understand why they sold it for so little money! + +You realize this quirk allows you to generate anagrams, which are words formed by rearranging the letters of another word. +Pleased with your finding, you spend the rest of the day generating hundreds of anagrams. diff --git a/exercises/practice/anagram/.meta/config.json b/exercises/practice/anagram/.meta/config.json new file mode 100644 index 00000000..9dcb2182 --- /dev/null +++ b/exercises/practice/anagram/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "anagram/anagram.factor" + ], + "test": [ + "anagram/anagram-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a word and a list of possible anagrams, select the correct sublist.", + "source": "Inspired by the Extreme Startup game", + "source_url": "https://github.com/rchatley/extreme_startup" +} diff --git a/exercises/practice/anagram/.meta/example.factor b/exercises/practice/anagram/.meta/example.factor new file mode 100644 index 00000000..159443e0 --- /dev/null +++ b/exercises/practice/anagram/.meta/example.factor @@ -0,0 +1,12 @@ +USING: kernel sequences sorting unicode ; +IN: anagram + +: sorted-lower ( str -- str' ) >lower natural-sort ; + +: anagram? ( subject candidate -- ? ) + 2dup [ >lower ] bi@ = not + -rot [ sorted-lower ] bi@ = + and ; + +: find-anagrams ( subject candidates -- anagrams ) + [ dupd anagram? ] filter nip ; diff --git a/exercises/practice/anagram/.meta/generator.jl b/exercises/practice/anagram/.meta/generator.jl new file mode 100644 index 00000000..2b2dca2e --- /dev/null +++ b/exercises/practice/anagram/.meta/generator.jl @@ -0,0 +1,10 @@ +module Anagram + +function gen_test_case(case) + subject = case["input"]["subject"] + candidates = format_string_vector(case["input"]["candidates"]) + expected = format_string_vector(case["expected"]) + return """{ $(expected) }\n[ "$(subject)" $(candidates) find-anagrams ] unit-test""" +end + +end diff --git a/exercises/practice/anagram/.meta/tests.toml b/exercises/practice/anagram/.meta/tests.toml new file mode 100644 index 00000000..4d905627 --- /dev/null +++ b/exercises/practice/anagram/.meta/tests.toml @@ -0,0 +1,86 @@ +# 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. + +[dd40c4d2-3c8b-44e5-992a-f42b393ec373] +description = "no matches" + +[b3cca662-f50a-489e-ae10-ab8290a09bdc] +description = "detects two anagrams" +include = false + +[03eb9bbe-8906-4ea0-84fa-ffe711b52c8b] +description = "detects two anagrams" +reimplements = "b3cca662-f50a-489e-ae10-ab8290a09bdc" + +[a27558ee-9ba0-4552-96b1-ecf665b06556] +description = "does not detect anagram subsets" + +[64cd4584-fc15-4781-b633-3d814c4941a4] +description = "detects anagram" + +[99c91beb-838f-4ccd-b123-935139917283] +description = "detects three anagrams" + +[78487770-e258-4e1f-a646-8ece10950d90] +description = "detects multiple anagrams with different case" + +[1d0ab8aa-362f-49b7-9902-3d0c668d557b] +description = "does not detect non-anagrams with identical checksum" + +[9e632c0b-c0b1-4804-8cc1-e295dea6d8a8] +description = "detects anagrams case-insensitively" + +[b248e49f-0905-48d2-9c8d-bd02d8c3e392] +description = "detects anagrams using case-insensitive subject" + +[f367325c-78ec-411c-be76-e79047f4bd54] +description = "detects anagrams using case-insensitive possible matches" + +[7cc195ad-e3c7-44ee-9fd2-d3c344806a2c] +description = "does not detect an anagram if the original word is repeated" +include = false + +[630abb71-a94e-4715-8395-179ec1df9f91] +description = "does not detect an anagram if the original word is repeated" +reimplements = "7cc195ad-e3c7-44ee-9fd2-d3c344806a2c" + +[9878a1c9-d6ea-4235-ae51-3ea2befd6842] +description = "anagrams must use all letters exactly once" + +[85757361-4535-45fd-ac0e-3810d40debc1] +description = "words are not anagrams of themselves (case-insensitive)" +include = false + +[68934ed0-010b-4ef9-857a-20c9012d1ebf] +description = "words are not anagrams of themselves" +reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" + +[589384f3-4c8a-4e7d-9edc-51c3e5f0c90e] +description = "words are not anagrams of themselves even if letter case is partially different" +reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" + +[ba53e423-7e02-41ee-9ae2-71f91e6d18e6] +description = "words are not anagrams of themselves even if letter case is completely different" +reimplements = "85757361-4535-45fd-ac0e-3810d40debc1" + +[a0705568-628c-4b55-9798-82e4acde51ca] +description = "words other than themselves can be anagrams" +include = false + +[33d3f67e-fbb9-49d3-a90e-0beb00861da7] +description = "words other than themselves can be anagrams" +reimplements = "a0705568-628c-4b55-9798-82e4acde51ca" + +[a6854f66-eec1-4afd-a137-62ef2870c051] +description = "handles case of greek letters" + +[fd3509e5-e3ba-409d-ac3d-a9ac84d13296] +description = "different characters may have the same bytes" diff --git a/exercises/practice/anagram/anagram/anagram-tests.factor b/exercises/practice/anagram/anagram/anagram-tests.factor new file mode 100644 index 00000000..be961bd2 --- /dev/null +++ b/exercises/practice/anagram/anagram/anagram-tests.factor @@ -0,0 +1,72 @@ +USING: anagram io kernel lexer tools.test unicode ; +IN: anagram.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Anagram:" print + +"no matches" print +{ V{ } } +[ "diaper" V{ "hello" "world" "zombies" "pants" } find-anagrams ] unit-test + +STOP-HERE + +"detects two anagrams" print +{ V{ "lemons" "melons" } } +[ "solemn" V{ "lemons" "cherry" "melons" } find-anagrams ] unit-test + +"does not detect anagram subsets" print +{ V{ } } +[ "good" V{ "dog" "goody" } find-anagrams ] unit-test + +"detects anagram" print +{ V{ "inlets" } } +[ "listen" V{ "enlists" "google" "inlets" "banana" } find-anagrams ] unit-test + +"detects three anagrams" print +{ V{ "gallery" "regally" "largely" } } +[ "allergy" V{ "gallery" "ballerina" "regally" "clergy" "largely" "leading" } find-anagrams ] unit-test + +"detects multiple anagrams with different case" print +{ V{ "Eons" "ONES" } } +[ "nose" V{ "Eons" "ONES" } find-anagrams ] unit-test + +"does not detect non-anagrams with identical checksum" print +{ V{ } } +[ "mass" V{ "last" } find-anagrams ] unit-test + +"detects anagrams case-insensitively" print +{ V{ "Carthorse" } } +[ "Orchestra" V{ "cashregister" "Carthorse" "radishes" } find-anagrams ] unit-test + +"detects anagrams using case-insensitive subject" print +{ V{ "carthorse" } } +[ "Orchestra" V{ "cashregister" "carthorse" "radishes" } find-anagrams ] unit-test + +"detects anagrams using case-insensitive possible matches" print +{ V{ "Carthorse" } } +[ "orchestra" V{ "cashregister" "Carthorse" "radishes" } find-anagrams ] unit-test + +"does not detect an anagram if the original word is repeated" print +{ V{ } } +[ "go" V{ "goGoGO" } find-anagrams ] unit-test + +"anagrams must use all letters exactly once" print +{ V{ } } +[ "tapper" V{ "patter" } find-anagrams ] unit-test + +"words are not anagrams of themselves even if letter case is completely different" print +{ V{ } } +[ "BANANA" V{ "banana" } find-anagrams ] unit-test + +"words other than themselves can be anagrams" print +{ V{ "Silent" } } +[ "LISTEN" V{ "LISTEN" "Silent" } find-anagrams ] unit-test + +"handles case of greek letters" print +{ V{ "ΒΓΑ" "γβα" } } +[ "ΑΒΓ" V{ "ΒΓΑ" "ΒΓΔ" "γβα" "αβγ" } find-anagrams ] unit-test + +"different characters may have the same bytes" print +{ V{ } } +[ "a⬂" V{ "€a" } find-anagrams ] unit-test diff --git a/exercises/practice/anagram/anagram/anagram.factor b/exercises/practice/anagram/anagram/anagram.factor new file mode 100644 index 00000000..85e13de0 --- /dev/null +++ b/exercises/practice/anagram/anagram/anagram.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: anagram + +: find-anagrams ( subject candidates -- anagrams ) + "unimplemented" throw ; diff --git a/exercises/practice/clock/.docs/instructions.md b/exercises/practice/clock/.docs/instructions.md new file mode 100644 index 00000000..a1efc789 --- /dev/null +++ b/exercises/practice/clock/.docs/instructions.md @@ -0,0 +1,7 @@ +# Instructions + +Implement a clock that handles times without dates. + +You should be able to add and subtract minutes to it. + +Two clocks that represent the same time should be equal to each other. diff --git a/exercises/practice/clock/.meta/config.json b/exercises/practice/clock/.meta/config.json new file mode 100644 index 00000000..95e56a11 --- /dev/null +++ b/exercises/practice/clock/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "clock/clock.factor" + ], + "test": [ + "clock/clock-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Implement a clock that handles times without dates.", + "source": "Pairing session with Erin Drummond" +} diff --git a/exercises/practice/clock/.meta/example.factor b/exercises/practice/clock/.meta/example.factor new file mode 100644 index 00000000..84424e65 --- /dev/null +++ b/exercises/practice/clock/.meta/example.factor @@ -0,0 +1,22 @@ +USING: formatting kernel math math.parser splitting ; +IN: clock + +: normalize ( minutes -- minutes' ) + 1440 mod dup 0 < [ 1440 + ] when ; + +: minutes>clock ( minutes -- str ) + normalize 60 /mod "%02d:%02d" sprintf ; + +: ( hour minute -- str ) + swap 60 * + minutes>clock ; + +: clock>minutes ( clock -- minutes ) + ":" split1 [ string>number ] bi@ swap 60 * + ; + +: add-minutes ( clock minutes -- clock' ) + [ clock>minutes ] dip + minutes>clock ; + +: subtract-minutes ( clock minutes -- clock' ) + neg add-minutes ; + +: clock= ( clock1 clock2 -- ? ) = ; diff --git a/exercises/practice/clock/.meta/generator.jl b/exercises/practice/clock/.meta/generator.jl new file mode 100644 index 00000000..79021ce9 --- /dev/null +++ b/exercises/practice/clock/.meta/generator.jl @@ -0,0 +1,30 @@ +module Clock + +function gen_test_case(case) + prop = case["property"] + expected = case["expected"] + if prop == "create" + h = Int(case["input"]["hour"]) + m = Int(case["input"]["minute"]) + return """{ "$(expected)" } [ $(h) $(m) ] unit-test""" + elseif prop == "add" + h = Int(case["input"]["hour"]) + m = Int(case["input"]["minute"]) + v = Int(case["input"]["value"]) + return """{ "$(expected)" } [ $(h) $(m) $(v) add-minutes ] unit-test""" + elseif prop == "subtract" + h = Int(case["input"]["hour"]) + m = Int(case["input"]["minute"]) + v = Int(case["input"]["value"]) + return """{ "$(expected)" } [ $(h) $(m) $(v) subtract-minutes ] unit-test""" + else # equal + h1 = Int(case["input"]["clock1"]["hour"]) + m1 = Int(case["input"]["clock1"]["minute"]) + h2 = Int(case["input"]["clock2"]["hour"]) + m2 = Int(case["input"]["clock2"]["minute"]) + exp = expected ? "t" : "f" + return "{ $(exp) } [ $(h1) $(m1) $(h2) $(m2) clock= ] unit-test" + end +end + +end diff --git a/exercises/practice/clock/.meta/tests.toml b/exercises/practice/clock/.meta/tests.toml new file mode 100644 index 00000000..712c87bc --- /dev/null +++ b/exercises/practice/clock/.meta/tests.toml @@ -0,0 +1,166 @@ +# 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. + +[a577bacc-106b-496e-9792-b3083ea8705e] +description = "Create a new clock with an initial time -> on the hour" + +[b5d0c360-3b88-489b-8e84-68a1c7a4fa23] +description = "Create a new clock with an initial time -> past the hour" + +[473223f4-65f3-46ff-a9f7-7663c7e59440] +description = "Create a new clock with an initial time -> midnight is zero hours" + +[ca95d24a-5924-447d-9a96-b91c8334725c] +description = "Create a new clock with an initial time -> hour rolls over" + +[f3826de0-0925-4d69-8ac8-89aea7e52b78] +description = "Create a new clock with an initial time -> hour rolls over continuously" + +[a02f7edf-dfd4-4b11-b21a-86de3cc6a95c] +description = "Create a new clock with an initial time -> sixty minutes is next hour" + +[8f520df6-b816-444d-b90f-8a477789beb5] +description = "Create a new clock with an initial time -> minutes roll over" + +[c75c091b-47ac-4655-8d40-643767fc4eed] +description = "Create a new clock with an initial time -> minutes roll over continuously" + +[06343ecb-cf39-419d-a3f5-dcbae0cc4c57] +description = "Create a new clock with an initial time -> hour and minutes roll over" + +[be60810e-f5d9-4b58-9351-a9d1e90e660c] +description = "Create a new clock with an initial time -> hour and minutes roll over continuously" + +[1689107b-0b5c-4bea-aad3-65ec9859368a] +description = "Create a new clock with an initial time -> hour and minutes roll over to exactly midnight" + +[d3088ee8-91b7-4446-9e9d-5e2ad6219d91] +description = "Create a new clock with an initial time -> negative hour" + +[77ef6921-f120-4d29-bade-80d54aa43b54] +description = "Create a new clock with an initial time -> negative hour rolls over" + +[359294b5-972f-4546-bb9a-a85559065234] +description = "Create a new clock with an initial time -> negative hour rolls over continuously" + +[509db8b7-ac19-47cc-bd3a-a9d2f30b03c0] +description = "Create a new clock with an initial time -> negative minutes" + +[5d6bb225-130f-4084-84fd-9e0df8996f2a] +description = "Create a new clock with an initial time -> negative minutes roll over" + +[d483ceef-b520-4f0c-b94a-8d2d58cf0484] +description = "Create a new clock with an initial time -> negative minutes roll over continuously" + +[1cd19447-19c6-44bf-9d04-9f8305ccb9ea] +description = "Create a new clock with an initial time -> negative sixty minutes is previous hour" + +[9d3053aa-4f47-4afc-bd45-d67a72cef4dc] +description = "Create a new clock with an initial time -> negative hour and minutes both roll over" + +[51d41fcf-491e-4ca0-9cae-2aa4f0163ad4] +description = "Create a new clock with an initial time -> negative hour and minutes both roll over continuously" + +[d098e723-ad29-4ef9-997a-2693c4c9d89a] +description = "Add minutes -> add minutes" + +[b6ec8f38-e53e-4b22-92a7-60dab1f485f4] +description = "Add minutes -> add no minutes" + +[efd349dd-0785-453e-9ff8-d7452a8e7269] +description = "Add minutes -> add to next hour" + +[749890f7-aba9-4702-acce-87becf4ef9fe] +description = "Add minutes -> add more than one hour" + +[da63e4c1-1584-46e3-8d18-c9dc802c1713] +description = "Add minutes -> add more than two hours with carry" + +[be167a32-3d33-4cec-a8bc-accd47ddbb71] +description = "Add minutes -> add across midnight" + +[6672541e-cdae-46e4-8be7-a820cc3be2a8] +description = "Add minutes -> add more than one day (1500 min = 25 hrs)" + +[1918050d-c79b-4cb7-b707-b607e2745c7e] +description = "Add minutes -> add more than two days" + +[37336cac-5ede-43a5-9026-d426cbe40354] +description = "Subtract minutes -> subtract minutes" + +[0aafa4d0-3b5f-4b12-b3af-e3a9e09c047b] +description = "Subtract minutes -> subtract to previous hour" + +[9b4e809c-612f-4b15-aae0-1df0acb801b9] +description = "Subtract minutes -> subtract more than an hour" + +[8b04bb6a-3d33-4e6c-8de9-f5de6d2c70d6] +description = "Subtract minutes -> subtract across midnight" + +[07c3bbf7-ce4d-4658-86e8-4a77b7a5ccd9] +description = "Subtract minutes -> subtract more than two hours" + +[90ac8a1b-761c-4342-9c9c-cdc3ed5db097] +description = "Subtract minutes -> subtract more than two hours with borrow" + +[2149f985-7136-44ad-9b29-ec023a97a2b7] +description = "Subtract minutes -> subtract more than one day (1500 min = 25 hrs)" + +[ba11dbf0-ac27-4acb-ada9-3b853ec08c97] +description = "Subtract minutes -> subtract more than two days" + +[f2fdad51-499f-4c9b-a791-b28c9282e311] +description = "Compare two clocks for equality -> clocks with same time" + +[5d409d4b-f862-4960-901e-ec430160b768] +description = "Compare two clocks for equality -> clocks a minute apart" + +[a6045fcf-2b52-4a47-8bb2-ef10a064cba5] +description = "Compare two clocks for equality -> clocks an hour apart" + +[66b12758-0be5-448b-a13c-6a44bce83527] +description = "Compare two clocks for equality -> clocks with hour overflow" + +[2b19960c-212e-4a71-9aac-c581592f8111] +description = "Compare two clocks for equality -> clocks with hour overflow by several days" + +[6f8c6541-afac-4a92-b0c2-b10d4e50269f] +description = "Compare two clocks for equality -> clocks with negative hour" + +[bb9d5a68-e324-4bf5-a75e-0e9b1f97a90d] +description = "Compare two clocks for equality -> clocks with negative hour that wraps" + +[56c0326d-565b-4d19-a26f-63b3205778b7] +description = "Compare two clocks for equality -> clocks with negative hour that wraps multiple times" + +[c90b9de8-ddff-4ffe-9858-da44a40fdbc2] +description = "Compare two clocks for equality -> clocks with minute overflow" + +[533a3dc5-59a7-491b-b728-a7a34fe325de] +description = "Compare two clocks for equality -> clocks with minute overflow by several days" + +[fff49e15-f7b7-4692-a204-0f6052d62636] +description = "Compare two clocks for equality -> clocks with negative minute" + +[605c65bb-21bd-43eb-8f04-878edf508366] +description = "Compare two clocks for equality -> clocks with negative minute that wraps" + +[b87e64ed-212a-4335-91fd-56da8421d077] +description = "Compare two clocks for equality -> clocks with negative minute that wraps multiple times" + +[822fbf26-1f3b-4b13-b9bf-c914816b53dd] +description = "Compare two clocks for equality -> clocks with negative hours and minutes" + +[e787bccd-cf58-4a1d-841c-ff80eaaccfaa] +description = "Compare two clocks for equality -> clocks with negative hours and minutes that wrap" + +[96969ca8-875a-48a1-86ae-257a528c44f5] +description = "Compare two clocks for equality -> full clock and zeroed clock" diff --git a/exercises/practice/clock/clock/clock-tests.factor b/exercises/practice/clock/clock/clock-tests.factor new file mode 100644 index 00000000..bdd81f41 --- /dev/null +++ b/exercises/practice/clock/clock/clock-tests.factor @@ -0,0 +1,164 @@ +USING: clock io kernel lexer tools.test unicode ; +IN: clock.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Clock:" print + +"on the hour" print +{ "08:00" } [ 8 0 ] unit-test + +STOP-HERE + +"past the hour" print +{ "11:09" } [ 11 9 ] unit-test + +"midnight is zero hours" print +{ "00:00" } [ 24 0 ] unit-test + +"hour rolls over" print +{ "01:00" } [ 25 0 ] unit-test + +"hour rolls over continuously" print +{ "04:00" } [ 100 0 ] unit-test + +"sixty minutes is next hour" print +{ "02:00" } [ 1 60 ] unit-test + +"minutes roll over" print +{ "02:40" } [ 0 160 ] unit-test + +"minutes roll over continuously" print +{ "04:43" } [ 0 1723 ] unit-test + +"hour and minutes roll over" print +{ "03:40" } [ 25 160 ] unit-test + +"hour and minutes roll over continuously" print +{ "11:01" } [ 201 3001 ] unit-test + +"hour and minutes roll over to exactly midnight" print +{ "00:00" } [ 72 8640 ] unit-test + +"negative hour" print +{ "23:15" } [ -1 15 ] unit-test + +"negative hour rolls over" print +{ "23:00" } [ -25 0 ] unit-test + +"negative hour rolls over continuously" print +{ "05:00" } [ -91 0 ] unit-test + +"negative minutes" print +{ "00:20" } [ 1 -40 ] unit-test + +"negative minutes roll over" print +{ "22:20" } [ 1 -160 ] unit-test + +"negative minutes roll over continuously" print +{ "16:40" } [ 1 -4820 ] unit-test + +"negative sixty minutes is previous hour" print +{ "01:00" } [ 2 -60 ] unit-test + +"negative hour and minutes both roll over" print +{ "20:20" } [ -25 -160 ] unit-test + +"negative hour and minutes both roll over continuously" print +{ "22:10" } [ -121 -5810 ] unit-test + +"add minutes" print +{ "10:03" } [ 10 0 3 add-minutes ] unit-test + +"add no minutes" print +{ "06:41" } [ 6 41 0 add-minutes ] unit-test + +"add to next hour" print +{ "01:25" } [ 0 45 40 add-minutes ] unit-test + +"add more than one hour" print +{ "11:01" } [ 10 0 61 add-minutes ] unit-test + +"add more than two hours with carry" print +{ "03:25" } [ 0 45 160 add-minutes ] unit-test + +"add across midnight" print +{ "00:01" } [ 23 59 2 add-minutes ] unit-test + +"add more than one day (1500 min = 25 hrs)" print +{ "06:32" } [ 5 32 1500 add-minutes ] unit-test + +"add more than two days" print +{ "11:21" } [ 1 1 3500 add-minutes ] unit-test + +"subtract minutes" print +{ "10:00" } [ 10 3 3 subtract-minutes ] unit-test + +"subtract to previous hour" print +{ "09:33" } [ 10 3 30 subtract-minutes ] unit-test + +"subtract more than an hour" print +{ "08:53" } [ 10 3 70 subtract-minutes ] unit-test + +"subtract across midnight" print +{ "23:59" } [ 0 3 4 subtract-minutes ] unit-test + +"subtract more than two hours" print +{ "21:20" } [ 0 0 160 subtract-minutes ] unit-test + +"subtract more than two hours with borrow" print +{ "03:35" } [ 6 15 160 subtract-minutes ] unit-test + +"subtract more than one day (1500 min = 25 hrs)" print +{ "04:32" } [ 5 32 1500 subtract-minutes ] unit-test + +"subtract more than two days" print +{ "00:20" } [ 2 20 3000 subtract-minutes ] unit-test + +"clocks with same time" print +{ t } [ 15 37 15 37 clock= ] unit-test + +"clocks a minute apart" print +{ f } [ 15 36 15 37 clock= ] unit-test + +"clocks an hour apart" print +{ f } [ 14 37 15 37 clock= ] unit-test + +"clocks with hour overflow" print +{ t } [ 10 37 34 37 clock= ] unit-test + +"clocks with hour overflow by several days" print +{ t } [ 3 11 99 11 clock= ] unit-test + +"clocks with negative hour" print +{ t } [ 22 40 -2 40 clock= ] unit-test + +"clocks with negative hour that wraps" print +{ t } [ 17 3 -31 3 clock= ] unit-test + +"clocks with negative hour that wraps multiple times" print +{ t } [ 13 49 -83 49 clock= ] unit-test + +"clocks with minute overflow" print +{ t } [ 0 1 0 1441 clock= ] unit-test + +"clocks with minute overflow by several days" print +{ t } [ 2 2 2 4322 clock= ] unit-test + +"clocks with negative minute" print +{ t } [ 2 40 3 -20 clock= ] unit-test + +"clocks with negative minute that wraps" print +{ t } [ 4 10 5 -1490 clock= ] unit-test + +"clocks with negative minute that wraps multiple times" print +{ t } [ 6 15 6 -4305 clock= ] unit-test + +"clocks with negative hours and minutes" print +{ t } [ 7 32 -12 -268 clock= ] unit-test + +"clocks with negative hours and minutes that wrap" print +{ t } [ 18 7 -54 -11513 clock= ] unit-test + +"full clock and zeroed clock" print +{ t } [ 24 0 0 0 clock= ] unit-test diff --git a/exercises/practice/clock/clock/clock.factor b/exercises/practice/clock/clock/clock.factor new file mode 100644 index 00000000..ecbdc2eb --- /dev/null +++ b/exercises/practice/clock/clock/clock.factor @@ -0,0 +1,14 @@ +USING: kernel ; +IN: clock + +: ( hour minute -- str ) + "unimplemented" throw ; + +: add-minutes ( clock minutes -- clock' ) + "unimplemented" throw ; + +: subtract-minutes ( clock minutes -- clock' ) + "unimplemented" throw ; + +: clock= ( clock1 clock2 -- ? ) + "unimplemented" throw ; diff --git a/exercises/practice/crypto-square/.docs/instructions.md b/exercises/practice/crypto-square/.docs/instructions.md new file mode 100644 index 00000000..6c3826ee --- /dev/null +++ b/exercises/practice/crypto-square/.docs/instructions.md @@ -0,0 +1,71 @@ +# Instructions + +Implement the classic method for composing secret messages called a square code. + +Given an English text, output the encoded version of that text. + +First, the input is normalized: the spaces and punctuation are removed from the English text and the message is down-cased. + +Then, the normalized characters are broken into rows. +These rows can be regarded as forming a rectangle when printed with intervening newlines. + +For example, the sentence + +```text +"If man was meant to stay on the ground, god would have given us roots." +``` + +is normalized to: + +```text +"ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots" +``` + +The plaintext should be organized into a rectangle as square as possible. +The size of the rectangle should be decided by the length of the message. + +If `c` is the number of columns and `r` is the number of rows, then for the rectangle `r` x `c` find the smallest possible integer `c` such that: + +- `r * c >= length of message`, +- and `c >= r`, +- and `c - r <= 1`. + +Our normalized text is 54 characters long, dictating a rectangle with `c = 8` and `r = 7`: + +```text +"ifmanwas" +"meanttos" +"tayonthe" +"groundgo" +"dwouldha" +"vegivenu" +"sroots " +``` + +The coded message is obtained by reading down the columns going left to right. + +The message above is coded as: + +```text +"imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau" +``` + +Output the encoded text in chunks that fill perfect rectangles `(r X c)`, with `c` chunks of `r` length, separated by spaces. +For phrases that are `n` characters short of the perfect rectangle, pad each of the last `n` chunks with a single trailing space. + +```text +"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau " +``` + +Notice that were we to stack these, we could visually decode the ciphertext back in to the original message: + +```text +"imtgdvs" +"fearwer" +"mayoogo" +"anouuio" +"ntnnlvt" +"wttddes" +"aohghn " +"sseoau " +``` diff --git a/exercises/practice/crypto-square/.meta/config.json b/exercises/practice/crypto-square/.meta/config.json new file mode 100644 index 00000000..69ce70da --- /dev/null +++ b/exercises/practice/crypto-square/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "crypto-square/crypto-square.factor" + ], + "test": [ + "crypto-square/crypto-square-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Implement the classic method for composing secret messages called a square code.", + "source": "J Dalbey's Programming Practice problems", + "source_url": "https://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html" +} diff --git a/exercises/practice/crypto-square/.meta/example.factor b/exercises/practice/crypto-square/.meta/example.factor new file mode 100644 index 00000000..74c857d7 --- /dev/null +++ b/exercises/practice/crypto-square/.meta/example.factor @@ -0,0 +1,15 @@ +USING: grouping kernel locals math math.functions sequences +strings unicode ; +IN: crypto-square + +:: ciphertext ( plaintext -- cipher ) + plaintext >lower [ [ Letter? ] [ digit? ] bi or ] filter + :> normalized + normalized length :> len + len 0 = [ "" ] [ + len sqrt ceiling >integer :> cols + normalized cols CHAR: \s pad-tail cols group :> rows + cols [| c | + rows [| row | c row length < [ c row nth ] [ CHAR: \s ] if ] map >string + ] map " " join + ] if ; diff --git a/exercises/practice/crypto-square/.meta/generator.jl b/exercises/practice/crypto-square/.meta/generator.jl new file mode 100644 index 00000000..a8c5dedd --- /dev/null +++ b/exercises/practice/crypto-square/.meta/generator.jl @@ -0,0 +1,9 @@ +module CryptoSquare + +function gen_test_case(case) + plaintext = escape_factor(case["input"]["plaintext"]) + expected = escape_factor(case["expected"]) + return """{ "$(expected)" }\n[ "$(plaintext)" ciphertext ] unit-test""" +end + +end diff --git a/exercises/practice/crypto-square/.meta/tests.toml b/exercises/practice/crypto-square/.meta/tests.toml new file mode 100644 index 00000000..94ef0819 --- /dev/null +++ b/exercises/practice/crypto-square/.meta/tests.toml @@ -0,0 +1,39 @@ +# 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. + +[407c3837-9aa7-4111-ab63-ec54b58e8e9f] +description = "empty plaintext results in an empty ciphertext" + +[aad04a25-b8bb-4304-888b-581bea8e0040] +description = "normalization results in empty plaintext" + +[64131d65-6fd9-4f58-bdd8-4a2370fb481d] +description = "Lowercase" + +[63a4b0ed-1e3c-41ea-a999-f6f26ba447d6] +description = "Remove spaces" + +[1b5348a1-7893-44c1-8197-42d48d18756c] +description = "Remove punctuation" + +[8574a1d3-4a08-4cec-a7c7-de93a164f41a] +description = "9 character plaintext results in 3 chunks of 3 characters" + +[a65d3fa1-9e09-43f9-bcec-7a672aec3eae] +description = "8 character plaintext results in 3 chunks, the last one with a trailing space" + +[fbcb0c6d-4c39-4a31-83f6-c473baa6af80] +description = "54 character plaintext results in 7 chunks, the last two with trailing spaces" +include = false + +[33fd914e-fa44-445b-8f38-ff8fbc9fe6e6] +description = "54 character plaintext results in 8 chunks, the last two with trailing spaces" +reimplements = "fbcb0c6d-4c39-4a31-83f6-c473baa6af80" diff --git a/exercises/practice/crypto-square/crypto-square/crypto-square-tests.factor b/exercises/practice/crypto-square/crypto-square/crypto-square-tests.factor new file mode 100644 index 00000000..b1a482d3 --- /dev/null +++ b/exercises/practice/crypto-square/crypto-square/crypto-square-tests.factor @@ -0,0 +1,40 @@ +USING: crypto-square io kernel lexer tools.test unicode ; +IN: crypto-square.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Crypto Square:" print + +"empty plaintext results in an empty ciphertext" print +{ "" } +[ "" ciphertext ] unit-test + +STOP-HERE + +"normalization results in empty plaintext" print +{ "" } +[ "... --- ..." ciphertext ] unit-test + +"Lowercase" print +{ "a" } +[ "A" ciphertext ] unit-test + +"Remove spaces" print +{ "b" } +[ " b " ciphertext ] unit-test + +"Remove punctuation" print +{ "1" } +[ "@1,%!" ciphertext ] unit-test + +"9 character plaintext results in 3 chunks of 3 characters" print +{ "tsf hiu isn" } +[ "This is fun!" ciphertext ] unit-test + +"8 character plaintext results in 3 chunks, the last one with a trailing space" print +{ "clu hlt io " } +[ "Chill out." ciphertext ] unit-test + +"54 character plaintext results in 8 chunks, the last two with trailing spaces" print +{ "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau " } +[ "If man was meant to stay on the ground, god would have given us roots." ciphertext ] unit-test diff --git a/exercises/practice/crypto-square/crypto-square/crypto-square.factor b/exercises/practice/crypto-square/crypto-square/crypto-square.factor new file mode 100644 index 00000000..e156d0bb --- /dev/null +++ b/exercises/practice/crypto-square/crypto-square/crypto-square.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: crypto-square + +: ciphertext ( plaintext -- cipher ) + "unimplemented" throw ; diff --git a/exercises/practice/dominoes/.docs/instructions.append.md b/exercises/practice/dominoes/.docs/instructions.append.md new file mode 100644 index 00000000..14ada56f --- /dev/null +++ b/exercises/practice/dominoes/.docs/instructions.append.md @@ -0,0 +1,9 @@ +# Instructions append + +## Input format + +Each domino is stored as a single byte, with the left half in the high [nibble] and the right half in the low [nibble]. + +For example, stones `[2|1]`, `[2|3]` and `[1|3]` are represented as the byte array `{ 0x21 0x23 0x13 }` + +[nibble]: https://en.wikipedia.org/wiki/Nibble diff --git a/exercises/practice/dominoes/.docs/instructions.md b/exercises/practice/dominoes/.docs/instructions.md new file mode 100644 index 00000000..75055b9e --- /dev/null +++ b/exercises/practice/dominoes/.docs/instructions.md @@ -0,0 +1,15 @@ +# Instructions + +Make a chain of dominoes. + +Compute a way to order a given set of domino stones so that they form a correct domino chain. +In the chain, the dots on one half of a stone must match the dots on the neighboring half of an adjacent stone. +Additionally, the dots on the halves of the stones without neighbors (the first and last stone) must match each other. + +For example given the stones `[2|1]`, `[2|3]` and `[1|3]` you should compute something +like `[1|2] [2|3] [3|1]` or `[3|2] [2|1] [1|3]` or `[1|3] [3|2] [2|1]` etc, where the first and last numbers are the same. + +For stones `[1|2]`, `[4|1]` and `[2|3]` the resulting chain is not valid: `[4|1] [1|2] [2|3]`'s first and last numbers are not the same. +4 != 3 + +Some test cases may use duplicate stones in a chain solution, assume that multiple Domino sets are being used. diff --git a/exercises/practice/dominoes/.docs/introduction.md b/exercises/practice/dominoes/.docs/introduction.md new file mode 100644 index 00000000..df248c21 --- /dev/null +++ b/exercises/practice/dominoes/.docs/introduction.md @@ -0,0 +1,13 @@ +# Introduction + +In Toyland, the trains are always busy delivering treasures across the city, from shiny marbles to rare building blocks. +The tracks they run on are made of colorful domino-shaped pieces, each marked with two numbers. +For the trains to move, the dominoes must form a perfect chain where the numbers match. + +Today, an urgent delivery of rare toys is on hold. +You've been handed a set of track pieces to inspect. +If they can form a continuous chain, the train will be on its way, bringing smiles across Toyland. +If not, the set will be discarded, and another will be tried. + +The toys are counting on you to solve this puzzle. +Will the dominoes connect the tracks and send the train rolling, or will the set be left behind? diff --git a/exercises/practice/dominoes/.meta/config.json b/exercises/practice/dominoes/.meta/config.json new file mode 100644 index 00000000..8d88cdb1 --- /dev/null +++ b/exercises/practice/dominoes/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "dominoes/dominoes.factor" + ], + "test": [ + "dominoes/dominoes-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Make a chain of dominoes." +} diff --git a/exercises/practice/dominoes/.meta/example.factor b/exercises/practice/dominoes/.meta/example.factor new file mode 100644 index 00000000..4b0598ad --- /dev/null +++ b/exercises/practice/dominoes/.meta/example.factor @@ -0,0 +1,37 @@ +USING: arrays kernel locals math math.bitwise ranges sequences ; +IN: dominoes + +! Union-Find on nibble values 0-15 +:: find-root ( parent i -- root ) + i :> r! + [ r parent nth r = not ] [ r parent nth r! ] while + r ; + +:: union ( parent a b -- ) + parent a find-root :> ra + parent b find-root :> rb + ra rb = not [ rb ra parent set-nth ] when ; + +:: can-chain? ( dominoes -- ? ) + dominoes empty? [ t ] [ + 16 0 :> degree + 16 >array :> parent + dominoes [| stone | + stone -4 shift :> left + stone 0xf bitand :> right + left degree [ 1 + ] change-nth + right degree [ 1 + ] change-nth + parent left right union + ] each + ! all degrees even + degree [ dup 0 = swap 2 mod 0 = or ] all? + ! all used vertices in same component + [ + 16 [ degree nth 0 > ] filter :> used + used empty? [ t ] [ + used first :> rep + parent rep find-root :> root + used [ parent swap find-root root = ] all? + ] if + ] [ f ] if + ] if ; diff --git a/exercises/practice/dominoes/.meta/generator.jl b/exercises/practice/dominoes/.meta/generator.jl new file mode 100644 index 00000000..5dcdc368 --- /dev/null +++ b/exercises/practice/dominoes/.meta/generator.jl @@ -0,0 +1,21 @@ +module Dominoes + +function encode_domino(d) + a = Int(d[1]) + b = Int(d[2]) + return "0x" * string(a, base=16) * string(b, base=16) +end + +function format_dominoes(dominoes) + parts = map(encode_domino, dominoes) + return "{ $(join(parts, " ")) }" +end + +function gen_test_case(case) + dominoes = case["input"]["dominoes"] + expected = case["expected"] ? "t" : "f" + arr = isempty(dominoes) ? "{ }" : format_dominoes(dominoes) + return "{ $(expected) } [ $(arr) can-chain? ] unit-test" +end + +end diff --git a/exercises/practice/dominoes/.meta/tests.toml b/exercises/practice/dominoes/.meta/tests.toml new file mode 100644 index 00000000..08c8e08d --- /dev/null +++ b/exercises/practice/dominoes/.meta/tests.toml @@ -0,0 +1,49 @@ +# 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. + +[31a673f2-5e54-49fe-bd79-1c1dae476c9c] +description = "empty input = empty output" + +[4f99b933-367b-404b-8c6d-36d5923ee476] +description = "singleton input = singleton output" + +[91122d10-5ec7-47cb-b759-033756375869] +description = "singleton that can't be chained" + +[be8bc26b-fd3d-440b-8e9f-d698a0623be3] +description = "three elements" + +[99e615c6-c059-401c-9e87-ad7af11fea5c] +description = "can reverse dominoes" + +[51f0c291-5d43-40c5-b316-0429069528c9] +description = "can't be chained" + +[9a75e078-a025-4c23-8c3a-238553657f39] +description = "disconnected - simple" + +[0da0c7fe-d492-445d-b9ef-1f111f07a301] +description = "disconnected - double loop" + +[b6087ff0-f555-4ea0-a71c-f9d707c5994a] +description = "disconnected - single isolated" + +[2174fbdc-8b48-4bac-9914-8090d06ef978] +description = "need backtrack" + +[167bb480-dfd1-4318-a20d-4f90adb4a09f] +description = "separate loops" + +[cd061538-6046-45a7-ace9-6708fe8f6504] +description = "nine elements" + +[44704c7c-3adb-4d98-bd30-f45527cf8b49] +description = "separate three-domino loops" diff --git a/exercises/practice/dominoes/dominoes/dominoes-tests.factor b/exercises/practice/dominoes/dominoes/dominoes-tests.factor new file mode 100644 index 00000000..1616fe15 --- /dev/null +++ b/exercises/practice/dominoes/dominoes/dominoes-tests.factor @@ -0,0 +1,47 @@ +USING: dominoes io kernel lexer tools.test unicode ; +IN: dominoes.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Dominoes:" print + +"empty input = empty output" print +{ t } [ { } can-chain? ] unit-test + +STOP-HERE + +"singleton input = singleton output" print +{ t } [ { 0x11 } can-chain? ] unit-test + +"singleton that can't be chained" print +{ f } [ { 0x12 } can-chain? ] unit-test + +"three elements" print +{ t } [ { 0x12 0x31 0x23 } can-chain? ] unit-test + +"can reverse dominoes" print +{ t } [ { 0x12 0x13 0x23 } can-chain? ] unit-test + +"can't be chained" print +{ f } [ { 0x12 0x41 0x23 } can-chain? ] unit-test + +"disconnected - simple" print +{ f } [ { 0x11 0x22 } can-chain? ] unit-test + +"disconnected - double loop" print +{ f } [ { 0x12 0x21 0x34 0x43 } can-chain? ] unit-test + +"disconnected - single isolated" print +{ f } [ { 0x12 0x23 0x31 0x44 } can-chain? ] unit-test + +"need backtrack" print +{ t } [ { 0x12 0x23 0x31 0x24 0x24 } can-chain? ] unit-test + +"separate loops" print +{ t } [ { 0x12 0x23 0x31 0x11 0x22 0x33 } can-chain? ] unit-test + +"nine elements" print +{ t } [ { 0x12 0x53 0x31 0x12 0x24 0x16 0x23 0x34 0x56 } can-chain? ] unit-test + +"separate three-domino loops" print +{ f } [ { 0x12 0x23 0x31 0x45 0x56 0x64 } can-chain? ] unit-test diff --git a/exercises/practice/dominoes/dominoes/dominoes.factor b/exercises/practice/dominoes/dominoes/dominoes.factor new file mode 100644 index 00000000..2486f6c1 --- /dev/null +++ b/exercises/practice/dominoes/dominoes/dominoes.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: dominoes + +: can-chain? ( dominoes -- ? ) + "unimplemented" throw ; diff --git a/exercises/practice/game-of-life/.docs/instructions.append.md b/exercises/practice/game-of-life/.docs/instructions.append.md new file mode 100644 index 00000000..1644fdb9 --- /dev/null +++ b/exercises/practice/game-of-life/.docs/instructions.append.md @@ -0,0 +1,21 @@ +# Instructions append + +Each row of the grid is represented as a 64 bit integer. + +For example, + +``` + 0 1 0 + 1 0 0 + 1 1 0 +``` + +is represented as + +``` +0b010 +0b100 +0b110 +``` + +The grid has at most 64 columns. diff --git a/exercises/practice/game-of-life/.docs/instructions.md b/exercises/practice/game-of-life/.docs/instructions.md new file mode 100644 index 00000000..49531406 --- /dev/null +++ b/exercises/practice/game-of-life/.docs/instructions.md @@ -0,0 +1,11 @@ +# Instructions + +After each generation, the cells interact with their eight neighbors, which are cells adjacent horizontally, vertically, or diagonally. + +The following rules are applied to each cell: + +- Any live cell with two or three live neighbors lives on. +- Any dead cell with exactly three live neighbors becomes a live cell. +- All other cells die or stay dead. + +Given a matrix of 1s and 0s (corresponding to live and dead cells), apply the rules to each cell, and return the next generation. diff --git a/exercises/practice/game-of-life/.docs/introduction.md b/exercises/practice/game-of-life/.docs/introduction.md new file mode 100644 index 00000000..2347b936 --- /dev/null +++ b/exercises/practice/game-of-life/.docs/introduction.md @@ -0,0 +1,9 @@ +# Introduction + +[Conway's Game of Life][game-of-life] is a fascinating cellular automaton created by the British mathematician John Horton Conway in 1970. + +The game consists of a two-dimensional grid of cells that can either be "alive" or "dead." + +After each generation, the cells interact with their eight neighbors via a set of rules, which define the new generation. + +[game-of-life]: https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life diff --git a/exercises/practice/game-of-life/.meta/config.json b/exercises/practice/game-of-life/.meta/config.json new file mode 100644 index 00000000..4d5ec2b8 --- /dev/null +++ b/exercises/practice/game-of-life/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "game-of-life/game-of-life.factor" + ], + "test": [ + "game-of-life/game-of-life-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Implement Conway's Game of Life.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life" +} diff --git a/exercises/practice/game-of-life/.meta/example.factor b/exercises/practice/game-of-life/.meta/example.factor new file mode 100644 index 00000000..8a33ee33 --- /dev/null +++ b/exercises/practice/game-of-life/.meta/example.factor @@ -0,0 +1,34 @@ +USING: kernel locals math math.bitwise ranges sequences ; +IN: game-of-life + +:: cell-alive? ( matrix cols r c -- ? ) + r 0 >= r matrix length < and + c 0 >= c cols < and and + [ r matrix nth cols 1 - c - bit? ] [ f ] if ; + +:: count-neighbors ( matrix cols r c -- n ) + 0 + r 1 - r 1 + [a..b] [| nr | + c 1 - c 1 + [a..b] [| nc | + nr r = nc c = and not [ + matrix cols nr nc cell-alive? [ 1 + ] when + ] when + ] each + ] each ; + +:: tick-cell ( matrix cols r c -- bit ) + matrix cols r c count-neighbors :> n + matrix cols r c cell-alive? + [ n 2 = n 3 = or ] + [ n 3 = ] if + [ 1 cols 1 - c - shift ] [ 0 ] if ; + +:: tick-row ( matrix cols r -- row ) + 0 cols [0..b) [| c | + matrix cols r c tick-cell bitor + ] each ; + +:: tick ( matrix cols -- matrix' ) + matrix length [0..b) [| r | + matrix cols r tick-row + ] map ; diff --git a/exercises/practice/game-of-life/.meta/generator.jl b/exercises/practice/game-of-life/.meta/generator.jl new file mode 100644 index 00000000..b00e692c --- /dev/null +++ b/exercises/practice/game-of-life/.meta/generator.jl @@ -0,0 +1,26 @@ +module GameOfLife + +function encode_row(row) + isempty(row) && return "0x0" + bits = join(map(x -> string(Int(x)), row), "") + return "0x" * string(parse(BigInt, bits, base=2), base=16) +end + +function format_matrix(matrix) + parts = map(encode_row, matrix) + return "{ $(join(parts, " ")) }" +end + +function gen_test_case(case) + matrix = case["input"]["matrix"] + expected = case["expected"] + if isempty(matrix) + return "{ { } } [ { } 0 tick ] unit-test" + end + cols = isempty(matrix[1]) ? 0 : length(matrix[1]) + inp = format_matrix(matrix) + exp = format_matrix(expected) + return "{ $(exp) }\n[ $(inp) $(cols) tick ] unit-test" +end + +end diff --git a/exercises/practice/game-of-life/.meta/supplements.json b/exercises/practice/game-of-life/.meta/supplements.json new file mode 100644 index 00000000..00006275 --- /dev/null +++ b/exercises/practice/game-of-life/.meta/supplements.json @@ -0,0 +1 @@ +[{"description": "matrix with 0 columns", "property": "tick", "input": {"matrix": [[], [], [], [], []]}, "expected": [[], [], [], [], []]}, {"description": "matrix with 32 columns", "property": "tick", "input": {"matrix": [[1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0], [1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0], [1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0], [1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0], [1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0]]}, "expected": [[1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0]]}, {"description": "matrix with 64 columns", "property": "tick", "input": {"matrix": [[1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0], [1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0]]}, "expected": [[0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0], [1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0], [0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0]]}] diff --git a/exercises/practice/game-of-life/.meta/tests.toml b/exercises/practice/game-of-life/.meta/tests.toml new file mode 100644 index 00000000..398cd454 --- /dev/null +++ b/exercises/practice/game-of-life/.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. + +[ae86ea7d-bd07-4357-90b3-ac7d256bd5c5] +description = "empty matrix" + +[4ea5ccb7-7b73-4281-954a-bed1b0f139a5] +description = "live cells with zero live neighbors die" + +[df245adc-14ff-4f9c-b2ae-f465ef5321b2] +description = "live cells with only one live neighbor die" + +[2a713b56-283c-48c8-adae-1d21306c80ae] +description = "live cells with two live neighbors stay alive" + +[86d5c5a5-ab7b-41a1-8907-c9b3fc5e9dae] +description = "live cells with three live neighbors stay alive" + +[015f60ac-39d8-4c6c-8328-57f334fc9f89] +description = "dead cells with three live neighbors become alive" + +[2ee69c00-9d41-4b8b-89da-5832e735ccf1] +description = "live cells with four or more neighbors die" + +[a79b42be-ed6c-4e27-9206-43da08697ef6] +description = "bigger matrix" diff --git a/exercises/practice/game-of-life/game-of-life/game-of-life-tests.factor b/exercises/practice/game-of-life/game-of-life/game-of-life-tests.factor new file mode 100644 index 00000000..2cfd1714 --- /dev/null +++ b/exercises/practice/game-of-life/game-of-life/game-of-life-tests.factor @@ -0,0 +1,51 @@ +USING: game-of-life io kernel lexer tools.test unicode ; +IN: game-of-life.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Game Of Life:" print + +"empty matrix" print +{ { } } [ { } 0 tick ] unit-test + +STOP-HERE + +"live cells with zero live neighbors die" print +{ { 0x0 0x0 0x0 } } +[ { 0x0 0x2 0x0 } 3 tick ] unit-test + +"live cells with only one live neighbor die" print +{ { 0x0 0x0 0x0 } } +[ { 0x0 0x2 0x2 } 3 tick ] unit-test + +"live cells with two live neighbors stay alive" print +{ { 0x0 0x5 0x0 } } +[ { 0x5 0x5 0x5 } 3 tick ] unit-test + +"live cells with three live neighbors stay alive" print +{ { 0x0 0x4 0x6 } } +[ { 0x2 0x4 0x6 } 3 tick ] unit-test + +"dead cells with three live neighbors become alive" print +{ { 0x0 0x6 0x0 } } +[ { 0x6 0x0 0x4 } 3 tick ] unit-test + +"live cells with four or more neighbors die" print +{ { 0x5 0x0 0x5 } } +[ { 0x7 0x7 0x7 } 3 tick ] unit-test + +"bigger matrix" print +{ { 0xd8 0x6 0xbd 0x81 0xc9 0xd1 0x80 0x3 } } +[ { 0xd8 0xb0 0xe7 0x6 0x8c 0xc7 0x29 0x83 } 8 tick ] unit-test + +"matrix with 0 columns" print +{ { 0x0 0x0 0x0 0x0 0x0 } } +[ { 0x0 0x0 0x0 0x0 0x0 } 0 tick ] unit-test + +"matrix with 32 columns" print +{ { 0xa26f4d98 0x1b08080 0x81f702a2 0x9573c200 0x3f01c1e } } +[ { 0xec6efb48 0xbeb23898 0xed06beb6 0x91205a96 0x93710c2c } 32 tick ] unit-test + +"matrix with 64 columns" print +{ { 0x41f020f868521f68 0xbfd1141075001842 0x35f8c70050a40b34 0x4117006050817e04 0x35761230487a1c38 } } +[ { 0xe1a9452f9072d77a 0x25150d7d533f22c 0x9c20fdcb0fadc212 0x55941c3f54993610 0x3ddd9f17d265087a } 64 tick ] unit-test diff --git a/exercises/practice/game-of-life/game-of-life/game-of-life.factor b/exercises/practice/game-of-life/game-of-life/game-of-life.factor new file mode 100644 index 00000000..e0e53db9 --- /dev/null +++ b/exercises/practice/game-of-life/game-of-life/game-of-life.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: game-of-life + +: tick ( matrix cols -- matrix' ) + "unimplemented" throw ; diff --git a/exercises/practice/list-ops/.docs/instructions.md b/exercises/practice/list-ops/.docs/instructions.md new file mode 100644 index 00000000..ebc5dffe --- /dev/null +++ b/exercises/practice/list-ops/.docs/instructions.md @@ -0,0 +1,19 @@ +# Instructions + +Implement basic list operations. + +In functional languages list operations like `length`, `map`, and `reduce` are very common. +Implement a series of basic list operations, without using existing functions. + +The precise number and names of the operations to be implemented will be track dependent to avoid conflicts with existing names, but the general operations you will implement include: + +- `append` (_given two lists, add all items in the second list to the end of the first list_); +- `concatenate` (_given a series of lists, combine all items in all lists into one flattened list_); +- `filter` (_given a predicate and a list, return the list of all items for which `predicate(item)` is True_); +- `length` (_given a list, return the total number of items within it_); +- `map` (_given a function and a list, return the list of the results of applying `function(item)` on all items_); +- `foldl` (_given a function, a list, and initial accumulator, fold (reduce) each item into the accumulator from the left_); +- `foldr` (_given a function, a list, and an initial accumulator, fold (reduce) each item into the accumulator from the right_); +- `reverse` (_given a list, return a list with all the original items, but in reversed order_). + +Note, the ordering in which arguments are passed to the fold functions (`foldl`, `foldr`) is significant. diff --git a/exercises/practice/list-ops/.meta/config.json b/exercises/practice/list-ops/.meta/config.json new file mode 100644 index 00000000..151a9dce --- /dev/null +++ b/exercises/practice/list-ops/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "list-ops/list-ops.factor" + ], + "test": [ + "list-ops/list-ops-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Implement basic list operations." +} diff --git a/exercises/practice/list-ops/.meta/example.factor b/exercises/practice/list-ops/.meta/example.factor new file mode 100644 index 00000000..9e0e7e7d --- /dev/null +++ b/exercises/practice/list-ops/.meta/example.factor @@ -0,0 +1,27 @@ +USING: arrays kernel locals math sequences ; +IN: list-ops + +: list-append ( seq1 seq2 -- seq ) append ; + +: list-concat ( seqs -- seq ) { } [ list-append ] reduce ; + +:: select ( seq quot: ( x -- ? ) -- seq' ) + V{ } clone seq [| x | x quot call [ x over push ] when ] each >array ; inline + +:: collect ( seq quot: ( x -- y ) -- seq' ) + seq length seq new-sequence seq [| x i | + x quot call i pick set-nth + ] each-index ; inline + +: list-length ( seq -- n ) 0 [ drop 1 + ] reduce ; + +:: list-reverse ( seq -- seq' ) + seq list-length :> len + len seq new-sequence + seq [| x i | x len 1 - i - pick set-nth ] each-index ; + +:: foldl ( seq init quot: ( acc el -- acc' ) -- result ) + init seq [ quot call ] each ; inline + +:: foldr ( seq init quot: ( acc el -- acc' ) -- result ) + init seq list-reverse [ quot call ] each ; inline diff --git a/exercises/practice/list-ops/.meta/generator.jl b/exercises/practice/list-ops/.meta/generator.jl new file mode 100644 index 00000000..6d34f531 --- /dev/null +++ b/exercises/practice/list-ops/.meta/generator.jl @@ -0,0 +1,56 @@ +module ListOps + +const WORD_NAMES = Dict( + "append" => "list-append", + "concat" => "list-concat", + "filter" => "select", + "length" => "list-length", + "map" => "collect", + "foldl" => "foldl", + "foldr" => "foldr", + "reverse" => "list-reverse", +) + +const FUNCTIONS = Dict( + "(x) -> x modulo 2 == 1" => "[ odd? ]", + "(x) -> x + 1" => "[ 1 + ]", + "(acc, el) -> el * acc" => "[ * ]", + "(acc, el) -> el + acc" => "[ + ]", + "(acc, el) -> el / acc" => "[ swap / ]", +) + +function gen_test_case(case) + prop = case["property"] + word = WORD_NAMES[prop] + expected = case["expected"] + + if prop == "append" + l1 = format_nested_array(case["input"]["list1"]) + l2 = format_nested_array(case["input"]["list2"]) + return "{ $(format_nested_array(expected)) }\n[ $(l1) $(l2) $(word) ] unit-test" + + elseif prop == "concat" + lists = format_nested_array(case["input"]["lists"]) + return "{ $(format_nested_array(expected)) }\n[ $(lists) $(word) ] unit-test" + + elseif prop in ("filter", "map") + list = format_int_array(case["input"]["list"]) + func = FUNCTIONS[case["input"]["function"]] + return "{ $(format_int_array(expected)) }\n[ $(list) $(func) $(word) ] unit-test" + + elseif prop in ("foldl", "foldr") + list = format_int_array(case["input"]["list"]) + func = FUNCTIONS[case["input"]["function"]] + return "{ $(to_int_str(expected)) }\n[ $(list) $(to_int_str(case["input"]["initial"])) $(func) $(word) ] unit-test" + + elseif prop == "length" + list = format_int_array(case["input"]["list"]) + return "{ $(Int(expected)) }\n[ $(list) $(word) ] unit-test" + + elseif prop == "reverse" + list = format_nested_array(case["input"]["list"]) + return "{ $(format_nested_array(expected)) }\n[ $(list) $(word) ] unit-test" + end +end + +end diff --git a/exercises/practice/list-ops/.meta/tests.toml b/exercises/practice/list-ops/.meta/tests.toml new file mode 100644 index 00000000..08b1edc0 --- /dev/null +++ b/exercises/practice/list-ops/.meta/tests.toml @@ -0,0 +1,106 @@ +# 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. + +[485b9452-bf94-40f7-a3db-c3cf4850066a] +description = "append entries to a list and return the new list -> empty lists" + +[2c894696-b609-4569-b149-8672134d340a] +description = "append entries to a list and return the new list -> list to empty list" + +[e842efed-3bf6-4295-b371-4d67a4fdf19c] +description = "append entries to a list and return the new list -> empty list to list" + +[71dcf5eb-73ae-4a0e-b744-a52ee387922f] +description = "append entries to a list and return the new list -> non-empty lists" + +[28444355-201b-4af2-a2f6-5550227bde21] +description = "concatenate a list of lists -> empty list" + +[331451c1-9573-42a1-9869-2d06e3b389a9] +description = "concatenate a list of lists -> list of lists" + +[d6ecd72c-197f-40c3-89a4-aa1f45827e09] +description = "concatenate a list of lists -> list of nested lists" + +[0524fba8-3e0f-4531-ad2b-f7a43da86a16] +description = "filter list returning only values that satisfy the filter function -> empty list" + +[88494bd5-f520-4edb-8631-88e415b62d24] +description = "filter list returning only values that satisfy the filter function -> non-empty list" + +[1cf0b92d-8d96-41d5-9c21-7b3c37cb6aad] +description = "returns the length of a list -> empty list" + +[d7b8d2d9-2d16-44c4-9a19-6e5f237cb71e] +description = "returns the length of a list -> non-empty list" + +[c0bc8962-30e2-4bec-9ae4-668b8ecd75aa] +description = "return a list of elements whose values equal the list value transformed by the mapping function -> empty list" + +[11e71a95-e78b-4909-b8e4-60cdcaec0e91] +description = "return a list of elements whose values equal the list value transformed by the mapping function -> non-empty list" + +[613b20b7-1873-4070-a3a6-70ae5f50d7cc] +description = "folds (reduces) the given list from the left with a function -> empty list" +include = false + +[e56df3eb-9405-416a-b13a-aabb4c3b5194] +description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list" +include = false + +[d2cf5644-aee1-4dfc-9b88-06896676fe27] +description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list" +include = false + +[36549237-f765-4a4c-bfd9-5d3a8f7b07d2] +description = "folds (reduces) the given list from the left with a function -> empty list" +reimplements = "613b20b7-1873-4070-a3a6-70ae5f50d7cc" + +[7a626a3c-03ec-42bc-9840-53f280e13067] +description = "folds (reduces) the given list from the left with a function -> direction independent function applied to non-empty list" +reimplements = "e56df3eb-9405-416a-b13a-aabb4c3b5194" + +[d7fcad99-e88e-40e1-a539-4c519681f390] +description = "folds (reduces) the given list from the left with a function -> direction dependent function applied to non-empty list" +reimplements = "d2cf5644-aee1-4dfc-9b88-06896676fe27" + +[aeb576b9-118e-4a57-a451-db49fac20fdc] +description = "folds (reduces) the given list from the right with a function -> empty list" +include = false + +[c4b64e58-313e-4c47-9c68-7764964efb8e] +description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list" +include = false + +[be396a53-c074-4db3-8dd6-f7ed003cce7c] +description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list" +include = false + +[17214edb-20ba-42fc-bda8-000a5ab525b0] +description = "folds (reduces) the given list from the right with a function -> empty list" +reimplements = "aeb576b9-118e-4a57-a451-db49fac20fdc" + +[e1c64db7-9253-4a3d-a7c4-5273b9e2a1bd] +description = "folds (reduces) the given list from the right with a function -> direction independent function applied to non-empty list" +reimplements = "c4b64e58-313e-4c47-9c68-7764964efb8e" + +[8066003b-f2ff-437e-9103-66e6df474844] +description = "folds (reduces) the given list from the right with a function -> direction dependent function applied to non-empty list" +reimplements = "be396a53-c074-4db3-8dd6-f7ed003cce7c" + +[94231515-050e-4841-943d-d4488ab4ee30] +description = "reverse the elements of the list -> empty list" + +[fcc03d1e-42e0-4712-b689-d54ad761f360] +description = "reverse the elements of the list -> non-empty list" + +[40872990-b5b8-4cb8-9085-d91fc0d05d26] +description = "reverse the elements of the list -> list of lists is not flattened" diff --git a/exercises/practice/list-ops/list-ops/list-ops-tests.factor b/exercises/practice/list-ops/list-ops/list-ops-tests.factor new file mode 100644 index 00000000..998a0dbc --- /dev/null +++ b/exercises/practice/list-ops/list-ops/list-ops-tests.factor @@ -0,0 +1,96 @@ +USING: io kernel lexer list-ops tools.test unicode ; +IN: list-ops.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"List Ops:" print + +"empty lists" print +{ { } } +[ { } { } list-append ] unit-test + +STOP-HERE + +"list to empty list" print +{ { 1 2 3 4 } } +[ { } { 1 2 3 4 } list-append ] unit-test + +"empty list to list" print +{ { 1 2 3 4 } } +[ { 1 2 3 4 } { } list-append ] unit-test + +"non-empty lists" print +{ { 1 2 2 3 4 5 } } +[ { 1 2 } { 2 3 4 5 } list-append ] unit-test + +"empty list" print +{ { } } +[ { } list-concat ] unit-test + +"list of lists" print +{ { 1 2 3 4 5 6 } } +[ { { 1 2 } { 3 } { } { 4 5 6 } } list-concat ] unit-test + +"list of nested lists" print +{ { { 1 } { 2 } { 3 } { } { 4 5 6 } } } +[ { { { 1 } { 2 } } { { 3 } } { { } } { { 4 5 6 } } } list-concat ] unit-test + +"empty list" print +{ { } } +[ { } [ odd? ] select ] unit-test + +"non-empty list" print +{ { 1 3 5 } } +[ { 1 2 3 5 } [ odd? ] select ] unit-test + +"empty list" print +{ 0 } +[ { } list-length ] unit-test + +"non-empty list" print +{ 4 } +[ { 1 2 3 4 } list-length ] unit-test + +"empty list" print +{ { } } +[ { } [ 1 + ] collect ] unit-test + +"non-empty list" print +{ { 2 4 6 8 } } +[ { 1 3 5 7 } [ 1 + ] collect ] unit-test + +"empty list" print +{ 2 } +[ { } 2 [ * ] foldl ] unit-test + +"direction independent function applied to non-empty list" print +{ 15 } +[ { 1 2 3 4 } 5 [ + ] foldl ] unit-test + +"direction dependent function applied to non-empty list" print +{ 64 } +[ { 1 2 3 4 } 24 [ swap / ] foldl ] unit-test + +"empty list" print +{ 2 } +[ { } 2 [ * ] foldr ] unit-test + +"direction independent function applied to non-empty list" print +{ 15 } +[ { 1 2 3 4 } 5 [ + ] foldr ] unit-test + +"direction dependent function applied to non-empty list" print +{ 9 } +[ { 1 2 3 4 } 24 [ swap / ] foldr ] unit-test + +"empty list" print +{ { } } +[ { } list-reverse ] unit-test + +"non-empty list" print +{ { 7 5 3 1 } } +[ { 1 3 5 7 } list-reverse ] unit-test + +"list of lists is not flattened" print +{ { { 4 5 6 } { } { 3 } { 1 2 } } } +[ { { 1 2 } { 3 } { } { 4 5 6 } } list-reverse ] unit-test diff --git a/exercises/practice/list-ops/list-ops/list-ops.factor b/exercises/practice/list-ops/list-ops/list-ops.factor new file mode 100644 index 00000000..088c51c9 --- /dev/null +++ b/exercises/practice/list-ops/list-ops/list-ops.factor @@ -0,0 +1,26 @@ +USING: kernel ; +IN: list-ops + +: list-append ( seq1 seq2 -- seq ) + "unimplemented" throw ; + +: list-concat ( seqs -- seq ) + "unimplemented" throw ; + +: select ( seq quot -- seq' ) + "unimplemented" throw ; inline + +: collect ( seq quot -- seq' ) + "unimplemented" throw ; inline + +: foldl ( seq init quot -- result ) + "unimplemented" throw ; inline + +: foldr ( seq init quot -- result ) + "unimplemented" throw ; inline + +: list-length ( seq -- n ) + "unimplemented" throw ; + +: list-reverse ( seq -- seq' ) + "unimplemented" throw ; diff --git a/exercises/practice/luhn/.docs/instructions.md b/exercises/practice/luhn/.docs/instructions.md new file mode 100644 index 00000000..7702c6bb --- /dev/null +++ b/exercises/practice/luhn/.docs/instructions.md @@ -0,0 +1,68 @@ +# Instructions + +Determine whether a number is valid according to the [Luhn formula][luhn]. + +The number will be provided as a string. + +## Validating a number + +Strings of length 1 or less are not valid. +Spaces are allowed in the input, but they should be stripped before checking. +All other non-digit characters are disallowed. + +## Examples + +### Valid credit card number + +The number to be checked is `4539 3195 0343 6467`. + +The first step of the Luhn algorithm is to start at the end of the number and double every second digit, beginning with the second digit from the right and moving left. + +```text +4539 3195 0343 6467 +↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ (double these) +``` + +If the result of doubling a digit is greater than 9, we subtract 9 from that result. +We end up with: + +```text +8569 6195 0383 3437 +``` + +Finally, we sum all digits. +If the sum is evenly divisible by 10, the original number is valid. + +```text +8 + 5 + 6 + 9 + 6 + 1 + 9 + 5 + 0 + 3 + 8 + 3 + 3 + 4 + 3 + 7 = 80 +``` + +80 is evenly divisible by 10, so number `4539 3195 0343 6467` is valid! + +### Invalid Canadian SIN + +The number to be checked is `066 123 478`. + +We start at the end of the number and double every second digit, beginning with the second digit from the right and moving left. + +```text +066 123 478 + ↑ ↑ ↑ ↑ (double these) +``` + +If the result of doubling a digit is greater than 9, we subtract 9 from that result. +We end up with: + +```text +036 226 458 +``` + +We sum the digits: + +```text +0 + 3 + 6 + 2 + 2 + 6 + 4 + 5 + 8 = 36 +``` + +36 is not evenly divisible by 10, so number `066 123 478` is not valid! + +[luhn]: https://en.wikipedia.org/wiki/Luhn_algorithm diff --git a/exercises/practice/luhn/.docs/introduction.md b/exercises/practice/luhn/.docs/introduction.md new file mode 100644 index 00000000..dee48006 --- /dev/null +++ b/exercises/practice/luhn/.docs/introduction.md @@ -0,0 +1,11 @@ +# Introduction + +At the Global Verification Authority, you've just been entrusted with a critical assignment. +Across the city, from online purchases to secure logins, countless operations rely on the accuracy of numerical identifiers like credit card numbers, bank account numbers, transaction codes, and tracking IDs. +The Luhn algorithm is a simple checksum formula used to help identify mistyped numbers. + +A batch of identifiers has just arrived on your desk. +All of them must pass the Luhn test to ensure they're legitimate. +If any fail, they'll be flagged as invalid, preventing mistakes such as incorrect transactions or failed account verifications. + +Can you ensure this is done right? The integrity of many services depends on you. diff --git a/exercises/practice/luhn/.meta/config.json b/exercises/practice/luhn/.meta/config.json new file mode 100644 index 00000000..29a65268 --- /dev/null +++ b/exercises/practice/luhn/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "luhn/luhn.factor" + ], + "test": [ + "luhn/luhn-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a number determine whether or not it is valid per the Luhn formula.", + "source": "The Luhn Algorithm on Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Luhn_algorithm" +} diff --git a/exercises/practice/luhn/.meta/example.factor b/exercises/practice/luhn/.meta/example.factor new file mode 100644 index 00000000..c79f2ca1 --- /dev/null +++ b/exercises/practice/luhn/.meta/example.factor @@ -0,0 +1,15 @@ +USING: kernel locals math sequences sets strings unicode ; +IN: luhn + +:: valid? ( value -- ? ) + value " " without :> stripped + stripped length 2 < [ f ] [ + stripped [ digit? ] all? [ + stripped reverse [| ch i | + ch CHAR: 0 - + i odd? [ + 2 * dup 9 > [ 9 - ] when + ] when + ] map-index sum 10 mod 0 = + ] [ f ] if + ] if ; diff --git a/exercises/practice/luhn/.meta/generator.jl b/exercises/practice/luhn/.meta/generator.jl new file mode 100644 index 00000000..d3130e56 --- /dev/null +++ b/exercises/practice/luhn/.meta/generator.jl @@ -0,0 +1,9 @@ +module Luhn + +function gen_test_case(case) + value = case["input"]["value"] + expected = case["expected"] ? "t" : "f" + return """{ $(expected) } [ "$(value)" valid? ] unit-test""" +end + +end diff --git a/exercises/practice/luhn/.meta/tests.toml b/exercises/practice/luhn/.meta/tests.toml new file mode 100644 index 00000000..c0be0c4d --- /dev/null +++ b/exercises/practice/luhn/.meta/tests.toml @@ -0,0 +1,76 @@ +# 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. + +[792a7082-feb7-48c7-b88b-bbfec160865e] +description = "single digit strings can not be valid" + +[698a7924-64d4-4d89-8daa-32e1aadc271e] +description = "a single zero is invalid" + +[73c2f62b-9b10-4c9f-9a04-83cee7367965] +description = "a simple valid SIN that remains valid if reversed" + +[9369092e-b095-439f-948d-498bd076be11] +description = "a simple valid SIN that becomes invalid if reversed" + +[8f9f2350-1faf-4008-ba84-85cbb93ffeca] +description = "a valid Canadian SIN" + +[1cdcf269-6560-44fc-91f6-5819a7548737] +description = "invalid Canadian SIN" + +[656c48c1-34e8-4e60-9a5a-aad8a367810a] +description = "invalid credit card" + +[20e67fad-2121-43ed-99a8-14b5b856adb9] +description = "invalid long number with an even remainder" + +[7e7c9fc1-d994-457c-811e-d390d52fba5e] +description = "invalid long number with a remainder divisible by 5" + +[ad2a0c5f-84ed-4e5b-95da-6011d6f4f0aa] +description = "valid number with an even number of digits" + +[ef081c06-a41f-4761-8492-385e13c8202d] +description = "valid number with an odd number of spaces" + +[bef66f64-6100-4cbb-8f94-4c9713c5e5b2] +description = "valid strings with a non-digit added at the end become invalid" + +[2177e225-9ce7-40f6-b55d-fa420e62938e] +description = "valid strings with punctuation included become invalid" + +[ebf04f27-9698-45e1-9afe-7e0851d0fe8d] +description = "valid strings with symbols included become invalid" + +[08195c5e-ce7f-422c-a5eb-3e45fece68ba] +description = "single zero with space is invalid" + +[12e63a3c-f866-4a79-8c14-b359fc386091] +description = "more than a single zero is valid" + +[ab56fa80-5de8-4735-8a4a-14dae588663e] +description = "input digit 9 is correctly converted to output digit 9" + +[b9887ee8-8337-46c5-bc45-3bcab51bc36f] +description = "very long input is valid" + +[8a7c0e24-85ea-4154-9cf1-c2db90eabc08] +description = "valid luhn with an odd number of digits and non zero first digit" + +[39a06a5a-5bad-4e0f-b215-b042d46209b1] +description = "using ascii value for non-doubled non-digit isn't allowed" + +[f94cf191-a62f-4868-bc72-7253114aa157] +description = "using ascii value for doubled non-digit isn't allowed" + +[8b72ad26-c8be-49a2-b99c-bcc3bf631b33] +description = "non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed" diff --git a/exercises/practice/luhn/luhn/luhn-tests.factor b/exercises/practice/luhn/luhn/luhn-tests.factor new file mode 100644 index 00000000..03012533 --- /dev/null +++ b/exercises/practice/luhn/luhn/luhn-tests.factor @@ -0,0 +1,74 @@ +USING: io kernel lexer luhn tools.test unicode ; +IN: luhn.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Luhn:" print + +"single digit strings can not be valid" print +{ f } [ "1" valid? ] unit-test + +STOP-HERE + +"a single zero is invalid" print +{ f } [ "0" valid? ] unit-test + +"a simple valid SIN that remains valid if reversed" print +{ t } [ "059" valid? ] unit-test + +"a simple valid SIN that becomes invalid if reversed" print +{ t } [ "59" valid? ] unit-test + +"a valid Canadian SIN" print +{ t } [ "055 444 285" valid? ] unit-test + +"invalid Canadian SIN" print +{ f } [ "055 444 286" valid? ] unit-test + +"invalid credit card" print +{ f } [ "8273 1232 7352 0569" valid? ] unit-test + +"invalid long number with an even remainder" print +{ f } [ "1 2345 6789 1234 5678 9012" valid? ] unit-test + +"invalid long number with a remainder divisible by 5" print +{ f } [ "1 2345 6789 1234 5678 9013" valid? ] unit-test + +"valid number with an even number of digits" print +{ t } [ "095 245 88" valid? ] unit-test + +"valid number with an odd number of spaces" print +{ t } [ "234 567 891 234" valid? ] unit-test + +"valid strings with a non-digit added at the end become invalid" print +{ f } [ "059a" valid? ] unit-test + +"valid strings with punctuation included become invalid" print +{ f } [ "055-444-285" valid? ] unit-test + +"valid strings with symbols included become invalid" print +{ f } [ "055# 444$ 285" valid? ] unit-test + +"single zero with space is invalid" print +{ f } [ " 0" valid? ] unit-test + +"more than a single zero is valid" print +{ t } [ "0000 0" valid? ] unit-test + +"input digit 9 is correctly converted to output digit 9" print +{ t } [ "091" valid? ] unit-test + +"very long input is valid" print +{ t } [ "9999999999 9999999999 9999999999 9999999999" valid? ] unit-test + +"valid luhn with an odd number of digits and non zero first digit" print +{ t } [ "109" valid? ] unit-test + +"using ascii value for non-doubled non-digit isn't allowed" print +{ f } [ "055b 444 285" valid? ] unit-test + +"using ascii value for doubled non-digit isn't allowed" print +{ f } [ ":9" valid? ] unit-test + +"non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed" print +{ f } [ "59%59" valid? ] unit-test diff --git a/exercises/practice/luhn/luhn/luhn.factor b/exercises/practice/luhn/luhn/luhn.factor new file mode 100644 index 00000000..3330b976 --- /dev/null +++ b/exercises/practice/luhn/luhn/luhn.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: luhn + +: valid? ( value -- ? ) + "unimplemented" throw ; diff --git a/exercises/practice/matching-brackets/.docs/instructions.md b/exercises/practice/matching-brackets/.docs/instructions.md new file mode 100644 index 00000000..ea170842 --- /dev/null +++ b/exercises/practice/matching-brackets/.docs/instructions.md @@ -0,0 +1,5 @@ +# Instructions + +Given a string containing brackets `[]`, braces `{}`, parentheses `()`, or any combination thereof, verify that any and all pairs are matched and nested correctly. +Any other characters should be ignored. +For example, `"{what is (42)}?"` is balanced and `"[text}"` is not. diff --git a/exercises/practice/matching-brackets/.docs/introduction.md b/exercises/practice/matching-brackets/.docs/introduction.md new file mode 100644 index 00000000..0618221b --- /dev/null +++ b/exercises/practice/matching-brackets/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +You're given the opportunity to write software for the Bracketeer™, an ancient but powerful mainframe. +The software that runs on it is written in a proprietary language. +Much of its syntax is familiar, but you notice _lots_ of brackets, braces and parentheses. +Despite the Bracketeer™ being powerful, it lacks flexibility. +If the source code has any unbalanced brackets, braces or parentheses, the Bracketeer™ crashes and must be rebooted. +To avoid such a scenario, you start writing code that can verify that brackets, braces, and parentheses are balanced before attempting to run it on the Bracketeer™. diff --git a/exercises/practice/matching-brackets/.meta/config.json b/exercises/practice/matching-brackets/.meta/config.json new file mode 100644 index 00000000..3067525d --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "matching-brackets/matching-brackets.factor" + ], + "test": [ + "matching-brackets/matching-brackets-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Make sure the brackets and braces all match.", + "source": "Ginna Baker" +} diff --git a/exercises/practice/matching-brackets/.meta/example.factor b/exercises/practice/matching-brackets/.meta/example.factor new file mode 100644 index 00000000..0dce44d8 --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/example.factor @@ -0,0 +1,23 @@ +USING: combinators kernel locals sequences ; +IN: matching-brackets + +: opener ( ch -- opener/f ) + { { CHAR: ) [ CHAR: ( ] } + { CHAR: ] [ CHAR: [ ] } + { CHAR: } [ CHAR: { ] } + [ drop f ] } case ; + +:: paired? ( str -- ? ) + V{ } clone :> stack + t :> ok! + str [| ch | + ok [ + ch "([{" member? [ ch stack push ] [ + ch opener [| expected | + stack empty? not stack ?last expected = and + [ stack pop* ] [ f ok! ] if + ] when* + ] if + ] when + ] each + ok stack empty? and ; diff --git a/exercises/practice/matching-brackets/.meta/generator.jl b/exercises/practice/matching-brackets/.meta/generator.jl new file mode 100644 index 00000000..9f5e9367 --- /dev/null +++ b/exercises/practice/matching-brackets/.meta/generator.jl @@ -0,0 +1,9 @@ +module MatchingBrackets + +function gen_test_case(case) + value = escape_factor(case["input"]["value"]) + expected = case["expected"] ? "t" : "f" + return """{ $(expected) } [ "$(value)" paired? ] unit-test""" +end + +end diff --git a/exercises/practice/matching-brackets/.meta/tests.toml b/exercises/practice/matching-brackets/.meta/tests.toml new file mode 100644 index 00000000..35a98a04 --- /dev/null +++ b/exercises/practice/matching-brackets/.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. + +[81ec11da-38dd-442a-bcf9-3de7754609a5] +description = "paired square brackets" + +[287f0167-ac60-4b64-8452-a0aa8f4e5238] +description = "empty string" + +[6c3615a3-df01-4130-a731-8ef5f5d78dac] +description = "unpaired brackets" + +[9d414171-9b98-4cac-a4e5-941039a97a77] +description = "wrong ordered brackets" + +[f0f97c94-a149-4736-bc61-f2c5148ffb85] +description = "wrong closing bracket" + +[754468e0-4696-4582-a30e-534d47d69756] +description = "paired with whitespace" + +[ba84f6ee-8164-434a-9c3e-b02c7f8e8545] +description = "partially paired brackets" + +[3c86c897-5ff3-4a2b-ad9b-47ac3a30651d] +description = "simple nested brackets" + +[2d137f2c-a19e-4993-9830-83967a2d4726] +description = "several paired brackets" + +[2e1f7b56-c137-4c92-9781-958638885a44] +description = "paired and nested brackets" + +[84f6233b-e0f7-4077-8966-8085d295c19b] +description = "unopened closing brackets" + +[9b18c67d-7595-4982-b2c5-4cb949745d49] +description = "unpaired and nested brackets" + +[a0205e34-c2ac-49e6-a88a-899508d7d68e] +description = "paired and wrong nested brackets" + +[1d5c093f-fc84-41fb-8c2a-e052f9581602] +description = "paired and wrong nested brackets but innermost are correct" + +[ef47c21b-bcfd-4998-844c-7ad5daad90a8] +description = "paired and incomplete brackets" + +[a4675a40-a8be-4fc2-bc47-2a282ce6edbe] +description = "too many closing brackets" + +[a345a753-d889-4b7e-99ae-34ac85910d1a] +description = "early unexpected brackets" + +[21f81d61-1608-465a-b850-baa44c5def83] +description = "early mismatched brackets" + +[99255f93-261b-4435-a352-02bdecc9bdf2] +description = "math expression" + +[8e357d79-f302-469a-8515-2561877256a1] +description = "complex latex expression" diff --git a/exercises/practice/matching-brackets/matching-brackets/matching-brackets-tests.factor b/exercises/practice/matching-brackets/matching-brackets/matching-brackets-tests.factor new file mode 100644 index 00000000..4f227879 --- /dev/null +++ b/exercises/practice/matching-brackets/matching-brackets/matching-brackets-tests.factor @@ -0,0 +1,68 @@ +USING: io kernel lexer matching-brackets tools.test unicode ; +IN: matching-brackets.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Matching Brackets:" print + +"paired square brackets" print +{ t } [ "[]" paired? ] unit-test + +STOP-HERE + +"empty string" print +{ t } [ "" paired? ] unit-test + +"unpaired brackets" print +{ f } [ "[[" paired? ] unit-test + +"wrong ordered brackets" print +{ f } [ "}{" paired? ] unit-test + +"wrong closing bracket" print +{ f } [ "{]" paired? ] unit-test + +"paired with whitespace" print +{ t } [ "{ }" paired? ] unit-test + +"partially paired brackets" print +{ f } [ "{[])" paired? ] unit-test + +"simple nested brackets" print +{ t } [ "{[]}" paired? ] unit-test + +"several paired brackets" print +{ t } [ "{}[]" paired? ] unit-test + +"paired and nested brackets" print +{ t } [ "([{}({}[])])" paired? ] unit-test + +"unopened closing brackets" print +{ f } [ "{[)][]}" paired? ] unit-test + +"unpaired and nested brackets" print +{ f } [ "([{])" paired? ] unit-test + +"paired and wrong nested brackets" print +{ f } [ "[({]})" paired? ] unit-test + +"paired and wrong nested brackets but innermost are correct" print +{ f } [ "[({}])" paired? ] unit-test + +"paired and incomplete brackets" print +{ f } [ "{}[" paired? ] unit-test + +"too many closing brackets" print +{ f } [ "[]]" paired? ] unit-test + +"early unexpected brackets" print +{ f } [ ")()" paired? ] unit-test + +"early mismatched brackets" print +{ f } [ "{)()" paired? ] unit-test + +"math expression" print +{ t } [ "(((185 + 223.85) * 15) - 543)/2" paired? ] unit-test + +"complex latex expression" print +{ t } [ "\\left(\\begin{array}{cc} \\frac{1}{3} & x\\\\ \\mathrm{e}^{x} &... x^2 \\end{array}\\right)" paired? ] unit-test diff --git a/exercises/practice/matching-brackets/matching-brackets/matching-brackets.factor b/exercises/practice/matching-brackets/matching-brackets/matching-brackets.factor new file mode 100644 index 00000000..092b1ee4 --- /dev/null +++ b/exercises/practice/matching-brackets/matching-brackets/matching-brackets.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: matching-brackets + +: paired? ( str -- ? ) + "unimplemented" throw ; diff --git a/exercises/practice/ocr-numbers/.docs/instructions.md b/exercises/practice/ocr-numbers/.docs/instructions.md new file mode 100644 index 00000000..8a391ce4 --- /dev/null +++ b/exercises/practice/ocr-numbers/.docs/instructions.md @@ -0,0 +1,47 @@ +# Instructions + +Optical Character Recognition or OCR is software that converts images of text into machine-readable text. +Given a grid of characters representing some digits, convert the grid to a string of digits. +If the grid has multiple rows of cells, the rows should be separated in the output with a `","`. + +- The grid is made of one of more lines of cells. +- Each line of the grid is made of one or more cells. +- Each cell is three columns wide and four rows high (3x4) and represents one digit. +- Digits are drawn using pipes (`"|"`), underscores (`"_"`), and spaces (`" "`). + +## Edge cases + +- If the input is not a valid size, your program should indicate there is an error. +- If the input is the correct size, but a cell is not recognizable, your program should output a `"?"` for that character. + +## Examples + +The following input (without the comments) is converted to `"1234567890"`. + +```text + _ _ _ _ _ _ _ _ # + | _| _||_||_ |_ ||_||_|| | # Decimal numbers. + ||_ _| | _||_| ||_| _||_| # + # The fourth line is always blank, +``` + +The following input is converted to `"123,456,789"`. + + + +```text + _ _ + | _| _| + ||_ _| + + _ _ +|_||_ |_ + | _||_| + + _ _ _ + ||_||_| + ||_| _| + +``` + + diff --git a/exercises/practice/ocr-numbers/.docs/introduction.md b/exercises/practice/ocr-numbers/.docs/introduction.md new file mode 100644 index 00000000..366d7606 --- /dev/null +++ b/exercises/practice/ocr-numbers/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +Your best friend Marta recently landed their dream job working with a local history museum's collections. +Knowing of your interests in programming, they confide in you about an issue at work for an upcoming exhibit on computing history. +A local university's math department had donated several boxes of historical printouts, but given the poor condition of the documents, the decision has been made to digitize the text. +However, the university's old printer had some quirks in how text was represented, and your friend could use your help to extract the data successfully. diff --git a/exercises/practice/ocr-numbers/.meta/config.json b/exercises/practice/ocr-numbers/.meta/config.json new file mode 100644 index 00000000..22114fa9 --- /dev/null +++ b/exercises/practice/ocr-numbers/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "ocr-numbers/ocr-numbers.factor" + ], + "test": [ + "ocr-numbers/ocr-numbers-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a 3 x 4 grid of pipes, underscores, and spaces, determine which number is represented, or whether it is garbled.", + "source": "Inspired by the Bank OCR kata", + "source_url": "https://codingdojo.org/kata/BankOCR/" +} diff --git a/exercises/practice/ocr-numbers/.meta/example.factor b/exercises/practice/ocr-numbers/.meta/example.factor new file mode 100644 index 00000000..aba1a96e --- /dev/null +++ b/exercises/practice/ocr-numbers/.meta/example.factor @@ -0,0 +1,33 @@ +USING: assocs grouping kernel locals math sequences strings ; +IN: ocr-numbers + +CONSTANT: digit-patterns H{ + { " _ | ||_| " CHAR: 0 } + { " | | " CHAR: 1 } + { " _ _||_ " CHAR: 2 } + { " _ _| _| " CHAR: 3 } + { " |_| | " CHAR: 4 } + { " _ |_ _| " CHAR: 5 } + { " _ |_ |_| " CHAR: 6 } + { " _ | | " CHAR: 7 } + { " _ |_||_| " CHAR: 8 } + { " _ |_| _| " CHAR: 9 } +} + +:: recognize-digit ( rows col -- ch ) + 4 [| r | + 3 [| c | col c + r rows nth nth ] map + ] map concat >string + digit-patterns at [ CHAR: ? ] unless* ; + +:: recognize-row ( rows -- str ) + rows first length 3 /i + [| i | rows i 3 * recognize-digit ] map >string ; + +:: convert ( rows -- str ) + rows length 4 mod 0 = not + [ "Number of input lines is not a multiple of four" throw ] when + rows first length 3 mod 0 = not + [ "Number of input columns is not a multiple of three" throw ] when + rows 4 group + [ recognize-row ] map "," join ; diff --git a/exercises/practice/ocr-numbers/.meta/generator.jl b/exercises/practice/ocr-numbers/.meta/generator.jl new file mode 100644 index 00000000..0c3acdd6 --- /dev/null +++ b/exercises/practice/ocr-numbers/.meta/generator.jl @@ -0,0 +1,14 @@ +module OcrNumbers + +function gen_test_case(case) + rows = format_string_array(case["input"]["rows"]) + expected = case["expected"] + if expected isa Dict + msg = expected["error"] + return """[ $(rows) convert ]\n[ "$(msg)" = ] must-fail-with""" + else + return """{ "$(expected)" }\n[ $(rows) convert ] unit-test""" + end +end + +end diff --git a/exercises/practice/ocr-numbers/.meta/tests.toml b/exercises/practice/ocr-numbers/.meta/tests.toml new file mode 100644 index 00000000..0d7a5b77 --- /dev/null +++ b/exercises/practice/ocr-numbers/.meta/tests.toml @@ -0,0 +1,61 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[5ee54e1a-b554-4bf3-a056-9a7976c3f7e8] +description = "Recognizes 0" + +[027ada25-17fd-4d78-aee6-35a19623639d] +description = "Recognizes 1" + +[3cce2dbd-01d9-4f94-8fae-419a822e89bb] +description = "Unreadable but correctly sized inputs return ?" + +[cb19b733-4e36-4cf9-a4a1-6e6aac808b9a] +description = "Input with a number of lines that is not a multiple of four raises an error" + +[235f7bd1-991b-4587-98d4-84206eec4cc6] +description = "Input with a number of columns that is not a multiple of three raises an error" + +[4a841794-73c9-4da9-a779-1f9837faff66] +description = "Recognizes 110101100" + +[70c338f9-85b1-4296-a3a8-122901cdfde8] +description = "Garbled numbers in a string are replaced with ?" + +[ea494ff4-3610-44d7-ab7e-72fdef0e0802] +description = "Recognizes 2" + +[1acd2c00-412b-4268-93c2-bd7ff8e05a2c] +description = "Recognizes 3" + +[eaec6a15-be17-4b6d-b895-596fae5d1329] +description = "Recognizes 4" + +[440f397a-f046-4243-a6ca-81ab5406c56e] +description = "Recognizes 5" + +[f4c9cf6a-f1e2-4878-bfc3-9b85b657caa0] +description = "Recognizes 6" + +[e24ebf80-c611-41bb-a25a-ac2c0f232df5] +description = "Recognizes 7" + +[b79cad4f-e264-4818-9d9e-77766792e233] +description = "Recognizes 8" + +[5efc9cfc-9227-4688-b77d-845049299e66] +description = "Recognizes 9" + +[f60cb04a-42be-494e-a535-3451c8e097a4] +description = "Recognizes string of decimal numbers" + +[b73ecf8b-4423-4b36-860d-3710bdb8a491] +description = "Numbers separated by empty lines are recognized. Lines are joined by commas." diff --git a/exercises/practice/ocr-numbers/ocr-numbers/ocr-numbers-tests.factor b/exercises/practice/ocr-numbers/ocr-numbers/ocr-numbers-tests.factor new file mode 100644 index 00000000..8f688e5c --- /dev/null +++ b/exercises/practice/ocr-numbers/ocr-numbers/ocr-numbers-tests.factor @@ -0,0 +1,76 @@ +USING: io kernel lexer ocr-numbers tools.test unicode ; +IN: ocr-numbers.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Ocr Numbers:" print + +"Recognizes 0" print +{ "0" } +[ { " _ " "| |" "|_|" " " } convert ] unit-test + +STOP-HERE + +"Recognizes 1" print +{ "1" } +[ { " " " |" " |" " " } convert ] unit-test + +"Unreadable but correctly sized inputs return ?" print +{ "?" } +[ { " " " _" " |" " " } convert ] unit-test + +"Input with a number of lines that is not a multiple of four raises an error" print +[ { " _ " "| |" " " } convert ] +[ "Number of input lines is not a multiple of four" = ] must-fail-with + +"Input with a number of columns that is not a multiple of three raises an error" print +[ { " " " |" " |" " " } convert ] +[ "Number of input columns is not a multiple of three" = ] must-fail-with + +"Recognizes 110101100" print +{ "110101100" } +[ { " _ _ _ _ " " | || | || | | || || |" " | ||_| ||_| | ||_||_|" " " } convert ] unit-test + +"Garbled numbers in a string are replaced with ?" print +{ "11?10?1?0" } +[ { " _ _ _ " " | || | || | || || |" " | | _| ||_| | ||_||_|" " " } convert ] unit-test + +"Recognizes 2" print +{ "2" } +[ { " _ " " _|" "|_ " " " } convert ] unit-test + +"Recognizes 3" print +{ "3" } +[ { " _ " " _|" " _|" " " } convert ] unit-test + +"Recognizes 4" print +{ "4" } +[ { " " "|_|" " |" " " } convert ] unit-test + +"Recognizes 5" print +{ "5" } +[ { " _ " "|_ " " _|" " " } convert ] unit-test + +"Recognizes 6" print +{ "6" } +[ { " _ " "|_ " "|_|" " " } convert ] unit-test + +"Recognizes 7" print +{ "7" } +[ { " _ " " |" " |" " " } convert ] unit-test + +"Recognizes 8" print +{ "8" } +[ { " _ " "|_|" "|_|" " " } convert ] unit-test + +"Recognizes 9" print +{ "9" } +[ { " _ " "|_|" " _|" " " } convert ] unit-test + +"Recognizes string of decimal numbers" print +{ "1234567890" } +[ { " _ _ _ _ _ _ _ _ " " | _| _||_||_ |_ ||_||_|| |" " ||_ _| | _||_| ||_| _||_|" " " } convert ] unit-test + +"Numbers separated by empty lines are recognized. Lines are joined by commas." print +{ "123,456,789" } +[ { " _ _ " " | _| _|" " ||_ _|" " " " _ _ " "|_||_ |_ " " | _||_|" " " " _ _ _ " " ||_||_|" " ||_| _|" " " } convert ] unit-test diff --git a/exercises/practice/ocr-numbers/ocr-numbers/ocr-numbers.factor b/exercises/practice/ocr-numbers/ocr-numbers/ocr-numbers.factor new file mode 100644 index 00000000..88ea04d4 --- /dev/null +++ b/exercises/practice/ocr-numbers/ocr-numbers/ocr-numbers.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: ocr-numbers + +: convert ( rows -- str ) + "unimplemented" throw ; diff --git a/exercises/practice/perfect-numbers/.docs/instructions.md b/exercises/practice/perfect-numbers/.docs/instructions.md new file mode 100644 index 00000000..b2bc82ca --- /dev/null +++ b/exercises/practice/perfect-numbers/.docs/instructions.md @@ -0,0 +1,39 @@ +# Instructions + +Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers. + +The Greek mathematician [Nicomachus][nicomachus] devised a classification scheme for positive integers, identifying each as belonging uniquely to the categories of [perfect](#perfect), [abundant](#abundant), or [deficient](#deficient) based on their [aliquot sum][aliquot-sum]. +The _aliquot sum_ is defined as the sum of the factors of a number not including the number itself. +For example, the aliquot sum of `15` is `1 + 3 + 5 = 9`. + +## Perfect + +A number is perfect when it equals its aliquot sum. +For example: + +- `6` is a perfect number because `1 + 2 + 3 = 6` +- `28` is a perfect number because `1 + 2 + 4 + 7 + 14 = 28` + +## Abundant + +A number is abundant when it is less than its aliquot sum. +For example: + +- `12` is an abundant number because `1 + 2 + 3 + 4 + 6 = 16` +- `24` is an abundant number because `1 + 2 + 3 + 4 + 6 + 8 + 12 = 36` + +## Deficient + +A number is deficient when it is greater than its aliquot sum. +For example: + +- `8` is a deficient number because `1 + 2 + 4 = 7` +- Prime numbers are deficient + +## Task + +Implement a way to determine whether a given number is [perfect](#perfect). +Depending on your language track, you may also need to implement a way to determine whether a given number is [abundant](#abundant) or [deficient](#deficient). + +[nicomachus]: https://en.wikipedia.org/wiki/Nicomachus +[aliquot-sum]: https://en.wikipedia.org/wiki/Aliquot_sum diff --git a/exercises/practice/perfect-numbers/.meta/config.json b/exercises/practice/perfect-numbers/.meta/config.json new file mode 100644 index 00000000..f89f484b --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "perfect-numbers/perfect-numbers.factor" + ], + "test": [ + "perfect-numbers/perfect-numbers-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Determine if a number is perfect, abundant, or deficient based on Nicomachus' (60 - 120 CE) classification scheme for positive integers.", + "source": "Taken from Chapter 2 of Functional Thinking by Neal Ford.", + "source_url": "https://www.oreilly.com/library/view/functional-thinking/9781449365509/" +} diff --git a/exercises/practice/perfect-numbers/.meta/example.factor b/exercises/practice/perfect-numbers/.meta/example.factor new file mode 100644 index 00000000..210af27c --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/example.factor @@ -0,0 +1,32 @@ +USING: combinators kernel locals math ; +IN: perfect-numbers + +:: sigma ( n -- sum ) + n :> remaining! + 1 :> result! + 2 :> p! + 1 :> step! + [ p p * remaining <= ] [ + remaining p mod 0 = [ + 1 :> series! + [ remaining p mod 0 = ] [ + remaining p /i remaining! + p series * 1 + series! + ] while + result series * result! + ] when + p step + p! + 2 step! + ] while + remaining 1 > [ + result remaining 1 + * result! + ] when + result ; + +: classify ( n -- str ) + dup 0 > [ "Classification is only possible for positive integers." throw ] unless + dup 2 * swap sigma { + { [ 2dup > ] [ 2drop "deficient" ] } + { [ 2dup < ] [ 2drop "abundant" ] } + [ 2drop "perfect" ] + } cond ; diff --git a/exercises/practice/perfect-numbers/.meta/generator.jl b/exercises/practice/perfect-numbers/.meta/generator.jl new file mode 100644 index 00000000..42851b5d --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/generator.jl @@ -0,0 +1,15 @@ +module PerfectNumbers + +function gen_test_case(case) + raw = case["input"]["number"] + number = raw isa AbstractFloat ? string(BigInt(round(raw))) : string(Int(raw)) + expected = case["expected"] + if expected isa Dict + msg = expected["error"] + return """[ $(number) classify ] [ "$(msg)" = ] must-fail-with""" + else + return """{ "$(expected)" } [ $(number) classify ] unit-test""" + end +end + +end diff --git a/exercises/practice/perfect-numbers/.meta/supplements.json b/exercises/practice/perfect-numbers/.meta/supplements.json new file mode 100644 index 00000000..41f1c649 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/supplements.json @@ -0,0 +1,8 @@ +[ + { + "description": "Large deficient number with repeated prime factor is classified correctly", + "property": "classify", + "input": {"number": 8796027459974431}, + "expected": "deficient" + } +] diff --git a/exercises/practice/perfect-numbers/.meta/tests.toml b/exercises/practice/perfect-numbers/.meta/tests.toml new file mode 100644 index 00000000..81d48408 --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[163e8e86-7bfd-4ee2-bd68-d083dc3381a3] +description = "Perfect numbers -> Smallest perfect number is classified correctly" + +[169a7854-0431-4ae0-9815-c3b6d967436d] +description = "Perfect numbers -> Medium perfect number is classified correctly" + +[ee3627c4-7b36-4245-ba7c-8727d585f402] +description = "Perfect numbers -> Large perfect number is classified correctly" + +[80ef7cf8-9ea8-49b9-8b2d-d9cb3db3ed7e] +description = "Abundant numbers -> Smallest abundant number is classified correctly" + +[3e300e0d-1a12-4f11-8c48-d1027165ab60] +description = "Abundant numbers -> Medium abundant number is classified correctly" + +[ec7792e6-8786-449c-b005-ce6dd89a772b] +description = "Abundant numbers -> Large abundant number is classified correctly" + +[05f15b93-849c-45e9-9c7d-1ea131ef7d10] +description = "Abundant numbers -> Perfect square abundant number is classified correctly" + +[e610fdc7-2b6e-43c3-a51c-b70fb37413ba] +description = "Deficient numbers -> Smallest prime deficient number is classified correctly" + +[0beb7f66-753a-443f-8075-ad7fbd9018f3] +description = "Deficient numbers -> Smallest non-prime deficient number is classified correctly" + +[1c802e45-b4c6-4962-93d7-1cad245821ef] +description = "Deficient numbers -> Medium deficient number is classified correctly" + +[47dd569f-9e5a-4a11-9a47-a4e91c8c28aa] +description = "Deficient numbers -> Large deficient number is classified correctly" + +[a696dec8-6147-4d68-afad-d38de5476a56] +description = "Deficient numbers -> Edge case (no factors other than itself) is classified correctly" + +[72445cee-660c-4d75-8506-6c40089dc302] +description = "Invalid inputs -> Zero is rejected (as it is not a positive integer)" + +[2d72ce2c-6802-49ac-8ece-c790ba3dae13] +description = "Invalid inputs -> Negative integer is rejected (as it is not a positive integer)" diff --git a/exercises/practice/perfect-numbers/perfect-numbers/perfect-numbers-tests.factor b/exercises/practice/perfect-numbers/perfect-numbers/perfect-numbers-tests.factor new file mode 100644 index 00000000..2d071bdf --- /dev/null +++ b/exercises/practice/perfect-numbers/perfect-numbers/perfect-numbers-tests.factor @@ -0,0 +1,53 @@ +USING: io kernel lexer perfect-numbers tools.test unicode ; +IN: perfect-numbers.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Perfect Numbers:" print + +"Smallest perfect number is classified correctly" print +{ "perfect" } [ 6 classify ] unit-test + +STOP-HERE + +"Medium perfect number is classified correctly" print +{ "perfect" } [ 28 classify ] unit-test + +"Large perfect number is classified correctly" print +{ "perfect" } [ 33550336 classify ] unit-test + +"Smallest abundant number is classified correctly" print +{ "abundant" } [ 12 classify ] unit-test + +"Medium abundant number is classified correctly" print +{ "abundant" } [ 30 classify ] unit-test + +"Large abundant number is classified correctly" print +{ "abundant" } [ 33550335 classify ] unit-test + +"Perfect square abundant number is classified correctly" print +{ "abundant" } [ 196 classify ] unit-test + +"Smallest prime deficient number is classified correctly" print +{ "deficient" } [ 2 classify ] unit-test + +"Smallest non-prime deficient number is classified correctly" print +{ "deficient" } [ 4 classify ] unit-test + +"Medium deficient number is classified correctly" print +{ "deficient" } [ 32 classify ] unit-test + +"Large deficient number is classified correctly" print +{ "deficient" } [ 33550337 classify ] unit-test + +"Edge case (no factors other than itself) is classified correctly" print +{ "deficient" } [ 1 classify ] unit-test + +"Zero is rejected (as it is not a positive integer)" print +[ 0 classify ] [ "Classification is only possible for positive integers." = ] must-fail-with + +"Negative integer is rejected (as it is not a positive integer)" print +[ -1 classify ] [ "Classification is only possible for positive integers." = ] must-fail-with + +"Large deficient number with repeated prime factor is classified correctly" print +{ "deficient" } [ 8796027459974431 classify ] unit-test diff --git a/exercises/practice/perfect-numbers/perfect-numbers/perfect-numbers.factor b/exercises/practice/perfect-numbers/perfect-numbers/perfect-numbers.factor new file mode 100644 index 00000000..9fa335df --- /dev/null +++ b/exercises/practice/perfect-numbers/perfect-numbers/perfect-numbers.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: perfect-numbers + +: classify ( n -- str ) + "unimplemented" throw ; diff --git a/exercises/practice/rail-fence-cipher/.docs/instructions.md b/exercises/practice/rail-fence-cipher/.docs/instructions.md new file mode 100644 index 00000000..e311de6c --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.docs/instructions.md @@ -0,0 +1,57 @@ +# Instructions + +Implement encoding and decoding for the rail fence cipher. + +The Rail Fence cipher is a form of transposition cipher that gets its name from the way in which it's encoded. +It was already used by the ancient Greeks. + +In the Rail Fence cipher, the message is written downwards on successive "rails" of an imaginary fence, then moving up when we get to the bottom (like a zig-zag). +Finally the message is then read off in rows. + +For example, using three "rails" and the message "WE ARE DISCOVERED FLEE AT ONCE", the cipherer writes out: + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . A . . . I . . . V . . . D . . . E . . . N . . +``` + +Then reads off: + +```text +WECRLTEERDSOEEFEAOCAIVDEN +``` + +To decrypt a message you take the zig-zag shape and fill the ciphertext along the rows. + +```text +? . . . ? . . . ? . . . ? . . . ? . . . ? . . . ? +. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +The first row has seven spots that can be filled with "WECRLTE". + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +Now the 2nd row takes "ERDSOEEFEAOC". + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +Leaving "AIVDEN" for the last row. + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . A . . . I . . . V . . . D . . . E . . . N . . +``` + +If you now read along the zig-zag shape you can read the original message. diff --git a/exercises/practice/rail-fence-cipher/.meta/config.json b/exercises/practice/rail-fence-cipher/.meta/config.json new file mode 100644 index 00000000..79b6134b --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "rail-fence-cipher/rail-fence-cipher.factor" + ], + "test": [ + "rail-fence-cipher/rail-fence-cipher-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Implement encoding and decoding for the rail fence cipher.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Transposition_cipher#Rail_Fence_cipher" +} diff --git a/exercises/practice/rail-fence-cipher/.meta/example.factor b/exercises/practice/rail-fence-cipher/.meta/example.factor new file mode 100644 index 00000000..af075c99 --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/example.factor @@ -0,0 +1,36 @@ +USING: arrays kernel locals math sequences strings ; +IN: rail-fence-cipher + +:: rail-pattern ( len rails -- seq ) + rails 1 - 2 * :> cycle + len [ + cycle mod dup cycle 2 /i > [ cycle swap - ] when + ] map ; + +:: encode ( msg rails -- cipher ) + msg length rails rail-pattern :> pattern + rails [| r | + msg length [ dup pattern nth r = [ msg nth ] [ drop f ] if ] map + [ ] filter >string + ] map concat ; + +:: decode ( msg rails -- plain ) + msg length rails rail-pattern :> pattern + rails [| r | pattern [ r = ] count ] map :> lengths + lengths length 0 :> offsets + 0 lengths length [| i | + dup i offsets set-nth + i lengths nth + + ] each drop + msg length 0 :> result + rails [| r | + 0 :> pos! + msg length [| i | + i pattern nth r = [ + r offsets nth pos + msg nth + i result set-nth + pos 1 + pos! + ] when + ] each + ] each + result >string ; diff --git a/exercises/practice/rail-fence-cipher/.meta/generator.jl b/exercises/practice/rail-fence-cipher/.meta/generator.jl new file mode 100644 index 00000000..54f8d5b9 --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/generator.jl @@ -0,0 +1,11 @@ +module RailFenceCipher + +function gen_test_case(case) + msg = case["input"]["msg"] + rails = Int(case["input"]["rails"]) + expected = case["expected"] + prop = case["property"] + return """{ "$(expected)" }\n[ "$(msg)" $(rails) $(prop) ] unit-test""" +end + +end diff --git a/exercises/practice/rail-fence-cipher/.meta/supplements.json b/exercises/practice/rail-fence-cipher/.meta/supplements.json new file mode 100644 index 00000000..7d0eaf12 --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/supplements.json @@ -0,0 +1,11 @@ +[ + { + "description": "decode with seven rails", + "property": "decode", + "input": { + "msg": "AGGWRHNAEROTOESTRADWETHCTTRENAAVOTHEAOECTRESIRMKEINNNEWOOENESANO", + "rails": 7 + }, + "expected": "ANANCIENTADAGEWARNSNEVERGOTOSEAWITHTWOCHRONOMETERSTAKEONEORTHREE" + } +] diff --git a/exercises/practice/rail-fence-cipher/.meta/tests.toml b/exercises/practice/rail-fence-cipher/.meta/tests.toml new file mode 100644 index 00000000..dfc5e16b --- /dev/null +++ b/exercises/practice/rail-fence-cipher/.meta/tests.toml @@ -0,0 +1,28 @@ +# 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. + +[46dc5c50-5538-401d-93a5-41102680d068] +description = "encode -> encode with two rails" + +[25691697-fbd8-4278-8c38-b84068b7bc29] +description = "encode -> encode with three rails" + +[384f0fea-1442-4f1a-a7c4-5cbc2044002c] +description = "encode -> encode with ending in the middle" + +[cd525b17-ec34-45ef-8f0e-4f27c24a7127] +description = "decode -> decode with three rails" + +[dd7b4a98-1a52-4e5c-9499-cbb117833507] +description = "decode -> decode with five rails" + +[93e1ecf4-fac9-45d9-9cd2-591f47d3b8d3] +description = "decode -> decode with six rails" diff --git a/exercises/practice/rail-fence-cipher/rail-fence-cipher/rail-fence-cipher-tests.factor b/exercises/practice/rail-fence-cipher/rail-fence-cipher/rail-fence-cipher-tests.factor new file mode 100644 index 00000000..2443f60e --- /dev/null +++ b/exercises/practice/rail-fence-cipher/rail-fence-cipher/rail-fence-cipher-tests.factor @@ -0,0 +1,36 @@ +USING: io kernel lexer rail-fence-cipher tools.test unicode ; +IN: rail-fence-cipher.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Rail Fence Cipher:" print + +"encode with two rails" print +{ "XXXXXXXXXOOOOOOOOO" } +[ "XOXOXOXOXOXOXOXOXO" 2 encode ] unit-test + +STOP-HERE + +"encode with three rails" print +{ "WECRLTEERDSOEEFEAOCAIVDEN" } +[ "WEAREDISCOVEREDFLEEATONCE" 3 encode ] unit-test + +"encode with ending in the middle" print +{ "ESXIEECSR" } +[ "EXERCISES" 4 encode ] unit-test + +"decode with three rails" print +{ "THEDEVILISINTHEDETAILS" } +[ "TEITELHDVLSNHDTISEIIEA" 3 decode ] unit-test + +"decode with five rails" print +{ "EXERCISMISAWESOME" } +[ "EIEXMSMESAORIWSCE" 5 decode ] unit-test + +"decode with six rails" print +{ "112358132134558914423337761098715972584418167651094617711286" } +[ "133714114238148966225439541018335470986172518171757571896261" 6 decode ] unit-test + +"decode with seven rails" print +{ "ANANCIENTADAGEWARNSNEVERGOTOSEAWITHTWOCHRONOMETERSTAKEONEORTHREE" } +[ "AGGWRHNAEROTOESTRADWETHCTTRENAAVOTHEAOECTRESIRMKEINNNEWOOENESANO" 7 decode ] unit-test diff --git a/exercises/practice/rail-fence-cipher/rail-fence-cipher/rail-fence-cipher.factor b/exercises/practice/rail-fence-cipher/rail-fence-cipher/rail-fence-cipher.factor new file mode 100644 index 00000000..03c77242 --- /dev/null +++ b/exercises/practice/rail-fence-cipher/rail-fence-cipher/rail-fence-cipher.factor @@ -0,0 +1,8 @@ +USING: kernel ; +IN: rail-fence-cipher + +: encode ( msg rails -- cipher ) + "unimplemented" throw ; + +: decode ( msg rails -- plain ) + "unimplemented" throw ; diff --git a/exercises/practice/rectangles/.docs/instructions.md b/exercises/practice/rectangles/.docs/instructions.md new file mode 100644 index 00000000..8eb4ed47 --- /dev/null +++ b/exercises/practice/rectangles/.docs/instructions.md @@ -0,0 +1,63 @@ +# Instructions + +Count the rectangles in an ASCII diagram like the one below. + +```text + +--+ + ++ | ++-++--+ +| | | ++--+--+ +``` + +The above diagram contains these 6 rectangles: + +```text + + ++-----+ +| | ++-----+ +``` + +```text + +--+ + | | + | | + | | + +--+ +``` + +```text + +--+ + | | + +--+ + + +``` + +```text + + + +--+ + | | + +--+ +``` + +```text + + ++--+ +| | ++--+ +``` + +```text + + ++ + ++ + + +``` + +You may assume that the input is always a proper rectangle (i.e. the length of every line equals the length of the first line). diff --git a/exercises/practice/rectangles/.meta/config.json b/exercises/practice/rectangles/.meta/config.json new file mode 100644 index 00000000..6785f1e8 --- /dev/null +++ b/exercises/practice/rectangles/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "rectangles/rectangles.factor" + ], + "test": [ + "rectangles/rectangles-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Count the rectangles in an ASCII diagram." +} diff --git a/exercises/practice/rectangles/.meta/example.factor b/exercises/practice/rectangles/.meta/example.factor new file mode 100644 index 00000000..256ab3a5 --- /dev/null +++ b/exercises/practice/rectangles/.meta/example.factor @@ -0,0 +1,39 @@ +USING: kernel locals math ranges sequences ; +IN: rectangles + +:: at ( grid r c -- ch ) + r grid nth c swap nth ; + +:: h-line? ( grid r c1 c2 -- ? ) + c1 c2 [a..b] [ grid r rot at "+-" member? ] all? ; + +:: v-line? ( grid c r1 r2 -- ? ) + r1 r2 [a..b] [ grid swap c at "|+" member? ] all? ; + +:: rect? ( grid r1 c1 r2 c2 -- ? ) + grid r1 c1 at CHAR: + = + grid r1 c2 at CHAR: + = and + grid r2 c1 at CHAR: + = and + grid r2 c2 at CHAR: + = and + [ grid r1 c1 c2 h-line? + grid r2 c1 c2 h-line? and + grid c1 r1 r2 v-line? and + grid c2 r1 r2 v-line? and + ] [ f ] if ; + +:: count-rectangles ( grid -- n ) + grid empty? [ 0 ] [ + grid length :> rows + grid first length :> cols + 0 + rows [0..b) [| r1 | + r1 1 + rows [a..b) [| r2 | + cols [0..b) [| c1 | + c1 1 + cols [a..b) [| c2 | + grid r1 c1 r2 c2 rect? + [ 1 + ] when + ] each + ] each + ] each + ] each + ] if ; diff --git a/exercises/practice/rectangles/.meta/generator.jl b/exercises/practice/rectangles/.meta/generator.jl new file mode 100644 index 00000000..ea5f9724 --- /dev/null +++ b/exercises/practice/rectangles/.meta/generator.jl @@ -0,0 +1,15 @@ +module Rectangles + +function gen_test_case(case) + expected = Int(case["expected"]) + strings = case["input"]["strings"] + if isempty(strings) + strings_str = "{ }" + else + lines = map(s -> " \"$(escape_factor(s))\"", strings) + strings_str = "{\n" * join(lines, "\n") * "\n}" + end + return "{ $(expected) }\n[ $(strings_str) count-rectangles ] unit-test" +end + +end diff --git a/exercises/practice/rectangles/.meta/supplements.json b/exercises/practice/rectangles/.meta/supplements.json new file mode 100644 index 00000000..30f9be48 --- /dev/null +++ b/exercises/practice/rectangles/.meta/supplements.json @@ -0,0 +1,32 @@ +[ + { + "description": "very large input", + "property": "rectangles", + "input": { + "strings": [ + " +-----+--------+ +-----+ ", + "++---++-----+--------+---++-----++", + "||+--++-----+-+-++ | || ||", + "||| || +-+-++-+ | || ||", + "||| || | | || | | || ||", + "||| +++-----+-+-++-+-+---++-+ ||", + "||| ||| | | || | |+--++-+-+ ||", + "||| +++---+-+-+-++-+-++--++-+ | ||", + "||| |||+--+-+-+-+| | |+--++---+ ||", + "||| |||| | | | || | |+-+|| ||", + "||+-++++--+-+++-++-+-++-+++---++||", + "|| |+++--+-+++-+--+-+| ||| ||||", + "+++-+++++---++--+-++-++-+++---+|||", + " |+-+++++---++--+ || || ||| ||||", + " | +++++---++--+-++-++-++++ ||||", + " | |||| |+----++-++-++++--+++|", + " | |+++---+| || || || || |", + "+++ |||+---++----+| || || || |", + "||| +++----++----++-++-++----++-+", + "+++---++----++-----+-++-++----++ ", + " +-+ " + ] + }, + "expected": 2063 + } +] diff --git a/exercises/practice/rectangles/.meta/tests.toml b/exercises/practice/rectangles/.meta/tests.toml new file mode 100644 index 00000000..28201503 --- /dev/null +++ b/exercises/practice/rectangles/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[485b7bab-4150-40aa-a8db-73013427d08c] +description = "no rows" + +[076929ed-27e8-45dc-b14b-08279944dc49] +description = "no columns" + +[0a8abbd1-a0a4-4180-aa4e-65c1b1a073fa] +description = "no rectangles" + +[a4ba42e9-4e7f-4973-b7c7-4ce0760ac6cd] +description = "one rectangle" + +[ced06550-83da-4d23-98b7-d24152e0db93] +description = "two rectangles without shared parts" + +[5942d69a-a07c-41c8-8b93-2d13877c706a] +description = "five rectangles with shared parts" + +[82d70be4-ab37-4bf2-a433-e33778d3bbf1] +description = "rectangle of height 1 is counted" + +[57f1bc0e-2782-401e-ab12-7c01d8bfc2e0] +description = "rectangle of width 1 is counted" + +[ef0bb65c-bd80-4561-9535-efc4067054f9] +description = "1x1 square is counted" + +[e1e1d444-e926-4d30-9bf3-7d8ec9a9e330] +description = "only complete rectangles are counted" + +[ca021a84-1281-4a56-9b9b-af14113933a4] +description = "rectangles can be of different sizes" + +[51f689a7-ef3f-41ae-aa2f-5ea09ad897ff] +description = "corner is required for a rectangle to be complete" + +[d78fe379-8c1b-4d3c-bdf7-29bfb6f6dc66] +description = "large input with many rectangles" + +[6ef24e0f-d191-46da-b929-4faca24b4cd2] +description = "rectangles must have four sides" diff --git a/exercises/practice/rectangles/rectangles/rectangles-tests.factor b/exercises/practice/rectangles/rectangles/rectangles-tests.factor new file mode 100644 index 00000000..eceb45ba --- /dev/null +++ b/exercises/practice/rectangles/rectangles/rectangles-tests.factor @@ -0,0 +1,155 @@ +USING: io kernel lexer rectangles tools.test unicode ; +IN: rectangles.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Rectangles:" print + +"no rows" print +{ 0 } +[ { } count-rectangles ] unit-test + +STOP-HERE + +"no columns" print +{ 0 } +[ { + "" +} count-rectangles ] unit-test + +"no rectangles" print +{ 0 } +[ { + " " +} count-rectangles ] unit-test + +"one rectangle" print +{ 1 } +[ { + "+-+" + "| |" + "+-+" +} count-rectangles ] unit-test + +"two rectangles without shared parts" print +{ 2 } +[ { + " +-+" + " | |" + "+-+-+" + "| | " + "+-+ " +} count-rectangles ] unit-test + +"five rectangles with shared parts" print +{ 5 } +[ { + " +-+" + " | |" + "+-+-+" + "| | |" + "+-+-+" +} count-rectangles ] unit-test + +"rectangle of height 1 is counted" print +{ 1 } +[ { + "+--+" + "+--+" +} count-rectangles ] unit-test + +"rectangle of width 1 is counted" print +{ 1 } +[ { + "++" + "||" + "++" +} count-rectangles ] unit-test + +"1x1 square is counted" print +{ 1 } +[ { + "++" + "++" +} count-rectangles ] unit-test + +"only complete rectangles are counted" print +{ 1 } +[ { + " +-+" + " |" + "+-+-+" + "| | -" + "+-+-+" +} count-rectangles ] unit-test + +"rectangles can be of different sizes" print +{ 3 } +[ { + "+------+----+" + "| | |" + "+---+--+ |" + "| | |" + "+---+-------+" +} count-rectangles ] unit-test + +"corner is required for a rectangle to be complete" print +{ 2 } +[ { + "+------+----+" + "| | |" + "+------+ |" + "| | |" + "+---+-------+" +} count-rectangles ] unit-test + +"large input with many rectangles" print +{ 60 } +[ { + "+---+--+----+" + "| +--+----+" + "+---+--+ |" + "| +--+----+" + "+---+--+--+-+" + "+---+--+--+-+" + "+------+ | |" + " +-+" +} count-rectangles ] unit-test + +"rectangles must have four sides" print +{ 5 } +[ { + "+-+ +-+" + "| | | |" + "+-+-+-+" + " | | " + "+-+-+-+" + "| | | |" + "+-+ +-+" +} count-rectangles ] unit-test + +"very large input" print +{ 2063 } +[ { + " +-----+--------+ +-----+ " + "++---++-----+--------+---++-----++" + "||+--++-----+-+-++ | || ||" + "||| || +-+-++-+ | || ||" + "||| || | | || | | || ||" + "||| +++-----+-+-++-+-+---++-+ ||" + "||| ||| | | || | |+--++-+-+ ||" + "||| +++---+-+-+-++-+-++--++-+ | ||" + "||| |||+--+-+-+-+| | |+--++---+ ||" + "||| |||| | | | || | |+-+|| ||" + "||+-++++--+-+++-++-+-++-+++---++||" + "|| |+++--+-+++-+--+-+| ||| ||||" + "+++-+++++---++--+-++-++-+++---+|||" + " |+-+++++---++--+ || || ||| ||||" + " | +++++---++--+-++-++-++++ ||||" + " | |||| |+----++-++-++++--+++|" + " | |+++---+| || || || || |" + "+++ |||+---++----+| || || || |" + "||| +++----++----++-++-++----++-+" + "+++---++----++-----+-++-++----++ " + " +-+ " +} count-rectangles ] unit-test diff --git a/exercises/practice/rectangles/rectangles/rectangles.factor b/exercises/practice/rectangles/rectangles/rectangles.factor new file mode 100644 index 00000000..dc74e18e --- /dev/null +++ b/exercises/practice/rectangles/rectangles/rectangles.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: rectangles + +: count-rectangles ( grid -- n ) + "unimplemented" throw ; diff --git a/exercises/practice/robot-simulator/.docs/instructions.md b/exercises/practice/robot-simulator/.docs/instructions.md new file mode 100644 index 00000000..0ac96ce0 --- /dev/null +++ b/exercises/practice/robot-simulator/.docs/instructions.md @@ -0,0 +1,25 @@ +# Instructions + +Write a robot simulator. + +A robot factory's test facility needs a program to verify robot movements. + +The robots have three possible movements: + +- turn right +- turn left +- advance + +Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x,y} coordinates, +e.g., {3,8}, with coordinates increasing to the north and east. + +The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing. + +- The letter-string "RAALAL" means: + - Turn right + - Advance twice + - Turn left + - Advance once + - Turn left yet again +- Say a robot starts at {7, 3} facing north. + Then running this stream of instructions should leave it at {9, 4} facing west. diff --git a/exercises/practice/robot-simulator/.meta/config.json b/exercises/practice/robot-simulator/.meta/config.json new file mode 100644 index 00000000..cf41f5fa --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "robot-simulator/robot-simulator.factor" + ], + "test": [ + "robot-simulator/robot-simulator-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Write a robot simulator.", + "source": "Inspired by an interview question at a famous company." +} diff --git a/exercises/practice/robot-simulator/.meta/example.factor b/exercises/practice/robot-simulator/.meta/example.factor new file mode 100644 index 00000000..6e0e3530 --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/example.factor @@ -0,0 +1,26 @@ +USING: arrays combinators kernel locals math sequences ; +IN: robot-simulator + +: ( x y dir -- robot ) 3array ; + +: turn ( robot offset -- robot ) + [ dup third { "north" "east" "south" "west" } index ] dip + + 4 + 4 mod { "north" "east" "south" "west" } nth + [ but-last ] dip suffix ; + +:: 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 ; + +: step ( robot char -- robot ) + { { CHAR: R [ 1 turn ] } + { CHAR: L [ -1 turn ] } + { 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 new file mode 100644 index 00000000..08389919 --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/generator.jl @@ -0,0 +1,23 @@ +module RobotSimulator + +function format_robot(obj) + x = Int(obj["position"]["x"]) + y = Int(obj["position"]["y"]) + dir = obj["direction"] + return """{ $(x) $(y) "$(dir)" }""" +end + +function gen_test_case(case) + expected = format_robot(case["expected"]) + x = Int(case["input"]["position"]["x"]) + y = Int(case["input"]["position"]["y"]) + dir = case["input"]["direction"] + if case["property"] == "create" + return """{ $(expected) }\n[ $(x) $(y) "$(dir)" ] unit-test""" + else + instructions = case["input"]["instructions"] + return """{ $(expected) }\n[ $(x) $(y) "$(dir)" "$(instructions)" move ] unit-test""" + end +end + +end diff --git a/exercises/practice/robot-simulator/.meta/tests.toml b/exercises/practice/robot-simulator/.meta/tests.toml new file mode 100644 index 00000000..16da03d4 --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/tests.toml @@ -0,0 +1,64 @@ +# 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. + +[c557c16d-26c1-4e06-827c-f6602cd0785c] +description = "Create robot -> at origin facing north" + +[bf0dffce-f11c-4cdb-8a5e-2c89d8a5a67d] +description = "Create robot -> at negative position facing south" + +[8cbd0086-6392-4680-b9b9-73cf491e67e5] +description = "Rotating clockwise -> changes north to east" + +[8abc87fc-eab2-4276-93b7-9c009e866ba1] +description = "Rotating clockwise -> changes east to south" + +[3cfe1b85-bbf2-4bae-b54d-d73e7e93617a] +description = "Rotating clockwise -> changes south to west" + +[5ea9fb99-3f2c-47bd-86f7-46b7d8c3c716] +description = "Rotating clockwise -> changes west to north" + +[fa0c40f5-6ba3-443d-a4b3-58cbd6cb8d63] +description = "Rotating counter-clockwise -> changes north to west" + +[da33d734-831f-445c-9907-d66d7d2a92e2] +description = "Rotating counter-clockwise -> changes west to south" + +[bd1ca4b9-4548-45f4-b32e-900fc7c19389] +description = "Rotating counter-clockwise -> changes south to east" + +[2de27b67-a25c-4b59-9883-bc03b1b55bba] +description = "Rotating counter-clockwise -> changes east to north" + +[f0dc2388-cddc-4f83-9bed-bcf46b8fc7b8] +description = "Moving forward one -> facing north increments Y" + +[2786cf80-5bbf-44b0-9503-a89a9c5789da] +description = "Moving forward one -> facing south decrements Y" + +[84bf3c8c-241f-434d-883d-69817dbd6a48] +description = "Moving forward one -> facing east increments X" + +[bb69c4a7-3bbf-4f64-b415-666fa72d7b04] +description = "Moving forward one -> facing west decrements X" + +[e34ac672-4ed4-4be3-a0b8-d9af259cbaa1] +description = "Follow series of instructions -> moving east and north from README" + +[f30e4955-4b47-4aa3-8b39-ae98cfbd515b] +description = "Follow series of instructions -> moving west and north" + +[3e466bf6-20ab-4d79-8b51-264165182fca] +description = "Follow series of instructions -> moving west and south" + +[41f0bb96-c617-4e6b-acff-a4b279d44514] +description = "Follow series of instructions -> moving east and north" diff --git a/exercises/practice/robot-simulator/robot-simulator/robot-simulator-tests.factor b/exercises/practice/robot-simulator/robot-simulator/robot-simulator-tests.factor new file mode 100644 index 00000000..f6af4dc6 --- /dev/null +++ b/exercises/practice/robot-simulator/robot-simulator/robot-simulator-tests.factor @@ -0,0 +1,80 @@ +USING: io kernel lexer robot-simulator tools.test unicode ; +IN: robot-simulator.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Robot Simulator:" print + +"at origin facing north" print +{ { 0 0 "north" } } +[ 0 0 "north" ] unit-test + +STOP-HERE + +"at negative position facing south" print +{ { -1 -1 "south" } } +[ -1 -1 "south" ] unit-test + +"changes north to east" print +{ { 0 0 "east" } } +[ 0 0 "north" "R" move ] unit-test + +"changes east to south" print +{ { 0 0 "south" } } +[ 0 0 "east" "R" move ] unit-test + +"changes south to west" print +{ { 0 0 "west" } } +[ 0 0 "south" "R" move ] unit-test + +"changes west to north" print +{ { 0 0 "north" } } +[ 0 0 "west" "R" move ] unit-test + +"changes north to west" print +{ { 0 0 "west" } } +[ 0 0 "north" "L" move ] unit-test + +"changes west to south" print +{ { 0 0 "south" } } +[ 0 0 "west" "L" move ] unit-test + +"changes south to east" print +{ { 0 0 "east" } } +[ 0 0 "south" "L" move ] unit-test + +"changes east to north" print +{ { 0 0 "north" } } +[ 0 0 "east" "L" move ] unit-test + +"facing north increments Y" print +{ { 0 1 "north" } } +[ 0 0 "north" "A" move ] unit-test + +"facing south decrements Y" print +{ { 0 -1 "south" } } +[ 0 0 "south" "A" move ] unit-test + +"facing east increments X" print +{ { 1 0 "east" } } +[ 0 0 "east" "A" move ] unit-test + +"facing west decrements X" print +{ { -1 0 "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 + +"moving west and north" print +{ { -4 1 "west" } } +[ 0 0 "north" "LAAARALA" move ] unit-test + +"moving west and south" print +{ { -3 -8 "south" } } +[ 2 -7 "east" "RRAAAAALA" move ] unit-test + +"moving east and north" print +{ { 11 5 "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 new file mode 100644 index 00000000..87cf640c --- /dev/null +++ b/exercises/practice/robot-simulator/robot-simulator/robot-simulator.factor @@ -0,0 +1,8 @@ +USING: kernel ; +IN: robot-simulator + +: ( x y dir -- robot ) + "unimplemented" throw ; + +: move ( robot instructions -- robot ) + "unimplemented" throw ; diff --git a/exercises/practice/run-length-encoding/.docs/instructions.md b/exercises/practice/run-length-encoding/.docs/instructions.md new file mode 100644 index 00000000..fc8ce056 --- /dev/null +++ b/exercises/practice/run-length-encoding/.docs/instructions.md @@ -0,0 +1,20 @@ +# Instructions + +Implement run-length encoding and decoding. + +Run-length encoding (RLE) is a simple form of data compression, where runs (consecutive data elements) are replaced by just one data value and count. + +For example we can represent the original 53 characters with only 13. + +```text +"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -> "12WB12W3B24WB" +``` + +RLE allows the original data to be perfectly reconstructed from the compressed data, which makes it a lossless data compression. + +```text +"AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE" +``` + +For simplicity, you can assume that the unencoded string will only contain the letters A through Z (either lower or upper case) and whitespace. +This way data to be encoded will never contain any numbers and numbers inside data to be decoded always represent the count for the following character. diff --git a/exercises/practice/run-length-encoding/.meta/config.json b/exercises/practice/run-length-encoding/.meta/config.json new file mode 100644 index 00000000..13cfbddc --- /dev/null +++ b/exercises/practice/run-length-encoding/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "run-length-encoding/run-length-encoding.factor" + ], + "test": [ + "run-length-encoding/run-length-encoding-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Implement run-length encoding and decoding.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Run-length_encoding" +} diff --git a/exercises/practice/run-length-encoding/.meta/example.factor b/exercises/practice/run-length-encoding/.meta/example.factor new file mode 100644 index 00000000..edb7353d --- /dev/null +++ b/exercises/practice/run-length-encoding/.meta/example.factor @@ -0,0 +1,24 @@ +USING: grouping kernel locals math math.parser sequences +splitting.monotonic strings unicode ; +IN: run-length-encoding + +: encode ( str -- encoded ) + dup empty? [ ] [ + [ = ] monotonic-split [ + dup length dup 1 > [ number>string ] [ drop "" ] if + swap first 1string append + ] map concat + ] if ; + +:: decode ( str -- decoded ) + "" 0 :> ( acc! n! ) + str [| ch | + ch digit? [ + n 10 * ch CHAR: 0 - + n! + ] [ + n 0 = [ 1 n! ] when + acc n ch 1string concat append acc! + 0 n! + ] if + ] each + acc ; diff --git a/exercises/practice/run-length-encoding/.meta/generator.jl b/exercises/practice/run-length-encoding/.meta/generator.jl new file mode 100644 index 00000000..51c24abf --- /dev/null +++ b/exercises/practice/run-length-encoding/.meta/generator.jl @@ -0,0 +1,14 @@ +module RunLengthEncoding + +function gen_test_case(case) + str = case["input"]["string"] + expected = case["expected"] + prop = case["property"] + if prop == "consistency" + return """{ "$(expected)" }\n[ "$(str)" encode decode ] unit-test""" + else + return """{ "$(expected)" }\n[ "$(str)" $(prop) ] unit-test""" + end +end + +end diff --git a/exercises/practice/run-length-encoding/.meta/supplements.json b/exercises/practice/run-length-encoding/.meta/supplements.json new file mode 100644 index 00000000..35ca7a91 --- /dev/null +++ b/exercises/practice/run-length-encoding/.meta/supplements.json @@ -0,0 +1,14 @@ +[ + { + "description": "long run encode", + "property": "encode", + "input": {"string": "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"}, + "expected": "123z" + }, + { + "description": "long run decode", + "property": "decode", + "input": {"string": "123z"}, + "expected": "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" + } +] diff --git a/exercises/practice/run-length-encoding/.meta/tests.toml b/exercises/practice/run-length-encoding/.meta/tests.toml new file mode 100644 index 00000000..7bdb8086 --- /dev/null +++ b/exercises/practice/run-length-encoding/.meta/tests.toml @@ -0,0 +1,49 @@ +# 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. + +[ad53b61b-6ffc-422f-81a6-61f7df92a231] +description = "run-length encode a string -> empty string" + +[52012823-b7e6-4277-893c-5b96d42f82de] +description = "run-length encode a string -> single characters only are encoded without count" + +[b7868492-7e3a-415f-8da3-d88f51f80409] +description = "run-length encode a string -> string with no single characters" + +[859b822b-6e9f-44d6-9c46-6091ee6ae358] +description = "run-length encode a string -> single characters mixed with repeated characters" + +[1b34de62-e152-47be-bc88-469746df63b3] +description = "run-length encode a string -> multiple whitespace mixed in string" + +[abf176e2-3fbd-40ad-bb2f-2dd6d4df721a] +description = "run-length encode a string -> lowercase characters" + +[7ec5c390-f03c-4acf-ac29-5f65861cdeb5] +description = "run-length decode a string -> empty string" + +[ad23f455-1ac2-4b0e-87d0-b85b10696098] +description = "run-length decode a string -> single characters only" + +[21e37583-5a20-4a0e-826c-3dee2c375f54] +description = "run-length decode a string -> string with no single characters" + +[1389ad09-c3a8-4813-9324-99363fba429c] +description = "run-length decode a string -> single characters with repeated characters" + +[3f8e3c51-6aca-4670-b86c-a213bf4706b0] +description = "run-length decode a string -> multiple whitespace mixed in string" + +[29f721de-9aad-435f-ba37-7662df4fb551] +description = "run-length decode a string -> lowercase string" + +[2a762efd-8695-4e04-b0d6-9736899fbc16] +description = "encode and then decode -> encode followed by decode gives original string" diff --git a/exercises/practice/run-length-encoding/run-length-encoding/run-length-encoding-tests.factor b/exercises/practice/run-length-encoding/run-length-encoding/run-length-encoding-tests.factor new file mode 100644 index 00000000..19737ecb --- /dev/null +++ b/exercises/practice/run-length-encoding/run-length-encoding/run-length-encoding-tests.factor @@ -0,0 +1,68 @@ +USING: io kernel lexer run-length-encoding tools.test unicode ; +IN: run-length-encoding.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Run Length Encoding:" print + +"empty string" print +{ "" } +[ "" encode ] unit-test + +STOP-HERE + +"single characters only are encoded without count" print +{ "XYZ" } +[ "XYZ" encode ] unit-test + +"string with no single characters" print +{ "2A3B4C" } +[ "AABBBCCCC" encode ] unit-test + +"single characters mixed with repeated characters" print +{ "12WB12W3B24WB" } +[ "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" encode ] unit-test + +"multiple whitespace mixed in string" print +{ "2 hs2q q2w2 " } +[ " hsqq qww " encode ] unit-test + +"lowercase characters" print +{ "2a3b4c" } +[ "aabbbcccc" encode ] unit-test + +"empty string" print +{ "" } +[ "" decode ] unit-test + +"single characters only" print +{ "XYZ" } +[ "XYZ" decode ] unit-test + +"string with no single characters" print +{ "AABBBCCCC" } +[ "2A3B4C" decode ] unit-test + +"single characters with repeated characters" print +{ "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" } +[ "12WB12W3B24WB" decode ] unit-test + +"multiple whitespace mixed in string" print +{ " hsqq qww " } +[ "2 hs2q q2w2 " decode ] unit-test + +"lowercase string" print +{ "aabbbcccc" } +[ "2a3b4c" decode ] unit-test + +"encode followed by decode gives original string" print +{ "zzz ZZ zZ" } +[ "zzz ZZ zZ" encode decode ] unit-test + +"long run encode" print +{ "123z" } +[ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" encode ] unit-test + +"long run decode" print +{ "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" } +[ "123z" decode ] unit-test diff --git a/exercises/practice/run-length-encoding/run-length-encoding/run-length-encoding.factor b/exercises/practice/run-length-encoding/run-length-encoding/run-length-encoding.factor new file mode 100644 index 00000000..2180f63c --- /dev/null +++ b/exercises/practice/run-length-encoding/run-length-encoding/run-length-encoding.factor @@ -0,0 +1,8 @@ +USING: kernel ; +IN: run-length-encoding + +: encode ( str -- encoded ) + "unimplemented" throw ; + +: decode ( str -- decoded ) + "unimplemented" throw ; diff --git a/exercises/practice/say/.docs/instructions.md b/exercises/practice/say/.docs/instructions.md new file mode 100644 index 00000000..3251c519 --- /dev/null +++ b/exercises/practice/say/.docs/instructions.md @@ -0,0 +1,12 @@ +# Instructions + +Given a number, your task is to express it in English words exactly as your friend should say it out loud. +Yaʻqūb expects to use numbers from 0 up to 999,999,999,999. + +Examples: + +- 0 → zero +- 1 → one +- 12 → twelve +- 123 → one hundred twenty-three +- 1,234 → one thousand two hundred thirty-four diff --git a/exercises/practice/say/.docs/introduction.md b/exercises/practice/say/.docs/introduction.md new file mode 100644 index 00000000..abd22851 --- /dev/null +++ b/exercises/practice/say/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +Your friend Yaʻqūb works the counter at the busiest deli in town, slicing, weighing, and wrapping orders for a never-ending line of hungry customers. +To keep things moving, each customer takes a numbered ticket when they arrive. + +When it’s time to call the next person, Yaʻqūb reads their number out loud, always in full English words to make sure everyone hears it clearly. diff --git a/exercises/practice/say/.meta/config.json b/exercises/practice/say/.meta/config.json new file mode 100644 index 00000000..04759131 --- /dev/null +++ b/exercises/practice/say/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "say/say.factor" + ], + "test": [ + "say/say-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a number from 0 to 999,999,999,999, spell out that number in English.", + "source": "A variation on the JavaRanch CattleDrive, Assignment 4", + "source_url": "https://web.archive.org/web/20240907035912/https://coderanch.com/wiki/718804" +} diff --git a/exercises/practice/say/.meta/example.factor b/exercises/practice/say/.meta/example.factor new file mode 100644 index 00000000..3053a231 --- /dev/null +++ b/exercises/practice/say/.meta/example.factor @@ -0,0 +1,36 @@ +USING: kernel locals math math.order sequences strings ; +IN: say + +CONSTANT: ones { "zero" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine" + "ten" "eleven" "twelve" "thirteen" "fourteen" "fifteen" "sixteen" "seventeen" "eighteen" "nineteen" } + +CONSTANT: tens { f f "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety" } + +: say-below-100 ( n -- str ) + dup 20 < [ ones nth ] [ + 10 /mod swap tens nth + swap dup 0 = [ drop ] [ ones nth "-" glue ] if + ] if ; + +: say-below-1000 ( n -- str ) + dup 100 < [ say-below-100 ] [ + 100 /mod swap ones nth " hundred" append + swap dup 0 = [ drop ] [ say-below-100 " " glue ] if + ] if ; + +:: say-chunk ( str n scale name -- str' remainder ) + n scale /mod :> ( q r ) + q say-below-1000 " " name 3append + str empty? [ ] [ str " " rot 3append ] if + r ; + +: say-positive ( n -- str ) + "" swap + dup 1000000000 >= [ 1000000000 "billion" say-chunk ] when + dup 1000000 >= [ 1000000 "million" say-chunk ] when + dup 1000 >= [ 1000 "thousand" say-chunk ] when + dup 0 > [ say-below-1000 swap dup empty? [ drop ] [ " " rot 3append ] if ] [ drop ] if ; + +: say ( n -- str ) + dup 0 999999999999 between? [ "input out of range" throw ] unless + dup 0 = [ drop "zero" ] [ say-positive ] if ; diff --git a/exercises/practice/say/.meta/generator.jl b/exercises/practice/say/.meta/generator.jl new file mode 100644 index 00000000..5ab169e1 --- /dev/null +++ b/exercises/practice/say/.meta/generator.jl @@ -0,0 +1,14 @@ +module Say + +function gen_test_case(case) + number = case["input"]["number"] + expected = case["expected"] + if expected isa Dict + msg = expected["error"] + return """[ $(number) say ] [ "$(msg)" = ] must-fail-with""" + else + return """{ "$(expected)" }\n[ $(number) say ] unit-test""" + end +end + +end diff --git a/exercises/practice/say/.meta/supplements.json b/exercises/practice/say/.meta/supplements.json new file mode 100644 index 00000000..23ce5485 --- /dev/null +++ b/exercises/practice/say/.meta/supplements.json @@ -0,0 +1,20 @@ +[ + { + "description": "additional big number", + "property": "say", + "input": {"number": 19011016013}, + "expected": "nineteen billion eleven million sixteen thousand thirteen" + }, + { + "description": "different big number", + "property": "say", + "input": {"number": 812000070017}, + "expected": "eight hundred twelve billion seventy thousand seventeen" + }, + { + "description": "alternative big number", + "property": "say", + "input": {"number": 60010015018}, + "expected": "sixty billion ten million fifteen thousand eighteen" + } +] diff --git a/exercises/practice/say/.meta/tests.toml b/exercises/practice/say/.meta/tests.toml new file mode 100644 index 00000000..a5532e9e --- /dev/null +++ b/exercises/practice/say/.meta/tests.toml @@ -0,0 +1,67 @@ +# 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. + +[5d22a120-ba0c-428c-bd25-8682235d83e8] +description = "zero" + +[9b5eed77-dbf6-439d-b920-3f7eb58928f6] +description = "one" + +[7c499be1-612e-4096-a5e1-43b2f719406d] +description = "fourteen" + +[f541dd8e-f070-4329-92b4-b7ce2fcf06b4] +description = "twenty" + +[d78601eb-4a84-4bfa-bf0e-665aeb8abe94] +description = "twenty-two" + +[f010d4ca-12c9-44e9-803a-27789841adb1] +description = "thirty" + +[738ce12d-ee5c-4dfb-ad26-534753a98327] +description = "ninety-nine" + +[e417d452-129e-4056-bd5b-6eb1df334dce] +description = "one hundred" + +[d6924f30-80ba-4597-acf6-ea3f16269da8] +description = "one hundred twenty-three" + +[2f061132-54bc-4fd4-b5df-0a3b778959b9] +description = "two hundred" + +[feed6627-5387-4d38-9692-87c0dbc55c33] +description = "nine hundred ninety-nine" + +[3d83da89-a372-46d3-b10d-de0c792432b3] +description = "one thousand" + +[865af898-1d5b-495f-8ff0-2f06d3c73709] +description = "one thousand two hundred thirty-four" + +[b6a3f442-266e-47a3-835d-7f8a35f6cf7f] +description = "one million" + +[2cea9303-e77e-4212-b8ff-c39f1978fc70] +description = "one million two thousand three hundred forty-five" + +[3e240eeb-f564-4b80-9421-db123f66a38f] +description = "one billion" + +[9a43fed1-c875-4710-8286-5065d73b8a9e] +description = "a big number" + +[49a6a17b-084e-423e-994d-a87c0ecc05ef] +description = "numbers below zero are out of range" + +[4d6492eb-5853-4d16-9d34-b0f61b261fd9] +description = "numbers above 999,999,999,999 are out of range" diff --git a/exercises/practice/say/say/say-tests.factor b/exercises/practice/say/say/say-tests.factor new file mode 100644 index 00000000..06e1f59a --- /dev/null +++ b/exercises/practice/say/say/say-tests.factor @@ -0,0 +1,94 @@ +USING: io kernel lexer say tools.test unicode ; +IN: say.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Say:" print + +"zero" print +{ "zero" } +[ 0 say ] unit-test + +STOP-HERE + +"one" print +{ "one" } +[ 1 say ] unit-test + +"fourteen" print +{ "fourteen" } +[ 14 say ] unit-test + +"twenty" print +{ "twenty" } +[ 20 say ] unit-test + +"twenty-two" print +{ "twenty-two" } +[ 22 say ] unit-test + +"thirty" print +{ "thirty" } +[ 30 say ] unit-test + +"ninety-nine" print +{ "ninety-nine" } +[ 99 say ] unit-test + +"one hundred" print +{ "one hundred" } +[ 100 say ] unit-test + +"one hundred twenty-three" print +{ "one hundred twenty-three" } +[ 123 say ] unit-test + +"two hundred" print +{ "two hundred" } +[ 200 say ] unit-test + +"nine hundred ninety-nine" print +{ "nine hundred ninety-nine" } +[ 999 say ] unit-test + +"one thousand" print +{ "one thousand" } +[ 1000 say ] unit-test + +"one thousand two hundred thirty-four" print +{ "one thousand two hundred thirty-four" } +[ 1234 say ] unit-test + +"one million" print +{ "one million" } +[ 1000000 say ] unit-test + +"one million two thousand three hundred forty-five" print +{ "one million two thousand three hundred forty-five" } +[ 1002345 say ] unit-test + +"one billion" print +{ "one billion" } +[ 1000000000 say ] unit-test + +"a big number" print +{ "nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three" } +[ 987654321123 say ] unit-test + +"numbers below zero are out of range" print +[ -1 say ] [ "input out of range" = ] must-fail-with + +"numbers above 999,999,999,999 are out of range" print +[ 1000000000000 say ] [ "input out of range" = ] must-fail-with + +"additional big number" print +{ "nineteen billion eleven million sixteen thousand thirteen" } +[ 19011016013 say ] unit-test + +"different big number" print +{ "eight hundred twelve billion seventy thousand seventeen" } +[ 812000070017 say ] unit-test + +"alternative big number" print +{ "sixty billion ten million fifteen thousand eighteen" } +[ 60010015018 say ] unit-test diff --git a/exercises/practice/say/say/say.factor b/exercises/practice/say/say/say.factor new file mode 100644 index 00000000..80ba3d2c --- /dev/null +++ b/exercises/practice/say/say/say.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: say + +: say ( n -- str ) + "unimplemented" throw ; diff --git a/exercises/practice/sieve/.docs/instructions.md b/exercises/practice/sieve/.docs/instructions.md new file mode 100644 index 00000000..71292e17 --- /dev/null +++ b/exercises/practice/sieve/.docs/instructions.md @@ -0,0 +1,101 @@ +# Instructions + +Your task is to create a program that implements the Sieve of Eratosthenes algorithm to find all prime numbers less than or equal to a given number. + +A prime number is a number larger than 1 that is only divisible by 1 and itself. +For example, 2, 3, 5, 7, 11, and 13 are prime numbers. +By contrast, 6 is _not_ a prime number as it not only divisible by 1 and itself, but also by 2 and 3. + +To use the Sieve of Eratosthenes, first, write out all the numbers from 2 up to and including your given number. +Then, follow these steps: + +1. Find the next unmarked number (skipping over marked numbers). + This is a prime number. +2. Mark all the multiples of that prime number as **not** prime. + +Repeat the steps until you've gone through every number. +At the end, all the unmarked numbers are prime. + +~~~~exercism/note +The Sieve of Eratosthenes marks off multiples of each prime using addition (repeatedly adding the prime) or multiplication (directly computing its multiples), rather than checking each number for divisibility. + +The tests don't check that you've implemented the algorithm, only that you've come up with the correct primes. +~~~~ + +## Example + +Let's say you're finding the primes less than or equal to 10. + +- Write out 2, 3, 4, 5, 6, 7, 8, 9, 10, leaving them all unmarked. + + ```text + 2 3 4 5 6 7 8 9 10 + ``` + +- 2 is unmarked and is therefore a prime. + Mark 4, 6, 8 and 10 as "not prime". + + ```text + 2 3 [4] 5 [6] 7 [8] 9 [10] + ↑ + ``` + +- 3 is unmarked and is therefore a prime. + Mark 6 and 9 as not prime _(marking 6 is optional - as it's already been marked)_. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 4 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 5 is unmarked and is therefore a prime. + Mark 10 as not prime _(optional - as it's already been marked)_. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 6 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 7 is unmarked and is therefore a prime. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 8 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 9 is marked as "not prime", so we skip over it. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +- 10 is marked as "not prime", so we stop as there are no more numbers to check. + + ```text + 2 3 [4] 5 [6] 7 [8] [9] [10] + ↑ + ``` + +You've examined all the numbers and found that 2, 3, 5, and 7 are still unmarked, meaning they're the primes less than or equal to 10. diff --git a/exercises/practice/sieve/.docs/introduction.md b/exercises/practice/sieve/.docs/introduction.md new file mode 100644 index 00000000..f6c1cf79 --- /dev/null +++ b/exercises/practice/sieve/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +You bought a big box of random computer parts at a garage sale. +You've started putting the parts together to build custom computers. + +You want to test the performance of different combinations of parts, and decide to create your own benchmarking program to see how your computers compare. +You choose the famous "Sieve of Eratosthenes" algorithm, an ancient algorithm, but one that should push your computers to the limits. diff --git a/exercises/practice/sieve/.meta/config.json b/exercises/practice/sieve/.meta/config.json new file mode 100644 index 00000000..722a70f4 --- /dev/null +++ b/exercises/practice/sieve/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "sieve/sieve.factor" + ], + "test": [ + "sieve/sieve-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Use the Sieve of Eratosthenes to find all the primes from 2 up to a given number.", + "source": "Sieve of Eratosthenes at Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes" +} diff --git a/exercises/practice/sieve/.meta/example.factor b/exercises/practice/sieve/.meta/example.factor new file mode 100644 index 00000000..e4a7bf45 --- /dev/null +++ b/exercises/practice/sieve/.meta/example.factor @@ -0,0 +1,20 @@ +USING: arrays kernel locals math math.functions ranges sequences ; +IN: sieve + +:: primes ( limit -- primes ) + limit 2 < [ V{ } ] [ + limit 1 + t :> sieve + limit sqrt >integer :> max-i + max-i 2 >= [ + 2 max-i [a..b] [| i | + i sieve nth [ + i i * :> j! + [ j limit <= ] [ + f j sieve set-nth + j i + j! + ] while + ] when + ] each + ] when + 2 limit [a..b] [ sieve nth ] filter + ] if ; diff --git a/exercises/practice/sieve/.meta/generator.jl b/exercises/practice/sieve/.meta/generator.jl new file mode 100644 index 00000000..46ee2e98 --- /dev/null +++ b/exercises/practice/sieve/.meta/generator.jl @@ -0,0 +1,9 @@ +module Sieve + +function gen_test_case(case) + limit = Int(case["input"]["limit"]) + expected = format_int_vector(case["expected"]) + return "{ $(expected) }\n[ $(limit) primes ] unit-test" +end + +end diff --git a/exercises/practice/sieve/.meta/tests.toml b/exercises/practice/sieve/.meta/tests.toml new file mode 100644 index 00000000..fec5e1a1 --- /dev/null +++ b/exercises/practice/sieve/.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. + +[88529125-c4ce-43cc-bb36-1eb4ddd7b44f] +description = "no primes under two" + +[4afe9474-c705-4477-9923-840e1024cc2b] +description = "find first prime" + +[974945d8-8cd9-4f00-9463-7d813c7f17b7] +description = "find primes up to 10" + +[2e2417b7-3f3a-452a-8594-b9af08af6d82] +description = "limit is prime" + +[92102a05-4c7c-47de-9ed0-b7d5fcd00f21] +description = "find primes up to 1000" diff --git a/exercises/practice/sieve/sieve/sieve-tests.factor b/exercises/practice/sieve/sieve/sieve-tests.factor new file mode 100644 index 00000000..d5dfe098 --- /dev/null +++ b/exercises/practice/sieve/sieve/sieve-tests.factor @@ -0,0 +1,28 @@ +USING: io kernel lexer sieve tools.test unicode ; +IN: sieve.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Sieve:" print + +"no primes under two" print +{ V{ } } +[ 1 primes ] unit-test + +STOP-HERE + +"find first prime" print +{ V{ 2 } } +[ 2 primes ] unit-test + +"find primes up to 10" print +{ V{ 2 3 5 7 } } +[ 10 primes ] unit-test + +"limit is prime" print +{ V{ 2 3 5 7 11 13 } } +[ 13 primes ] unit-test + +"find primes up to 1000" print +{ V{ 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997 } } +[ 1000 primes ] unit-test diff --git a/exercises/practice/sieve/sieve/sieve.factor b/exercises/practice/sieve/sieve/sieve.factor new file mode 100644 index 00000000..6d8f06e8 --- /dev/null +++ b/exercises/practice/sieve/sieve/sieve.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: sieve + +: primes ( limit -- primes ) + "unimplemented" throw ; diff --git a/exercises/practice/space-age/.docs/instructions.md b/exercises/practice/space-age/.docs/instructions.md new file mode 100644 index 00000000..f23b5e2c --- /dev/null +++ b/exercises/practice/space-age/.docs/instructions.md @@ -0,0 +1,28 @@ +# Instructions + +Given an age in seconds, calculate how old someone would be on a planet in our Solar System. + +One Earth year equals 365.25 Earth days, or 31,557,600 seconds. +If you were told someone was 1,000,000,000 seconds old, their age would be 31.69 Earth-years. + +For the other planets, you have to account for their orbital period in Earth Years: + +| Planet | Orbital period in Earth Years | +| ------- | ----------------------------- | +| Mercury | 0.2408467 | +| Venus | 0.61519726 | +| Earth | 1.0 | +| Mars | 1.8808158 | +| Jupiter | 11.862615 | +| Saturn | 29.447498 | +| Uranus | 84.016846 | +| Neptune | 164.79132 | + +~~~~exercism/note +The actual length of one complete orbit of the Earth around the sun is closer to 365.256 days (1 sidereal year). +The Gregorian calendar has, on average, 365.2425 days. +While not entirely accurate, 365.25 is the value used in this exercise. +See [Year on Wikipedia][year] for more ways to measure a year. + +[year]: https://en.wikipedia.org/wiki/Year#Summary +~~~~ diff --git a/exercises/practice/space-age/.docs/introduction.md b/exercises/practice/space-age/.docs/introduction.md new file mode 100644 index 00000000..014d7885 --- /dev/null +++ b/exercises/practice/space-age/.docs/introduction.md @@ -0,0 +1,20 @@ +# Introduction + +The year is 2525 and you've just embarked on a journey to visit all planets in the Solar System (Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus and Neptune). +The first stop is Mercury, where customs require you to fill out a form (bureaucracy is apparently _not_ Earth-specific). +As you hand over the form to the customs officer, they scrutinize it and frown. +"Do you _really_ expect me to believe you're just 50 years old? +You must be closer to 200 years old!" + +Amused, you wait for the customs officer to start laughing, but they appear to be dead serious. +You realize that you've entered your age in _Earth years_, but the officer expected it in _Mercury years_! +As Mercury's orbital period around the sun is significantly shorter than Earth, you're actually a lot older in Mercury years. +After some quick calculations, you're able to provide your age in Mercury Years. +The customs officer smiles, satisfied, and waves you through. +You make a mental note to pre-calculate your planet-specific age _before_ future customs checks, to avoid such mix-ups. + +~~~~exercism/note +If you're wondering why Pluto didn't make the cut, go watch [this YouTube video][pluto-video]. + +[pluto-video]: https://www.youtube.com/watch?v=Z_2gbGXzFbs +~~~~ diff --git a/exercises/practice/space-age/.meta/config.json b/exercises/practice/space-age/.meta/config.json new file mode 100644 index 00000000..e4f77a2e --- /dev/null +++ b/exercises/practice/space-age/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "space-age/space-age.factor" + ], + "test": [ + "space-age/space-age-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given an age in seconds, calculate how old someone is in terms of a given planet's solar years.", + "source": "Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial.", + "source_url": "https://pine.fm/LearnToProgram/chap_01.html" +} diff --git a/exercises/practice/space-age/.meta/example.factor b/exercises/practice/space-age/.meta/example.factor new file mode 100644 index 00000000..f1a5c30d --- /dev/null +++ b/exercises/practice/space-age/.meta/example.factor @@ -0,0 +1,13 @@ +USING: kernel math ; +IN: space-age + +: earth-year ( -- seconds ) 31557600.0 ; + +: 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 / ; diff --git a/exercises/practice/space-age/.meta/generator.jl b/exercises/practice/space-age/.meta/generator.jl new file mode 100644 index 00000000..078b3d62 --- /dev/null +++ b/exercises/practice/space-age/.meta/generator.jl @@ -0,0 +1,22 @@ +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"] + seconds = case["input"]["seconds"] + expected = case["expected"] + word = PLANETS[planet] + return "{ $(expected) 0.005 } [ $(seconds) $(word) ] unit-test~" +end + +end diff --git a/exercises/practice/space-age/.meta/tests.toml b/exercises/practice/space-age/.meta/tests.toml new file mode 100644 index 00000000..a62017e4 --- /dev/null +++ b/exercises/practice/space-age/.meta/tests.toml @@ -0,0 +1,38 @@ +# 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. + +[84f609af-5a91-4d68-90a3-9e32d8a5cd34] +description = "age on Earth" + +[ca20c4e9-6054-458c-9312-79679ffab40b] +description = "age on Mercury" + +[502c6529-fd1b-41d3-8fab-65e03082b024] +description = "age on Venus" + +[9ceadf5e-a0d5-4388-9d40-2c459227ceb8] +description = "age on Mars" + +[42927dc3-fe5e-4f76-a5b5-f737fc19bcde] +description = "age on Jupiter" + +[8469b332-7837-4ada-b27c-00ee043ebcad] +description = "age on Saturn" + +[999354c1-76f8-4bb5-a672-f317b6436743] +description = "age on Uranus" + +[80096d30-a0d4-4449-903e-a381178355d8] +description = "age on Neptune" + +[57b96e2a-1178-40b7-b34d-f3c9c34e4bf4] +description = "invalid planet causes error" +include = false diff --git a/exercises/practice/space-age/space-age/space-age-tests.factor b/exercises/practice/space-age/space-age/space-age-tests.factor new file mode 100644 index 00000000..0f89a5e2 --- /dev/null +++ b/exercises/practice/space-age/space-age/space-age-tests.factor @@ -0,0 +1,32 @@ +USING: io kernel lexer space-age tools.test unicode ; +IN: space-age.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Space Age:" print + +"age on Earth" print +{ 31.69 0.005 } [ 1000000000 on-earth ] unit-test~ + +STOP-HERE + +"age on Mercury" print +{ 280.88 0.005 } [ 2134835688 on-mercury ] unit-test~ + +"age on Venus" print +{ 9.78 0.005 } [ 189839836 on-venus ] unit-test~ + +"age on Mars" print +{ 35.88 0.005 } [ 2129871239 on-mars ] unit-test~ + +"age on Jupiter" print +{ 2.41 0.005 } [ 901876382 on-jupiter ] unit-test~ + +"age on Saturn" print +{ 2.15 0.005 } [ 2000000000 on-saturn ] unit-test~ + +"age on Uranus" print +{ 0.46 0.005 } [ 1210123456 on-uranus ] unit-test~ + +"age on Neptune" print +{ 0.35 0.005 } [ 1821023456 on-neptune ] unit-test~ diff --git a/exercises/practice/space-age/space-age/space-age.factor b/exercises/practice/space-age/space-age/space-age.factor new file mode 100644 index 00000000..d9b42101 --- /dev/null +++ b/exercises/practice/space-age/space-age/space-age.factor @@ -0,0 +1,26 @@ +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 ; + +: on-neptune ( seconds -- years ) + "unimplemented" throw ; diff --git a/exercises/practice/square-root/.docs/instructions.md b/exercises/practice/square-root/.docs/instructions.md new file mode 100644 index 00000000..d258b868 --- /dev/null +++ b/exercises/practice/square-root/.docs/instructions.md @@ -0,0 +1,18 @@ +# Instructions + +Your task is to calculate the square root of a given number. + +- Try to avoid using the pre-existing math libraries of your language. +- As input you'll be given a positive whole number, i.e. 1, 2, 3, 4… +- You are only required to handle cases where the result is a positive whole number. + +Some potential approaches: + +- Linear or binary search for a number that gives the input number when squared. +- Successive approximation using Newton's or Heron's method. +- Calculating one digit at a time or one bit at a time. + +You can check out the Wikipedia pages on [integer square root][integer-square-root] and [methods of computing square roots][computing-square-roots] to help with choosing a method of calculation. + +[integer-square-root]: https://en.wikipedia.org/wiki/Integer_square_root +[computing-square-roots]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots diff --git a/exercises/practice/square-root/.docs/introduction.md b/exercises/practice/square-root/.docs/introduction.md new file mode 100644 index 00000000..1d692934 --- /dev/null +++ b/exercises/practice/square-root/.docs/introduction.md @@ -0,0 +1,10 @@ +# Introduction + +We are launching a deep space exploration rocket and we need a way to make sure the navigation system stays on target. + +As the first step in our calculation, we take a target number and find its square root (that is, the number that when multiplied by itself equals the target number). + +The journey will be very long. +To make the batteries last as long as possible, we had to make our rocket's onboard computer very power efficient. +Unfortunately that means that we can't rely on fancy math libraries and functions, as they use more power. +Instead we want to implement our own square root calculation. diff --git a/exercises/practice/square-root/.meta/config.json b/exercises/practice/square-root/.meta/config.json new file mode 100644 index 00000000..93d4fa6b --- /dev/null +++ b/exercises/practice/square-root/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "square-root/square-root.factor" + ], + "test": [ + "square-root/square-root-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a natural radicand, return its square root.", + "source": "wolf99", + "source_url": "https://github.com/exercism/problem-specifications/pull/1582" +} diff --git a/exercises/practice/square-root/.meta/example.factor b/exercises/practice/square-root/.meta/example.factor new file mode 100644 index 00000000..67fd0955 --- /dev/null +++ b/exercises/practice/square-root/.meta/example.factor @@ -0,0 +1,7 @@ +USING: kernel math ; +IN: square-root + +: square-root ( n -- root ) + 0 over [ 2dup = not ] + [ nip dup dup 1 + * pick + over 2 * /i ] + while nip nip ; diff --git a/exercises/practice/square-root/.meta/generator.jl b/exercises/practice/square-root/.meta/generator.jl new file mode 100644 index 00000000..ba6d9fab --- /dev/null +++ b/exercises/practice/square-root/.meta/generator.jl @@ -0,0 +1,9 @@ +module SquareRoot + +function gen_test_case(case) + radicand = Int(case["input"]["radicand"]) + expected = Int(case["expected"]) + return "{ $(expected) } [ $(radicand) square-root ] unit-test" +end + +end diff --git a/exercises/practice/square-root/.meta/supplements.json b/exercises/practice/square-root/.meta/supplements.json new file mode 100644 index 00000000..4fbdc729 --- /dev/null +++ b/exercises/practice/square-root/.meta/supplements.json @@ -0,0 +1,8 @@ +[ + { + "description": "root of 4905601600", + "property": "squareRoot", + "input": {"radicand": 4905601600}, + "expected": 70040 + } +] diff --git a/exercises/practice/square-root/.meta/tests.toml b/exercises/practice/square-root/.meta/tests.toml new file mode 100644 index 00000000..ead7882f --- /dev/null +++ b/exercises/practice/square-root/.meta/tests.toml @@ -0,0 +1,28 @@ +# 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. + +[9b748478-7b0a-490c-b87a-609dacf631fd] +description = "root of 1" + +[7d3aa9ba-9ac6-4e93-a18b-2e8b477139bb] +description = "root of 4" + +[6624aabf-3659-4ae0-a1c8-25ae7f33c6ef] +description = "root of 25" + +[93beac69-265e-4429-abb1-94506b431f81] +description = "root of 81" + +[fbddfeda-8c4f-4bc4-87ca-6991af35360e] +description = "root of 196" + +[c03d0532-8368-4734-a8e0-f96a9eb7fc1d] +description = "root of 65025" diff --git a/exercises/practice/square-root/square-root/square-root-tests.factor b/exercises/practice/square-root/square-root/square-root-tests.factor new file mode 100644 index 00000000..ed654f16 --- /dev/null +++ b/exercises/practice/square-root/square-root/square-root-tests.factor @@ -0,0 +1,29 @@ +USING: io kernel lexer square-root tools.test unicode ; +IN: square-root.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Square Root:" print + +"root of 1" print +{ 1 } [ 1 square-root ] unit-test + +STOP-HERE + +"root of 4" print +{ 2 } [ 4 square-root ] unit-test + +"root of 25" print +{ 5 } [ 25 square-root ] unit-test + +"root of 81" print +{ 9 } [ 81 square-root ] unit-test + +"root of 196" print +{ 14 } [ 196 square-root ] unit-test + +"root of 65025" print +{ 255 } [ 65025 square-root ] unit-test + +"root of 4905601600" print +{ 70040 } [ 4905601600 square-root ] unit-test diff --git a/exercises/practice/square-root/square-root/square-root.factor b/exercises/practice/square-root/square-root/square-root.factor new file mode 100644 index 00000000..64b50c3e --- /dev/null +++ b/exercises/practice/square-root/square-root/square-root.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: square-root + +: square-root ( n -- root ) + "unimplemented" throw ; diff --git a/exercises/practice/word-count/.docs/instructions.md b/exercises/practice/word-count/.docs/instructions.md new file mode 100644 index 00000000..064393c8 --- /dev/null +++ b/exercises/practice/word-count/.docs/instructions.md @@ -0,0 +1,47 @@ +# Instructions + +Your task is to count how many times each word occurs in a subtitle of a drama. + +The subtitles from these dramas use only ASCII characters. + +The characters often speak in casual English, using contractions like _they're_ or _it's_. +Though these contractions come from two words (e.g. _we are_), the contraction (_we're_) is considered a single word. + +Words can be separated by any form of punctuation (e.g. ":", "!", or "?") or whitespace (e.g. "\t", "\n", or " "). +The only punctuation that does not separate words is the apostrophe in contractions. + +Numbers are considered words. +If the subtitles say _It costs 100 dollars._ then _100_ will be its own word. + +Words are case insensitive. +For example, the word _you_ occurs three times in the following sentence: + +> You come back, you hear me? DO YOU HEAR ME? + +The ordering of the word counts in the results doesn't matter. + +Here's an example that incorporates several of the elements discussed above: + +- simple words +- contractions +- numbers +- case insensitive words +- punctuation (including apostrophes) to separate words +- different forms of whitespace to separate words + +`"That's the password: 'PASSWORD 123'!", cried the Special Agent.\nSo I fled.` + +The mapping for this subtitle would be: + +```text +123: 1 +agent: 1 +cried: 1 +fled: 1 +i: 1 +password: 2 +so: 1 +special: 1 +that's: 1 +the: 2 +``` diff --git a/exercises/practice/word-count/.docs/introduction.md b/exercises/practice/word-count/.docs/introduction.md new file mode 100644 index 00000000..1654508e --- /dev/null +++ b/exercises/practice/word-count/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +You teach English as a foreign language to high school students. + +You've decided to base your entire curriculum on TV shows. +You need to analyze which words are used, and how often they're repeated. + +This will let you choose the simplest shows to start with, and to gradually increase the difficulty as time passes. diff --git a/exercises/practice/word-count/.meta/config.json b/exercises/practice/word-count/.meta/config.json new file mode 100644 index 00000000..6a1f7381 --- /dev/null +++ b/exercises/practice/word-count/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "word-count/word-count.factor" + ], + "test": [ + "word-count/word-count-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Given a phrase, count the occurrences of each word in that phrase.", + "source": "This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour." +} diff --git a/exercises/practice/word-count/.meta/example.factor b/exercises/practice/word-count/.meta/example.factor new file mode 100644 index 00000000..38117ebe --- /dev/null +++ b/exercises/practice/word-count/.meta/example.factor @@ -0,0 +1,24 @@ +USING: assocs hashtables kernel locals math sequences splitting +strings unicode ; +IN: word-count + +: word-char? ( ch -- ? ) + dup Letter? over digit? or swap CHAR: ' = or ; + +:: strip-apostrophes ( str -- str' ) + str dup empty? not [ + dup first CHAR: ' = [ rest ] when + dup empty? not [ dup last CHAR: ' = [ but-last ] when ] when + ] when ; + +:: count-words ( sentence -- counts ) + H{ } clone :> result + sentence >lower + [ word-char? not ] split-when + [ empty? not ] filter + [ strip-apostrophes ] map + [ empty? not ] filter + [| word | + word result at 0 or 1 + word result set-at + ] each + result ; diff --git a/exercises/practice/word-count/.meta/generator.jl b/exercises/practice/word-count/.meta/generator.jl new file mode 100644 index 00000000..e7e246c5 --- /dev/null +++ b/exercises/practice/word-count/.meta/generator.jl @@ -0,0 +1,16 @@ +module WordCount + +function format_expected(expected) + pairs = map(sort(collect(expected), by=first)) do (k, v) + "{ \"$(k)\" $(Int(v)) }" + end + return "H{ $(join(pairs, " ")) }" +end + +function gen_test_case(case) + sentence = escape_factor(case["input"]["sentence"]) + expected = format_expected(case["expected"]) + return """{ $(expected) }\n[ "$(sentence)" count-words ] unit-test""" +end + +end diff --git a/exercises/practice/word-count/.meta/tests.toml b/exercises/practice/word-count/.meta/tests.toml new file mode 100644 index 00000000..1be425b3 --- /dev/null +++ b/exercises/practice/word-count/.meta/tests.toml @@ -0,0 +1,57 @@ +# 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. + +[61559d5f-2cad-48fb-af53-d3973a9ee9ef] +description = "count one word" + +[5abd53a3-1aed-43a4-a15a-29f88c09cbbd] +description = "count one of each word" + +[2a3091e5-952e-4099-9fac-8f85d9655c0e] +description = "multiple occurrences of a word" + +[e81877ae-d4da-4af4-931c-d923cd621ca6] +description = "handles cramped lists" + +[7349f682-9707-47c0-a9af-be56e1e7ff30] +description = "handles expanded lists" + +[a514a0f2-8589-4279-8892-887f76a14c82] +description = "ignore punctuation" + +[d2e5cee6-d2ec-497b-bdc9-3ebe092ce55e] +description = "include numbers" + +[dac6bc6a-21ae-4954-945d-d7f716392dbf] +description = "normalize case" + +[4185a902-bdb0-4074-864c-f416e42a0f19] +description = "with apostrophes" +include = false + +[4ff6c7d7-fcfc-43ef-b8e7-34ff1837a2d3] +description = "with apostrophes" +reimplements = "4185a902-bdb0-4074-864c-f416e42a0f19" + +[be72af2b-8afe-4337-b151-b297202e4a7b] +description = "with quotations" + +[8d6815fe-8a51-4a65-96f9-2fb3f6dc6ed6] +description = "substrings from the beginning" + +[c5f4ef26-f3f7-4725-b314-855c04fb4c13] +description = "multiple spaces not detected as a word" + +[50176e8a-fe8e-4f4c-b6b6-aa9cf8f20360] +description = "alternating word separators not detected as a word" + +[6d00f1db-901c-4bec-9829-d20eb3044557] +description = "quotation for word with apostrophe" diff --git a/exercises/practice/word-count/word-count/word-count-tests.factor b/exercises/practice/word-count/word-count/word-count-tests.factor new file mode 100644 index 00000000..98509741 --- /dev/null +++ b/exercises/practice/word-count/word-count/word-count-tests.factor @@ -0,0 +1,64 @@ +USING: io kernel lexer tools.test unicode word-count ; +IN: word-count.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Word Count:" print + +"count one word" print +{ H{ { "word" 1 } } } +[ "word" count-words ] unit-test + +STOP-HERE + +"count one of each word" print +{ H{ { "each" 1 } { "of" 1 } { "one" 1 } } } +[ "one of each" count-words ] unit-test + +"multiple occurrences of a word" print +{ H{ { "blue" 1 } { "fish" 4 } { "one" 1 } { "red" 1 } { "two" 1 } } } +[ "one fish two fish red fish blue fish" count-words ] unit-test + +"handles cramped lists" print +{ H{ { "one" 1 } { "three" 1 } { "two" 1 } } } +[ "one,two,three" count-words ] unit-test + +"handles expanded lists" print +{ H{ { "one" 1 } { "three" 1 } { "two" 1 } } } +[ "one,\ntwo,\nthree" count-words ] unit-test + +"ignore punctuation" print +{ H{ { "as" 1 } { "car" 1 } { "carpet" 1 } { "java" 1 } { "javascript" 1 } } } +[ "car: carpet as java: javascript!!&@$%^&" count-words ] unit-test + +"include numbers" print +{ H{ { "1" 1 } { "2" 1 } { "testing" 2 } } } +[ "testing, 1, 2 testing" count-words ] unit-test + +"normalize case" print +{ H{ { "go" 3 } { "stop" 2 } } } +[ "go Go GO Stop stop" count-words ] unit-test + +"with apostrophes" print +{ H{ { "cry" 1 } { "don't" 2 } { "first" 1 } { "getting" 1 } { "it" 1 } { "laugh" 1 } { "then" 1 } { "you're" 1 } } } +[ "'First: don't laugh. Then: don't cry. You're getting it.'" count-words ] unit-test + +"with quotations" print +{ H{ { "and" 1 } { "between" 1 } { "can't" 1 } { "joe" 1 } { "large" 2 } { "tell" 1 } } } +[ "Joe can't tell between 'large' and large." count-words ] unit-test + +"substrings from the beginning" print +{ H{ { "a" 1 } { "and" 1 } { "app" 1 } { "apple" 1 } { "between" 1 } { "can't" 1 } { "joe" 1 } { "tell" 1 } } } +[ "Joe can't tell between app, apple and a." count-words ] unit-test + +"multiple spaces not detected as a word" print +{ H{ { "multiple" 1 } { "whitespaces" 1 } } } +[ " multiple whitespaces" count-words ] unit-test + +"alternating word separators not detected as a word" print +{ H{ { "one" 1 } { "three" 1 } { "two" 1 } } } +[ ",\n,one,\n ,two \n 'three'" count-words ] unit-test + +"quotation for word with apostrophe" print +{ H{ { "can" 1 } { "can't" 2 } } } +[ "can, can't, 'can't'" count-words ] unit-test diff --git a/exercises/practice/word-count/word-count/word-count.factor b/exercises/practice/word-count/word-count/word-count.factor new file mode 100644 index 00000000..60b12dd2 --- /dev/null +++ b/exercises/practice/word-count/word-count/word-count.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: word-count + +: count-words ( sentence -- counts ) + "unimplemented" throw ; diff --git a/exercises/practice/yacht/.docs/instructions.md b/exercises/practice/yacht/.docs/instructions.md new file mode 100644 index 00000000..519b7a68 --- /dev/null +++ b/exercises/practice/yacht/.docs/instructions.md @@ -0,0 +1,30 @@ +# Instructions + +Given five dice and a category, calculate the score of the dice for that category. + +~~~~exercism/note +You'll always be presented with five dice. +Each dice's value will be between one and six inclusively. +The dice may be unordered. +~~~~ + +## Scores in Yacht + +| Category | Score | Description | Example | +| --------------- | ---------------------- | ---------------------------------------- | ------------------- | +| Ones | 1 × number of ones | Any combination | 1 1 1 4 5 scores 3 | +| Twos | 2 × number of twos | Any combination | 2 2 3 4 5 scores 4 | +| Threes | 3 × number of threes | Any combination | 3 3 3 3 3 scores 15 | +| Fours | 4 × number of fours | Any combination | 1 2 3 3 5 scores 0 | +| Fives | 5 × number of fives | Any combination | 5 1 5 2 5 scores 15 | +| Sixes | 6 × number of sixes | Any combination | 2 3 4 5 6 scores 6 | +| Full House | Total of the dice | Three of one number and two of another | 3 3 3 5 5 scores 19 | +| Four of a Kind | Total of the four dice | At least four dice showing the same face | 4 4 4 4 6 scores 16 | +| Little Straight | 30 points | 1-2-3-4-5 | 1 2 3 4 5 scores 30 | +| Big Straight | 30 points | 2-3-4-5-6 | 2 3 4 5 6 scores 30 | +| Choice | Sum of the dice | Any combination | 2 3 3 4 6 scores 18 | +| Yacht | 50 points | All five dice showing the same face | 4 4 4 4 4 scores 50 | + +If the dice do **not** satisfy the requirements of a category, the score is zero. +If, for example, _Four Of A Kind_ is entered in the _Yacht_ category, zero points are scored. +A _Yacht_ scores zero if entered in the _Full House_ category. diff --git a/exercises/practice/yacht/.docs/introduction.md b/exercises/practice/yacht/.docs/introduction.md new file mode 100644 index 00000000..5b541f56 --- /dev/null +++ b/exercises/practice/yacht/.docs/introduction.md @@ -0,0 +1,11 @@ +# Introduction + +Each year, something new is "all the rage" in your high school. +This year it is a dice game: [Yacht][yacht]. + +The game of Yacht is from the same family as Poker Dice, Generala and particularly Yahtzee, of which it is a precursor. +The game consists of twelve rounds. +In each, five dice are rolled and the player chooses one of twelve categories. +The chosen category is then used to score the throw of the dice. + +[yacht]: https://en.wikipedia.org/wiki/Yacht_(dice_game) diff --git a/exercises/practice/yacht/.meta/config.json b/exercises/practice/yacht/.meta/config.json new file mode 100644 index 00000000..c8f4a288 --- /dev/null +++ b/exercises/practice/yacht/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "yacht/yacht.factor" + ], + "test": [ + "yacht/yacht-tests.factor" + ], + "example": [ + ".meta/example.factor" + ] + }, + "blurb": "Score a single throw of dice in the game Yacht.", + "source": "James Kilfiger, using Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Yacht_(dice_game)" +} diff --git a/exercises/practice/yacht/.meta/example.factor b/exercises/practice/yacht/.meta/example.factor new file mode 100644 index 00000000..72eced5a --- /dev/null +++ b/exercises/practice/yacht/.meta/example.factor @@ -0,0 +1,44 @@ +USING: assocs combinators kernel locals math sequences sets sorting ; +IN: yacht + +: count-die ( dice n -- count ) [ = ] curry count ; + +: score-number ( dice n -- score ) [ count-die ] keep * ; + +:: full-house ( dice -- score ) + dice sort members :> vals + vals length 2 = [ + vals first dice swap count-die { 2 3 } member? + [ dice sum ] [ 0 ] if + ] [ 0 ] if ; + +:: four-of-a-kind ( dice -- score ) + dice members [| v | + v dice swap count-die 4 >= + ] find nip + [ 4 * ] [ 0 ] if* ; + +: little-straight ( dice -- score ) + sort { 1 2 3 4 5 } = [ 30 ] [ 0 ] if ; + +: big-straight ( dice -- score ) + sort { 2 3 4 5 6 } = [ 30 ] [ 0 ] if ; + +: yacht-score ( dice -- score ) + dup first [ = ] curry all? [ 50 ] [ 0 ] if ; + +: score ( dice category -- score ) + { + { "ones" [ 1 score-number ] } + { "twos" [ 2 score-number ] } + { "threes" [ 3 score-number ] } + { "fours" [ 4 score-number ] } + { "fives" [ 5 score-number ] } + { "sixes" [ 6 score-number ] } + { "full house" [ full-house ] } + { "four of a kind" [ four-of-a-kind ] } + { "little straight" [ little-straight ] } + { "big straight" [ big-straight ] } + { "choice" [ sum ] } + { "yacht" [ yacht-score ] } + } case ; diff --git a/exercises/practice/yacht/.meta/generator.jl b/exercises/practice/yacht/.meta/generator.jl new file mode 100644 index 00000000..702ef742 --- /dev/null +++ b/exercises/practice/yacht/.meta/generator.jl @@ -0,0 +1,14 @@ +module Yacht + +function format_dice(dice) + return "{ $(join(map(d -> string(Int(d)), dice), " ")) }" +end + +function gen_test_case(case) + dice = format_dice(case["input"]["dice"]) + category = case["input"]["category"] + expected = Int(case["expected"]) + return """{ $(expected) } [ $(dice) "$(category)" score ] unit-test""" +end + +end diff --git a/exercises/practice/yacht/.meta/tests.toml b/exercises/practice/yacht/.meta/tests.toml new file mode 100644 index 00000000..b9d92037 --- /dev/null +++ b/exercises/practice/yacht/.meta/tests.toml @@ -0,0 +1,97 @@ +# 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. + +[3060e4a5-4063-4deb-a380-a630b43a84b6] +description = "Yacht" + +[15026df2-f567-482f-b4d5-5297d57769d9] +description = "Not Yacht" + +[36b6af0c-ca06-4666-97de-5d31213957a4] +description = "Ones" + +[023a07c8-6c6e-44d0-bc17-efc5e1b8205a] +description = "Ones, out of order" + +[7189afac-cccd-4a74-8182-1cb1f374e496] +description = "No ones" + +[793c4292-dd14-49c4-9707-6d9c56cee725] +description = "Twos" + +[dc41bceb-d0c5-4634-a734-c01b4233a0c6] +description = "Fours" + +[f6125417-5c8a-4bca-bc5b-b4b76d0d28c8] +description = "Yacht counted as threes" + +[464fc809-96ed-46e4-acb8-d44e302e9726] +description = "Yacht of 3s counted as fives" + +[d054227f-3a71-4565-a684-5c7e621ec1e9] +description = "Fives" + +[e8a036e0-9d21-443a-8b5f-e15a9e19a761] +description = "Sixes" + +[51cb26db-6b24-49af-a9ff-12f53b252eea] +description = "Full house two small, three big" + +[1822ca9d-f235-4447-b430-2e8cfc448f0c] +description = "Full house three small, two big" + +[b208a3fc-db2e-4363-a936-9e9a71e69c07] +description = "Two pair is not a full house" + +[b90209c3-5956-445b-8a0b-0ac8b906b1c2] +description = "Four of a kind is not a full house" + +[32a3f4ee-9142-4edf-ba70-6c0f96eb4b0c] +description = "Yacht is not a full house" + +[b286084d-0568-4460-844a-ba79d71d79c6] +description = "Four of a Kind" + +[f25c0c90-5397-4732-9779-b1e9b5f612ca] +description = "Yacht can be scored as Four of a Kind" + +[9f8ef4f0-72bb-401a-a871-cbad39c9cb08] +description = "Full house is not Four of a Kind" + +[b4743c82-1eb8-4a65-98f7-33ad126905cd] +description = "Little Straight" + +[7ac08422-41bf-459c-8187-a38a12d080bc] +description = "Little Straight as Big Straight" + +[97bde8f7-9058-43ea-9de7-0bc3ed6d3002] +description = "Four in order but not a little straight" + +[cef35ff9-9c5e-4fd2-ae95-6e4af5e95a99] +description = "No pairs but not a little straight" + +[fd785ad2-c060-4e45-81c6-ea2bbb781b9d] +description = "Minimum is 1, maximum is 5, but not a little straight" + +[35bd74a6-5cf6-431a-97a3-4f713663f467] +description = "Big Straight" + +[87c67e1e-3e87-4f3a-a9b1-62927822b250] +description = "Big Straight as little straight" + +[c1fa0a3a-40ba-4153-a42d-32bc34d2521e] +description = "No pairs but not a big straight" + +[207e7300-5d10-43e5-afdd-213e3ac8827d] +description = "Choice" + +[b524c0cf-32d2-4b40-8fb3-be3500f3f135] +description = "Yacht as choice" diff --git a/exercises/practice/yacht/yacht/yacht-tests.factor b/exercises/practice/yacht/yacht/yacht-tests.factor new file mode 100644 index 00000000..615672c3 --- /dev/null +++ b/exercises/practice/yacht/yacht/yacht-tests.factor @@ -0,0 +1,95 @@ +USING: io kernel lexer tools.test unicode yacht ; +IN: yacht.tests + +: STOP-HERE ( -- ) lexer get [ text>> length ] keep line<< ; parsing + +"Yacht:" print + +"Yacht" print +{ 50 } [ { 5 5 5 5 5 } "yacht" score ] unit-test + +STOP-HERE + +"Not Yacht" print +{ 0 } [ { 1 3 3 2 5 } "yacht" score ] unit-test + +"Ones" print +{ 3 } [ { 1 1 1 3 5 } "ones" score ] unit-test + +"Ones, out of order" print +{ 3 } [ { 3 1 1 5 1 } "ones" score ] unit-test + +"No ones" print +{ 0 } [ { 4 3 6 5 5 } "ones" score ] unit-test + +"Twos" print +{ 2 } [ { 2 3 4 5 6 } "twos" score ] unit-test + +"Fours" print +{ 8 } [ { 1 4 1 4 1 } "fours" score ] unit-test + +"Yacht counted as threes" print +{ 15 } [ { 3 3 3 3 3 } "threes" score ] unit-test + +"Yacht of 3s counted as fives" print +{ 0 } [ { 3 3 3 3 3 } "fives" score ] unit-test + +"Fives" print +{ 10 } [ { 1 5 3 5 3 } "fives" score ] unit-test + +"Sixes" print +{ 6 } [ { 2 3 4 5 6 } "sixes" score ] unit-test + +"Full house two small, three big" print +{ 16 } [ { 2 2 4 4 4 } "full house" score ] unit-test + +"Full house three small, two big" print +{ 19 } [ { 5 3 3 5 3 } "full house" score ] unit-test + +"Two pair is not a full house" print +{ 0 } [ { 2 2 4 4 5 } "full house" score ] unit-test + +"Four of a kind is not a full house" print +{ 0 } [ { 1 4 4 4 4 } "full house" score ] unit-test + +"Yacht is not a full house" print +{ 0 } [ { 2 2 2 2 2 } "full house" score ] unit-test + +"Four of a Kind" print +{ 24 } [ { 6 6 4 6 6 } "four of a kind" score ] unit-test + +"Yacht can be scored as Four of a Kind" print +{ 12 } [ { 3 3 3 3 3 } "four of a kind" score ] unit-test + +"Full house is not Four of a Kind" print +{ 0 } [ { 3 3 3 5 5 } "four of a kind" score ] unit-test + +"Little Straight" print +{ 30 } [ { 3 5 4 1 2 } "little straight" score ] unit-test + +"Little Straight as Big Straight" print +{ 0 } [ { 1 2 3 4 5 } "big straight" score ] unit-test + +"Four in order but not a little straight" print +{ 0 } [ { 1 1 2 3 4 } "little straight" score ] unit-test + +"No pairs but not a little straight" print +{ 0 } [ { 1 2 3 4 6 } "little straight" score ] unit-test + +"Minimum is 1, maximum is 5, but not a little straight" print +{ 0 } [ { 1 1 3 4 5 } "little straight" score ] unit-test + +"Big Straight" print +{ 30 } [ { 4 6 2 5 3 } "big straight" score ] unit-test + +"Big Straight as little straight" print +{ 0 } [ { 6 5 4 3 2 } "little straight" score ] unit-test + +"No pairs but not a big straight" print +{ 0 } [ { 6 5 4 3 1 } "big straight" score ] unit-test + +"Choice" print +{ 23 } [ { 3 3 5 6 6 } "choice" score ] unit-test + +"Yacht as choice" print +{ 10 } [ { 2 2 2 2 2 } "choice" score ] unit-test diff --git a/exercises/practice/yacht/yacht/yacht.factor b/exercises/practice/yacht/yacht/yacht.factor new file mode 100644 index 00000000..b64f8223 --- /dev/null +++ b/exercises/practice/yacht/yacht/yacht.factor @@ -0,0 +1,5 @@ +USING: kernel ; +IN: yacht + +: score ( dice category -- score ) + "unimplemented" throw ;