Skip to content

Instantly share code, notes, and snippets.

@timiles
Created August 21, 2018 11:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timiles/a1dcc6e2922e2ecdae11202863a10477 to your computer and use it in GitHub Desktop.
Save timiles/a1dcc6e2922e2ecdae11202863a10477 to your computer and use it in GitHub Desktop.
KenKen is a Sudoku-like puzzle. I solved one just by lots of guessing, and was surprised that I got it perfectly right first time. I wondered if actually there were many solutions and I had found just one. So I wrote a bit of code, and actually it turns out I was just lucky. I think KenKen is a stupid puzzle that requires brute forcing rather th…
// These values are specific to the problem I was solving
const GRID_SIZE = 6;
const BLOCK_CELL_INDICES = [[0, 6], [1, 2], [3, 4], [5, 11], [7, 13], [8, 14], [9, 15], [10, 16],
[12, 18], [17, 23], [19, 20], [21, 22], [24, 25], [26, 32], [27, 33], [28, 29], [30, 31], [34, 35]];
const BLOCK_RULES = [[1, '-'], [3, '/'], [4, '-'], [4, '-'], [4, '-'], [1, '-'], [5, '+'], [2, '-'], [10, '*'],
[2, '-'], [2, '/'], [2, '-'], [6, '*'], [1, '-'], [3, '-'], [2, '/'], [4, '-'], [3, '-']];
// The rest of the code from here should work for any KenKen
const operatorFuncs = new Map();
operatorFuncs.set('+', (a, b) => (a + b));
operatorFuncs.set('-', (a, b) => (a - b));
operatorFuncs.set('*', (a, b) => (a * b));
operatorFuncs.set('/', (a, b) => (a / b));
function isRuleSatisfied(a, b, c, operator) {
const func = operatorFuncs.get(operator);
return (func(a, b) === c || func(b, a) === c);
}
function isGridValid(grid) {
// Scan horizontally for duplicates
for (let y = 0; y < GRID_SIZE; y++) {
const numbersSeen = [];
for (let x = 0; x < GRID_SIZE; x++) {
const number = grid[x + (y * GRID_SIZE)];
if (number > 0 && numbersSeen.indexOf(number) >= 0) {
return false;
}
numbersSeen.push(number);
}
}
// Scan vertically for duplicates
for (let x = 0; x < GRID_SIZE; x++) {
const numbersSeen = [];
for (let y = 0; y < GRID_SIZE; y++) {
const number = grid[x + (y * GRID_SIZE)];
if (number > 0 && numbersSeen.indexOf(number) >= 0) {
return false;
}
numbersSeen.push(number);
}
}
// Scan blocks for rules
for (let i = 0; i < BLOCK_CELL_INDICES.length; i++) {
const cellIndices = BLOCK_CELL_INDICES[i];
const a = grid[cellIndices[0]];
const b = grid[cellIndices[1]];
if (a > 0 && b > 0 && !isRuleSatisfied(a, b, BLOCK_RULES[i][0], BLOCK_RULES[i][1])) {
return false;
}
}
return true;
}
function step(grid, index) {
if (index >= grid.length) {
// We have completed the grid :)
return grid;
}
for (let i = 0; i < GRID_SIZE; i++) {
grid[index] = i + 1;
if (isGridValid(grid)) {
try {
return step(grid, index + 1);
}
catch { }
}
}
// We have hit a dead end. Reset this cell and backtrack our recursion by throwing an exception
grid[index] = 0;
throw 'up';
}
function solve() {
// Initialise empty grid
const grid = new Array(GRID_SIZE * GRID_SIZE);
for (let i = 0; i < grid.length; i++) {
grid[i] = 0;
}
// Start filling up grid
const completedGrid = step(grid, 0);
// Write out completed grid in a nice way
let gridDisplay = '';
for (let i = 0; i < completedGrid.length; i++) {
gridDisplay += completedGrid[i] + ',';
if ((i + 1) % GRID_SIZE === 0) {
gridDisplay += '\n';
}
}
console.log(gridDisplay);
}
solve();
@timiles
Copy link
Author

timiles commented Aug 21, 2018

KenKen is a Sudoku-like puzzle. I solved one just by lots of guessing, and was surprised that I got it perfectly right first time. I wondered if actually there were many solutions and I had found just one. So I wrote a bit of code, and actually it turns out I was just lucky. I think KenKen is a stupid puzzle that requires brute forcing rather than logic to solve. The code took me just over an hour to write so there may be bugs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment