diff --git a/frontend/src/html/popups.html b/frontend/src/html/popups.html index 6ccbc4029..169734dcb 100644 --- a/frontend/src/html/popups.html +++ b/frontend/src/html/popups.html @@ -430,6 +430,7 @@
+
diff --git a/frontend/src/ts/test/words-generator.ts b/frontend/src/ts/test/words-generator.ts index 33f80bbf7..4bf9db161 100644 --- a/frontend/src/ts/test/words-generator.ts +++ b/frontend/src/ts/test/words-generator.ts @@ -727,6 +727,8 @@ export async function getNextWord( (wordset.length < 4 || PractiseWords.before.mode !== null) ) { randomWord = wordset.randomWord(funboxFrequency); + } else if (Config.mode === "custom" && CustomText.getMode() === "shuffle") { + randomWord = wordset.shuffledWord(); } else if ( Config.mode === "custom" && CustomText.getLimitMode() === "section" diff --git a/frontend/src/ts/test/wordset.ts b/frontend/src/ts/test/wordset.ts index 0eefcd4e2..9eb08c5da 100644 --- a/frontend/src/ts/test/wordset.ts +++ b/frontend/src/ts/test/wordset.ts @@ -1,28 +1,47 @@ import * as FunboxList from "./funbox/funbox-list"; import { dreymarIndex } from "../utils/misc"; -import { randomElementFromArray } from "../utils/arrays"; +import { randomElementFromArray, shuffle } from "../utils/arrays"; import Config from "../config"; -let currentWordset: Wordset | null = null; +let currentWordset: MonkeyTypes.Wordset | null = null; + +export class Wordset implements MonkeyTypes.Wordset { + words: string[]; + length: number; + + shuffledIndexes: number[]; -export class Wordset { - public words: string[]; - public length: number; constructor(words: string[]) { this.words = words; this.length = this.words.length; + this.shuffledIndexes = []; } - public randomWord(mode: MonkeyTypes.FunboxWordsFrequency): string { + randomWord(mode: MonkeyTypes.FunboxWordsFrequency): string { if (mode === "zipf") { return this.words[dreymarIndex(this.words.length)] as string; } else { return randomElementFromArray(this.words); } } + + shuffledWord(): string { + if (this.shuffledIndexes.length === 0) { + this.generateShuffledIndexes(); + } + return this.words[this.shuffledIndexes.pop() as number] as string; + } + + generateShuffledIndexes(): void { + this.shuffledIndexes = []; + for (let i = 0; i < this.length; i++) { + this.shuffledIndexes.push(i); + } + shuffle(this.shuffledIndexes); + } } -export async function withWords(words: string[]): Promise { +export async function withWords(words: string[]): Promise { const wordFunbox = FunboxList.get(Config.funbox).find( (f) => f.functions?.withWords ); diff --git a/frontend/src/ts/types/types.d.ts b/frontend/src/ts/types/types.d.ts index 936b796af..978fed90c 100644 --- a/frontend/src/ts/types/types.d.ts +++ b/frontend/src/ts/types/types.d.ts @@ -109,10 +109,13 @@ declare namespace MonkeyTypes { | `wordOrder:${FunboxWordOrder}`; class Wordset { - public words: string[]; - public length: number; + words: string[]; + length: number; + shuffledIndexes: number[]; constructor(words: string[]); randomWord(mode: MonkeyTypes.FunboxWordsFrequency): string; + shuffledWord(): string; + generateShuffledIndexes(): void; } class Section { diff --git a/shared-types/types.d.ts b/shared-types/types.d.ts index 2a461425b..dc4d29e5b 100644 --- a/shared-types/types.d.ts +++ b/shared-types/types.d.ts @@ -239,7 +239,7 @@ declare namespace SharedTypes { hash?: string; } - type CustomTextMode = "repeat" | "random"; + type CustomTextMode = "repeat" | "random" | "shuffle"; type CustomTextLimitMode = "word" | "time" | "section"; type CustomTextLimit = { value: number;