Skip to content

Commit

Permalink
Add killer-sudoku-helper exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
ageron committed Sep 28, 2024
1 parent 731b01d commit c26039f
Show file tree
Hide file tree
Showing 8 changed files with 304 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,14 @@
"prerequisites": [],
"difficulty": 5
},
{
"slug": "killer-sudoku-helper",
"name": "Killer Sudoku Helper",
"uuid": "e12846f9-8905-406d-8b3d-b5e0fa4e3c0a",
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "acronym",
"name": "Acronym",
Expand Down
85 changes: 85 additions & 0 deletions exercises/practice/killer-sudoku-helper/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions exercises/practice/killer-sudoku-helper/.meta/Example.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module [combinations]

Combination : List U8

combinations : { sum : U8, size : U8, exclude ? List U8 } -> List Combination
combinations = \{ sum, size, exclude ? [] } ->
help = \target, digits ->
if target == 0 then
[[]]
else

when digits is
[] -> [[]]
[single] -> if single == target then [[single]] else []
[first, .. as rest] ->
if first > target then
[]
else

help (target - first) rest
|> List.map \combi -> combi |> List.prepend first
|> List.concat (help target rest)
availableDigits =
[1, 2, 3, 4, 5, 6, 7, 8, 9]
|> List.dropIf \digit -> exclude |> List.contains digit
help sum availableDigits
|> List.keepIf \combi -> List.len combi == size |> Num.toU64
19 changes: 19 additions & 0 deletions exercises/practice/killer-sudoku-helper/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"ageron"
],
"files": {
"solution": [
"KillerSudokuHelper.roc"
],
"test": [
"killer-sudoku-helper-test.roc"
],
"example": [
".meta/Example.roc"
]
},
"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"
}
31 changes: 31 additions & 0 deletions exercises/practice/killer-sudoku-helper/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{%- import "generator_macros.j2" as macros with context -%}
{{ macros.canonical_ref() }}
{{ macros.header() }}

import {{ exercise | to_pascal }} exposing [combinations]

{% macro to_cage(cage) -%}
{ sum: {{ cage["sum"] }}, size: {{ cage["size"] }}
{%- if cage["exclude"] != [] -%}
, exclude: {{ cage["exclude"] | to_roc }}
{%- endif %} }
{%- endmacro %}

{% for supercase in cases %}
## {{ supercase["description"] }}
{%- if supercase["cases"] -%}
{%- set cases = supercase["cases"] -%}
{%- else -%}
{%- set cases = [supercase] -%}
{%- endif -%}
{%- for case in cases %}
{%- if case["description"] != supercase["description"] %}
# {{ case["description"] }}
{%- endif %}
expect
result = {{ case["property"] }} {{ to_cage(case["input"]["cage"]) }}
result == {{ case["expected"] | to_roc }}

{% endfor -%}
{%- endfor %}

49 changes: 49 additions & 0 deletions exercises/practice/killer-sudoku-helper/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module [combinations]

Combination : List U8

combinations : { sum : U8, size : U8, exclude ? List U8 } -> List Combination
combinations = \{ sum, size, exclude ? [] } ->
crash "Please implement the 'combinations' function"
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# These tests are auto-generated with test data from:
# https://github.com/exercism/problem-specifications/tree/main/exercises/killer-sudoku-helper/canonical-data.json
# File last updated on 2024-09-28
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
}

main =
Task.ok {}

import KillerSudokuHelper exposing [combinations]

## Trivial 1-digit cages
# 1
expect
result = combinations { sum: 1, size: 1 }
result == [[1]]

# 2
expect
result = combinations { sum: 2, size: 1 }
result == [[2]]

# 3
expect
result = combinations { sum: 3, size: 1 }
result == [[3]]

# 4
expect
result = combinations { sum: 4, size: 1 }
result == [[4]]

# 5
expect
result = combinations { sum: 5, size: 1 }
result == [[5]]

# 6
expect
result = combinations { sum: 6, size: 1 }
result == [[6]]

# 7
expect
result = combinations { sum: 7, size: 1 }
result == [[7]]

# 8
expect
result = combinations { sum: 8, size: 1 }
result == [[8]]

# 9
expect
result = combinations { sum: 9, size: 1 }
result == [[9]]

## Cage with sum 45 contains all digits 1:9
expect
result = combinations { sum: 45, size: 9 }
result == [[1, 2, 3, 4, 5, 6, 7, 8, 9]]

## Cage with only 1 possible combination
expect
result = combinations { sum: 7, size: 3 }
result == [[1, 2, 4]]

## Cage with several combinations
expect
result = combinations { sum: 10, size: 2 }
result == [[1, 9], [2, 8], [3, 7], [4, 6]]

## Cage with several combinations that is restricted
expect
result = combinations { sum: 10, size: 2, exclude: [1, 4] }
result == [[2, 8], [3, 7]]

0 comments on commit c26039f

Please sign in to comment.