fix: ensure nightwave daily challenges are unique #2422
@ -151,4 +151,57 @@ export class SRng {
|
|||||||
arr[lastIdx] = tmp;
|
arr[lastIdx] = tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shuffledArray<T>(inarr: readonly T[]): T[] {
|
||||||
|
const arr = [...inarr];
|
||||||
|
this.shuffleArray(arr);
|
||||||
|
return arr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sequentallyUniqueRandomElement = <T>(
|
||||||
|
deck: readonly T[],
|
||||||
|
idx: number,
|
||||||
|
lookbehind: number,
|
||||||
|
seed: number = 0
|
||||||
|
): T | undefined => {
|
||||||
|
// This algorithm may modify a shuffle up to index `lookbehind + 1`. It assumes that the last `lookbehind` cards are not adjusted.
|
||||||
|
if (lookbehind + 1 >= deck.length - lookbehind) {
|
||||||
|
throw new Error(
|
||||||
|
`this algorithm cannot guarantee ${lookbehind} unique cards in a row with a deck of size ${deck.length}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const iteration = Math.trunc(idx / deck.length);
|
||||||
|
const card = idx % deck.length;
|
||||||
|
const currentShuffle = new SRng(mixSeeds(new SRng(iteration).randomInt(0, 100_000), seed)).shuffledArray(deck);
|
||||||
|
if (card < currentShuffle.length - lookbehind) {
|
||||||
|
// We are indexing before the end of the deck, so adjustments may be needed to achieve uniqueness.
|
||||||
|
const window: T[] = [];
|
||||||
|
{
|
||||||
|
const previousShuffle = new SRng(
|
||||||
|
mixSeeds(new SRng(iteration - 1).randomInt(0, 100_000), seed)
|
||||||
|
).shuffledArray(deck);
|
||||||
|
for (let i = previousShuffle.length - lookbehind; i != previousShuffle.length; ++i) {
|
||||||
|
window.push(previousShuffle[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// From this point on, `window.length == lookbehind` should hold.
|
||||||
|
for (let i = 0; i != lookbehind; ++i) {
|
||||||
|
if (window.indexOf(currentShuffle[i]) != -1) {
|
||||||
|
for (let j = i; ; ++j) {
|
||||||
|
// `j < currentShuffle.length - lookbehind` should hold.
|
||||||
|
if (window.indexOf(currentShuffle[j]) == -1) {
|
||||||
|
const tmp = currentShuffle[j];
|
||||||
|
currentShuffle[j] = currentShuffle[i];
|
||||||
|
currentShuffle[i] = tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.splice(0, 1);
|
||||||
|
window.push(currentShuffle[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentShuffle[card];
|
||||||
|
};
|
||||||
|
@ -9,7 +9,7 @@ import darvoDeals from "@/static/fixed_responses/worldState/darvoDeals.json";
|
|||||||
import { buildConfig } from "@/src/services/buildConfigService";
|
import { buildConfig } from "@/src/services/buildConfigService";
|
||||||
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
import { unixTimesInMs } from "@/src/constants/timeConstants";
|
||||||
import { config } from "@/src/services/configService";
|
import { config } from "@/src/services/configService";
|
||||||
import { getRandomElement, getRandomInt, SRng } from "@/src/services/rngService";
|
import { getRandomElement, getRandomInt, sequentallyUniqueRandomElement, SRng } from "@/src/services/rngService";
|
||||||
import { eMissionType, ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
|
import { eMissionType, ExportRegions, ExportSyndicates, IRegion } from "warframe-public-export-plus";
|
||||||
import {
|
import {
|
||||||
ICalendarDay,
|
ICalendarDay,
|
||||||
@ -385,13 +385,12 @@ const getSeasonChallengePools = (syndicateTag: string): IRotatingSeasonChallenge
|
|||||||
const getSeasonDailyChallenge = (pools: IRotatingSeasonChallengePools, day: number): ISeasonChallenge => {
|
const getSeasonDailyChallenge = (pools: IRotatingSeasonChallengePools, day: number): ISeasonChallenge => {
|
||||||
const dayStart = EPOCH + day * 86400000;
|
const dayStart = EPOCH + day * 86400000;
|
||||||
const dayEnd = EPOCH + (day + 3) * 86400000;
|
const dayEnd = EPOCH + (day + 3) * 86400000;
|
||||||
const rng = new SRng(new SRng(day).randomInt(0, 100_000));
|
|
||||||
return {
|
return {
|
||||||
_id: { $oid: "67e1b5ca9d00cb47" + day.toString().padStart(8, "0") },
|
_id: { $oid: "67e1b5ca9d00cb47" + day.toString().padStart(8, "0") },
|
||||||
Daily: true,
|
Daily: true,
|
||||||
Activation: { $date: { $numberLong: dayStart.toString() } },
|
Activation: { $date: { $numberLong: dayStart.toString() } },
|
||||||
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
Expiry: { $date: { $numberLong: dayEnd.toString() } },
|
||||||
Challenge: rng.randomElement(pools.daily)!
|
Challenge: sequentallyUniqueRandomElement(pools.daily, day, 2, 605732938)!
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user