-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: number to words multiple choice
Closes #92.
- Loading branch information
Showing
14 changed files
with
253 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// TODO: Use locale on computer's machine | ||
const numberFormatter = new Intl.NumberFormat('en-US'); | ||
|
||
export default function formatNumber(num: number): string { | ||
return numberFormatter.format(num); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import randomElement from './randomElement'; | ||
import { randomGenerator } from './RandomNumberGenerator'; | ||
import shuffle from './shuffle'; | ||
|
||
// TODO: Make these work for numbers with decimal places | ||
function toDigits(n: number): string[] { | ||
return n.toString().split(''); | ||
} | ||
|
||
function insertDigit(n: number): number { | ||
const nDigits = toDigits(n); | ||
const insertAt = randomGenerator.integer(nDigits.length - 1); | ||
nDigits.splice(insertAt, 0, randomGenerator.integer(9).toString()); | ||
return parseFloat(nDigits.join('')); | ||
} | ||
|
||
function removeDigitOrRandom(n: number): number { | ||
const nDigits = toDigits(n); | ||
if (nDigits.length > 1) { | ||
const removeAt = randomGenerator.integer(nDigits.length - 1); | ||
nDigits.splice(removeAt, 1); | ||
return parseFloat(nDigits.join('')); | ||
} | ||
return randomGenerator.integer(n); | ||
} | ||
|
||
function replaceDigit(n: number): number { | ||
const nDigits = toDigits(n); | ||
const replaceAt = randomGenerator.integer(nDigits.length - 1); | ||
nDigits.splice(replaceAt, 1, randomGenerator.integer(9).toString()); | ||
return parseFloat(nDigits.join('')); | ||
} | ||
|
||
function shuffleDigits(n: number): number { | ||
return parseFloat(shuffle(toDigits(n)).join('')); | ||
} | ||
|
||
function numberInRange(n: number): number { | ||
const digitLength = (n.toString()).replace('.', '').length; | ||
return randomGenerator.integer((10 ** (digitLength)) - 1); | ||
} | ||
|
||
const mangleFunctions = [ | ||
insertDigit, | ||
removeDigitOrRandom, | ||
replaceDigit, | ||
shuffleDigits, | ||
numberInRange, | ||
]; | ||
|
||
export default function mutateNumber(n: number): number { | ||
return randomElement(mangleFunctions)(n); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* eslint-disable no-param-reassign */ | ||
|
||
const { floor, random } = Math; | ||
|
||
export default function shuffle<T>(inputArray: T[]) { | ||
const array = inputArray.slice(0); | ||
let currentIndex = array.length; | ||
let randomIndex: number; | ||
|
||
// While there remain elements to shuffle. | ||
while (currentIndex !== 0) { | ||
// Pick a remaining element. | ||
randomIndex = floor(random() * currentIndex); | ||
currentIndex -= 1; | ||
|
||
// And swap it with the current element. | ||
[array[currentIndex], array[randomIndex]] = [ | ||
array[randomIndex], array[currentIndex]]; | ||
} | ||
|
||
return array; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import tryByKey from './tryByKey'; | ||
|
||
describe('tryByKey()', () => { | ||
it('retries on unique numbers', () => { | ||
const keys = [1, 8, 3, 1, 2, 4, 8]; | ||
const got: number[] = []; | ||
const limitedRetries = tryByKey(); | ||
for (let i = 0; i < keys.length; i++) { | ||
limitedRetries(keys[i], () => got.push(keys[i])); | ||
} | ||
expect(got).toEqual([1, 8, 3, 2, 4]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function uniqueElements<T>(array: T[]): T[] { | ||
return Array.from(new Set(array)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { magNFromMagnitude, maxFromMagnitude } from '../../lib/math/magnitude'; | ||
import mutateNumber from '../../lib/mutateNumber'; | ||
import { randomGenerator } from '../../lib/RandomNumberGenerator'; | ||
import shuffle from '../../lib/shuffle'; | ||
import tryByKey from '../../lib/tryByKey'; | ||
import NumbersToWordsData from './NumbersToWordsData'; | ||
|
||
export interface NumToWordsProblem { | ||
number: number; | ||
choices: number[]; | ||
} | ||
|
||
function generateChoices(number: number): number[] { | ||
const choices: Set<number> = new Set([number]); | ||
while (choices.size < 4) { | ||
choices.add(mutateNumber(number)); | ||
} | ||
const shuffled = shuffle(Array.from(choices)); | ||
return shuffled; | ||
} | ||
|
||
function choiceProblem(number: number): NumToWordsProblem { | ||
const choices: number[] = generateChoices(number); | ||
return { number, choices }; | ||
} | ||
|
||
function fillInTheBlankProblem(number: number): NumToWordsProblem { | ||
return { number, choices: [] }; | ||
} | ||
|
||
export default function generateProblems({ | ||
count, magnitude, problemType, | ||
}: NumbersToWordsData): NumToWordsProblem[] { | ||
const max = maxFromMagnitude(magnitude); | ||
const magNumber = magNFromMagnitude(magnitude); | ||
|
||
const problems: Array<NumToWordsProblem> = []; | ||
const limitedRetries = tryByKey(max); | ||
|
||
while (problems.length < count) { | ||
const number = randomGenerator.stepMagnitude(magNumber); | ||
limitedRetries(number, () => { | ||
problems.push( | ||
(problemType === 'blanks' ? fillInTheBlankProblem : choiceProblem)(number), | ||
); | ||
}); | ||
} | ||
|
||
return problems; | ||
} |
Oops, something went wrong.