Skip to content

Commit 27d9490

Browse files
sougat818Sougata Khan
andauthored
Add practice exercise: killer-sudoku-helper (exercism#2551)
* Add practice exercise: killer-sudoku-helper * killer-sudoku-helper: Remove final & Add @disabled to tests * killer-sudoku-helper: Set difficulty and UUID * killer-sudoku-helper: Missing newline in config.json * killer-sudoku-helper: Fix missing contributors in config.json * killer-sudoku-helper: changes requested in PR * killer-sudoku-helper: changes requested in PR * killer-sudoku-helper: changes requested in PR --------- Co-authored-by: Sougata Khan <sougata.khan@digio.com.au>
1 parent e6918e3 commit 27d9490

9 files changed

Lines changed: 311 additions & 0 deletions

File tree

config.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,22 @@
11351135
"strings"
11361136
]
11371137
},
1138+
{
1139+
"slug": "killer-sudoku-helper",
1140+
"name": "Killer Sudoku Helper",
1141+
"uuid": "8c45a47d-b3e3-484c-a124-90136ec838fd",
1142+
"practices": [
1143+
"for-loops",
1144+
"if-else-statements",
1145+
"numbers",
1146+
"lists"
1147+
],
1148+
"prerequisites": [
1149+
"numbers",
1150+
"lists"
1151+
],
1152+
"difficulty": 4
1153+
},
11381154
{
11391155
"slug": "kindergarten-garden",
11401156
"name": "Kindergarten Garden",
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Instructions
2+
3+
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.
4+
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.
5+
6+
To make the output of your program easy to read, the combinations it returns must be sorted.
7+
8+
## Killer Sudoku Rules
9+
10+
- [Standard Sudoku rules][sudoku-rules] apply.
11+
- The digits in a cage, usually marked by a dotted line, add up to the small number given in the corner of the cage.
12+
- A digit may only occur once in a cage.
13+
14+
For a more detailed explanation, check out [this guide][killer-guide].
15+
16+
## Example 1: Cage with only 1 possible combination
17+
18+
In a 3-digit cage with a sum of 7, there is only one valid combination: 124.
19+
20+
- 1 + 2 + 4 = 7
21+
- Any other combination that adds up to 7, e.g. 232, would violate the rule of not repeating digits within a cage.
22+
23+
![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]
24+
25+
## Example 2: Cage with several combinations
26+
27+
In a 2-digit cage with a sum 10, there are 4 possible combinations:
28+
29+
- 19
30+
- 28
31+
- 37
32+
- 46
33+
34+
![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]
35+
36+
## Example 3: Cage with several combinations that is restricted
37+
38+
In a 2-digit cage with a sum 10, where the column already contains a 1 and a 4, there are 2 possible combinations:
39+
40+
- 28
41+
- 37
42+
43+
19 and 46 are not possible due to the 1 and 4 in the column according to standard Sudoku rules.
44+
45+
![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]
46+
47+
## Trying it yourself
48+
49+
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].
50+
51+
You can also find Killer Sudokus in varying difficulty in numerous newspapers, as well as Sudoku apps, books and websites.
52+
53+
## Credit
54+
55+
The screenshots above have been generated using [F-Puzzles.com](https://www.f-puzzles.com/), a Puzzle Setting Tool by Eric Fox.
56+
57+
[sudoku-rules]: https://masteringsudoku.com/sudoku-rules-beginners/
58+
[killer-guide]: https://masteringsudoku.com/killer-sudoku/
59+
[one-solution-img]: https://assets.exercism.org/images/exercises/killer-sudoku-helper/example1.png
60+
[four-solutions-img]: https://assets.exercism.org/images/exercises/killer-sudoku-helper/example2.png
61+
[not-possible-img]: https://assets.exercism.org/images/exercises/killer-sudoku-helper/example3.png
62+
[clover-puzzle]: https://app.crackingthecryptic.com/sudoku/HqTBn3Pr6R
63+
[goodliffe-video]: https://youtu.be/c_NjEbFEeW0?t=1180
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"authors": [
3+
"sougat818"
4+
],
5+
"files": {
6+
"solution": [
7+
"src/main/java/KillerSudokuHelper.java"
8+
],
9+
"test": [
10+
"src/test/java/KillerSudokuHelperTest.java"
11+
],
12+
"example": [
13+
".meta/src/reference/java/KillerSudokuHelper.java"
14+
],
15+
"invalidator": [
16+
"build.gradle"
17+
]
18+
},
19+
"blurb": "Write a tool that makes it easier to solve Killer Sudokus",
20+
"source": "Created by Sascha Mann, Jeremy Walker, and BethanyG for the Julia track on Exercism.",
21+
"source_url": "https://github.com/exercism/julia/pull/413"
22+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import java.util.ArrayList;
2+
import java.util.List;
3+
4+
public class KillerSudokuHelper {
5+
6+
// Method to be called when no numbers are to be excluded.
7+
public List<List<Integer>> combinationsInCage(Integer cageSum, Integer cageSize) {
8+
// Call the main method with an empty exclude list.
9+
return combinationsInCage(cageSum, cageSize, new ArrayList<>());
10+
}
11+
12+
// Main method that includes to exclude list in the parameters.
13+
public List<List<Integer>> combinationsInCage(Integer cageSum, Integer cageSize, List<Integer> exclude) {
14+
// The range of valid numbers for a standard Sudoku puzzle.
15+
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9));
16+
numbers.removeAll(exclude);
17+
18+
return findCombinations(numbers, cageSum, cageSize, 0, new ArrayList<>());
19+
}
20+
21+
// Helper method to recursively find the combinations.
22+
private List<List<Integer>> findCombinations(List<Integer> numbers, Integer cageSum, Integer cageSize,
23+
Integer start,
24+
List<Integer> current) {
25+
List<List<Integer>> results = new ArrayList<>();
26+
27+
if (cageSize == 0) {
28+
// Base case: if the current combination sums up to the cage sum, add it to the results.
29+
if (sum(current) == cageSum) {
30+
results.add(new ArrayList<>(current));
31+
}
32+
return results;
33+
}
34+
35+
for (int i = start; i < numbers.size(); i++) {
36+
// Choose the number at the current position.
37+
current.add(numbers.get(i));
38+
// Explore further with the chosen number.
39+
results.addAll(findCombinations(numbers, cageSum, cageSize - 1, i + 1, current));
40+
// Un-choose the number for backtracking.
41+
current.remove(current.size() - 1);
42+
}
43+
44+
return results;
45+
}
46+
47+
private int sum(List<Integer> list) {
48+
int sum = 0;
49+
for (int number : list) {
50+
sum += number;
51+
}
52+
return sum;
53+
}
54+
55+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[2aaa8f13-11b5-4054-b95c-a906e4d79fb6]
13+
description = "Trivial 1-digit cages -> 1"
14+
15+
[4645da19-9fdd-4087-a910-a6ed66823563]
16+
description = "Trivial 1-digit cages -> 2"
17+
18+
[07cfc704-f8aa-41b2-8f9a-cbefb674cb48]
19+
description = "Trivial 1-digit cages -> 3"
20+
21+
[22b8b2ba-c4fd-40b3-b1bf-40aa5e7b5f24]
22+
description = "Trivial 1-digit cages -> 4"
23+
24+
[b75d16e2-ff9b-464d-8578-71f73094cea7]
25+
description = "Trivial 1-digit cages -> 5"
26+
27+
[bcbf5afc-4c89-4ff6-9357-07ab4d42788f]
28+
description = "Trivial 1-digit cages -> 6"
29+
30+
[511b3bf8-186f-4e35-844f-c804d86f4a7a]
31+
description = "Trivial 1-digit cages -> 7"
32+
33+
[bd09a60d-3aca-43bd-b6aa-6ccad01bedda]
34+
description = "Trivial 1-digit cages -> 8"
35+
36+
[9b539f27-44ea-4ff8-bd3d-c7e136bee677]
37+
description = "Trivial 1-digit cages -> 9"
38+
39+
[0a8b2078-b3a4-4dbd-be0d-b180f503d5c3]
40+
description = "Cage with sum 45 contains all digits 1:9"
41+
42+
[2635d7c9-c716-4da1-84f1-c96e03900142]
43+
description = "Cage with only 1 possible combination"
44+
45+
[a5bde743-e3a2-4a0c-8aac-e64fceea4228]
46+
description = "Cage with several combinations"
47+
48+
[dfbf411c-737d-465a-a873-ca556360c274]
49+
description = "Cage with several combinations that is restricted"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
plugins {
2+
id "java"
3+
}
4+
5+
repositories {
6+
mavenCentral()
7+
}
8+
9+
dependencies {
10+
testImplementation platform("org.junit:junit-bom:5.10.0")
11+
testImplementation "org.junit.jupiter:junit-jupiter"
12+
testImplementation "org.assertj:assertj-core:3.15.0"
13+
}
14+
15+
test {
16+
useJUnitPlatform()
17+
18+
testLogging {
19+
exceptionFormat = "full"
20+
showStandardStreams = true
21+
events = ["passed", "failed", "skipped"]
22+
}
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import java.util.ArrayList;
2+
import java.util.List;
3+
4+
public class KillerSudokuHelper {
5+
6+
List<List<Integer>> combinationsInCage(Integer cageSum, Integer cageSize, List<Integer> exclude) {
7+
throw new UnsupportedOperationException("Delete this statement and write your own implementation.");
8+
}
9+
10+
List<List<Integer>> combinationsInCage(Integer cageSum, Integer cageSize) {
11+
throw new UnsupportedOperationException("Delete this statement and write your own implementation.");
12+
}
13+
14+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import org.junit.jupiter.api.BeforeEach;
2+
import org.junit.jupiter.api.Disabled;
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.util.List;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
public class KillerSudokuHelperTest {
11+
12+
private KillerSudokuHelper helper;
13+
14+
@BeforeEach
15+
void setUp() {
16+
helper = new KillerSudokuHelper();
17+
}
18+
19+
@Test
20+
@DisplayName("Trivial 1-digit cages -> 1 to 9")
21+
public void testTrivialOneDigitCages() {
22+
for (int n = 1; n <= 9; n++) {
23+
List<List<Integer>> expected = List.of(List.of(n));
24+
assertThat(helper.combinationsInCage(n, 1)).isEqualTo(expected);
25+
}
26+
}
27+
28+
@Test
29+
@Disabled("Remove to run test")
30+
@DisplayName("Cage with sum 45 contains all digits 1:9")
31+
public void testCageWithSum45ContainsAllDigits() {
32+
List<List<Integer>> expected = List.of(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9));
33+
assertThat(helper.combinationsInCage(45, 9)).isEqualTo(expected);
34+
}
35+
36+
@Test
37+
@Disabled("Remove to run test")
38+
@DisplayName("Cage with only 1 possible combination")
39+
public void testCageWithOnlyOnePossibleCombination() {
40+
List<List<Integer>> expected = List.of(List.of(1, 2, 4));
41+
assertThat(helper.combinationsInCage(7, 3)).isEqualTo(expected);
42+
}
43+
44+
@Test
45+
@Disabled("Remove to run test")
46+
@DisplayName("Cage with several combinations")
47+
public void testCageWithSeveralCombinations() {
48+
List<List<Integer>> expected = List.of(
49+
List.of(1, 9),
50+
List.of(2, 8),
51+
List.of(3, 7),
52+
List.of(4, 6)
53+
);
54+
assertThat(helper.combinationsInCage(10, 2)).isEqualTo(expected);
55+
}
56+
57+
@Test
58+
@Disabled("Remove to run test")
59+
@DisplayName("Cage with several combinations that is restricted")
60+
public void testCageWithSeveralCombinationsThatIsRestricted() {
61+
List<List<Integer>> expected = List.of(
62+
List.of(2, 8),
63+
List.of(3, 7)
64+
);
65+
assertThat(helper.combinationsInCage(10, 2, List.of(1, 4))).isEqualTo(expected);
66+
}
67+
}
68+

exercises/settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ include 'practice:high-scores'
6969
include 'practice:house'
7070
include 'practice:isbn-verifier'
7171
include 'practice:isogram'
72+
include 'practice:killer-sudoku-helper'
7273
include 'practice:kindergarten-garden'
7374
include 'practice:knapsack'
7475
include 'practice:largest-series-product'

0 commit comments

Comments
 (0)