waves/public/assets/g/wordlebot/js/main.js
2025-04-09 17:11:14 -05:00

1055 lines
33 KiB
JavaScript

var word_length = 5;
var wordbank = 'restricted';
var pairings = [];
var seconds = {};
// word length constants
const SMALLEST_WORD = 3, LARGEST_WORD = 11, DEFAULT_LENGTH = 5;
// class constants to assign colors to tiles
const CORRECT = "G", INCORRECT = "B", WRONG_SPOT = "Y", EMPTY = "X";
// difficulty constants
const NORMAL = 0, HARD = 1;
// list size constants
const CHECK_SIZE = 50, TOP_TEN_LENGTH = 10, MAX_TIME = 1000;
// misc constants
const NOT_YET_TESTED = .999, SIZE_FACTOR = 5, INFINITY = 9999999;
function setBotMode(type) {
bot = new Bot(type);
setMaxGuesses();
pairings = [];
}
function setMaxGuesses() {
if (!localStorage.getItem('guesses' + bot.type)) {
if (bot.isFor(DORDLE)) {
localStorage.setItem('guesses' + bot.type, 7);
} else if (bot.isFor(WOODLE) || bot.isFor(HARDLE)) {
localStorage.setItem('guesses' + bot.type, 8);
} else if (bot.isFor(XORDLE) || bot.isFor(FIBBLE) || bot.isFor(QUORDLE)) {
localStorage.setItem('guesses' + bot.type, 9);
} else if (bot.isFor(OCTORDLE)) {
localStorage.setItem('guesses' + bot.type, 13);
}
}
}
function setLength() {
word_length = document.getElementById("word-length").value;
document.getElementById('word-entered').setAttribute('maxlength', word_length);
document.getElementById('word-entered').value = "";
clearHTML(document.getElementById('next-previous-buttons'));
words = big_list.filter((a) => a.length == word_length);
// words = official_guesses.slice(); // uncomment to use original wordle guess list
clearGrids();
setWordbank();
}
function setWordbank() {
let banks = document.getElementsByClassName('wordbank');
for (let i = 0; i < banks.length; i++) {
if (banks[i].checked == true) {
wordbank = banks[i].id;
break;
}
if (i == banks.length - 1) {
banks[0].checked = true;
}
}
if (wordbank == 'restricted') {
common = common_words.slice();
} else {
common = all_common_words.slice();
}
common = common.filter(a => a.length == word_length).sort();
common = [...new Set(common)];
// common = officical_answers.slice(); // uncomment to use original wordle answer list
}
function getBestOf(list) {
let best_list;
if (list[bot.type]) {
best_list = list[bot.type];
best_list = best_list.filter(a => a[wordbank] != null);
best_list = best_list.map(a => Object.assign({}, {word: a.word, average: a[wordbank].average, wrong: a[wordbank].wrong}));
return best_list;
}
list[bot.type] = [];
return list[bot.type];
}
// gets all possible likely and unlikely answers left
// sorts the answer & potential guess list based on the most common letters
// gets the best guesses for normal and hard mode
// passes the data to update the list of suggestions and letters in the HTML
function update() {
let difficulty = getDifficulty();
let lists = getPotentialGuessesAndAnswers(difficulty);
let best_guesses = [];
if (!timeToShowFinalOptions(lists.unique)) {
best_guesses = getBestGuesses(lists.answers, lists.guesses, difficulty, lists.unique);
best_guesses = removeUsedGuesses(best_guesses);
}
updateLists(lists.answers, lists.unlikely, best_guesses);
}
function removeUsedGuesses(list) {
for (let i = 0; i < guessesMadeSoFar(); i++) {
list = list.filter(function(a) { return a.word !== getWord(i); });
}
return list;
}
function uniqueWordsFrom(list) {
if (!list.length) return [];
if (typeof list[0] == 'object') {
let unique = [];
for (let i = 0; i < list.length; i++) {
unique = combineLists(unique, Object.values(list[i]));
}
return [... new Set(unique)];
} else return [... new Set(list)];
}
function dontNeedToCheck(answers, unique_answers) {
return (answers.length <=2 && !bot.isFor(ANTI) && bot.getCount() == 1)
|| (unique_answers.length <= 2 && !bot.isFor(ANTI))
|| numberOfGuessesMadeIs(0)
}
function separateListByLikelihood(list) {
let likely = [];
let unlikely = [];
for (let i = 0; i < list.length; i++) {
if (Array.isArray(list[i])) {
let new_lists = separateListByLikelihood(list[i]);
likely.push(new_lists.likely);
unlikely.push(new_lists.unlikely)
}
else if (bot.isLikely(list[i])) {
likely.push(list[i]);
}
else {
unlikely.push(list[i]);
}
}
return {likely: likely, unlikely: unlikely};
}
function getPotentialGuessesAndAnswers() {
let all_possible_answers = bot.getAllPossibleAnswersFrom(words.slice());
let separated_lists = separateListByLikelihood(all_possible_answers);
let answer_list = separated_lists.likely;
let unlikely_answers = separated_lists.unlikely;
let unique_answers= uniqueWordsFrom(answer_list);
if (dontNeedToCheck(answer_list, unique_answers)) {
return {
guesses: unique_answers,
answers: answer_list,
unique: unique_answers,
all: all_possible_answers,
unlikely: unlikely_answers,
};
}
let alphabet = bot.getBestLetters(unique_answers);
let sorted_answer_list = sortList(unique_answers, alphabet);
let sorted_guess_list = initialGuesses(answer_list, sorted_answer_list, unique_answers, all_possible_answers, alphabet);
return {
guesses: sorted_guess_list,
answers: answer_list,
unique: sorted_answer_list,
all: all_possible_answers,
unlikely: unlikely_answers,
};
}
function initialGuesses(answer_list, sorted_answer_list, unique_answers, all_possible_words, alphabet) {
let sorted_guess_list = words.slice();
if (bot.isFor(THIRDLE)) sorted_guess_list = allCombinations("", []);
if (twoAnswersLeft(answer_list) && !bot.isFor(ANTI)) {
// sorted_guess_list = unique_answers;
sorted_guess_list = sorted_answer_list;
} else if (isDifficulty(HARD)){
sorted_guess_list = uniqueWordsFrom(all_possible_words.slice());
// sorted_guess_list = unique_answers.slice();
} else if (bot.isFor(ANTI)) {
sorted_guess_list = filterList(sorted_guess_list, 0, true);
}
sorted_guess_list = sortList(sorted_guess_list, alphabet);
if (!bot.isFor(ANTI)) {
sorted_guess_list = combineLists(sorted_answer_list, sorted_guess_list);
}
sorted_guess_list = reduceListSize(sorted_guess_list, sorted_answer_list, bot.getAnswerListLength(answer_list));
// new_lists = reduceListSize(sorted_guess_list, sorted_answer_list, bot.getAnswerListLength(answer_list));
// sorted_guess_list = new_lists.guesses;
return sorted_guess_list;
}
function twoAnswersLeft(answers) {
if (bot.getCount() == 1) {
return answers.length <= 2;
}
return uniqueWordsFrom(answers).length <= 2;
}
function getUnfoundAnswers(answer_lists) {
unfound_answers = [];
for (let i = 0; i < answer_lists.length; i++) {
if (answer_lists[i].length == 1) {
unfound_answers.push(answer_lists[i][0]);
}
}
for (let i = 0; i < guessesMadeSoFar(); i++) {
let colors = bot.getRowColor(i);
if (colors.includes(CORRECT.repeat(word_length))) {
let pos = unfound_answers.indexOf(getWord(i));
if (pos != -1) {
unfound_answers.splice(pos, 1);
}
}
}
return unfound_answers;
}
function allCombinations(string, list) {
if (string.length == word_length) {
list.push(string);
} else {
for (let c = 65; c <= 90; c++) {
allCombinations(string + String.fromCharCode(c), list);
}
}
return list;
}
// creates the suggetsions for both normal and hard mode
// updates the headers to reflect how many words are left
// adds those suggestions to the respective slides
// creates a dropdown list showing all possible words
function updateLists(likely_answers, unlikely_answers, best_guesses) {
let guess_list = writeBestGuessList(best_guesses);
updateHeaders(bot.getAnswerListLength(likely_answers), bot.getAnswerListLength(unlikely_answers));
addToSlides("Your best possible guesses are:", guess_list);
createAnswerDropdown(likely_answers, unlikely_answers);
if (isEmpty(likely_answers) && isEmpty(unlikely_answers)) {
return addToSlides("", noWordsLeftMessage());
}
if (timeToShowFinalOptions(likely_answers)) {
// will only show the final two options as suggestions
// ie: 'its either 'THIS' or 'THAT'
return showFinalOptions(likely_answers, unlikely_answers);
}
let unfound_answers = getUnfoundAnswers(likely_answers);
if (unfound_answers.length) {
addToSlides("", unfoundAnswersMessage(unfound_answers));
}
}
function timeToShowFinalOptions(answers) {
return bot.getAnswerListLength(answers) <= 2 && !bot.isFor(ANTI)
}
// creates and returns the top 10 list of suggestions
// suggestions will then be added to the HTLM of either the suggestions
// for hard mode or normal mode
function writeBestGuessList(guesses) {
let data = "not fully tested";
let list = [];
let list_length = Math.min(guesses.length, TOP_TEN_LENGTH);
for (let i = 0; i < list_length; i++) {
let num_guesses = (guesses[i].average - guessesMadeSoFar()).toFixed(3);
let percent_wrong = decimalToPercent(1-guesses[i].wrong);
if (!notFullyTested(guesses[i])) {
data = getDataFor(guesses[i], percent_wrong, num_guesses);
}
let list_item = createListItem(guesses[i].word, data, i+1);
list.push(list_item);
}
return list;
}
function getDataFor(guess, percent_wrong, num_guesses) {
if (guess.wrong > 0) {
return percent_wrong + " solve rate";
}
if (!guessesMadeSoFar(0)) {
return num_guesses + " guesses";
}
return num_guesses + " guesses left";
}
function notFullyTested(guess) {
return guess.wrong == NOT_YET_TESTED || bot.getCount() > 1;
}
function createListItem(word, data, rank) {
let suggestion = createElement('span', word, 'click');
let word_with_ranking = createElement('div', rank + ". " + suggestion.outerHTML, 'suggestion');
let score = createElement('div', data, 'score');
let list_item = createElement('li', word_with_ranking.outerHTML + score.outerHTML);
return list_item;
}
// function updateHeaders(likely_answers, unlikely_answers) {
function updateHeaders(likely_length, unlikely_length) {
let heading = document.getElementsByClassName("possibilities total")[0];
let subheading = document.getElementsByClassName("possibilities separated")[0];
let total_length = unlikely_length + likely_length;
let class_name = numberOfGuessesMadeIs(0) ? '' : 'showlist';
let likely_html = likely_length + " probable answer" + pluralOrSingle(likely_length, "", "s");
let unlikely_html = unlikely_length + " unlikley possibilit" + pluralOrSingle(unlikely_length, "y", "ies");
let likely_span = createElement('span', likely_html, class_name);
let unlikely_span = createElement('span', unlikely_html, class_name);
if (guessesMadeSoFar() > 0) {
likely_span.prepend(createElement('div', '', ''));
unlikely_span.prepend(createElement('div', '', ''));
}
heading.innerHTML = total_length + " possibilit" + pluralOrSingle(total_length, "y", "ies");
subheading.innerHTML = likely_span.outerHTML + ", " + unlikely_span.outerHTML + ".";
}
// creates a dropdown of all possible words left
// dropdown is viewable if you click on the section that lists
// how many likely/unlikely answers are remaining
function createAnswerDropdown(likely_answers, unlikely_answers) {
if (numberOfGuessesMadeIs(0)) return;
let word_lists = document.getElementsByClassName("showlist");
let potential_answers = word_lists[0].getElementsByTagName("div")[0];
let technically_words = word_lists[1].getElementsByTagName("div")[0];
for (let i = 0; i < likely_answers.length; i++) {
addWordsToDiv(likely_answers[i], potential_answers, bot.getCount() > 1 ? COLORS[i] : "black");
}
for (let i = 0; i < unlikely_answers.length; i++) {
addWordsToDiv(unlikely_answers[i], technically_words, bot.getCount() > 1 ? COLORS[i] : "black");
}
if (bot.getAnswerListLength(likely_answers) < 1) {
setHTML(potential_answers, "goose egg");
}
if (bot.getAnswerListLength(unlikely_answers) < 1) {
setHTML(technically_words, "goose egg")
}
}
const COLORS = [
"black", "green", "blue", "red", "purple", "orange", "brown", "gray"
];
function addWordsToDiv(words, div, color) {
if (Array.isArray(words)) {
for (let i = 0; i < words.length; i++) {
addWordsToDiv(words[i], div, color);
}
} else {
let answer = createElement('p', printAnswer(words), color);
div.append(answer);
}
}
const NO_WORDS_LEFT_MESSAGE = "it doesn't look like we have this word. double check to make sure you all the clues you entered are correct.";
function noWordsLeftMessage() {
let message = createElement('div', NO_WORDS_LEFT_MESSAGE, '', 'nowords')
return [message];
}
function unfoundAnswersMessage(unfound_answers) {
let text = unfound_answers.length + " of the answers " + pluralOrSingle(unfound_answers.length, "is ", "are ");
for (let i = 0; i < unfound_answers.length; i++) {
let answer = createElement('span', printAnswer(unfound_answers[i]), 'final');
text += answer.outerHTML;
if (i == unfound_answers.length-1) {
text += "."
}
else if (i == unfound_answers.length-2) {
text += ", and ";
} else {
text += ", ";
}
}
let message = createElement('div', text, 'multi-answer');
return [message];
}
// only called if there are less than two likely answers left
// shows: almost certainly 'THIS' or 'THAT'
// unlikely but it could be: 'SOMETHING', 'ELSE'
function showFinalOptions(sorted, less_likely) {
let all_suggestions = [];
if (bot.getCount() > 1) {
sorted = uniqueWordsFrom(sorted);
less_likely = uniqueWordsFrom(less_likely);
}
if (bot.getAnswerListLength(sorted)) {
let final_words = createElement('li', '', 'likely');
let first_answer = createElement('span', printAnswer(sorted[0]), 'final');
final_words.innerHTML = "The word is almost certainly " + first_answer.outerHTML;
if (sorted.length == 2) {
let second_answer = createElement('span', printAnswer(sorted[1]), 'final');
final_words.innerHTML += " or " + second_answer.outerHTML;
}
final_words.innerHTML += ".";
all_suggestions.push(final_words);
}
if (bot.getAnswerListLength(less_likely)) {
let unlikely = createElement('li', "Unlikely, but it might be ", 'others');
for (let i = 0; i < less_likely.length; i++) {
let unlikely_answer = createElement('span', printAnswer(less_likely[i]), 'final');
unlikely.innerHTML += unlikely_answer.outerHTML;
(i < less_likely.length - 1) ? unlikely.innerHTML += ", " : unlikely.innerHTML += ".";
}
all_suggestions.push(unlikely);
}
addToSlides("", all_suggestions);
}
function printAnswer(answer) {
if (typeof answer == 'string') {
return createElement('span', answer, 'click').outerHTML;
}
if (Array.isArray(answer) && answer.length) {
return printAnswer(answer[0]);
}
if (typeof answer == 'object' && bot.isFor(XORDLE)) {
return printAnswer(answer.word1) + "/" + printAnswer(answer.word2);
}
}
// adds the heading, normal suggestsions, and hard suggestions
// to the respective HTML element
function addToSlides(heading_html, suggestions) {
let header = document.getElementsByClassName("mini-title")[0];
let list = document.getElementsByClassName('best-guesses')[0].getElementsByTagName('ul')[0];
setHTML(header, heading_html);
clearHTML(list);
suggestions.forEach(function(a) {
list.append(a);
});
}
// returns the number of guesses made to far
function guessesMadeSoFar() {
return document.getElementsByClassName("row").length/bot.getCount();
}
// checks if the number of guesses so far equals number
function numberOfGuessesMadeIs(number) {
return guessesMadeSoFar() == number;
}
// checks if we're playing on hard mode or normal mode
function isDifficulty(difficulty) {
return getDifficulty() == difficulty;
}
function getDifficulty() {
if (botIsOn()) return true;
return Number(document.getElementById("mode")?.checked);
}
function botIsOn() {
return document.getElementById('results');
}
/*
TABLE FUNCTIONS
creates the rows of guesses & buttons
modifies the tiles/buttons when clicked
accesses information about the guesses/current state
*/
function makeTables(val, c) {
if (c == null) c = "normal";
let grids = document.getElementsByClassName('grid');
if (val) {
for (let i = 0; i < bot.getCount(); i++) {
let row = createRow(val, c);
grids[i].append(row);
bot.setChangeEvents(row);
}
}
if (numberOfGuessesMadeIs(1) && c == 'normal') {
addButtons();
let full_grid = document.getElementById("hints");
full_grid.classList.remove('empty');
}
document.getElementById("word-entered").value = "";
clearValue(document.getElementById('word-entered'));
}
function createRow(word, mode) {
let row = createElement('div', '', 'row ' + mode + ' ' + bot.type);
for (let i = 0; i < word.length; i++) {
let button = createElement('button', word.charAt(i), 'B tile ' + bot.type);
row.append(button);
}
if (bot.isFor(WOODLE)) {
row.append(makeWoodleDropdowns())
}
return row;
}
function makeWoodleDropdowns() {
let container = createElement('div', '', 'tracker');
let correct_count = createElement('select', '', 'woodle-count ' + CORRECT);
let wrong_spot_count = createElement('select', '', 'woodle-count ' + WRONG_SPOT);
container.append(correct_count);
container.append(wrong_spot_count);
return container;
}
function addButtons() {
let undo = createElement('button', 'remove last guess', 'undo');
let filter = createElement('button', 'calculate next guess', 'filter');
let button_container = document.getElementById('next-previous-buttons');
button_container.append(undo);
button_container.append(filter);
filter.addEventListener('click', function() {
let difficulty = NORMAL;
if (bot.hasHardMode()) {
difficulty = getDifficulty();
}
update(difficulty);
});
undo.addEventListener('click', function() {
let grids = document.getElementsByClassName('grid');
for (let i = 0; i < grids.length; i++) {
let rows = grids[i].getElementsByClassName('row');
rows[rows.length-1].remove();
if (!rows.length) {
clearHTML(document.getElementById('next-previous-buttons'));
let full_grid = document.getElementById('hints');
full_grid.classList.add('empty');
}
}
let difficulty = NORMAL;
if (bot.hasHardMode()) {
difficulty = getDifficulty();
}
update(difficulty);
});
}
function getWord(number) {
let row = document.getElementsByClassName("row")[number];
let tiles = row.getElementsByClassName("tile");
let guess = "";
for (let i = 0; i < word_length; i++) {
guess += tiles[i].innerHTML;
}
return guess;
}
/*
GUESS FUNCTIONS
calculates the best guess at any given turn
accesses guesses that are predetermined
sets new guesses to memory
finds the color difference between two words
*/
function guessesArePrecomputed(difficulty) {
let diff = "";
let word = "";
for (let i = 0; i < guessesMadeSoFar(); i++) {
diff += bot.getRowColor(i);
word += getWord(i);
}
let hash = makeHash(bot.type, wordbank, difficulty,
bot.guessesAllowed(), document.getElementsByClassName('warmle-selector')[0]?.value, diff);
if (seconds[word] != null) {
if (seconds[word][hash] != null) {
return JSON.parse(seconds[word][hash]);
}
} else seconds[word] = {};
return 0;
}
function makeHash(game, list_type, difficulty, guesses, extras, string) {
return game + "/" + list_type + "/" + difficulty + "/" + guesses + "/" + extras + "/" + string;
}
function setBestGuesses(best_guesses, difficulty) {
let diff = "";
let word = "";
for (let i = 0; i < guessesMadeSoFar(); i++) {
diff += bot.getRowColor(i);
word += getWord(i);
}
let hash = makeHash(bot.type, wordbank, difficulty, bot.guessesAllowed(),
document.getElementsByClassName('warmle-selector')[0]?.value, diff);
seconds[word][hash] = JSON.stringify(best_guesses.slice(0, TOP_TEN_LENGTH));
}
function getBestGuesses(answer_list, guess_list, difficulty, unique_answers) {
let best_guesses = guessesArePrecomputed(difficulty);
if (best_guesses) {
return sortByWrongThenAverage(best_guesses);
}
if (numberOfGuessesMadeIs(0)) {
return getFirstGuesses(difficulty);
}
if (answer_list.length > 1000) {
return getTempList(guess_list, answer_list);
}
if (guessesMadeSoFar() == bot.guessesAllowed()-1) {
guess_list = unique_answers;
}
let initial_guesses = bot.reducesListBest(answer_list, guess_list);
if (bot.getCount() > 1) {
best_guesses = initial_guesses;
} else {
best_guesses = calculateGuessList(unique_answers, guess_list, initial_guesses, difficulty);
}
setBestGuesses(best_guesses, difficulty);
return best_guesses;
}
// reduces list of possibilities when list is too large to check efficiently
function reduceListSize(guesses, answers, answers_size) {
// if you have <10 words left, removeUselessGuesses will actually remove some ideal guesses
if (answers.length > 10 && !bot.isFor(ANTI)) {
guesses = removeUselessGuesses(guesses, answers);
}
const MAXIMUM = 100000;
// let reduced = false;
if (answers_size * guesses.length * bot.getCount() > MAXIMUM) {
// if (!bot.isFor(ANTI)) {
// guesses = combineLists(answers, guesses);
// }
let current = answers_size * guesses.length * bot.getCount();
let ratio = current/MAXIMUM;
guesses = guesses.slice(0, guesses.length/ratio);
// reduced = true;
}
for (let guess = 0; guess < guessesMadeSoFar(); guess++) {
guesses = guesses.filter(a => a != getWord(guess));
}
// return {guesses: guesses, answers: answers, reduced: reduced};
return guesses;
}
// remove words that have letters already grayed out
// remove words that have yellow letters in the wrong spot
function removeUselessGuesses(list, possibilities) {
let alphabet = bot.getBestLetters(possibilities);
for (let i = 0; i < list.length; i++) {
for (let j = 0; j < word_length; j++) {
let c = list[i].charAt(j);
if (alphabet[c][word_length] == 0 || // if letter isn't in any of the words
(alphabet[c][j] == 0 && alphabet[c][word_length] == possibilities.length)) { // if letter isn't in that spot in any word
list.splice(i, 1);
i--;
break;
}
}
}
return list;
}
function getFirstGuesses(difficulty) {
let first_guesses = isDifficulty(HARD) ? hard : easy;
first_guesses = getBestOf(first_guesses).filter(a => a.word.length == word_length);
if (!first_guesses.length) {
first_guesses = getTempList(words.slice(), common.slice());
}
return sortByWrongThenAverage(first_guesses);
}
function getTempList(guesses, answers) {
let letters = bot.getBestLetters(uniqueWordsFrom(answers.slice()));
guesses = sortList(guesses.slice(), letters);
guesses = bot.reducesListBest(answers.slice(), guesses.slice(0, 100));
guesses = guesses.map(a => Object.assign ({}, {word: a.word, average: a.adjusted, wrong: NOT_YET_TESTED}));
return guesses;
}
function calculateGuessList(answers, guesses, best_words, difficulty) {
const start_time = performance.now();
let can_finish = false;
for (let i = 0; i < best_words.length; i++) {
let remaining = best_words[i].differences;
let num_guesses = bot.guessesAllowed();
if (num_guesses == INFINITY) num_guesses = 6;
let results = Array.apply(null, Array(num_guesses));
results.forEach(function(a, index) { results[index] = []});
results['w'] = [];
best_words[i].results = results;
Object.keys(remaining).forEach(function(key) {
countResults(best_words[i], remaining[key], guesses, results, guessesMadeSoFar(), difficulty, key);
});
best_words[i].wrong = best_words[i].results['w'].length/answers.length;
// if (bot.isFor(ANTI)) best_words[i].wrong = 1 - best_words[i].results.length/100; //uncomment to for longest path antiwordle
if (best_words[i].wrong == 0) {
can_finish = true;
}
// if (performance.now() - start_time > MAX_TIME || (can_finish && i >= CHECK_SIZE)) {
if (shouldStopTesting(start_time, performance.now(), can_finish, i)) {
console.log("only calculated " + (i+1) + " words in " + ((performance.now()-start_time)/1000).toFixed(3) + " seconds");
best_words = best_words.slice(0, i+1);
break;
}
}
sortByWrongThenAverage(best_words);
return best_words.map(a => Object.assign({}, {word: a.word, average: a.average, wrong: a.wrong})).slice(0, TOP_TEN_LENGTH);
}
function shouldStopTesting(start_time, end_time, can_finish, i) {
return end_time - start_time > MAX_TIME || (can_finish && i >= CHECK_SIZE);
}
function getNextGuesses(new_guesses, answers, best, differences, difficulty) {
let list;
if (isDifficulty(HARD, difficulty)) {
list = filterList(new_guesses, {word: best.word, colors: differences});
} else if (!bot.isFor(ANTI)) {
list = reduceListSize(new_guesses, uniqueWordsFrom(answers), answers.length);
} else {
list = filterList(new_guesses, {word: best.word, colors: differences}, true);
}
if (!bot.isFor(ANTI) && !isDifficulty(HARD)) {
list = combineLists(uniqueWordsFrom(answers), new_guesses);
}
return list;
}
function countResults(best, answers, guesses, results, attempt, difficulty, differences) {
let new_guesses = combineLists(uniqueWordsFrom(answers), guesses);
new_guesses = getNextGuesses(new_guesses, answers, best, differences, difficulty);
if (answers.length <= 2 && (!bot.isFor(ANTI) || new_guesses.length == answers.length || !answers.length)) {
addToResults(results, answers, attempt, best.word, bot.guessesAllowed());
} else if (attempt < bot.guessesAllowed()-1) {
if (attempt == bot.guessesAllowed()-2) {
new_guesses = uniqueWordsFrom(answers.slice());
}
let best_words = bot.reducesListBest(answers, new_guesses, true);
if (!best_words[0]) return;
let remaining = best_words[0].differences;
Object.keys(remaining).forEach(function(key) {
countResults(best_words[0], remaining[key], new_guesses, results, attempt+1, difficulty, key);
});
}
if (attempt >= bot.guessesAllowed()-1) {
results['w'] = combineLists(results['w'], answers);
}
calculateAverageGuesses(best, results);
}
function addToResults(results, answers, attempt, current_answer, max_guesses) {
// if (answers.length == 0) {
if (isEmpty(answers)) {
addToSpot(results, current_answer, attempt);
} else if (attempt < max_guesses) {
addToSpot(results, answers.pop(), attempt+1);
}
if (answers.length && attempt < max_guesses-1) {
addToSpot(results, answers.pop(), attempt+2);
}
}
function addToSpot(results, answer, index) {
if (index >= results.length) {
if (bot.isFor(ANTI)) {
for (let i = results.length; i <= index; i++) {
results[i] = [];
}
} else {
index = 'w';
}
}
results[index].push(answer);
}
function calculateAverageGuesses(current_word, results) {
let avg = 0;
let sum = 0;
for (let i = 0; i < results.length; i++) {
let count = results[i].length;
sum += count;
avg += count*(i+1);
}
current_word.results = results;
avg = avg/sum;
current_word.average = avg;
}
/* FILTER FUNCTIONS */
function filterList(list, letters, reduced_filter, split) {
if (numberOfGuessesMadeIs(0)) return list;
if (letters) {
return createFilteredList(list, letters.word, letters.colors, reduced_filter, split);
}
for (let guess = 0; guess < guessesMadeSoFar(); guess++) {
list = createFilteredList(list, getWord(guess), bot.getRowColor(guess), reduced_filter, split);
}
return list;
}
function createFilteredList(old_list, guess, difference, reduced_filter, split) {
let temp_list = uniqueWordsFrom(old_list);
let new_list = new Array(bot.getCount());
for (let i = 0; i < new_list.length; i++) {
new_list[i] = [];
}
difference = bot.getAllDifferences(difference, guess, reduced_filter);
for (let i = 0; i < temp_list.length; i++) {
let list_index = differencesMatch(guess, temp_list[i], difference);
if (list_index.length) {
if (bot.getCount() > 1) {
addToList(old_list, list_index, temp_list[i], new_list);
} else {
new_list[0].push(temp_list[i]);
}
}
}
if (!split) new_list = uniqueWordsFrom(new_list);
return new_list;
}
function addToList(all_lists, indices, new_word, new_lists) {
for (let i = 0; i < indices.length; i++) {
let pos = indices[i]
if (typeof all_lists[0] == 'string' || all_lists[pos].includes(new_word)) {
new_lists[pos].push(new_word);
}
}
}
function differencesMatch(guess, answer, all_diffs) {
let correct_diff = bot.getDifference(guess, answer);
let indices = [];
for (let i = 0; i < all_diffs.length; i++) {
if (correct_diff == all_diffs[i]) {
indices.push(i);
}
}
return indices;
}
function xordleFilter(list) {
if (list.length > 1000) return list;
if (numberOfGuessesMadeIs(0)) return list;
let doubles = [];
for (let i = 0; i < list.length; i++) {
let rest = list.slice(i+1, -1).filter(a => bot.getDifference(list[i], a) == INCORRECT.repeat(word_length));
for (let j = 0; j < rest.length; j++) {
let guess = {word1: list[i], word2: rest[j]};
if (couldBeAnswer(guess)) {
doubles.push(guess);
}
}
}
return doubles;
}
function couldBeAnswer(guess) {
for (let i = 0; i < guessesMadeSoFar(); i++) {
if (bot.getDifference(getWord(i), guess) != bot.getRowColor(i)) {
return false;
}
}
return true;
}
/* SORT FUNCTIONS */
// sorts the list based on which words have the most common letters
// used when the list is too large to check against all possibilities
function sortList(list, alphabet) {
if (!list.length) return [];
if (!alphabet) alphabet = bot.getBestLetters(list);
let newranks = [];
list.forEach(function(w) {
newranks.push({word: w, average: 0});
});
checked = [];
for (let i = 0; i < newranks.length; i++) {
for (let j = 0; j < word_length; j++) {
if (checked[i + " " + newranks[i].word.charAt(j)] == true) continue; //no extra credit to letters with doubles
// if (alphabet[newranks[i].word.charAt(j)][word_length] == alphabet[newranks[i].word.charAt(j)][j]) continue;
newranks[i].average += alphabet[newranks[i].word.charAt(j)][word_length];
newranks[i].average += alphabet[newranks[i].word.charAt(j)][j];
checked[i + " " + newranks[i].word.charAt(j)] = true;
}
newranks[i].average = 1/newranks[i].average;
}
newranks = sortListByAverage(newranks);
return newranks.map(a => a.word);
}
function sortListByAverage(list) {
if (bot.isFor(ANTI))
return list.sort((a, b) => (a.average <= b.average) ? 1 : -1);
return list.sort((a, b) => (a.average >= b.average) ? 1 : -1);
}
function sortByWrongThenAverage(guesses) {
guesses.sort(function(a,b) {
if(a.wrong > b.wrong) {return 1;}
if(a.wrong < b.wrong) {return -1;}
if(bot.isBetter(a.average, b.average)) {return -1;}
if(!bot.isBetter(a.average, b.average)) {return 1;}
return 0;
});
return guesses;
}