Skip to content

Commit

Permalink
Update swapper implementation for new mc
Browse files Browse the repository at this point in the history
  • Loading branch information
leung018 committed Oct 26, 2023
1 parent db43bb1 commit 36a9aef
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 76 deletions.
15 changes: 7 additions & 8 deletions src/model/mc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@ export class MultipleChoiceError extends CustomBaseError {
}
}

export interface Choice {
answer: string
isFixedPosition: boolean
}

// TODO: Migrate MultipleChoice to NewVersionMultipleChoice
export class NewVersionMultipleChoice {
choices: ReadonlyArray<{
answer: string
isFixedPosition: boolean
}>
choices: ReadonlyArray<Choice>

correctChoiceIndex: number

Expand Down Expand Up @@ -100,10 +102,7 @@ export class NewVersionMultipleChoice {
choices,
correctChoiceIndex,
}: {
choices: ReadonlyArray<{
answer: string
isFixedPosition: boolean
}>
choices: ReadonlyArray<Choice>
correctChoiceIndex: number
}) {
this.choices = choices
Expand Down
90 changes: 22 additions & 68 deletions src/model/swap.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,38 @@
import { getPermutations } from '../utils/permutation'
import { MultipleChoice, NewVersionMultipleChoice } from './mc'
import { NewVersionMultipleChoice, Choice } from './mc'

export class MultipleChoiceSwapper {
static getSignificantlySwapped(
originalMc: NewVersionMultipleChoice,
): Set<NewVersionMultipleChoice> {
const oldVersionOriginalMc = new MultipleChoice({
choices: originalMc.choices.map((c) => c.answer),
correctChoiceIndex: originalMc.correctChoiceIndex,
})
const lockedChoiceIndices = new Set<number>(
originalMc.choices
.map((c, i) => (c.isFixedPosition ? i : null))
.filter((i) => i !== null) as number[],
)
const swappedMCs = new MultipleChoiceSwapper(
oldVersionOriginalMc,
lockedChoiceIndices,
).getSignificantlySwapped()
return new Set(
Array.from(swappedMCs).map((mc) => {
const newChoices = mc.choices.map((choice) => ({
answer: choice,
isFixedPosition: lockedChoiceIndices.has(
oldVersionOriginalMc.choices.findIndex((e) => e == choice),
),
}))
return new NewVersionMultipleChoice({
choices: newChoices,
correctChoiceIndex: mc.correctChoiceIndex,
})
}),
)
}

/**
* Computes a set of MultipleChoice objects where the choices are significantly swapped.
* Significantly swapped means that each choice is in a different position from the original MultipleChoice object,
* except for those choices that are locked and should not be swapped.
* except for those choices that are fixed position and should not be swapped.
*
* @param originalMc The original MultipleChoice object to be used as the basis for the swaps.
* @param lockedChoiceIndices A set of indices of choices that are locked and should not be swapped.
* @returns A set of MultipleChoice objects where the choices are significantly swapped.
*/
static oldGetSignificantlySwapped(
originalMc: MultipleChoice,
lockedChoiceIndices: ReadonlySet<number> = new Set(),
): Set<MultipleChoice> {
return new MultipleChoiceSwapper(
originalMc,
lockedChoiceIndices,
).getSignificantlySwapped()
static getSignificantlySwapped(
originalMc: NewVersionMultipleChoice,
): Set<NewVersionMultipleChoice> {
return new MultipleChoiceSwapper(originalMc).getSignificantlySwapped()
}

private readonly originalChoices: ReadonlyArray<string>
private readonly lockedChoiceToOriginalIndexMap: ReadonlyMap<string, number>
private readonly correctChoice: string
private readonly originalChoices: ReadonlyArray<Choice>
private readonly lockedChoiceToOriginalIndexMap: ReadonlyMap<Choice, number>
private readonly correctChoice: Choice

private constructor(
originalMc: MultipleChoice,
lockedChoiceIndices: ReadonlySet<number>,
) {
private constructor(originalMc: NewVersionMultipleChoice) {
this.originalChoices = originalMc.choices
this.correctChoice = originalMc.choices[originalMc.correctChoiceIndex]
this.lockedChoiceToOriginalIndexMap = this.chosenChoicesToIndexMap(
lockedChoiceIndices,
this.originalChoices,
)
}

private chosenChoicesToIndexMap(
chosenIndices: ReadonlySet<number>,
choices: ReadonlyArray<string>,
): ReadonlyMap<string, number> {
const map = new Map<string, number>()
chosenIndices.forEach((index) => {
map.set(choices[index], index)
const lockedChoiceToOriginalIndexMap = new Map<Choice, number>()
originalMc.choices.forEach((c, i) => {
if (c.isFixedPosition) {
lockedChoiceToOriginalIndexMap.set(c, i)
}
})
return map
this.lockedChoiceToOriginalIndexMap = lockedChoiceToOriginalIndexMap
}

private getSignificantlySwapped(): Set<MultipleChoice> {
private getSignificantlySwapped(): Set<NewVersionMultipleChoice> {
const allPossibleChoices = getPermutations(this.originalChoices)
const significantSwappedChoices = Array.from(allPossibleChoices).filter(
this.areSignificantlySwapped,
Expand All @@ -89,7 +41,7 @@ export class MultipleChoiceSwapper {
}

private areSignificantlySwapped = (
newChoices: ReadonlyArray<string>,
newChoices: ReadonlyArray<Choice>,
): boolean => {
for (let i = 0; i < newChoices.length; i++) {
const newChoice = newChoices[i]
Expand All @@ -105,10 +57,12 @@ export class MultipleChoiceSwapper {
return true
}

private mapToMc = (choices: ReadonlyArray<string>): MultipleChoice => {
private mapToMc = (
choices: ReadonlyArray<Choice>,
): NewVersionMultipleChoice => {
const correctChoiceIndex = choices.findIndex(
(c) => c === this.correctChoice,
)
return new MultipleChoice({ choices, correctChoiceIndex })
return new NewVersionMultipleChoice({ choices, correctChoiceIndex })
}
}

0 comments on commit 36a9aef

Please sign in to comment.