wordle-solver/wordle.js

189 lines
4.5 KiB
JavaScript

// encodes the result of the guess as a number in base 3
// where
// 0 means no match
// 1 means an exact match, and
// 2 means it's somewhere in the word
function compute_match(guess, word) {
var ret = 0
var matched = 0
const notmatched = []
const last = Math.min(guess.length, word.length)-1
for (var i = last; i >= 0; i--) {
ret *= 3
matched *= 2
if (guess[i] == word[i]) {
//console.log("match ", i)
ret += 1
matched += 1
} else {
notmatched.push(word[i])
}
}
var mul = 1
for (var i = 0; i <= last; i++) {
if ((matched % 2) == 0) {
// check against the notmatched array
for (var j = 0; j < notmatched.length; j++) {
if (notmatched[j] == guess[i]) {
//console.log("exists", i, j, guess, notmatched)
notmatched[j] = '-'
ret += 2*mul
break
}
}
}
mul *= 3
matched = Math.floor(matched/2)
}
return ret
}
// converts the base 3 number into something that's readable
function dump_match(v) {
var ret = []
for (var i = 0; i < 5; i++) {
ret.push(v % 3)
v = Math.floor(v/3)
}
return ret
}
function undump_match(l) {
var mul = 1
var ret = 0
for (var i = 0; i < 5; i++) {
ret += l[i]*mul
mul *= 3
}
return ret
}
var all_matches_buffer = null
var all_matches = null
const precompute_all = (() => {
var idx = 0
var init_start = null
var num_loops = 10
return () => {
const l = words.length
const start = Date.now()
if (idx == 0) {
all_matches_buffer = new ArrayBuffer(l*l)
all_matches = new Uint8Array(all_matches_buffer)
init_start = start
}
for (var i = 0; i < num_loops && idx < l; i++, idx++) {
for (var j = 0; j < l; j++) {
all_matches[idx*l + j] = compute_match(words[idx], words[j])
}
}
if (idx < l) {
status("computing word " + idx + "/" + words.length + " " + words[idx])
}
const end = Date.now()
const millis = end - start
num_loops = Math.floor(num_loops - 0.5*(millis - 50))
num_loops = Math.max(num_loops, 1)
if (idx >= l) {
inited = true
console.log("precompute done, mem = " + all_matches.length + " bytes")
log("precompute done, mem = " + all_matches.length + " bytes")
status("precompute done, mem = " + all_matches.length + " bytes")
log("took " + (end - init_start) + " ms")
console.log("took " + (end - init_start) + " ms")
} else {
setTimeout(precompute_all, 0)
}
}
})()
const LIST_END = (1<<16) - 1
function calc_entropy_with_cache(valid_words, guess_idx, cache) {
var entropy = 0
cache.fill(0)
const l = words.length
var view = new Uint8Array(all_matches_buffer, guess_idx*l)
var num_words = 0
for (var i = 0; i < valid_words.length; i++) {
if (valid_words[i] == LIST_END) break
cache[view[valid_words[i]]] += 1
num_words++
}
const log2 = Math.log(2)
for (var i = 0; i < 5*5*5*5*5; i++) {
if (cache[i] == 0) continue
var prob = cache[i]/num_words
//console.log("prob ", i, prob)
entropy += -Math.log(prob)/log2*prob
}
return entropy
}
function calc_entropy(valid_words, guess_idx) {
return calc_entropy_with_cache(valid_words, guess_idx, new Uint16Array(5*5*5*5*5))
}
function calc_best_word(valid_words) {
var cur_best = -1
var best_entropy = -1
const cache = new Uint16Array(5*5*5*5*5)
/*
for (var i = 0; i < valid_words.length; i++) {
if (valid_words[i] == LIST_END) break
var entropy = calc_entropy_with_cache(valid_words, valid_words[i], cache)
if (entropy > best_entropy) {
cur_best = valid_words[i]
best_entropy = entropy
}
}
*/
for (var i = 0; i < words.length; i++) {
var entropy = calc_entropy_with_cache(valid_words, i, cache)
if (entropy > best_entropy) {
cur_best = i
best_entropy = entropy
}
}
return [cur_best, best_entropy]
}
function invalidate_words(valid_words, guess, match_result) {
const l = words.length
var view = new Uint8Array(all_matches_buffer, guess*l)
var idx = 0
for (var i = 0; i < valid_words.length; i++) {
if (valid_words[i] == LIST_END) break
if (view[valid_words[i]] == match_result) {
valid_words[idx] = valid_words[i]
idx++
}
}
if (idx != valid_words.length) valid_words[idx] = LIST_END
return idx
}
function gen_all_words_list() {
var ret = new Uint16Array(words.length)
for (var i = 0; i < words.length; i++) {
ret[i] = i
}
return ret
}
function words_len(v) {
for (var i = 0; i < v.length; i++) {
if (v[i] == LIST_END) return i
}
return v.length
}