initial commit
This commit is contained in:
commit
a8c8e1d7e2
5 changed files with 218 additions and 0 deletions
152
app.js
Executable file
152
app.js
Executable file
|
@ -0,0 +1,152 @@
|
|||
document.getElementById('start-ranking').addEventListener('click', startRanking);
|
||||
document.getElementById('help-button').addEventListener('click', toggleHelp);
|
||||
document.getElementById('copy-results').addEventListener('click', copyResultsToClipboard);
|
||||
|
||||
let words = [];
|
||||
let currentPair = [];
|
||||
let scores = {};
|
||||
let rounds = 0;
|
||||
let currentRound = 0;
|
||||
let playedPairs = new Set();
|
||||
const scoreThreshold = 5;
|
||||
let totalVotes = 0;
|
||||
let votesDone = 0;
|
||||
|
||||
function startRanking() {
|
||||
const wordList = document.getElementById('word-list').value.trim().split('\n');
|
||||
words = wordList.filter(word => word.trim() !== '');
|
||||
if (words.length < 2) {
|
||||
showMessage('Please enter at least two words.');
|
||||
return;
|
||||
}
|
||||
|
||||
words.forEach(word => scores[word] = 1000);
|
||||
rounds = Math.ceil(Math.log2(words.length)) + 2; // Number of rounds for Swiss-system
|
||||
currentRound = 0;
|
||||
playedPairs.clear();
|
||||
totalVotes = rounds * (words.length / 2);
|
||||
votesDone = 0;
|
||||
|
||||
document.getElementById('ranking-section').classList.remove('hidden');
|
||||
document.getElementById('progress').style.width = '0%';
|
||||
updateRankingList();
|
||||
nextPair();
|
||||
}
|
||||
|
||||
function nextPair() {
|
||||
if (currentRound >= rounds) {
|
||||
showMessage('Ranking complete!');
|
||||
disableVoteButtons();
|
||||
return;
|
||||
}
|
||||
|
||||
currentPair = getNextPair();
|
||||
if (!currentPair) {
|
||||
currentRound++;
|
||||
nextPair();
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('word1').textContent = currentPair[0];
|
||||
document.getElementById('word2').textContent = currentPair[1];
|
||||
|
||||
document.getElementById('word1').onclick = () => vote(currentPair[0], currentPair[1]);
|
||||
document.getElementById('word2').onclick = () => vote(currentPair[1], currentPair[0]);
|
||||
}
|
||||
|
||||
function getNextPair() {
|
||||
const sortedWords = Object.keys(scores).sort((a, b) => scores[b] - scores[a]);
|
||||
for (let i = 0; i < sortedWords.length - 1; i++) {
|
||||
for (let j = i + 1; j < sortedWords.length; j++) {
|
||||
if (!hasPlayed(sortedWords[i], sortedWords[j]) && Math.abs(scores[sortedWords[i]] - scores[sortedWords[j]]) <= scoreThreshold) {
|
||||
return [sortedWords[i], sortedWords[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function hasPlayed(word1, word2) {
|
||||
const pair = [word1, word2].sort().join('-');
|
||||
return playedPairs.has(pair);
|
||||
}
|
||||
|
||||
function vote(winner, loser) {
|
||||
const k = 32;
|
||||
const expectedScoreWinner = 1 / (1 + Math.pow(10, (scores[loser] - scores[winner]) / 400));
|
||||
const expectedScoreLoser = 1 - expectedScoreWinner;
|
||||
|
||||
scores[winner] += k * (1 - expectedScoreWinner);
|
||||
scores[loser] += k * (0 - expectedScoreLoser);
|
||||
|
||||
const pair = [winner, loser].sort().join('-');
|
||||
playedPairs.add(pair);
|
||||
|
||||
votesDone++;
|
||||
updateProgress();
|
||||
updateRankingList();
|
||||
nextPair();
|
||||
}
|
||||
|
||||
function updateProgress() {
|
||||
const progress = (votesDone / totalVotes) * 100;
|
||||
document.getElementById('progress').style.width = progress + '%';
|
||||
}
|
||||
|
||||
function updateRankingList() {
|
||||
const rankingList = document.getElementById('ranking-list');
|
||||
rankingList.innerHTML = '';
|
||||
|
||||
const sortedWords = Object.keys(scores).sort((a, b) => scores[b] - scores[a]);
|
||||
sortedWords.forEach(word => {
|
||||
const listItem = document.createElement('li');
|
||||
listItem.classList.add('ranking-item', 'p-2', 'border', 'rounded', 'bg-gray-100', 'flex', 'justify-between');
|
||||
const wordSpan = document.createElement('span');
|
||||
wordSpan.classList.add('word');
|
||||
wordSpan.textContent = word;
|
||||
const scoreSpan = document.createElement('span');
|
||||
scoreSpan.classList.add('score');
|
||||
scoreSpan.textContent = Math.round(scores[word]);
|
||||
listItem.appendChild(wordSpan);
|
||||
listItem.appendChild(scoreSpan);
|
||||
rankingList.appendChild(listItem);
|
||||
});
|
||||
}
|
||||
|
||||
function disableVoteButtons() {
|
||||
document.getElementById('word1').disabled = true;
|
||||
document.getElementById('word2').disabled = true;
|
||||
document.getElementById('word1').classList.add('bg-gray-500', 'cursor-not-allowed');
|
||||
document.getElementById('word2').classList.add('bg-gray-500', 'cursor-not-allowed');
|
||||
}
|
||||
|
||||
function toggleHelp() {
|
||||
const helpSection = document.getElementById('help-section');
|
||||
helpSection.classList.toggle('hidden');
|
||||
if (!helpSection.classList.contains('hidden')) {
|
||||
helpSection.style.maxHeight = helpSection.scrollHeight + "px";
|
||||
} else {
|
||||
helpSection.style.maxHeight = null;
|
||||
}
|
||||
}
|
||||
|
||||
function showMessage(message) {
|
||||
const messageBox = document.getElementById('message-box');
|
||||
const messageText = document.getElementById('message-text');
|
||||
messageText.textContent = message;
|
||||
messageBox.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function copyResultsToClipboard() {
|
||||
const sortedWords = Object.keys(scores).sort((a, b) => scores[b] - scores[a]);
|
||||
const results = sortedWords.map(word => `${word}: ${Math.round(scores[word])}`).join('\n');
|
||||
navigator.clipboard.writeText(results).then(() => {
|
||||
const clipboardOkIcon = document.querySelector('.clipboard-ok');
|
||||
clipboardOkIcon.classList.remove('hidden');
|
||||
setTimeout(() => {
|
||||
clipboardOkIcon.classList.add('hidden');
|
||||
}, 5000);
|
||||
}).catch(err => {
|
||||
alert('Failed to copy results: ', err);
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue