Wordle Suggestion Engine

try to take over the wordle!

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Wordle Suggestion Engine
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  try to take over the wordle!
// @author       iam7mza
// @match        https://www.nytimes.com/games/wordle/index.html
// @icon         https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Wordle_Logo.svg/960px-Wordle_Logo.svg.png
// @grant        none
// ==/UserScript==

(async function() {
    'use strict';


// getting tiles
const tiles = document.getElementsByClassName("Tile-module_tile__UWEHN");

// function to read current state of the game
function read() {

    let letters = [];
    let states = [];
    // getting letters and states of letters from tiles
    for (var i = 0; i < 30; i++) {

        letters.push(tiles[i].textContent);
        states.push(tiles[i].dataset.state);
    }


    // index of next empty tile


    // returning next index, and last 5 letters with their states
    return [letters, states];
};


let ghostIndex = null;
// function to write the suggested word
function write(word, nextIndex, lastState) {

    // NOTEs: Index must be >= 5
    // if index is not devisible by 5 round it to next multiple of 5

    if (nextIndex % 5 != 0 || lastState == "tbd") {
        return;
    }


    for (var i = 0; i < 5; i++) {
        tiles[nextIndex + i].textContent = word[i];
        tiles[nextIndex + i].style.color = 'grey';
    }
    ghostIndex = nextIndex;
}

function clearGhost() {
    if (ghostIndex == null) {
        return;
    }
    for (var i = 0; i < 5; i++) {
        tiles[ghostIndex + i].style.color = 'white';
        tiles[ghostIndex + i].textContent = "";
    }
    ghostIndex = null;
}




// key listener
// clears ghost word on any key press except enter
document.addEventListener(
    "keydown",
    (e) => {
        if (ghostIndex != null && e.key !== "Enter") {
            clearGhost();
        }
    }
);


// getting word list

let url = "https://raw.githubusercontent.com/seanpatlan/wordle-words/main/word-bank.csv" // word list I found on github
async function loadWordList() {
  const res = await fetch(url);
  const text = await res.text();

  // CSV to array
  return text
    .split("\n")
    .map(w => w.trim())
    .filter(Boolean);
}


let words = await loadWordList();


// updating wordlist given previous word results

function updateWordList(letters, states, nextIndex) {
   // First pass: identify which letters are confirmed in the word
   const confirmedLetters = new Set();
   for (let i = nextIndex - 5; i < nextIndex; i++) {
       if (states[i] === "correct" || states[i] === "present") {
           confirmedLetters.add(letters[i]);
       }
   }

   // Second pass: filter words
   for (let i = nextIndex - 5; i < nextIndex; i++) {
       const letter = letters[i];
       const state = states[i];
       const position = i % 5;

       if (state === "correct") {
           words = words.filter(w => w[position] === letter);
       } else if (state === "present") {
           words = words.filter(w => w.includes(letter) && w[position] !== letter);
       } else if (state === "absent") {
           // Only remove words with this letter if it's NOT confirmed elsewhere
           if (!confirmedLetters.has(letter)) {
               words = words.filter(w => !w.includes(letter));
           }
           // If the letter IS confirmed, this "absent" just means "not at THIS position"
           // which is already handled by correct/present constraints
       }
   }
}


// animation end listener
// writes ghost word after tile animation ends
document.addEventListener("animationend", (e) => {
    const tile = e.target;

    if (
        tile.classList.contains("Tile-module_tile__UWEHN") &&
        tile.dataset.animation === "idle" &&
        ghostIndex == null
    ) {
        const [letters, states] = read();
        const nextIndex = letters.indexOf("");
        if (states[nextIndex - 1] !== "tbd") {
        updateWordList(letters, states, nextIndex);
        console.log(nextIndex);
        console.log(letters);
        console.log(states);
        console.log("words left:", words?.length);
        }

        // Game End Condition!!
        let gameOver = false;
        if(states.slice(nextIndex-5, nextIndex).every(i => i === "correct")){
            gameOver = true;
        }
        let randomWord = words[Math.floor(Math.random() * words.length)];

        if(gameOver){
            randomWord = "★★★★★";
        }
        write(randomWord, nextIndex, states[nextIndex - 1]);
    }
});

})();