"use strict"; var currentBoard = null; var selected = {}; var won = [] function newGroup() { var categoryPoint = Math.random(); for (var category of corpus) { if (categoryPoint < category.portion) { continue; } var group = category.groups[category.groups.length * Math.random() << 0] var clues = randomSelect(group.clues, 4) break; } return { hint: group.hint, clues: clues, fullClues: group.clues, }; } function first(xs) { return xs[0] } function newNonintersectingGroup(oldGroups) { var group = newGroup(); for (var oldGroup of oldGroups) { var intersection = oldGroup.fullClues.map(first).filter(value => group.fullClues.map(first).includes(value)); if (intersection.length > 0) { return newNonintersectingGroup(oldGroups); } } return group; } function newBoard() { var groups = []; for (var i = 0; i < 4; i++) { var group = newNonintersectingGroup(groups); groups.push(group); } var newGroups = []; for (var i = 0; i < 4; i++) { newGroups.push({hint: groups[i].hint, clues: groups[i].clues}); } return newGroups; } function chooseBox(box) { var clue = parseInt(box.getAttribute('x-clue')); if (box.classList.contains('selected')) { box.classList.remove('selected'); delete selected[clue]; } else { box.classList.add('selected'); selected[clue] = parseInt(box.getAttribute('x-group')); } if (Object.keys(selected).length == 4) { var [ok, grp] = checkGuess(selected) if (ok) { consume(grp); } else { clear(); } } } function clear() { selected = {} var els = [...document.getElementsByClassName('selected')]; for (var i = 0; i < 4; i++) { els[i].classList.remove('selected'); } } function consume(grp) { won.push(grp) selected = {} var els = document.querySelectorAll(`[x-group="${grp}"]`); for (var el of els) { el.remove(); } var group = currentBoard[grp]; var cluesString = group.clues.map(x => x[0]).join(', ') var defnsString = group.clues.map(clue => { var full = getFull(clue); var defn = full[1]; return `${full[2]}: ${defn}` }).join('
') var s = `
${cluesString}${defnsString}
`; document.getElementById('answers').innerHTML += `
${group.hint} [flag]
${s}
`; } document.getElementById('answers').addEventListener('click', function(evt) { if (evt.target.classList.contains('flag')) { evt.preventDefault(); let options = { method: 'GET', headers: {} }; fetch('/flag?q=' + encodeURIComponent(evt.target.getAttribute('x-flag')), options) .then(body => { var el = document.createElement('span') el.innerText = `flagged` el.classList.add('flagged') evt.target.replaceWith(el) }); } }); function checkGuess(selected) { var ans = null; for (var clue of Object.keys(selected)) { var group = selected[clue]; if (ans === null) { ans = group; } else if (ans != group) { return [false, 0]; } } return [true, group]; } function getFull(clue) { var [i, j, k] = clue[1]; return fullCorpus[i].groups[j].clues[k]; } function serializeBoard(board) { var copy = [] for (var group of board) { copy.push({ clues: group.clues, hint: group.hint, }) } return encodeURIComponent(btoa(JSON.stringify(copy))) } function deserializeBoard(s) { var copy = JSON.parse(atob(decodeURIComponent(s))) var board = [] for (var group of copy) { board.push({ clues: group.clues, hint: group.hint, }) } return board; } function generateUrl(board) { return `${location.origin}/infinite-connections?q=${serializeBoard(board)}` } function startBoard(board) { selected = {}; document.getElementById('answers').innerHTML = ''; var el = document.getElementById('board'); currentBoard = board; won = []; var clues = []; for (var [i, group] of board.entries()) { for (var [j, clue] of group.clues.entries()) { clues.push([i, i*4+j, clue]); } } shuffle(clues); el.innerHTML = ``; for (var [i, j, clue] of clues) { el.innerHTML += `
${clue[0]}
`; } } function giveUp() { for (var i = 0; i < 4; i++) { if (!won.includes(i)) { consume(i); } } } document.getElementById('board').addEventListener('click', function(evt) { if (evt.target.classList.contains('box')) { evt.preventDefault(); chooseBox(evt.target); } }); document.getElementById('new-board').addEventListener('click', function(evt) { startBoard(newBoard()); }); document.getElementById('copy-link').addEventListener('click', function(evt) { navigator.clipboard.writeText(generateUrl(currentBoard)) }); document.getElementById('clear').addEventListener('click', function(evt) { clear(); }); document.getElementById('reset').addEventListener('click', function(evt) { startBoard(currentBoard); }); document.getElementById('give-up').addEventListener('click', function(evt) { giveUp(); }); // Source: https://stackoverflow.com/a/19270021 function randomSelect(arr, n) { var result = new Array(n), len = arr.length, taken = new Array(len); if (n > len) throw new RangeError("randomSelect: more elements taken than available"); while (n--) { var x = Math.floor(Math.random() * len); result[n] = arr[x in taken ? taken[x] : x]; taken[x] = --len in taken ? taken[len] : len; } return result; } // Source: https://stackoverflow.com/a/2450976 function shuffle(array) { let currentIndex = array.length, randomIndex; // While there remain elements to shuffle. while (currentIndex > 0) { // Pick a remaining element. randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex]]; } return array; } var urlParams = new URLSearchParams(window.location.search); var q = urlParams.get('q') if (q) { startBoard(deserializeBoard(q)); } else { startBoard(newBoard()); }