From db43bb151264ee11d6d6e7ce55ead88cfbcbde00 Mon Sep 17 00:00:00 2001 From: Leung Cheng Date: Thu, 26 Oct 2023 17:18:11 +0800 Subject: [PATCH] Refactor swapper to use new mc --- src/model/mc.ts | 27 +++++++++++- src/model/swap.test.ts | 95 ++++++++++++++++++++++-------------------- src/model/swap.ts | 36 +++++++++++++++- 3 files changed, 108 insertions(+), 50 deletions(-) diff --git a/src/model/mc.ts b/src/model/mc.ts index 90c6ee5..d8740e6 100644 --- a/src/model/mc.ts +++ b/src/model/mc.ts @@ -67,18 +67,41 @@ export class MultipleChoiceError extends CustomBaseError { // TODO: Migrate MultipleChoice to NewVersionMultipleChoice export class NewVersionMultipleChoice { choices: ReadonlyArray<{ - description: string + answer: string isFixedPosition: boolean }> correctChoiceIndex: number + static createWithNoFixedChoices({ + answers = ['a', 'b'], + correctAnswerIndex = 0, + } = {}): NewVersionMultipleChoice { + return new NewVersionMultipleChoice({ + choices: answers.map((answer) => ({ + answer, + isFixedPosition: false, + })), + correctChoiceIndex: correctAnswerIndex, + }) + } + + static createTestInstance({ + choices = [ + { answer: 'a', isFixedPosition: false }, + { answer: 'b', isFixedPosition: false }, + ], + correctChoiceIndex = 0, + }): NewVersionMultipleChoice { + return new NewVersionMultipleChoice({ choices, correctChoiceIndex }) + } + constructor({ choices, correctChoiceIndex, }: { choices: ReadonlyArray<{ - description: string + answer: string isFixedPosition: boolean }> correctChoiceIndex: number diff --git a/src/model/swap.test.ts b/src/model/swap.test.ts index 06e1337..fc56e16 100644 --- a/src/model/swap.test.ts +++ b/src/model/swap.test.ts @@ -1,82 +1,85 @@ import { MultipleChoiceSwapper } from './swap' -import { MultipleChoice } from './mc' +import { MultipleChoice, NewVersionMultipleChoice } from './mc' describe('MultipleChoiceSwapper.getSignificantlySwapped', () => { it('should compute swaps for two choices', () => { - const mc = new MultipleChoice({ - choices: ['a', 'b'], - correctChoiceIndex: 0, + const mc = NewVersionMultipleChoice.createWithNoFixedChoices({ + answers: ['a', 'b'], + correctAnswerIndex: 0, }) expect(MultipleChoiceSwapper.getSignificantlySwapped(mc)).toEqual( new Set([ - new MultipleChoice({ choices: ['b', 'a'], correctChoiceIndex: 1 }), + NewVersionMultipleChoice.createWithNoFixedChoices({ + answers: ['b', 'a'], + correctAnswerIndex: 1, + }), ]), ) }) it('should compute swaps for three choices', () => { - const mc = new MultipleChoice({ - choices: ['a', 'b', 'c'], - correctChoiceIndex: 1, + const mc = NewVersionMultipleChoice.createWithNoFixedChoices({ + answers: ['a', 'b', 'c'], + correctAnswerIndex: 1, }) expect(MultipleChoiceSwapper.getSignificantlySwapped(mc)).toEqual( new Set([ - new MultipleChoice({ choices: ['b', 'c', 'a'], correctChoiceIndex: 0 }), - new MultipleChoice({ choices: ['c', 'a', 'b'], correctChoiceIndex: 2 }), + NewVersionMultipleChoice.createWithNoFixedChoices({ + answers: ['b', 'c', 'a'], + correctAnswerIndex: 0, + }), + NewVersionMultipleChoice.createWithNoFixedChoices({ + answers: ['c', 'a', 'b'], + correctAnswerIndex: 2, + }), ]), ) }) - it('should further limit output when lockedChoiceIndices have one element', () => { - const mc = new MultipleChoice({ - choices: ['Apple only', 'Banana only', 'None of the above'], + it('should further limit output when one choice is fixed position', () => { + const mc = new NewVersionMultipleChoice({ + choices: [ + { answer: 'Apple only', isFixedPosition: false }, + { answer: 'Banana only', isFixedPosition: false }, + { answer: 'None of the above', isFixedPosition: true }, + ], correctChoiceIndex: 1, }) - const lockedChoiceIndices = new Set([2]) - expect( - MultipleChoiceSwapper.getSignificantlySwapped(mc, lockedChoiceIndices), - ).toEqual( + expect(MultipleChoiceSwapper.getSignificantlySwapped(mc)).toEqual( new Set([ - new MultipleChoice({ - choices: ['Banana only', 'Apple only', 'None of the above'], + new NewVersionMultipleChoice({ + choices: [ + { answer: 'Banana only', isFixedPosition: false }, + { answer: 'Apple only', isFixedPosition: false }, + { answer: 'None of the above', isFixedPosition: true }, + ], correctChoiceIndex: 0, }), ]), ) }) - it('should ignore lockedChoiceIndices when they are out of range', () => { - const mc = new MultipleChoice({ - choices: ['a', 'b'], - correctChoiceIndex: 1, - }) - const lockedChoiceIndices = new Set([2]) - expect( - MultipleChoiceSwapper.getSignificantlySwapped(mc, lockedChoiceIndices), - ).toEqual( - new Set([ - new MultipleChoice({ choices: ['b', 'a'], correctChoiceIndex: 0 }), - ]), - ) - }) - it('should return same set when lockedChoiceIndices contain all choices', () => { - const mc = MultipleChoice.createTestInstance({ - choices: ['a', 'b', 'c'], + const mc = NewVersionMultipleChoice.createTestInstance({ + choices: [ + { answer: 'a', isFixedPosition: true }, + { answer: 'b', isFixedPosition: true }, + { answer: 'c', isFixedPosition: true }, + ], }) - const lockedChoiceIndices = new Set([0, 1, 2]) - expect( - MultipleChoiceSwapper.getSignificantlySwapped(mc, lockedChoiceIndices), - ).toEqual(new Set([mc])) + expect(MultipleChoiceSwapper.getSignificantlySwapped(mc)).toEqual( + new Set([mc]), + ) }) it('should return empty set when lockedChoiceIndices contain all choices except one', () => { - const mc = MultipleChoice.createTestInstance({ - choices: ['a', 'b', 'c'], + const mc = NewVersionMultipleChoice.createTestInstance({ + choices: [ + { answer: 'a', isFixedPosition: true }, + { answer: 'b', isFixedPosition: true }, + { answer: 'c', isFixedPosition: false }, + ], }) - const lockedChoiceIndices = new Set([0, 1]) - expect( - MultipleChoiceSwapper.getSignificantlySwapped(mc, lockedChoiceIndices), - ).toEqual(new Set()) + expect(MultipleChoiceSwapper.getSignificantlySwapped(mc)).toEqual(new Set()) }) }) diff --git a/src/model/swap.ts b/src/model/swap.ts index f38ad69..811a2df 100644 --- a/src/model/swap.ts +++ b/src/model/swap.ts @@ -1,7 +1,39 @@ import { getPermutations } from '../utils/permutation' -import { MultipleChoice } from './mc' +import { MultipleChoice, NewVersionMultipleChoice } from './mc' export class MultipleChoiceSwapper { + static getSignificantlySwapped( + originalMc: NewVersionMultipleChoice, + ): Set { + const oldVersionOriginalMc = new MultipleChoice({ + choices: originalMc.choices.map((c) => c.answer), + correctChoiceIndex: originalMc.correctChoiceIndex, + }) + const lockedChoiceIndices = new Set( + 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, @@ -11,7 +43,7 @@ export class MultipleChoiceSwapper { * @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 getSignificantlySwapped( + static oldGetSignificantlySwapped( originalMc: MultipleChoice, lockedChoiceIndices: ReadonlySet = new Set(), ): Set {