add challenge scripts
This commit is contained in:
151
js/common_v=2.js
Normal file
151
js/common_v=2.js
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
NOTE: this just contains user interface logic;
|
||||
there is probably not much interesting stuff here
|
||||
*/
|
||||
|
||||
let name, n, x, n_old, n_last;
|
||||
|
||||
async function init() {
|
||||
const params = (new URL(document.URL)).searchParams;
|
||||
if(params.has('name')) {
|
||||
name = params.get('name');
|
||||
} else {
|
||||
/* missing name, redirect to start screen */
|
||||
location.replace('.');
|
||||
return;
|
||||
}
|
||||
/* people can do more than 2^53 button clicks, so we need big integers */
|
||||
if(params.has('n') && params.has('x')) {
|
||||
n = BigInt(params.get('n'));
|
||||
x = BigInt(params.get('x'));
|
||||
} else {
|
||||
n = 0n;
|
||||
x = 0n;
|
||||
}
|
||||
n_old = 0n;
|
||||
n_last = -1n;
|
||||
if(typeof prepare == 'function') {
|
||||
await prepare();
|
||||
}
|
||||
const button = document.querySelector('button');
|
||||
button.textContent = n;
|
||||
button.addEventListener('click', onClick);
|
||||
setInterval(() => {
|
||||
if(n > n_last) {
|
||||
/* to spare the browser, we do this at most 10 times per second */
|
||||
const params = new URLSearchParams({name, n, x});
|
||||
history.replaceState(null, '', '?' + params);
|
||||
button.textContent = n;
|
||||
n_last = n;
|
||||
}
|
||||
}, 100);
|
||||
communicate();
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
|
||||
async function communicate() {
|
||||
const error = document.querySelector('#error');
|
||||
try {
|
||||
/* only submit increased click counts, otherwise just fetch scores */
|
||||
const path = 'server.php' + ((n_last > n_old) ? location.search : '');
|
||||
const result = await fetch(path);
|
||||
if(!result.ok) {
|
||||
throw new Error(`${result.status} ${result.statusText}`);
|
||||
}
|
||||
if(n_last > n_old) {
|
||||
n_old = n_last;
|
||||
}
|
||||
const json = await result.json();
|
||||
if(json.flag && !location.pathname.endsWith('/' + json.flag)) {
|
||||
/* advance to next level */
|
||||
location.replace(json.flag + location.search);
|
||||
return;
|
||||
}
|
||||
if(json.scores) {
|
||||
displayScores(json.scores, json.time);
|
||||
}
|
||||
if(json.error) {
|
||||
throw new Error(json.error);
|
||||
}
|
||||
const timeStr = new Date(json.time * 1000).toLocaleTimeString('de-DE');
|
||||
error.textContent = `Zuletzt aktualisiert: ${timeStr}`;
|
||||
} catch(e) {
|
||||
console.warn(e);
|
||||
error.textContent = e;
|
||||
}
|
||||
setTimeout(communicate, 1000);
|
||||
}
|
||||
|
||||
function displayScores(scores, time) {
|
||||
const table = document.querySelector('table');
|
||||
while(table.rows.length > 1) {
|
||||
table.deleteRow(-1);
|
||||
}
|
||||
scores.sort((a, b) => {
|
||||
const n_a = BigInt(a.n);
|
||||
const n_b = BigInt(b.n);
|
||||
if(n_a > n_b) {
|
||||
return -1;
|
||||
}
|
||||
if(n_a < n_b) {
|
||||
return 1;
|
||||
}
|
||||
if(a.time < b.time) {
|
||||
return -1;
|
||||
}
|
||||
if(a.time > b.time) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
scores.forEach((entry) => {
|
||||
const row = table.insertRow();
|
||||
|
||||
const nameCell = row.insertCell();
|
||||
/* protect against unicode bidi control characters */
|
||||
const bdi = document.createElement('bdi');
|
||||
bdi.textContent = entry.name;
|
||||
nameCell.appendChild(bdi);
|
||||
nameCell.title = entry.name;
|
||||
|
||||
const timeCell = row.insertCell();
|
||||
const date = new Date(entry.time * 1000);
|
||||
if(time - entry.time < 86400) {
|
||||
timeCell.textContent = date.toLocaleTimeString('de-DE');
|
||||
} else {
|
||||
timeCell.textContent = date.toLocaleDateString('de-DE');
|
||||
}
|
||||
timeCell.title = date.toLocaleString('de-DE');
|
||||
|
||||
const nCell = row.insertCell();
|
||||
nCell.textContent = entry.n;
|
||||
nCell.title = entry.n;
|
||||
});
|
||||
}
|
||||
|
||||
/* compute SHA-256 digest represented as integer (big endian) */
|
||||
async function sha256(str) {
|
||||
const msg = new TextEncoder().encode(str);
|
||||
const buffer = await crypto.subtle.digest('SHA-256', msg);
|
||||
const bytes = new Uint8Array(buffer);
|
||||
let val = 0n;
|
||||
for(const b of bytes) {
|
||||
val = (val << 8n) | BigInt(b);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/* compute a^k modulo m */
|
||||
function pow(a, k, m) {
|
||||
let b = 1n;
|
||||
for(; k; k >>= 1n) {
|
||||
if(k & 1n) {
|
||||
b *= a;
|
||||
b %= m;
|
||||
}
|
||||
a *= a;
|
||||
a %= m;
|
||||
}
|
||||
return b;
|
||||
}
|
Reference in New Issue
Block a user