commit a8c8e1d7e2b762d0e0927d80c43e45254f26e2bd Author: gribse Date: Mon Feb 17 16:41:26 2025 +0000 initial commit diff --git a/app.js b/app.js new file mode 100755 index 0000000..6191923 --- /dev/null +++ b/app.js @@ -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); + }); +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100755 index 0000000..695b313 --- /dev/null +++ b/index.html @@ -0,0 +1,66 @@ + + + + + + ELO Ranking System 2 + + + + +
+
+

Input Words

+ + +
+ +
+ +
+
+

Ranking

+
+ + +
+
+
+
+ +
+
+

Current Rankings

+
    + +
+
+
+ + + \ No newline at end of file diff --git a/info.png b/info.png new file mode 100755 index 0000000..0261e24 Binary files /dev/null and b/info.png differ diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e69de29 diff --git a/styles.css b/styles.css new file mode 100755 index 0000000..e69de29