Switch to a hash-based shuffle so adding new items won't change the shuffled order of existing items

This commit is contained in:
xenofem 2025-09-09 20:52:09 -04:00
parent cf17ec85a7
commit bede558347

View file

@ -1,31 +1,36 @@
const LCG_M = Math.pow(2, 32);
const LCG_A = 0xd9f5;
const LCG_C = 69;
function lcg(seed) {
let value = seed % LCG_M;
return (n) => {
value = (LCG_A * value + LCG_C) % LCG_M;
return Math.floor(n * value / LCG_M);
};
// cyrb53a beta (c) 2023 bryc (github.com/bryc)
function cyrb53a_beta(str, seed = 0) {
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
for (const codePoint of str) {
ch = codePoint.codePointAt(0);
h1 = Math.imul(h1 ^ ch, 0x85ebca77);
h2 = Math.imul(h2 ^ ch, 0xc2b2ae3d);
}
h1 ^= Math.imul(h1 ^ (h2 >>> 15), 0x735a2d97);
h2 ^= Math.imul(h2 ^ (h1 >>> 15), 0xcaf649a9);
h1 ^= h2 >>> 16;
h2 ^= h1 >>> 16;
return 2097152 * (h2 >>> 0) + (h1 >>> 11);
}
function seedableShuffledCopy(list, seed) {
const gen = lcg(seed);
const l = [...list];
const SEED_BOUND = Math.pow(2, 32);
for (let i = 0; i < l.length - 1; ++i) {
j = i + gen(l.length - i);
const tmp = l[i];
l[i] = l[j];
l[j] = tmp;
}
return l;
function seedableShuffledCopy(list, key, seed) {
const hashedIndices = list.map((elem, idx) => ({ idx, hash: cyrb53a_beta(key(elem), seed) }));
hashedIndices.sort((a, b) => {
if (a.hash > b.hash) {
return 1;
}
if (a.hash < b.hash) {
return -1;
}
return 0;
});
return hashedIndices.map((entry) => list[entry.idx]);
}
function newSeed() {
return Math.floor(Math.random() * LCG_M);
return Math.floor(Math.random() * SEED_BOUND);
}
let receivedStore = JSON.parse(localStorage.getItem('receivedStore')) || {};
@ -96,7 +101,7 @@ function setupIndex() {
switch (ordering) {
case 'shuffle':
orderedWorks = seedableShuffledCopy(WORKS, shuffleSeed);
orderedWorks = seedableShuffledCopy(WORKS, (work) => work.id, shuffleSeed);
break;
case 'dateAsc':
orderedWorks = WORKS.toReversed();