From ca625ad8cc594ee1fac639b54da2bf1356970406 Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Thu, 7 May 2026 11:41:20 -0400 Subject: [PATCH] Add killer-sudoku-helper --- config.json | 8 ++ .../practice/killer-sudoku-helper/.busted | 5 ++ .../.docs/instructions.md | 85 +++++++++++++++++++ .../killer-sudoku-helper/.meta/config.json | 19 +++++ .../killer-sudoku-helper/.meta/example.moon | 26 ++++++ .../.meta/spec_generator.moon | 16 ++++ .../killer-sudoku-helper/.meta/tests.toml | 49 +++++++++++ .../killer_sudoku_helper.moon | 4 + .../killer_sudoku_helper_spec.moon | 76 +++++++++++++++++ .../shared/templates/spec_generator.moon | 2 +- 10 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 exercises/practice/killer-sudoku-helper/.busted create mode 100644 exercises/practice/killer-sudoku-helper/.docs/instructions.md create mode 100644 exercises/practice/killer-sudoku-helper/.meta/config.json create mode 100644 exercises/practice/killer-sudoku-helper/.meta/example.moon create mode 100644 exercises/practice/killer-sudoku-helper/.meta/spec_generator.moon create mode 100644 exercises/practice/killer-sudoku-helper/.meta/tests.toml create mode 100644 exercises/practice/killer-sudoku-helper/killer_sudoku_helper.moon create mode 100644 exercises/practice/killer-sudoku-helper/killer_sudoku_helper_spec.moon diff --git a/config.json b/config.json index c7ba4e4..9929ecf 100644 --- a/config.json +++ b/config.json @@ -730,6 +730,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "killer-sudoku-helper", + "name": "Killer Sudoku Helper", + "uuid": "af673916-a7af-4e9e-ac98-db885de660c8", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "knapsack", "name": "Knapsack", diff --git a/exercises/practice/killer-sudoku-helper/.busted b/exercises/practice/killer-sudoku-helper/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/killer-sudoku-helper/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/killer-sudoku-helper/.docs/instructions.md b/exercises/practice/killer-sudoku-helper/.docs/instructions.md new file mode 100644 index 0000000..4153c3e --- /dev/null +++ b/exercises/practice/killer-sudoku-helper/.docs/instructions.md @@ -0,0 +1,85 @@ +# Instructions + +A friend of yours is learning how to solve Killer Sudokus (rules below) but struggling to figure out which digits can go in a cage. +They ask you to help them out by writing a small program that lists all valid combinations for a given cage, and any constraints that affect the cage. + +To make the output of your program easy to read, the combinations it returns must be sorted. + +## Killer Sudoku Rules + +- [Standard Sudoku rules][sudoku-rules] apply. +- The digits in a cage, usually marked by a dotted line, add up to the small number given in the corner of the cage. +- A digit may only occur once in a cage. + +For a more detailed explanation, check out [this guide][killer-guide]. + +## Example 1: Cage with only 1 possible combination + +In a 3-digit cage with a sum of 7, there is only one valid combination: 124. + +- 1 + 2 + 4 = 7 +- Any other combination that adds up to 7, e.g. 232, would violate the rule of not repeating digits within a cage. + +![Sudoku grid, with three killer cages that are marked as grouped together. +The first killer cage is in the 3×3 box in the top left corner of the grid. +The middle column of that box forms the cage, with the followings cells from top to bottom: first cell contains a 1 and a pencil mark of 7, indicating a cage sum of 7, second cell contains a 2, third cell contains a 5. +The numbers are highlighted in red to indicate a mistake. +The second killer cage is in the central 3×3 box of the grid. +The middle column of that box forms the cage, with the followings cells from top to bottom: first cell contains a 1 and a pencil mark of 7, indicating a cage sum of 7, second cell contains a 2, third cell contains a 4. +None of the numbers in this cage are highlighted and therefore don't contain any mistakes. +The third killer cage follows the outside corner of the central 3×3 box of the grid. +It is made up of the following three cells: the top left cell of the cage contains a 2, highlighted in red, and a cage sum of 7. +The top right cell of the cage contains a 3. +The bottom right cell of the cage contains a 2, highlighted in red. All other cells are empty.][one-solution-img] + +## Example 2: Cage with several combinations + +In a 2-digit cage with a sum 10, there are 4 possible combinations: + +- 19 +- 28 +- 37 +- 46 + +![Sudoku grid, all squares empty except for the middle column, column 5, which has 8 rows filled. +Each continguous two rows form a killer cage and are marked as grouped together. +From top to bottom: first group is a cell with value 1 and a pencil mark indicating a cage sum of 10, cell with value 9. +Second group is a cell with value 2 and a pencil mark of 10, cell with value 8. +Third group is a cell with value 3 and a pencil mark of 10, cell with value 7. +Fourth group is a cell with value 4 and a pencil mark of 10, cell with value 6. +The last cell in the column is empty.][four-solutions-img] + +## Example 3: Cage with several combinations that is restricted + +In a 2-digit cage with a sum 10, where the column already contains a 1 and a 4, there are 2 possible combinations: + +- 28 +- 37 + +19 and 46 are not possible due to the 1 and 4 in the column according to standard Sudoku rules. + +![Sudoku grid, all squares empty except for the middle column, column 5, which has 8 rows filled. +The first row contains a 4, the second is empty, and the third contains a 1. +The 1 is highlighted in red to indicate a mistake. +The last 6 rows in the column form killer cages of two cells each. +From top to bottom: first group is a cell with value 2 and a pencil mark indicating a cage sum of 10, cell with value 8. +Second group is a cell with value 3 and a pencil mark of 10, cell with value 7. +Third group is a cell with value 1, highlighted in red, and a pencil mark of 10, cell with value 9.][not-possible-img] + +## Trying it yourself + +If you want to give an approachable Killer Sudoku a go, you can try out [this puzzle][clover-puzzle] by Clover, featured by [Mark Goodliffe on Cracking The Cryptic on the 21st of June 2021][goodliffe-video]. + +You can also find Killer Sudokus in varying difficulty in numerous newspapers, as well as Sudoku apps, books and websites. + +## Credit + +The screenshots above have been generated using F-Puzzles.com, a Puzzle Setting Tool by Eric Fox. + +[sudoku-rules]: https://en.wikipedia.org/wiki/Sudoku +[killer-guide]: https://en.wikipedia.org/wiki/Killer_sudoku +[one-solution-img]: https://assets.exercism.org/images/exercises/killer-sudoku-helper/example1.png +[four-solutions-img]: https://assets.exercism.org/images/exercises/killer-sudoku-helper/example2.png +[not-possible-img]: https://assets.exercism.org/images/exercises/killer-sudoku-helper/example3.png +[clover-puzzle]: https://sudokupad.app/HqTBn3Pr6R +[goodliffe-video]: https://youtu.be/c_NjEbFEeW0?t=1180 diff --git a/exercises/practice/killer-sudoku-helper/.meta/config.json b/exercises/practice/killer-sudoku-helper/.meta/config.json new file mode 100644 index 0000000..47f2e22 --- /dev/null +++ b/exercises/practice/killer-sudoku-helper/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "killer_sudoku_helper.moon" + ], + "test": [ + "killer_sudoku_helper_spec.moon" + ], + "example": [ + ".meta/example.moon" + ] + }, + "blurb": "Write a tool that makes it easier to solve Killer Sudokus", + "source": "Created by Sascha Mann, Jeremy Walker, and BethanyG for the Julia track on Exercism.", + "source_url": "https://github.com/exercism/julia/pull/413" +} diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.moon b/exercises/practice/killer-sudoku-helper/.meta/example.moon new file mode 100644 index 0000000..f3781a6 --- /dev/null +++ b/exercises/practice/killer-sudoku-helper/.meta/example.moon @@ -0,0 +1,26 @@ +List = require 'pl.List' + +combinations = (input) -> + {:sum, :size, :exclude} = input + exclude = List exclude + + if size == 1 + if 1 <= sum and sum <= 9 and not exclude\contains sum + return List {List {sum}} + else + return {} + + result = List! + for n = 1, 9 + if not exclude\contains n + for c in *combinations { + sum: sum - n, + size: size - 1, + exclude: exclude\append n + } + combo = c\append(n)\sort! + if not result\contains combo + result\append combo + result + +{ :combinations } diff --git a/exercises/practice/killer-sudoku-helper/.meta/spec_generator.moon b/exercises/practice/killer-sudoku-helper/.meta/spec_generator.moon new file mode 100644 index 0000000..5b33ba9 --- /dev/null +++ b/exercises/practice/killer-sudoku-helper/.meta/spec_generator.moon @@ -0,0 +1,16 @@ +import indent, int_list, int_lists, table_tostring from require 'spec_helpers' + +cage = (c) -> + "sum: #{c.sum}, size: #{c.size}, exclude: #{int_list c.exclude}" + +{ + module_imports: {'combinations'}, + + generate_test: (case, level) -> + lines = { + "result = #{case.property} #{cage case.input.cage}", + "expected = #{int_lists case.expected, level}", + "assert.are.same expected, result" + } + table.concat [indent line, level for line in *lines], '\n' +} diff --git a/exercises/practice/killer-sudoku-helper/.meta/tests.toml b/exercises/practice/killer-sudoku-helper/.meta/tests.toml new file mode 100644 index 0000000..19c23e8 --- /dev/null +++ b/exercises/practice/killer-sudoku-helper/.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. + +[2aaa8f13-11b5-4054-b95c-a906e4d79fb6] +description = "Trivial 1-digit cages -> 1" + +[4645da19-9fdd-4087-a910-a6ed66823563] +description = "Trivial 1-digit cages -> 2" + +[07cfc704-f8aa-41b2-8f9a-cbefb674cb48] +description = "Trivial 1-digit cages -> 3" + +[22b8b2ba-c4fd-40b3-b1bf-40aa5e7b5f24] +description = "Trivial 1-digit cages -> 4" + +[b75d16e2-ff9b-464d-8578-71f73094cea7] +description = "Trivial 1-digit cages -> 5" + +[bcbf5afc-4c89-4ff6-9357-07ab4d42788f] +description = "Trivial 1-digit cages -> 6" + +[511b3bf8-186f-4e35-844f-c804d86f4a7a] +description = "Trivial 1-digit cages -> 7" + +[bd09a60d-3aca-43bd-b6aa-6ccad01bedda] +description = "Trivial 1-digit cages -> 8" + +[9b539f27-44ea-4ff8-bd3d-c7e136bee677] +description = "Trivial 1-digit cages -> 9" + +[0a8b2078-b3a4-4dbd-be0d-b180f503d5c3] +description = "Cage with sum 45 contains all digits 1:9" + +[2635d7c9-c716-4da1-84f1-c96e03900142] +description = "Cage with only 1 possible combination" + +[a5bde743-e3a2-4a0c-8aac-e64fceea4228] +description = "Cage with several combinations" + +[dfbf411c-737d-465a-a873-ca556360c274] +description = "Cage with several combinations that is restricted" diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.moon b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.moon new file mode 100644 index 0000000..af54859 --- /dev/null +++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.moon @@ -0,0 +1,4 @@ +{ + combinations: (input) -> + error 'Implement me' +} diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper_spec.moon b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper_spec.moon new file mode 100644 index 0000000..86a95e4 --- /dev/null +++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper_spec.moon @@ -0,0 +1,76 @@ +import combinations from require 'killer_sudoku_helper' + +describe 'killer-sudoku-helper:', -> + describe 'Trivial 1-digit cages:', -> + it '1', -> + result = combinations sum: 1, size: 1, exclude: {} + expected = {{1}} + assert.are.same expected, result + + pending '2', -> + result = combinations sum: 2, size: 1, exclude: {} + expected = {{2}} + assert.are.same expected, result + + pending '3', -> + result = combinations sum: 3, size: 1, exclude: {} + expected = {{3}} + assert.are.same expected, result + + pending '4', -> + result = combinations sum: 4, size: 1, exclude: {} + expected = {{4}} + assert.are.same expected, result + + pending '5', -> + result = combinations sum: 5, size: 1, exclude: {} + expected = {{5}} + assert.are.same expected, result + + pending '6', -> + result = combinations sum: 6, size: 1, exclude: {} + expected = {{6}} + assert.are.same expected, result + + pending '7', -> + result = combinations sum: 7, size: 1, exclude: {} + expected = {{7}} + assert.are.same expected, result + + pending '8', -> + result = combinations sum: 8, size: 1, exclude: {} + expected = {{8}} + assert.are.same expected, result + + pending '9', -> + result = combinations sum: 9, size: 1, exclude: {} + expected = {{9}} + assert.are.same expected, result + + pending 'Cage with sum 45 contains all digits 1:9', -> + result = combinations sum: 45, size: 9, exclude: {} + expected = {{1, 2, 3, 4, 5, 6, 7, 8, 9}} + assert.are.same expected, result + + pending 'Cage with only 1 possible combination', -> + result = combinations sum: 7, size: 3, exclude: {} + expected = {{1, 2, 4}} + assert.are.same expected, result + + pending 'Cage with several combinations', -> + result = combinations sum: 10, size: 2, exclude: {} + expected = { + {1, 9}, + {2, 8}, + {3, 7}, + {4, 6}, + } + assert.are.same expected, result + + pending 'Cage with several combinations that is restricted', -> + result = combinations sum: 10, size: 2, exclude: {1, 4} + expected = { + {2, 8}, + {3, 7}, + } + assert.are.same expected, result diff --git a/exercises/shared/templates/spec_generator.moon b/exercises/shared/templates/spec_generator.moon index 78f65ae..e1c17b5 100644 --- a/exercises/shared/templates/spec_generator.moon +++ b/exercises/shared/templates/spec_generator.moon @@ -1,4 +1,4 @@ -import int_list, word_list from require 'test_helpers' +import indent, quote, int_list, word_list from require 'spec_helpers' { -- one of: