"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 +=
`
`;
}
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;
}
window.onload = function() {
var urlParams = new URLSearchParams(window.location.search);
var q = urlParams.get('q')
if (q) {
startBoard(deserializeBoard(q));
} else {
startBoard(newBoard());
}
}