-
-
Save amacleod/4f73e8fefdf821d0183c to your computer and use it in GitHub Desktop.
Recursive Letter Sudoku // source https://jsbin.com/gazihu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<script src="https://code.jquery.com/jquery-2.1.4.js"></script> | |
<meta charset="utf-8"> | |
<title>Recursive Letter Sudoku</title> | |
<style id="jsbin-css"> | |
.letter-grid-box { | |
width: 30px; | |
text-align: center; | |
} | |
.neighbor-display { | |
text-align: center; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="letter-template-input-grid"> | |
<h3>Starting Letters</h3> | |
<table border-width="1"> | |
<tr> | |
<td><input id="letter-northwest" value="A" class="letter-grid-box"/></td> | |
<td><input id="letter-north" value="N" class="letter-grid-box"/></td> | |
<td><input id="letter-northeast" value="B" class="letter-grid-box"/></td> | |
</tr> | |
<tr> | |
<td><input id="letter-west" value="W" class="letter-grid-box"/></td> | |
<td><input id="letter-middle" value="O" class="letter-grid-box"/></td> | |
<td><input id="letter-east" value="E" class="letter-grid-box"/></td> | |
</tr> | |
<tr> | |
<td><input id="letter-southwest" value="C" class="letter-grid-box"/></td> | |
<td><input id="letter-south" value="S" class="letter-grid-box"/></td> | |
<td><input id="letter-southeast" value="D" class="letter-grid-box"/></td> | |
</tr> | |
</table> | |
</div> | |
<div id="word-input"> | |
<h3>Word</h3> | |
<input id="word" value="snowed"/> | |
<button onClick="analyze();">Ok</button> | |
</div> | |
<div id="word-neighbor-grid"> | |
<h3>Neighbors</h3> | |
<table border="true"> | |
<tr> | |
<td id="neighbor-northwest" class="neighbor-display"></td> | |
<td id="neighbor-north" class="neighbor-display"></td> | |
<td id="neighbor-northeast" class="neighbor-display"></td> | |
</tr> | |
<tr> | |
<td id="neighbor-west" class="neighbor-display"></td> | |
<td id="neighbor-self" class="neighbor-display"></td> | |
<td id="neighbor-east" class="neighbor-display"></td> | |
</tr> | |
<tr> | |
<td id="neighbor-southwest" class="neighbor-display"></td> | |
<td id="neighbor-south" class="neighbor-display"></td> | |
<td id="neighbor-southeast" class="neighbor-display"></td> | |
</tr> | |
</table> | |
</div> | |
<div id="analysis-message-area"> | |
<h3>Analysis</h3> | |
<ul id="analysis-messages"> | |
<li>Waiting for input.</li> | |
</ul> | |
</div> | |
<script id="jsbin-javascript"> | |
function analyze() { | |
var messages = new Messages(); | |
var grid = new Grid(messages); | |
var word = $('#word').val().toUpperCase(); | |
messages.log("Word is " + word + "."); | |
var letters = word.split(""); | |
$('#neighbor-northwest').html(grid.neighbor(letters, -1, -1)); | |
$('#neighbor-north').html(grid.neighbor(letters, -1, 0)); | |
$('#neighbor-northeast').html(grid.neighbor(letters, -1, 1)); | |
$('#neighbor-west').html(grid.neighbor(letters, 0, -1)); | |
$('#neighbor-self').html(grid.neighbor(letters, 0, 0)); | |
$('#neighbor-east').html(grid.neighbor(letters, 0, 1)); | |
$('#neighbor-southwest').html(grid.neighbor(letters, 1, -1)); | |
$('#neighbor-south').html(grid.neighbor(letters, 1, 0)); | |
$('#neighbor-southeast').html(grid.neighbor(letters, 1, 1)); | |
} | |
function Messages() { | |
var self = this; | |
self.output = $('#analysis-messages'); | |
self.output.empty(); | |
} | |
Messages.prototype.log = function(message) { | |
var self = this; | |
self.output.append("<li>" + message + "</li>"); | |
}; | |
function Grid(messages) { | |
var self = this; | |
self.messages = messages; | |
var result = [[], [], []]; | |
result[0][0] = $('#letter-northwest').val(); | |
result[0][1] = $('#letter-north').val(); | |
result[0][2] = $('#letter-northeast').val(); | |
result[1][0] = $('#letter-west').val(); | |
result[1][1] = $('#letter-middle').val(); | |
result[1][2] = $('#letter-east').val(); | |
result[2][0] = $('#letter-southwest').val(); | |
result[2][1] = $('#letter-south').val(); | |
result[2][2] = $('#letter-southeast').val(); | |
self.grid = result; | |
self.table = {}; | |
for (var row = 0; row < 3; row++) { | |
for (var col = 0; col < 3; col++) { | |
var letter = self.grid[row][col]; | |
self.table[letter] = {row: row, col: col}; | |
} | |
} | |
} | |
Grid.prototype.log = function(message) { | |
var self = this; | |
self.messages.log(message); | |
}; | |
/** | |
* Find one neighbor sequence for a sequence of letters from | |
* the grid. | |
* @param {array} letters - sequence of grid letters. | |
* @param {number} rowDelta - row offset: negative is up, positive is down. | |
* @param {number} colDelta - column offset: negative is left, positive is right. | |
*/ | |
Grid.prototype.neighbor = function(letters, rowDelta, colDelta) { | |
var self = this; | |
self.log("Finding neighbor at (" + rowDelta + ", " + colDelta + ")."); | |
var sequence = neighborSequence(self, letters, rowDelta, colDelta); | |
if (sequence) { | |
return sequence.join(""); | |
} else { | |
return "ø"; | |
} | |
}; | |
/** | |
* Given a sequence of letters that specifies fractally subdivided | |
* grid coordinates, find its neighbors. Coordinates near the edge | |
* of the grossest grid might have empty neighbors, since the | |
* topmost grid does have actual edges. | |
*/ | |
function neighborSequence(self, letters, rowDelta, colDelta) { | |
if (letters.length < 1) { | |
return null; | |
} | |
if (rowDelta === 0 && colDelta === 0) { | |
return letters; | |
} | |
var last = letters[letters.length - 1]; | |
var row = self.row(last) + rowDelta; | |
var col = self.col(last) + colDelta; | |
var finial = self.letterWrap(row, col); | |
/* Calculate the deltas of the grosser grid. */ | |
var grossRowDelta = gridDirection(row); | |
var grossColDelta = gridDirection(col); | |
var startingStem = letters.slice(0, -1); | |
var result; | |
if (grossRowDelta === 0 && grossColDelta === 0) { | |
result = startingStem.slice(); | |
result.push(finial); | |
return result; | |
} else { | |
var stem = neighborSequence(self, startingStem, grossRowDelta, grossColDelta); | |
if (stem) { | |
self.log(startingStem.join("") + " => " + stem.join("")); | |
result = stem.slice(); // Shallow copy. | |
result.push(finial); | |
return result; | |
} else { | |
return null; | |
} | |
} | |
} | |
/** | |
* Tell whether a grid index would fall off the local grid, and if | |
* so which direction. A result of -1 indicates "up" or "left", and | |
* 1 indicates "down" or "right". | |
*/ | |
function gridDirection(index) { | |
if (index < 0) { | |
return -1; | |
} | |
if (index >= 3) { | |
return 1; | |
} | |
return 0; | |
} | |
/** | |
* Find the letter corresponding to row and column, allowing for | |
* edge "wrapping" behavior due to grids lying adjacent to each | |
* other. | |
*/ | |
Grid.prototype.letterWrap = function(row, col) { | |
var self = this; | |
// Adding three to avoid modulo on negative numbers, since I forget how that behaves in JS. ~ACM | |
var r = (row + 3) % 3; | |
var c = (col + 3) % 3; | |
return self.grid[r][c]; | |
}; | |
/** | |
* Look up row by letter, which must match one of the letters of | |
* the original grid. | |
*/ | |
Grid.prototype.row = function(letter) { | |
var self = this; | |
return self.table[letter].row; | |
}; | |
/** | |
* Look up column by letter, which must match one of the letters | |
* from the original grid. | |
*/ | |
Grid.prototype.col = function(letter) { | |
var self = this; | |
return self.table[letter].col; | |
}; | |
</script> | |
<script id="jsbin-source-css" type="text/css">.letter-grid-box { | |
width: 30px; | |
text-align: center; | |
} | |
.neighbor-display { | |
text-align: center; | |
}</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.letter-grid-box { | |
width: 30px; | |
text-align: center; | |
} | |
.neighbor-display { | |
text-align: center; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function analyze() { | |
var messages = new Messages(); | |
var grid = new Grid(messages); | |
var word = $('#word').val().toUpperCase(); | |
messages.log("Word is " + word + "."); | |
var letters = word.split(""); | |
$('#neighbor-northwest').html(grid.neighbor(letters, -1, -1)); | |
$('#neighbor-north').html(grid.neighbor(letters, -1, 0)); | |
$('#neighbor-northeast').html(grid.neighbor(letters, -1, 1)); | |
$('#neighbor-west').html(grid.neighbor(letters, 0, -1)); | |
$('#neighbor-self').html(grid.neighbor(letters, 0, 0)); | |
$('#neighbor-east').html(grid.neighbor(letters, 0, 1)); | |
$('#neighbor-southwest').html(grid.neighbor(letters, 1, -1)); | |
$('#neighbor-south').html(grid.neighbor(letters, 1, 0)); | |
$('#neighbor-southeast').html(grid.neighbor(letters, 1, 1)); | |
} | |
function Messages() { | |
var self = this; | |
self.output = $('#analysis-messages'); | |
self.output.empty(); | |
} | |
Messages.prototype.log = function(message) { | |
var self = this; | |
self.output.append("<li>" + message + "</li>"); | |
}; | |
function Grid(messages) { | |
var self = this; | |
self.messages = messages; | |
var result = [[], [], []]; | |
result[0][0] = $('#letter-northwest').val(); | |
result[0][1] = $('#letter-north').val(); | |
result[0][2] = $('#letter-northeast').val(); | |
result[1][0] = $('#letter-west').val(); | |
result[1][1] = $('#letter-middle').val(); | |
result[1][2] = $('#letter-east').val(); | |
result[2][0] = $('#letter-southwest').val(); | |
result[2][1] = $('#letter-south').val(); | |
result[2][2] = $('#letter-southeast').val(); | |
self.grid = result; | |
self.table = {}; | |
for (var row = 0; row < 3; row++) { | |
for (var col = 0; col < 3; col++) { | |
var letter = self.grid[row][col]; | |
self.table[letter] = {row: row, col: col}; | |
} | |
} | |
} | |
Grid.prototype.log = function(message) { | |
var self = this; | |
self.messages.log(message); | |
}; | |
/** | |
* Find one neighbor sequence for a sequence of letters from | |
* the grid. | |
* @param {array} letters - sequence of grid letters. | |
* @param {number} rowDelta - row offset: negative is up, positive is down. | |
* @param {number} colDelta - column offset: negative is left, positive is right. | |
*/ | |
Grid.prototype.neighbor = function(letters, rowDelta, colDelta) { | |
var self = this; | |
self.log("Finding neighbor at (" + rowDelta + ", " + colDelta + ")."); | |
var sequence = neighborSequence(self, letters, rowDelta, colDelta); | |
if (sequence) { | |
return sequence.join(""); | |
} else { | |
return "ø"; | |
} | |
}; | |
/** | |
* Given a sequence of letters that specifies fractally subdivided | |
* grid coordinates, find its neighbors. Coordinates near the edge | |
* of the grossest grid might have empty neighbors, since the | |
* topmost grid does have actual edges. | |
*/ | |
function neighborSequence(self, letters, rowDelta, colDelta) { | |
if (letters.length < 1) { | |
return null; | |
} | |
if (rowDelta === 0 && colDelta === 0) { | |
return letters; | |
} | |
var last = letters[letters.length - 1]; | |
var row = self.row(last) + rowDelta; | |
var col = self.col(last) + colDelta; | |
var finial = self.letterWrap(row, col); | |
/* Calculate the deltas of the grosser grid. */ | |
var grossRowDelta = gridDirection(row); | |
var grossColDelta = gridDirection(col); | |
var startingStem = letters.slice(0, -1); | |
var result; | |
if (grossRowDelta === 0 && grossColDelta === 0) { | |
result = startingStem.slice(); | |
result.push(finial); | |
return result; | |
} else { | |
var stem = neighborSequence(self, startingStem, grossRowDelta, grossColDelta); | |
if (stem) { | |
self.log(startingStem.join("") + " => " + stem.join("")); | |
result = stem.slice(); // Shallow copy. | |
result.push(finial); | |
return result; | |
} else { | |
return null; | |
} | |
} | |
} | |
/** | |
* Tell whether a grid index would fall off the local grid, and if | |
* so which direction. A result of -1 indicates "up" or "left", and | |
* 1 indicates "down" or "right". | |
*/ | |
function gridDirection(index) { | |
if (index < 0) { | |
return -1; | |
} | |
if (index >= 3) { | |
return 1; | |
} | |
return 0; | |
} | |
/** | |
* Find the letter corresponding to row and column, allowing for | |
* edge "wrapping" behavior due to grids lying adjacent to each | |
* other. | |
*/ | |
Grid.prototype.letterWrap = function(row, col) { | |
var self = this; | |
// Adding three to avoid modulo on negative numbers, since I forget how that behaves in JS. ~ACM | |
var r = (row + 3) % 3; | |
var c = (col + 3) % 3; | |
return self.grid[r][c]; | |
}; | |
/** | |
* Look up row by letter, which must match one of the letters of | |
* the original grid. | |
*/ | |
Grid.prototype.row = function(letter) { | |
var self = this; | |
return self.table[letter].row; | |
}; | |
/** | |
* Look up column by letter, which must match one of the letters | |
* from the original grid. | |
*/ | |
Grid.prototype.col = function(letter) { | |
var self = this; | |
return self.table[letter].col; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment