diff --git a/config.json b/config.json index c2311aa..2d268b9 100644 --- a/config.json +++ b/config.json @@ -511,6 +511,14 @@ "prerequisites": [], "difficulty": 4 }, + { + "slug": "anagram", + "name": "Anagram", + "uuid": "ced95ca8-5145-472e-aaf8-a5998883dbe6", + "practices": [], + "prerequisites": [], + "difficulty": 3 + }, { "slug": "armstrong-numbers", "name": "Armstrong Numbers", diff --git a/exercises/practice/anagram/.docs/instructions.md b/exercises/practice/anagram/.docs/instructions.md new file mode 100644 index 0000000..7d1c828 --- /dev/null +++ b/exercises/practice/anagram/.docs/instructions.md @@ -0,0 +1,13 @@ +# Instructions + +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"`. + +Given a target word and a set of candidate words, this exercise requests the anagram set: the subset of the candidates that are anagrams of the target. + +The target and candidates are words 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 anagram set is the subset of the candidate set that are anagrams of the target (in any order). +Words in the anagram set should have the same letter case as in the candidate set. + +Given the target `"stone"` and candidates `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, `"Seton"`, the anagram set is `"tones"`, `"notes"`, `"Seton"`. diff --git a/exercises/practice/anagram/.meta/config.json b/exercises/practice/anagram/.meta/config.json new file mode 100644 index 0000000..51452d6 --- /dev/null +++ b/exercises/practice/anagram/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "anagram.v" + ], + "test": [ + "run_test.v" + ], + "example": [ + ".meta/example.v" + ] + }, + "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.v b/exercises/practice/anagram/.meta/example.v new file mode 100644 index 0000000..da33b5b --- /dev/null +++ b/exercises/practice/anagram/.meta/example.v @@ -0,0 +1,18 @@ +module main + +fn find_anagrams(subject string, candidates []string) []string { + mut result := []string{cap: candidates.len} + mut subject_runes := subject.to_lower().runes() + subject_runes.sort() + for candidate in candidates { + if candidate.to_lower() == subject.to_lower() { + continue + } + mut candidate_runes := candidate.to_lower().runes() + candidate_runes.sort() + if candidate_runes == subject_runes { + result << candidate + } + } + return result +} diff --git a/exercises/practice/anagram/.meta/tests.toml b/exercises/practice/anagram/.meta/tests.toml new file mode 100644 index 0000000..8a8923b --- /dev/null +++ b/exercises/practice/anagram/.meta/tests.toml @@ -0,0 +1,80 @@ +# This is an auto-generated file. Regular comments will be removed when this +# file is regenerated. Regenerating will not touch any manually added keys, +# so comments can be added in 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" +include = false + +[fd3509e5-e3ba-409d-ac3d-a9ac84d13296] +description = "different characters may have the same bytes" diff --git a/exercises/practice/anagram/anagram.v b/exercises/practice/anagram/anagram.v new file mode 100644 index 0000000..b43f03e --- /dev/null +++ b/exercises/practice/anagram/anagram.v @@ -0,0 +1,4 @@ +module main + +fn find_anagrams(subject string, candidates []string) []string { +} diff --git a/exercises/practice/anagram/run_test.v b/exercises/practice/anagram/run_test.v new file mode 100644 index 0000000..7147207 --- /dev/null +++ b/exercises/practice/anagram/run_test.v @@ -0,0 +1,103 @@ +module main + +fn test_no_matches() { + candidates := ['hello', 'world', 'zombies', 'pants'] + expected := []string{} + assert find_anagrams('diaper', candidates) == expected +} + +fn test_detects_two_anagrams() { + candidates := ['lemons', 'cherry', 'melons'] + expected := ['lemons', 'melons'] + assert find_anagrams('solemn', candidates) == expected +} + +fn test_does_not_detect_anagram_subsets() { + candidates := ['dog', 'goody'] + expected := []string{} + assert find_anagrams('good', candidates) == expected +} + +fn test_detects_anagram() { + candidates := ['enlists', 'google', 'inlets', 'banana'] + expected := ['inlets'] + assert find_anagrams('listen', candidates) == expected +} + +fn test_detects_three_anagrams() { + candidates := ['gallery', 'ballerina', 'regally', 'clergy', 'largely', 'leading'] + expected := ['gallery', 'regally', 'largely'] + assert find_anagrams('allergy', candidates) == expected +} + +fn test_detects_multiple_anagrams_with_different_case() { + candidates := ['Eons', 'ONES'] + expected := ['Eons', 'ONES'] + assert find_anagrams('nose', candidates) == expected +} + +fn test_does_not_detect_non_anagrams_with_identical_checksum() { + candidates := ['last'] + expected := []string{} + assert find_anagrams('mass', candidates) == expected +} + +fn test_detects_anagrams_case_insensitively() { + candidates := ['cashregister', 'Carthorse', 'radishes'] + expected := ['Carthorse'] + assert find_anagrams('Orchestra', candidates) == expected +} + +fn test_detects_anagrams_using_case_insensitive_subject() { + candidates := ['cashregister', 'carthorse', 'radishes'] + expected := ['carthorse'] + assert find_anagrams('Orchestra', candidates) == expected +} + +fn test_detects_anagrams_using_case_insensitive_possible_matches() { + candidates := ['cashregister', 'Carthorse', 'radishes'] + expected := ['Carthorse'] + assert find_anagrams('orchestra', candidates) == expected +} + +fn test_does_not_detect_an_anagram_if_the_original_word_is_repeated() { + candidates := ['goGoGO'] + expected := []string{} + assert find_anagrams('go', candidates) == expected +} + +fn test_anagrams_must_use_all_letters_exactly_once() { + candidates := ['patter'] + expected := []string{} + assert find_anagrams('tapper', candidates) == expected +} + +fn test_words_are_not_anagrams_of_themselves() { + candidates := ['BANANA'] + expected := []string{} + assert find_anagrams('BANANA', candidates) == expected +} + +fn test_words_are_not_anagrams_of_themselves_even_if_letter_case_is_partially_different() { + candidates := ['Banana'] + expected := []string{} + assert find_anagrams('BANANA', candidates) == expected +} + +fn test_words_are_not_anagrams_of_themselves_even_if_letter_case_is_completely_different() { + candidates := ['banana'] + expected := []string{} + assert find_anagrams('BANANA', candidates) == expected +} + +fn test_words_other_than_themselves_can_be_anagrams() { + candidates := ['LISTEN', 'Silent'] + expected := ['Silent'] + assert find_anagrams('LISTEN', candidates) == expected +} + +fn test_different_characters_may_have_the_same_bytes() { + candidates := ['€a'] + expected := []string{} + assert find_anagrams('a⬂', candidates) == expected +}