JSFiddle - React, Tailwind, and code Playground
HTML
<body>
<div id="panel">
<label for="url">Dict URL:</label>
<input id="url" type="text" name="dict_url" value="http://cartou.ch/sharedstuff/testdict.txt" />
<label for="word_len">Word Length</label>
<input id="word_len" type="number" value="11" />
<label for="mask_count">Mask Count</label>
<input id="mask_count" type="number" value="3" />
<input type="checkbox" id="output_result" checked="checked" />
<label for="output_result"> Output Result</label>
<input type="button" id="button" value="Run" />
</div>
<pre id="output" style="background-color: #222; color: #eee; width: 90ex; min-height: 20em; overflow: auto">
</pre>
</body>
CSS
input[type=number] {
max-width: 5ex;
}
CoffeeScript
MASK_CHAR = '-'
output = (text) ->
div = $('#output')
div.html(div.html() + text + '\n')
clear = () ->
$('#output').html('')
compare = (a, b) ->
if a < b then return -1
if a > b then return 1
return 0
class Result
constructor: (@mask, @words) ->
@count = @words.length
toString: () ->
@count + ': ' + @mask + ': ' + @words.join ','
calc = (words, wordLen, maskCount) ->
words1 = words.filter (w) -> w.length == wordLen
results = []
for mask in genMasks(wordLen, maskCount)
calcForMask(words1, mask, results)
results.sort (r1, r2) -> - compare(r1.count, r2.count)
return results.filter (r) -> r.count > 1
applyMask = (mask, word) ->
((if mask[i] != MASK_CHAR then word[i] else MASK_CHAR) for i in [0..mask.length-1]).join ''
calcForMask = (words, mask, results) ->
resultMap = {}
for word in words
maskedWord = applyMask(mask, word)
if not resultMap[maskedWord]?
resultMap[maskedWord] = [word]
else
resultMap[maskedWord].push(word)
for maskedWord, words of resultMap
results.push(new Result(maskedWord, words))
genMasks = (n, m) ->
if n < m then return []
if m == 0
if n == 0 then return [[]]
return [('x' for i in [1..n])]
use = genMasks(n - 1, m - 1)
notUse = genMasks(n - 1, m)
for x in use
x.push(MASK_CHAR)
for x in notUse
x.push('x')
return use.concat notUse
run = (dictURL, wordLen, maskCount, onStart, onFinish) ->
$.ajax
method: 'get'
url: dictURL
dataType: 'text'
.success (data) ->
output('File size: ' + data.length)
words = data.match(/[a-z]+/g)
output('Calculating...')
onStart(words)
results = calc(words, wordLen, maskCount)
onFinish(results)
.fail (error) ->
output('Failed to fetch words')
output('Error: ' + arguments)
profile = (dictURL, wordLen, maskCount) ->
timer = {}
clear()
# output(('-' for i in [1..120]).join '')
output('Fetching words...')
onStart = (words) ->
output('Got ' + words.length...