diff --git a/config.json b/config.json
index 9455b508e..d2a270f9e 100644
--- a/config.json
+++ b/config.json
@@ -2205,6 +2205,19 @@
"strings"
],
"difficulty": 3
+ },
+ {
+ "slug": "killer-sudoku-helper",
+ "name": "Killer Sudoku Helper",
+ "uuid": "beb3f222-54b2-459c-9704-f2e6516542bb",
+ "practices": [
+ "lists"
+ ],
+ "prerequisites": [
+ "lists",
+ "recursion"
+ ],
+ "difficulty": 5
}
],
"foregone": [
diff --git a/exercises/Exercises.sln b/exercises/Exercises.sln
index 78358a3d0..b676be98e 100644
--- a/exercises/Exercises.sln
+++ b/exercises/Exercises.sln
@@ -305,6 +305,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Zipper", "practice\zipper\Z
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ResistorColorTrio", "practice\resistor-color-trio\ResistorColorTrio.fsproj", "{1850FAE9-5ACB-41D0-91BB-AD17A1021248}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "KillerSudokuHelper", "practice\killer-sudoku-helper\KillerSudokuHelper.fsproj", "{FCE9E627-CFF9-4EF3-84BE-D42B354825AA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -898,6 +900,10 @@ Global
{1850FAE9-5ACB-41D0-91BB-AD17A1021248}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1850FAE9-5ACB-41D0-91BB-AD17A1021248}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1850FAE9-5ACB-41D0-91BB-AD17A1021248}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FCE9E627-CFF9-4EF3-84BE-D42B354825AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FCE9E627-CFF9-4EF3-84BE-D42B354825AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FCE9E627-CFF9-4EF3-84BE-D42B354825AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FCE9E627-CFF9-4EF3-84BE-D42B354825AA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B404AA3C-A226-409A-A035-6C1DC66940DD} = {B7E719DB-FB8D-43B4-B529-55FCF6E3DC3F}
@@ -1049,5 +1055,6 @@ Global
{A6E25412-34F6-49ED-834B-8A551CF3F2D3} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
{32F8738C-2782-4881-95C0-C621DC0D7ED9} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
{1850FAE9-5ACB-41D0-91BB-AD17A1021248} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
+ {FCE9E627-CFF9-4EF3-84BE-D42B354825AA} = {391BEEC4-91A8-43F3-AE94-D5CB9A8FA611}
EndGlobalSection
EndGlobal
diff --git a/exercises/practice/killer-sudoku-helper/.config/dotnet-tools.json b/exercises/practice/killer-sudoku-helper/.config/dotnet-tools.json
new file mode 100644
index 000000000..0f7926bad
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.config/dotnet-tools.json
@@ -0,0 +1,12 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "fantomas-tool": {
+ "version": "4.7.9",
+ "commands": [
+ "fantomas"
+ ]
+ }
+ }
+}
\ No newline at end of file
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 000000000..fdafdca8f
--- /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](https://www.f-puzzles.com/), a Puzzle Setting Tool by Eric Fox.
+
+[sudoku-rules]: https://masteringsudoku.com/sudoku-rules-beginners/
+[killer-guide]: https://masteringsudoku.com/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://app.crackingthecryptic.com/sudoku/HqTBn3Pr6R
+[goodliffe-video]: https://youtu.be/c_NjEbFEeW0?t=1180
diff --git a/exercises/practice/killer-sudoku-helper/.meta/Example.fs b/exercises/practice/killer-sudoku-helper/.meta/Example.fs
new file mode 100644
index 000000000..b4caf1f3a
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/Example.fs
@@ -0,0 +1,16 @@
+module KillerSudokuHelper
+
+module List =
+ let rec combinations n l =
+ match n, l with
+ | 0, _ -> [ [] ]
+ | _, [] -> []
+ | k, (x :: xs) ->
+ List.map ((@) [ x ]) (combinations (k - 1) xs)
+ @ combinations k xs
+
+let combinations sum size exclude =
+ [ 1..9 ]
+ |> List.except exclude
+ |> List.combinations size
+ |> List.filter (fun combination -> List.sum combination = sum)
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 000000000..a35696faa
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/config.json
@@ -0,0 +1,19 @@
+{
+ "authors": [
+ "erikschierboom"
+ ],
+ "files": {
+ "solution": [
+ "KillerSudokuHelper.fs"
+ ],
+ "test": [
+ "KillerSudokuHelperTests.fs"
+ ],
+ "example": [
+ ".meta/Example.fs"
+ ]
+ },
+ "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/tests.toml b/exercises/practice/killer-sudoku-helper/.meta/tests.toml
new file mode 100644
index 000000000..19c23e8a9
--- /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/KillerSudokuHelper.fs b/exercises/practice/killer-sudoku-helper/KillerSudokuHelper.fs
new file mode 100644
index 000000000..da79e195f
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/KillerSudokuHelper.fs
@@ -0,0 +1,4 @@
+module KillerSudokuHelper
+
+let combinations sum size exclude =
+ failwith "Please implement the 'combinations' function"
diff --git a/exercises/practice/killer-sudoku-helper/KillerSudokuHelper.fsproj b/exercises/practice/killer-sudoku-helper/KillerSudokuHelper.fsproj
new file mode 100644
index 000000000..24cf39ce6
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/KillerSudokuHelper.fsproj
@@ -0,0 +1,22 @@
+
+
+ net8.0
+ false
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
\ No newline at end of file
diff --git a/exercises/practice/killer-sudoku-helper/KillerSudokuHelperTests.fs b/exercises/practice/killer-sudoku-helper/KillerSudokuHelperTests.fs
new file mode 100644
index 000000000..33727c111
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/KillerSudokuHelperTests.fs
@@ -0,0 +1,67 @@
+module KillerSudokuHelperTests
+
+open FsUnit.Xunit
+open Xunit
+
+open KillerSudokuHelper
+
+[]
+let ``1`` () =
+ combinations 1 1 [] |> should equal [ [ 1 ] ]
+
+[]
+let ``2`` () =
+ combinations 2 1 [] |> should equal [ [ 2 ] ]
+
+[]
+let ``3`` () =
+ combinations 3 1 [] |> should equal [ [ 3 ] ]
+
+[]
+let ``4`` () =
+ combinations 4 1 [] |> should equal [ [ 4 ] ]
+
+[]
+let ``5`` () =
+ combinations 5 1 [] |> should equal [ [ 5 ] ]
+
+[]
+let ``6`` () =
+ combinations 6 1 [] |> should equal [ [ 6 ] ]
+
+[]
+let ``7`` () =
+ combinations 7 1 [] |> should equal [ [ 7 ] ]
+
+[]
+let ``8`` () =
+ combinations 8 1 [] |> should equal [ [ 8 ] ]
+
+[]
+let ``9`` () =
+ combinations 9 1 [] |> should equal [ [ 9 ] ]
+
+[]
+let ``Cage with sum 45 contains all digits 1:9`` () =
+ combinations 45 9 []
+ |> should equal [ [ 1; 2; 3; 4; 5; 6; 7; 8; 9 ] ]
+
+[]
+let ``Cage with only 1 possible combination`` () =
+ combinations 7 3 []
+ |> should equal [ [ 1; 2; 4 ] ]
+
+[]
+let ``Cage with several combinations`` () =
+ combinations 10 2 []
+ |> should
+ equal
+ [ [ 1; 9 ]
+ [ 2; 8 ]
+ [ 3; 7 ]
+ [ 4; 6 ] ]
+
+[]
+let ``Cage with several combinations that is restricted`` () =
+ combinations 10 2 [ 1; 4 ]
+ |> should equal [ [ 2; 8 ]; [ 3; 7 ] ]
diff --git a/generators/Generators.fs b/generators/Generators.fs
index c814e814e..23fe473dd 100644
--- a/generators/Generators.fs
+++ b/generators/Generators.fs
@@ -2050,3 +2050,6 @@ type BottleSong() =
type ResistorColorTrio() =
inherit ExerciseGenerator()
+
+type KillerSudokuHelper() =
+ inherit ExerciseGenerator()