Skip to content

Instantly share code, notes, and snippets.

@nicolas-goudry
Last active April 30, 2025 12:23
Show Gist options
  • Save nicolas-goudry/3912ca31f1d8f630a8af160eedbe4df6 to your computer and use it in GitHub Desktop.
Save nicolas-goudry/3912ca31f1d8f630a8af160eedbe4df6 to your computer and use it in GitHub Desktop.
Want to be the best at Lucca Faces? Run the game, then execute this code into your console. Let it play until you are the best!
// New version courtesy of JCluzet. Thanks!
(() => {
const STORAGE_KEY = 'lucca_faces_data_v2';
let people = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
let currentImageHash = '';
let retryAttempts = 0;
const MAX_RETRY_ATTEMPTS = 5; // Adjust max attempts count
function getImageHash(imageSrc) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
canvas.toBlob(blob => {
const reader = new FileReader();
reader.onloadend = () => {
crypto.subtle.digest('SHA-256', reader.result).then(hashBuffer => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
resolve(hashHex);
});
};
reader.onerror = reject;
reader.readAsArrayBuffer(blob);
});
};
img.onerror = () => {
reject('Image load error');
};
img.src = imageSrc;
});
}
async function handleNewImage(imageSrc) {
try {
const hashHex = await getImageHash(imageSrc);
if (hashHex !== currentImageHash) {
currentImageHash = hashHex;
if (people[currentImageHash]) {
const choices = document.querySelectorAll('#game .answers .answer');
const correctAnswer = [...choices].find(choice => choice.textContent.trim() === people[currentImageHash].trim());
if (correctAnswer) {
correctAnswer.click();
}
} else {
chooseRandomAnswer();
}
}
retryAttempts = 0; // Reset attempts counter on success
} catch (error) {
console.error('Error processing image:', error);
retryAttempts++;
if (retryAttempts <= MAX_RETRY_ATTEMPTS) {
console.log(`Retrying (${retryAttempts}/${MAX_RETRY_ATTEMPTS})...`);
retryHandleImage(imageSrc);
} else {
console.log('Max retry attempts reached. Skipping image.');
retryAttempts = 0; // Reset attempts counter if limit reached
clickGoButton(); // Click on "Go" button after reaching end of the game
}
}
}
function retryHandleImage(imageSrc) {
setTimeout(() => handleNewImage(imageSrc), 100);
}
function chooseRandomAnswer() {
const choices = document.querySelectorAll('#game .answers .answer');
const randomChoice = choices[Math.floor(Math.random() * choices.length)];
randomChoice.click();
setTimeout(() => {
const correctAnswer = document.querySelector('#game .answers .is-right');
if (correctAnswer) {
const name = correctAnswer.textContent.trim();
people[currentImageHash] = name;
localStorage.setItem(STORAGE_KEY, JSON.stringify(people));
console.log(`Discovered ${name} for hash ${currentImageHash}`);
} else {
console.error('Failed to discover the correct answer!');
}
}, 500); // Wait a bit to allow correct answer to be shown
}
function startGame() {
const startButtonContainer = document.querySelector('.main-container.has-loaded .logo .rotation-loader');
if (startButtonContainer) {
startButtonContainer.click();
console.log('Clicked the start button');
} else {
console.error('Start button not found. Make sure the game is loaded.');
}
}
function restartGame() {
const replayButton = document.querySelector('.main-container .results-card button.button.mod-pill.palette-secondary.mod-XL');
if (replayButton) {
replayButton.click();
console.log('Clicked the replay button');
} else {
console.log('Please click "Replay" to restart the game.');
}
}
function clickGoButton() {
// Try to click on several elements until "Go" button is found
const elementsToClick = [
document.querySelector('.main-container .logo .rotation-loader'),
document.querySelector('.main-container .results-card button.button.mod-pill.palette-secondary.mod-XL'),
document.querySelector('.main-container .results-card button.button.mod-pill.palette-primary.mod-L'),
document.querySelector('.main-container .results-card button.button.mod-pill.palette-primary.mod-XL'),
document.querySelector('.main-container .results-card button.button.mod-pill.palette-tertiary.mod-XL')
// Add other CSS selectors here if needed
];
let clicked = false;
elementsToClick.forEach(element => {
if (element && !clicked) {
element.click();
console.log('Clicked a button to restart the game.');
clicked = true;
}
});
if (!clicked) {
console.log('Go button not found. Please click "Replay" and "Go" manually to restart.');
}
}
function observeGame() {
const gameElement = document.querySelector('#game');
if (!gameElement) {
console.error('Game element not found. Make sure the game is loaded.');
return;
}
const observer = new MutationObserver(() => {
const imageElement = document.querySelector('#game app-timer .image');
if (imageElement) {
const imageSrc = imageElement.style.backgroundImage.match(/url\("(.*)"\)/)[1];
handleNewImage(imageSrc);
}
const gameEnded = !!document.querySelector('.main-container .results-card');
if (gameEnded) {
restartGame();
startGame(); // Restart game after click on "Rejouer" and "Go"
}
});
observer.observe(gameElement, { childList: true, subtree: true });
}
function main() {
startGame();
observeGame();
console.log('Script loaded: It will automatically start the game and handle answers.');
}
// Start main function right away
main();
})();
@JCluzet
Copy link

JCluzet commented Jul 2, 2024

(enter this into your console.log after the game start)

@JCluzet
Copy link

JCluzet commented Jul 2, 2024

now a new auto version ( just paste it when you see the Go button ) :

(() => {
const STORAGE_KEY = 'lucca_faces_data_v2';
let people = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
let currentImageHash = '';
let retryAttempts = 0;
const MAX_RETRY_ATTEMPTS = 5; // Augmenter le nombre maximum de tentatives

function getImageHash(imageSrc) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.crossOrigin = 'Anonymous';
        img.onload = () => {
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img, 0, 0, img.width, img.height);
            canvas.toBlob(blob => {
                const reader = new FileReader();
                reader.onloadend = () => {
                    crypto.subtle.digest('SHA-256', reader.result).then(hashBuffer => {
                        const hashArray = Array.from(new Uint8Array(hashBuffer));
                        const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
                        resolve(hashHex);
                    });
                };
                reader.onerror = reject;
                reader.readAsArrayBuffer(blob);
            });
        };
        img.onerror = () => {
            reject('Image load error');
        };
        img.src = imageSrc;
    });
}

async function handleNewImage(imageSrc) {
    try {
        const hashHex = await getImageHash(imageSrc);
        if (hashHex !== currentImageHash) {
            currentImageHash = hashHex;
            if (people[currentImageHash]) {
                const choices = document.querySelectorAll('#game .answers .answer');
                const correctAnswer = [...choices].find(choice => choice.textContent.trim() === people[currentImageHash].trim());
                if (correctAnswer) {
                    correctAnswer.click();
                }
            } else {
                chooseRandomAnswer();
            }
        }
        retryAttempts = 0; // Réinitialiser les tentatives en cas de succès
    } catch (error) {
        console.error('Error processing image:', error);
        retryAttempts++;
        if (retryAttempts <= MAX_RETRY_ATTEMPTS) {
            console.log(`Retrying (${retryAttempts}/${MAX_RETRY_ATTEMPTS})...`);
            retryHandleImage(imageSrc);
        } else {
            console.log('Max retry attempts reached. Skipping image.');
            retryAttempts = 0; // Réinitialiser les tentatives si on atteint la limite
            clickGoButton(); // Cliquer sur le bouton "Go" après avoir atteint la fin du jeu
        }
    }
}

function retryHandleImage(imageSrc) {
    setTimeout(() => handleNewImage(imageSrc), 100);
}

function chooseRandomAnswer() {
    const choices = document.querySelectorAll('#game .answers .answer');
    const randomChoice = choices[Math.floor(Math.random() * choices.length)];
    randomChoice.click();

    setTimeout(() => {
        const correctAnswer = document.querySelector('#game .answers .is-right');
        if (correctAnswer) {
            const name = correctAnswer.textContent.trim();
            people[currentImageHash] = name;
            localStorage.setItem(STORAGE_KEY, JSON.stringify(people));
            console.log(`Discovered ${name} for hash ${currentImageHash}`);
        } else {
            console.error('Failed to discover the correct answer!');
        }
    }, 500); // Attendre brièvement pour permettre à la bonne réponse d'être mise en évidence
}

function startGame() {
    const startButtonContainer = document.querySelector('.main-container.has-loaded .logo .rotation-loader');
    if (startButtonContainer) {
        startButtonContainer.click();
        console.log('Clicked the start button');
    } else {
        console.error('Start button not found. Make sure the game is loaded.');
    }
}

function restartGame() {
    const replayButton = document.querySelector('.main-container .results-card button.button.mod-pill.palette-secondary.mod-XL');
    if (replayButton) {
        replayButton.click();
        console.log('Clicked the replay button');
    } else {
        console.log('Please click "Replay" to restart the game.');
    }
}

function clickGoButton() {
    // Essayer de cliquer sur plusieurs éléments jusqu'à trouver le bouton "Go"
    const elementsToClick = [
        document.querySelector('.main-container .logo .rotation-loader'),
        document.querySelector('.main-container .results-card button.button.mod-pill.palette-secondary.mod-XL'),
        document.querySelector('.main-container .results-card button.button.mod-pill.palette-primary.mod-L'),
        document.querySelector('.main-container .results-card button.button.mod-pill.palette-primary.mod-XL'),
        document.querySelector('.main-container .results-card button.button.mod-pill.palette-tertiary.mod-XL')
        // Ajoutez d'autres sélecteurs CSS si nécessaire pour d'autres éléments sur la page
    ];

    let clicked = false;
    elementsToClick.forEach(element => {
        if (element && !clicked) {
            element.click();
            console.log('Clicked a button to restart the game.');
            clicked = true;
        }
    });

    if (!clicked) {
        console.log('Go button not found. Please click "Replay" and "Go" manually to restart.');
    }
}

function observeGame() {
    const gameElement = document.querySelector('#game');
    if (!gameElement) {
        console.error('Game element not found. Make sure the game is loaded.');
        return;
    }
    const observer = new MutationObserver(() => {
        const imageElement = document.querySelector('#game app-timer .image');
        if (imageElement) {
            const imageSrc = imageElement.style.backgroundImage.match(/url\("(.*)"\)/)[1];
            handleNewImage(imageSrc);
        }

        const gameEnded = !!document.querySelector('.main-container .results-card');
        if (gameEnded) {
            restartGame();
            startGame(); // Redémarrer le jeu après avoir cliqué sur "Rejouer" et "Go"
        }
    });
    observer.observe(gameElement, { childList: true, subtree: true });
}

function main() {
    startGame();
    observeGame();
    console.log('Script loaded: It will automatically start the game and handle answers.');
}

// Démarrer la fonction principale immédiatement
main();

})();

@nicolas-goudry
Copy link
Author

@JCluzet Dude that’s neat! I can’t test it but I will trust that it works 🙂

I updated the gist with your code 😉

@kapik
Copy link

kapik commented Jul 13, 2024

This is too slow !
You can use network request only with NodeJs for example 😇 😉

I try to stay under 5 seconds for all 10 questions.

@JCluzet
Copy link

JCluzet commented Jul 13, 2024

by making more than 1550 or 1600 you will be banned for 1 month, you can try my new script :

(() => {
const STORAGE_KEY = 'lucca_faces_data_v2';
let people = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
let currentImageHash = '';
let retryAttempts = 0;
const MAX_RETRY_ATTEMPTS = 5;
let totalPointsNeeded = 0;
let questionsAnswered = 0; // Compteur de questions répondues
let currentTotalScore = 0; // Score total actuel

// Demander le score cible à l'utilisateur
let targetScore = parseInt(prompt('Veuillez entrer le score cible (entre 500 et 1700) :'), 10);
if (isNaN(targetScore) || targetScore < 500 || targetScore > 1700) {
targetScore = 1300; // Valeur par défaut si l'entrée est invalide
}

// Ajuster le calcul du délai en fonction du score cible
const minScore = 500;
const maxScore = 1700;
const minTimePerQuestion = 3000; // Délai pour un score de 500
const maxTimePerQuestion = 0; // Délai pour un score de 1700

// Calculer le délai pour chaque question en fonction du score cible
const timePerQuestion = calculateTimePerQuestion(targetScore, minScore, maxScore, minTimePerQuestion, maxTimePerQuestion);
console.log(Time per question: ${timePerQuestion}ms for target score: ${targetScore});

function calculateTimePerQuestion(score, minScore, maxScore, minTime, maxTime) {
if (score <= minScore) {
return minTime;
}
if (score >= maxScore) {
return maxTime;
}

// Calculer le délai en utilisant une interpolation linéaire
const scoreRange = maxScore - minScore;
const timeRange = minTime - maxTime;
const timePerQuestion = maxTime + ((score - minScore) / scoreRange) * timeRange;

return Math.round(timePerQuestion); // Arrondir le délai

}

function getTotalPointsFromHeaderText(headerText) {
const regex = /(\d+) pts/;
const match = headerText.match(regex);
if (match && match.length > 1) {
return parseInt(match[1], 10);
}
return 0;
}

function getPointsFromSpanText(spanText) {
const regex = /+ (\d+) pts/;
const match = spanText.match(regex);
if (match && match.length > 1) {
return parseInt(match[1], 10);
}
return 0;
}

function getImageHash(imageSrc) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
canvas.toBlob(blob => {
const reader = new FileReader();
reader.onloadend = () => {
crypto.subtle.digest('SHA-256', reader.result).then(hashBuffer => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
resolve(hashHex);
});
};
reader.onerror = reject;
reader.readAsArrayBuffer(blob);
});
};
img.onerror = () => {
reject('Image load error');
};
img.src = imageSrc;
});
}

function handleNewImage(imageSrc) {
getImageHash(imageSrc)
.then(hashHex => {
if (hashHex !== currentImageHash) {
currentImageHash = hashHex;
if (people[currentImageHash]) {
const choices = document.querySelectorAll('#game .answers .answer');
const correctAnswer = [...choices].find(choice => choice.textContent.trim() === people[currentImageHash].trim());
if (correctAnswer) {
setTimeout(() => {
correctAnswer.click();

            // Polling pour observer les changements dans l'élément qui affiche le score gagné
            pollForScoreElement((pointsEarned, newTotalScore) => {
              questionsAnswered++;
              currentTotalScore = newTotalScore;

              // Estimer le nombre de questions restantes
              const questionsRemaining = 10 - questionsAnswered;

              // Ajuster le délai en fonction du nombre de questions restantes et du score nécessaire
              const adjustedTimePerQuestion = calculateAdjustedTimePerQuestion(totalPointsNeeded, questionsRemaining);

              const scoreNeededForNextQuestion = questionsRemaining > 0 ? Math.ceil((targetScore - currentTotalScore) / questionsRemaining) : 0;

              console.log(`${questionsAnswered}/10: ${people[currentImageHash]} , score obtenu: ${pointsEarned} , score total: ${currentTotalScore} , score visé pour la prochaine question: ${scoreNeededForNextQuestion}`);

              if (questionsAnswered < 10) {
                setTimeout(() => {
                  handleNewImage(imageSrc);
                }, adjustedTimePerQuestion);
              } else {
                console.log('Game completed. Total points achieved.');
              }
            });

          }, timePerQuestion);
        } else {
          console.error('Correct answer element not found.');
          chooseRandomAnswer();
        }
      } else {
        chooseRandomAnswer();
        console.log(`PERSONNE INCONNU : Enregistrement du hash ${currentImageHash} pour la personne ${imageSrc}`);
      }
    }
    retryAttempts = 0;
  })
  .catch(error => {
    console.error('Error processing image:', error);
    retryAttempts++;
    if (retryAttempts <= MAX_RETRY_ATTEMPTS) {
      console.log(`Retrying (${retryAttempts}/${MAX_RETRY_ATTEMPTS})...`);
      retryHandleImage(imageSrc);
    } else {
      console.log('Max retry attempts reached. Skipping image.');
      retryAttempts = 0;
      clickGoButton();
    }
  });

}

function pollForScoreElement(callback) {
const interval = setInterval(() => {
const imageOverlayElement = document.querySelector('.image-container .image-overlay span.score');
if (imageOverlayElement) {
clearInterval(interval);
const pointsEarned = getPointsFromSpanText(imageOverlayElement.textContent);
const currentTotalScore = getTotalPointsFromHeaderText(document.querySelector('.score-header .score').textContent);
callback(pointsEarned, currentTotalScore);
}
}, 100);
}

function calculateAdjustedTimePerQuestion(totalPointsNeeded, questionsRemaining) {
// Calculer un délai ajusté en fonction du nombre de points nécessaires et des questions restantes
// Par exemple, vous pouvez ralentir légèrement si le score nécessaire diminue
const baseTime = 3000; // Délai de base pour un score de 500
const targetTime = 0; // Délai cible pour un score de 1700
const timeRange = baseTime - targetTime;
const adjustedTime = targetTime + ((totalPointsNeeded / targetScore) * timeRange);

// Vous pouvez ajuster davantage en fonction du nombre de questions restantes
const adjustedTimePerQuestion = adjustedTime / questionsRemaining;

return Math.round(adjustedTimePerQuestion); // Arrondir le délai ajusté

}

function retryHandleImage(imageSrc) {
setTimeout(() => handleNewImage(imageSrc), 100);
}

function chooseRandomAnswer() {
const choices = document.querySelectorAll('#game .answers .answer');
if (choices.length > 0) {
const randomChoice = choices[Math.floor(Math.random() * choices.length)];
randomChoice.click();
setTimeout(() => {
const correctAnswer = document.querySelector('#game .answers .is-right');
if (correctAnswer) {
const name = correctAnswer.textContent.trim();
people[currentImageHash] = name;
localStorage.setItem(STORAGE_KEY, JSON.stringify(people));
console.log(Discovered ${name} for hash ${currentImageHash});
} else {
console.error('Failed to discover the correct answer!');
}
}, 500);
} else {
console.error('No choices available to click.');
}
}

function startGame() {
const startButtonContainer = document.querySelector('.main-container.has-loaded .logo .rotation-loader');
if (startButtonContainer) {
startButtonContainer.click();
console.log('Clicked the start button');
} else {
console.error('Start button not found. Make sure the game is loaded.');
}
}

function restartGame() {
const replayButton = document.querySelector('.main-container .results-card button.button.mod-pill.palette-secondary.mod-XL');
if (replayButton) {
replayButton.click();
console.log('Clicked the replay button');
} else {
console.log('Please click "Replay" to restart the game.');
}

const goButton = document.querySelector('.main-container .results-card button.button.mod-pill.palette-primary.mod-L, .main-container .results-card button.button.mod-pill.palette-secondary.mod-XL');
if (goButton) {
  goButton.click();
  console.log('Clicked the go button');
} else {
  console.log('Please click "Go" to restart the game.');
}

}

function observeGame() {
const gameElement = document.querySelector('#game');
if (!gameElement) {
console.error('Game element not found. Make sure the game is loaded.');
return;
}
const observer = new MutationObserver(() => {
const imageElement = document.querySelector('#game app-timer .image');
if (imageElement) {
const imageSrc = imageElement.style.backgroundImage.match(/url("(.*)")/)[1];
handleNewImage(imageSrc);
}

  const gameEnded = !!document.querySelector('.main-container .results-card');
  if (gameEnded) {
    const headerText = document.querySelector('.main-container .results-card .header').textContent;
    const pointsEarned = getTotalPointsFromHeaderText(headerText);
    totalPointsNeeded -= pointsEarned;

    if (questionsAnswered < 10) {
      restartGame();
      startGame();
    } else {
      console.log('Game completed. Total points achieved.');
    }
  }
});
observer.observe(gameElement, { childList: true, subtree: true });

}

function main() {
startGame();
observeGame();
console.log('Script loaded: It will automatically start the game and handle answers.');
}

main();
})();

And the speed, you can disable throting in dev console inspect with Chrome, and disable cache, like this you can do the maximum score :)

@kapik
Copy link

kapik commented Jul 13, 2024

hum 🤔
J'avais une version à exécuter comme ça dans le navigateur mais je trouvais ça trop lent.
Je fais des call en direct à l'API sans délais mais je n'arrive pas à atteindre les 1500.

@lpierrewanadev
Copy link

lpierrewanadev commented Apr 30, 2025

I tweaked the code a bit so it could run before even pressing "Go."

So, it's very simple:

  1. Enter the code in the console
  2. Enter
  3. Press "Go"
;(() => {
    const STORAGE_KEY = 'lucca_faces_data_v2'
    let people = JSON.parse(localStorage.getItem(STORAGE_KEY)) || {}
    let currentImageHash = ''
    let retryAttempts = 0
    const MAX_RETRY_ATTEMPTS = 5
    let totalPointsNeeded = 0
    let questionsAnswered = 0
    let currentTotalScore = 0

    const targetScore = (() => {
        const input = prompt('Quel score cible veux-tu atteindre ? (entre 500 et 1700)', '1700')
        const parsed = parseInt(input, 10)
        return isNaN(parsed) || parsed < 500 || parsed > 1700 ? 1700 : parsed
    })()
    const minScore = 500
    const maxScore = 1700
    const minTimePerQuestion = 3000
    const maxTimePerQuestion = 0

    const timePerQuestion = calculateTimePerQuestion(
        targetScore,
        minScore,
        maxScore,
        minTimePerQuestion,
        maxTimePerQuestion
    )
    console.log(`Time per question: ${timePerQuestion}ms for target score: ${targetScore}`)

    function calculateTimePerQuestion(score, minScore, maxScore, minTime, maxTime) {
        if (score <= minScore) return minTime
        if (score >= maxScore) return maxTime

        const scoreRange = maxScore - minScore
        const timeRange = minTime - maxTime
        const timePerQuestion = maxTime + ((score - minScore) / scoreRange) * timeRange
        return Math.round(timePerQuestion)
    }

    function getTotalPointsFromHeaderText(headerText) {
        const regex = /(\d+) pts/
        const match = headerText.match(regex)
        return match ? parseInt(match[1], 10) : 0
    }

    function getPointsFromSpanText(spanText) {
        const regex = /\+ (\d+) pts/
        const match = spanText.match(regex)
        return match ? parseInt(match[1], 10) : 0
    }

    function getImageHash(imageSrc) {
        return new Promise((resolve, reject) => {
            const img = new Image()
            img.crossOrigin = 'Anonymous'
            img.onload = () => {
                const canvas = document.createElement('canvas')
                const ctx = canvas.getContext('2d')
                canvas.width = img.width
                canvas.height = img.height
                ctx.drawImage(img, 0, 0)
                canvas.toBlob((blob) => {
                    const reader = new FileReader()
                    reader.onloadend = () => {
                        crypto.subtle.digest('SHA-256', reader.result).then((hashBuffer) => {
                            const hashArray = Array.from(new Uint8Array(hashBuffer))
                            const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
                            resolve(hashHex)
                        })
                    }
                    reader.onerror = reject
                    reader.readAsArrayBuffer(blob)
                })
            }
            img.onerror = () => reject('Image load error')
            img.src = imageSrc
        })
    }

    function handleNewImage(imageSrc) {
        getImageHash(imageSrc)
            .then((hashHex) => {
                if (hashHex !== currentImageHash) {
                    currentImageHash = hashHex
                    if (people[currentImageHash]) {
                        const choices = document.querySelectorAll('#game .answers .answer')
                        const correctAnswer = [...choices].find(
                            (choice) => choice.textContent.trim() === people[currentImageHash].trim()
                        )
                        if (correctAnswer) {
                            setTimeout(() => {
                                correctAnswer.click()

                                pollForScoreElement((pointsEarned, newTotalScore) => {
                                    questionsAnswered++
                                    currentTotalScore = newTotalScore
                                    const questionsRemaining = 10 - questionsAnswered
                                    const adjustedTimePerQuestion = calculateAdjustedTimePerQuestion(
                                        targetScore - currentTotalScore,
                                        questionsRemaining
                                    )
                                    const scoreNeededForNextQuestion =
                                        questionsRemaining > 0
                                            ? Math.ceil((targetScore - currentTotalScore) / questionsRemaining)
                                            : 0

                                    console.log(
                                        `${questionsAnswered}/10: ${people[currentImageHash]}, score obtenu: ${pointsEarned}, score total: ${currentTotalScore}, score visé pour la prochaine question: ${scoreNeededForNextQuestion}`
                                    )

                                    if (questionsAnswered < 10) {
                                        setTimeout(() => {
                                            handleNewImage(imageSrc)
                                        }, adjustedTimePerQuestion)
                                    } else {
                                        console.log('Game completed. Total points achieved.')
                                    }
                                })
                            }, timePerQuestion)
                        } else {
                            console.error('Correct answer element not found.')
                            chooseRandomAnswer()
                        }
                    } else {
                        chooseRandomAnswer()
                        console.log(
                            `PERSONNE INCONNUE : Enregistrement du hash ${currentImageHash} pour la personne ${imageSrc}`
                        )
                    }
                }
                retryAttempts = 0
            })
            .catch((error) => {
                console.error('Error processing image:', error)
                retryAttempts++
                if (retryAttempts <= MAX_RETRY_ATTEMPTS) {
                    console.log(`Retrying (${retryAttempts}/${MAX_RETRY_ATTEMPTS})...`)
                    retryHandleImage(imageSrc)
                } else {
                    console.log('Max retry attempts reached. Skipping image.')
                    retryAttempts = 0
                    clickGoButton()
                }
            })
    }

    function pollForScoreElement(callback) {
        const interval = setInterval(() => {
            const imageOverlayElement = document.querySelector('.image-container .image-overlay span.score')
            if (imageOverlayElement) {
                clearInterval(interval)
                const pointsEarned = getPointsFromSpanText(imageOverlayElement.textContent)
                const currentTotalScore = getTotalPointsFromHeaderText(
                    document.querySelector('.score-header .score').textContent
                )
                callback(pointsEarned, currentTotalScore)
            }
        }, 100)
    }

    function calculateAdjustedTimePerQuestion(totalPointsNeeded, questionsRemaining) {
        const baseTime = 3000
        const targetTime = 0
        const timeRange = baseTime - targetTime
        const adjustedTime = targetTime + (totalPointsNeeded / targetScore) * timeRange
        const adjustedTimePerQuestion = adjustedTime / questionsRemaining
        return Math.round(adjustedTimePerQuestion)
    }

    function retryHandleImage(imageSrc) {
        setTimeout(() => handleNewImage(imageSrc), 100)
    }

    function chooseRandomAnswer() {
        const choices = document.querySelectorAll('#game .answers .answer')
        if (choices.length > 0) {
            const randomChoice = choices[Math.floor(Math.random() * choices.length)]
            randomChoice.click()
            setTimeout(() => {
                const correctAnswer = document.querySelector('#game .answers .is-right')
                if (correctAnswer) {
                    const name = correctAnswer.textContent.trim()
                    people[currentImageHash] = name
                    localStorage.setItem(STORAGE_KEY, JSON.stringify(people))
                    console.log(`Discovered ${name} for hash ${currentImageHash}`)
                } else {
                    console.error('Failed to discover the correct answer!')
                }
            }, 500)
        } else {
            console.error('No choices available to click.')
        }
    }

    function startGame() {
        const startButtonContainer = document.querySelector('.main-container.has-loaded .logo .rotation-loader')
        if (startButtonContainer) {
            startButtonContainer.click()
            console.log('Clicked the start button')
        } else {
            console.error('Start button not found. Make sure the game is loaded.')
        }
    }

    function restartGame() {
        const replayButton = document.querySelector(
            '.main-container .results-card button.button.mod-pill.palette-secondary.mod-XL'
        )
        if (replayButton) {
            replayButton.click()
            console.log('Clicked the replay button')
        } else {
            console.log('Please click "Replay" to restart the game.')
        }

        const goButton = document.querySelector(
            '.main-container .results-card button.button.mod-pill.palette-primary.mod-L, .main-container .results-card button.button.mod-pill.palette-secondary.mod-XL'
        )
        if (goButton) {
            goButton.click()
            console.log('Clicked the go button')
        } else {
            console.log('Please click "Go" to restart the game.')
        }
    }

    function waitForGameElement() {
        const checkInterval = setInterval(() => {
            const gameElement = document.querySelector('#game')
            if (gameElement) {
                clearInterval(checkInterval)
                observeGame()
            }
        }, 200)
    }

    function observeGame() {
        const gameElement = document.querySelector('#game')
        if (!gameElement) {
            console.error('Game element not found. Make sure the game is loaded.')
            return
        }

        const observer = new MutationObserver(() => {
            const imageElement = document.querySelector('#game app-timer .image')
            if (imageElement) {
                const imageSrcMatch = imageElement.style.backgroundImage.match(/url\("(.*)"\)/)
                if (imageSrcMatch) {
                    const imageSrc = imageSrcMatch[1]
                    handleNewImage(imageSrc)
                }
            }

            const gameEnded = !!document.querySelector('.main-container .results-card')
            if (gameEnded) {
                const headerText = document.querySelector('.main-container .results-card .header').textContent
                const pointsEarned = getTotalPointsFromHeaderText(headerText)
                totalPointsNeeded -= pointsEarned

                if (questionsAnswered < 10) {
                    restartGame()
                    startGame()
                } else {
                    console.log('Game completed. Total points achieved.')
                }
            }
        })

        observer.observe(gameElement, { childList: true, subtree: true })
    }

    waitForGameElement()
})()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment