@khanacademy/perseus-score 8.3.1 → 8.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/index.js +2 -2
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/es/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { KhanMath, geometry, angles, coefficients, number } from '@khanacademy/k
|
|
|
4
4
|
import { ErrorCodes, PerseusError, Errors, getDivideSymbol, getDecimalSeparator, GrapherUtil, approximateDeepEqual, approximateEqual, deepClone, getMatrixSize, getWidgetIdsFromContent, Registry } from '@khanacademy/perseus-core';
|
|
5
5
|
import _ from 'underscore';
|
|
6
6
|
|
|
7
|
-
const libName="@khanacademy/perseus-score";const libVersion="8.
|
|
7
|
+
const libName="@khanacademy/perseus-score";const libVersion="8.4.0";addLibraryVersionToPerseusDebug(libName,libVersion);
|
|
8
8
|
|
|
9
9
|
const MAXERROR_EPSILON=Math.pow(2,-42);const KhanAnswerTypes={predicate:{defaultForms:"integer, proper, improper, mixed, decimal",createValidatorFunctional:function(predicate,rawOptions){const options={simplify:"required",ratio:false,forms:KhanAnswerTypes.predicate.defaultForms,...rawOptions};let acceptableForms;if(!_.isArray(options.forms)){acceptableForms=options.forms.split(/\s*,\s*/);}else {acceptableForms=options.forms;}if(options.inexact===undefined){options.maxError=0;}const maxError=+(options.maxError??0)+MAXERROR_EPSILON;if(_.contains(acceptableForms,"percent")){acceptableForms=_.without(acceptableForms,"percent");acceptableForms.push("percent");}const fractionTransformer=function(text){text=text.replace(/\u2212/,"-").replace(/([+-])\s+/g,"$1").replace(/(^\s*)|(\s*$)/gi,"");const match=text.match(/^([+-]?\d+)\s*\/\s*([+-]?\d+)$/);const mobileDeviceMatch=text.match(/^([+-]?)\\frac\{([+-]?\d+)\}\{([+-]?\d+)\}$/);const parsedInt=parseInt(text,10);if(match||mobileDeviceMatch){let num;let denom;let simplified=true;if(match){num=parseFloat(match[1]);denom=parseFloat(match[2]);}else {num=parseFloat(mobileDeviceMatch[2]);if(mobileDeviceMatch[1]==="-"){if(num<0){simplified=false;}num=-num;}denom=parseFloat(mobileDeviceMatch[3]);}simplified=simplified&&denom>0&&(options.ratio||denom!==1)&&KhanMath.getGCD(num,denom)===1;return [{value:num/denom,exact:simplified}]}if(!isNaN(parsedInt)&&""+parsedInt===text){return [{value:parsedInt,exact:true}]}return []};const forms={integer:function(text){const decimal=forms.decimal(text);const rounded=forms.decimal(text,1);if(decimal[0]?.value!=null&&decimal[0].value===rounded[0]?.value||decimal[1]?.value!=null&&decimal[1].value===rounded[1]?.value){return decimal}return []},proper:function(text){const transformed=fractionTransformer(text);return transformed.flatMap(o=>{if(Math.abs(o.value)<1){return [o]}return []})},improper:function(text){const fractionExists=text.includes("/")||text.match(/\\(d?frac)/);if(!fractionExists){return []}const transformed=fractionTransformer(text);return transformed.flatMap(o=>{if(Math.abs(o.value)>=1){return [o]}return []})},pi:function(text){let match;let possibilities=[];text=text.replace(/\u2212/,"-");if(match=text.match(/^([+-]?)\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)){possibilities=[{value:parseFloat(match[1]+"1"),exact:true}];}else if(match=text.match(/^([+-]?\s*\d+\s*(?:\/\s*[+-]?\s*\d+)?)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)){possibilities=fractionTransformer(match[1]);}else if(match=text.match(/^([+-]?)\s*(\d+)\s*([+-]?\d+)\s*\/\s*([+-]?\d+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)){const sign=parseFloat(match[1]+"1");const integ=parseFloat(match[2]);const num=parseFloat(match[3]);const denom=parseFloat(match[4]);const simplified=num<denom&&KhanMath.getGCD(num,denom)===1;possibilities=[{value:sign*(integ+num/denom),exact:simplified}];}else if(match=text.match(/^([+-]?\s*\d+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)\s*(?:\/\s*([+-]?\s*\d+))?$/i)){possibilities=fractionTransformer(match[1]+"/"+match[3]);}else if(match=text.match(/^([+-]?)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)\s*(?:\/\s*([+-]?\d+))?$/i)){possibilities=fractionTransformer(match[1]+"1/"+match[3]);}else if(text==="0"){possibilities=[{value:0,exact:true}];}else if(match=text.match(/^(.+)\s*\*?\s*(\\?pi|p|\u03c0|\\?tau|t|\u03c4|pau)$/i)){possibilities=forms.decimal(match[1]);}else {possibilities=_.reduce(KhanAnswerTypes.predicate.defaultForms.split(/\s*,\s*/),function(memo,form){return memo.concat(forms[form](text))},[]);let approximatesPi=false;const number=parseFloat(text);if(!isNaN(number)&&number!==parseInt(text)){const piMult=Math.PI/12;const roundedNumber=piMult*Math.round(number/piMult);if(Math.abs(number-roundedNumber)<.01){approximatesPi=true;}}else if(text.match(/\/\s*7/)){approximatesPi=true;}if(approximatesPi){_.each(possibilities,function(possibility){possibility.piApprox=true;});}return possibilities}let multiplier=Math.PI;if(text.match(/\\?tau|t|\u03c4/)){multiplier=Math.PI*2;}if(text.match(/pau/)){multiplier=Math.PI*1.5;}possibilities.forEach(possibility=>{possibility.value*=multiplier;});return possibilities},coefficient:function(text){let possibilities=[];text=text.replace(/\u2212/,"-");if(text===""){possibilities=[{value:1,exact:true}];}else if(text==="-"){possibilities=[{value:-1,exact:true}];}return possibilities},log:function(text){let match;let possibilities=[];text=text.replace(/\u2212/,"-");text=text.replace(/[ ()]/g,"");if(match=text.match(/^log\s*(\S+)\s*$/i)){possibilities=forms.decimal(match[1]);}else if(text==="0"){possibilities=[{value:0,exact:true}];}return possibilities},percent:function(text){text=String(text).trim();let hasPercentSign=false;if(text.indexOf("%")===text.length-1){text=text.substring(0,text.length-1).trim();hasPercentSign=true;}const transformed=forms.decimal(text);transformed.forEach(t=>{t.exact=hasPercentSign;t.value=t.value/100;});return transformed},mixed:function(text){const match=text.replace(/\u2212/,"-").replace(/([+-])\s+/g,"$1").match(/^([+-]?)(\d+)\s+(\d+)\s*\/\s*(\d+)$/);if(match){const sign=parseFloat(match[1]+"1");const integ=parseFloat(match[2]);const num=parseFloat(match[3]);const denom=parseFloat(match[4]);const simplified=num<denom&&KhanMath.getGCD(num,denom)===1;return [{value:sign*(integ+num/denom),exact:simplified}]}return []},decimal:function(text,precision=1e10){const normal=function(text){text=String(text).trim();const match=text.replace(/\u2212/,"-").replace(/([+-])\s+/g,"$1").match(/^([+-]?(?:\d{1,3}(?:[, ]?\d{3})*\.?|\d{0,3}(?:[, ]?\d{3})*\.(?:\d{3}[, ]?)*\d{1,3}))$/);const badLeadingZero=text.match(/^0[0,]*,/);if(match&&!badLeadingZero){let x=parseFloat(match[1].replace(/[, ]/g,""));if(options.inexact===undefined){x=Math.round(x*precision)/precision;}return x}};const commas=function(text){text=text.replace(/([.,])/g,function(_,c){return c==="."?",":"."});return normal(text)};const results=[{value:normal(text),exact:true}];if(options.decimal_separator===","){results.push({value:commas(text),exact:true});}return results}};return function(guess){const fallback=options.fallback!=null?""+options.fallback:"";guess=String(guess).trim()||fallback;const score={empty:guess==="",correct:false,message:null,guess:guess};const findCorrectAnswer=()=>{for(const form of acceptableForms){const transformed=forms[form](guess);for(let j=0,l=transformed.length;j<l;j++){const val=transformed[j].value;const exact=transformed[j].exact;const piApprox=transformed[j].piApprox;if(predicate(val,maxError)){if(exact||options.simplify==="optional"){score.correct=true;score.message=options.message||null;score.empty=false;}else if(form==="percent"){score.empty=true;score.message=ErrorCodes.MISSING_PERCENT_ERROR;}else {if(options.simplify!=="enforced"){score.empty=true;}score.message=ErrorCodes.NEEDS_TO_BE_SIMPLIFIED_ERROR;}return false}if(piApprox&&predicate(val,Math.abs(val*.001))){score.empty=true;score.message=ErrorCodes.APPROXIMATED_PI_ERROR;}}}};findCorrectAnswer();if(score.correct===false){let interpretedGuess=false;_.each(forms,function(form){const anyAreNaN=_.any(form(guess),function(t){return t.value!=null&&!_.isNaN(t.value)});if(anyAreNaN){interpretedGuess=true;}});if(!interpretedGuess){score.empty=true;score.message=ErrorCodes.EXTRA_SYMBOLS_ERROR;return score}}return score}}},number:{createValidatorFunctional:function(correctAnswer,options){const correctFloat=parseFloat(correctAnswer);return KhanAnswerTypes.predicate.createValidatorFunctional((guess,maxError)=>Math.abs(guess-correctFloat)<maxError,{...options})}},expression:{parseSolution:function(solutionString,options){let solution=KAS.parse(solutionString,options);if(!solution.parsed){throw new PerseusError("The provided solution ("+solutionString+") didn't parse.",Errors.InvalidInput)}else if(options.simplified&&!solution.expr.isSimplified()){throw new PerseusError("The provided solution ("+solutionString+") isn't fully expanded and simplified.",Errors.InvalidInput)}else {solution=solution.expr;}return solution},createValidatorFunctional:function(solution,options){return function(guess){const score={empty:false,correct:false,message:null,guess:guess,ungraded:false};if(!guess){score.empty=true;return score}const answer=KAS.parse(guess,options);if(!answer.parsed){score.empty=true;return score}if(typeof solution==="string"){solution=KhanAnswerTypes.expression.parseSolution(solution,options);}const result=KAS.compare(answer.expr,solution,options);if(result.equal){score.correct=true;}else if(result.message){score.message=result.message;}else {const answerX=KAS.parse(guess.replace(/[xX]/g,"*"),options);if(answerX.parsed){const resultX=KAS.compare(answerX.expr,solution,options);if(resultX.equal){score.ungraded=true;score.message=ErrorCodes.MULTIPLICATION_SIGN_ERROR;}else if(resultX.message){score.message=resultX.message+" Also, I'm a computer. I only understand "+"multiplication if you use an "+"asterisk (*) as the multiplication "+"sign.";}}}return score}}}};
|
|
10
10
|
|
|
@@ -26,7 +26,7 @@ function getCoefficientsByType(data){if(data.coords==null){return undefined}if(d
|
|
|
26
26
|
|
|
27
27
|
function scoreIframe(userInput){if(userInput==null){return {type:"invalid",message:null}}if(userInput.status==="correct"){return {type:"points",earned:1,total:1,message:userInput.message||null}}if(userInput.status==="incorrect"){return {type:"points",earned:0,total:1,message:userInput.message||null}}return {type:"invalid",message:"Keep going, you're not there yet!"}}
|
|
28
28
|
|
|
29
|
-
const{collinear,canonicalSineCoefficients,similar,clockwise}=geometry;const{getClockwiseAngle}=angles;const{getSinusoidCoefficients,getQuadraticCoefficients}=coefficients;function scoreInteractiveGraph(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}if(userInput.type==="none"&&rubric.correct.type==="none"){return {type:"points",earned:0,total:0,message:null}}const hasValue=Boolean(userInput.coords||userInput.center&&userInput.radius);if(userInput.type===rubric.correct.type&&hasValue){if(userInput.type==="linear"&&rubric.correct.type==="linear"&&userInput.coords!=null){const guess=userInput.coords;const correct=rubric.correct.coords;if(collinear(correct[0],correct[1],guess[0])&&collinear(correct[0],correct[1],guess[1])){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="linear-system"&&rubric.correct.type==="linear-system"&&userInput.coords!=null){const guess=userInput.coords;const correct=rubric.correct.coords;if(collinear(correct[0][0],correct[0][1],guess[0][0])&&collinear(correct[0][0],correct[0][1],guess[0][1])&&collinear(correct[1][0],correct[1][1],guess[1][0])&&collinear(correct[1][0],correct[1][1],guess[1][1])||collinear(correct[0][0],correct[0][1],guess[1][0])&&collinear(correct[0][0],correct[0][1],guess[1][1])&&collinear(correct[1][0],correct[1][1],guess[0][0])&&collinear(correct[1][0],correct[1][1],guess[0][1])){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="quadratic"&&rubric.correct.type==="quadratic"&&userInput.coords!=null){const guessCoeffs=getQuadraticCoefficients(userInput.coords);const correctCoeffs=getQuadraticCoefficients(rubric.correct.coords);if(approximateDeepEqual(guessCoeffs,correctCoeffs)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="sinusoid"&&rubric.correct.type==="sinusoid"&&userInput.coords!=null){const guessCoeffs=getSinusoidCoefficients(userInput.coords);const correctCoeffs=getSinusoidCoefficients(rubric.correct.coords);const canonicalGuessCoeffs=canonicalSineCoefficients(guessCoeffs);const canonicalCorrectCoeffs=canonicalSineCoefficients(correctCoeffs);if(approximateDeepEqual(canonicalGuessCoeffs,canonicalCorrectCoeffs)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="circle"&&rubric.correct.type==="circle"){if(approximateDeepEqual(userInput.center,rubric.correct.center)&&approximateEqual(userInput.radius,rubric.correct.radius)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="point"&&rubric.correct.type==="point"&&userInput.coords!=null){let correct=rubric.correct.coords;if(correct==null){throw new Error("Point graph rubric has null coords")}const guess=userInput.coords.slice();correct=correct.slice();guess?.sort();correct.sort();if(approximateDeepEqual(guess,correct)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="polygon"&&rubric.correct.type==="polygon"&&userInput.coords!=null){const guess=userInput.coords.slice();const correct=rubric.correct.coords.slice();let match;if(rubric.correct.match==="similar"){match=similar(guess,correct,Number.POSITIVE_INFINITY);}else if(rubric.correct.match==="congruent"){match=similar(guess,correct,number.DEFAULT_TOLERANCE);}else if(rubric.correct.match==="approx"){match=similar(guess,correct,.1);}else {guess.sort();correct.sort();match=approximateDeepEqual(guess,correct);}if(match){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="segment"&&rubric.correct.type==="segment"&&userInput.coords!=null){let guess=deepClone(userInput.coords);let correct=deepClone(rubric.correct.coords);guess=_.invoke(guess,"sort").sort();correct=_.invoke(correct,"sort").sort();if(approximateDeepEqual(guess,correct)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="ray"&&rubric.correct.type==="ray"&&userInput.coords!=null){const guess=userInput.coords;const correct=rubric.correct.coords;if(approximateDeepEqual(guess[0],correct[0])&&collinear(correct[0],correct[1],guess[1])){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="angle"&&rubric.correct.type==="angle"){const coords=userInput.coords;const correct=rubric.correct.coords;const allowReflexAngles=rubric.correct.allowReflexAngles;if(!coords){return {type:"invalid",message:null}}const areClockwise=clockwise([coords[0],coords[2],coords[1]]);const shouldReverseCoords=areClockwise&&!allowReflexAngles;const guess=shouldReverseCoords?coords.slice().reverse():coords;let match;if(rubric.correct.match==="congruent"){const angles=_.map([guess,correct],function(coords){if(!coords){return false}const angle=getClockwiseAngle(coords,allowReflexAngles);return angle});match=approximateEqual(...angles);}else {match=approximateDeepEqual(guess[1],correct[1])&&collinear(correct[1],correct[0],guess[0])&&collinear(correct[1],correct[2],guess[2]);}if(match){return {type:"points",earned:1,total:1,message:null}}}}if(!hasValue||_.isEqual(userInput,rubric.graph)){return {type:"invalid",message:null}}return {type:"points",earned:0,total:1,message:null}}
|
|
29
|
+
const{collinear,canonicalSineCoefficients,canonicalTangentCoefficients,similar,clockwise}=geometry;const{getClockwiseAngle}=angles;const{getAbsoluteValueCoefficients,getSinusoidCoefficients,getQuadraticCoefficients,getExponentialCoefficients,getTangentCoefficients}=coefficients;function scoreInteractiveGraph(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}if(userInput.type==="none"&&rubric.correct.type==="none"){return {type:"points",earned:0,total:0,message:null}}const hasValue=Boolean(userInput.coords||userInput.center&&userInput.radius);if(userInput.type===rubric.correct.type&&hasValue){if(userInput.type==="linear"&&rubric.correct.type==="linear"&&userInput.coords!=null){const guess=userInput.coords;const correct=rubric.correct.coords;if(collinear(correct[0],correct[1],guess[0])&&collinear(correct[0],correct[1],guess[1])){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="linear-system"&&rubric.correct.type==="linear-system"&&userInput.coords!=null){const guess=userInput.coords;const correct=rubric.correct.coords;if(collinear(correct[0][0],correct[0][1],guess[0][0])&&collinear(correct[0][0],correct[0][1],guess[0][1])&&collinear(correct[1][0],correct[1][1],guess[1][0])&&collinear(correct[1][0],correct[1][1],guess[1][1])||collinear(correct[0][0],correct[0][1],guess[1][0])&&collinear(correct[0][0],correct[0][1],guess[1][1])&&collinear(correct[1][0],correct[1][1],guess[0][0])&&collinear(correct[1][0],correct[1][1],guess[0][1])){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="quadratic"&&rubric.correct.type==="quadratic"&&userInput.coords!=null){const guessCoeffs=getQuadraticCoefficients(userInput.coords);const correctCoeffs=getQuadraticCoefficients(rubric.correct.coords);if(approximateDeepEqual(guessCoeffs,correctCoeffs)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="sinusoid"&&rubric.correct.type==="sinusoid"&&userInput.coords!=null){const guessCoeffs=getSinusoidCoefficients(userInput.coords);const correctCoeffs=getSinusoidCoefficients(rubric.correct.coords);const canonicalGuessCoeffs=canonicalSineCoefficients(guessCoeffs);const canonicalCorrectCoeffs=canonicalSineCoefficients(correctCoeffs);if(approximateDeepEqual(canonicalGuessCoeffs,canonicalCorrectCoeffs)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="exponential"&&rubric.correct.type==="exponential"&&userInput.coords!=null&&userInput.asymptote!=null){const guessCoeffs=getExponentialCoefficients(userInput.coords,userInput.asymptote);const correctCoeffs=getExponentialCoefficients(rubric.correct.coords,rubric.correct.asymptote);if(guessCoeffs!=null&&correctCoeffs!=null&&approximateDeepEqual([guessCoeffs.a,guessCoeffs.b,guessCoeffs.c],[correctCoeffs.a,correctCoeffs.b,correctCoeffs.c])){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="absolute-value"&&rubric.correct.type==="absolute-value"&&userInput.coords!=null){const userCoeffs=getAbsoluteValueCoefficients(userInput.coords);const rubricCoeffs=getAbsoluteValueCoefficients(rubric.correct.coords);if(userCoeffs!==undefined&&rubricCoeffs!==undefined&&approximateDeepEqual(userCoeffs,rubricCoeffs)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="tangent"&&rubric.correct.type==="tangent"&&userInput.coords!=null){const guessCoeffs=getTangentCoefficients(userInput.coords);const correctCoeffs=getTangentCoefficients(rubric.correct.coords);const canonicalGuessCoeffs=canonicalTangentCoefficients(guessCoeffs);const canonicalCorrectCoeffs=canonicalTangentCoefficients(correctCoeffs);if(approximateDeepEqual(canonicalGuessCoeffs,canonicalCorrectCoeffs)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="circle"&&rubric.correct.type==="circle"){if(approximateDeepEqual(userInput.center,rubric.correct.center)&&approximateEqual(userInput.radius,rubric.correct.radius)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="point"&&rubric.correct.type==="point"&&userInput.coords!=null){let correct=rubric.correct.coords;if(correct==null){throw new Error("Point graph rubric has null coords")}const guess=userInput.coords.slice();correct=correct.slice();guess?.sort();correct.sort();if(approximateDeepEqual(guess,correct)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="polygon"&&rubric.correct.type==="polygon"&&userInput.coords!=null){const guess=userInput.coords.slice();const correct=rubric.correct.coords.slice();let match;if(rubric.correct.match==="similar"){match=similar(guess,correct,Number.POSITIVE_INFINITY);}else if(rubric.correct.match==="congruent"){match=similar(guess,correct,number.DEFAULT_TOLERANCE);}else if(rubric.correct.match==="approx"){match=similar(guess,correct,.1);}else {guess.sort();correct.sort();match=approximateDeepEqual(guess,correct);}if(match){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="segment"&&rubric.correct.type==="segment"&&userInput.coords!=null){let guess=deepClone(userInput.coords);let correct=deepClone(rubric.correct.coords);guess=_.invoke(guess,"sort").sort();correct=_.invoke(correct,"sort").sort();if(approximateDeepEqual(guess,correct)){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="ray"&&rubric.correct.type==="ray"&&userInput.coords!=null){const guess=userInput.coords;const correct=rubric.correct.coords;if(approximateDeepEqual(guess[0],correct[0])&&collinear(correct[0],correct[1],guess[1])){return {type:"points",earned:1,total:1,message:null}}}else if(userInput.type==="angle"&&rubric.correct.type==="angle"){const coords=userInput.coords;const correct=rubric.correct.coords;const allowReflexAngles=rubric.correct.allowReflexAngles;if(!coords){return {type:"invalid",message:null}}const areClockwise=clockwise([coords[0],coords[2],coords[1]]);const shouldReverseCoords=areClockwise&&!allowReflexAngles;const guess=shouldReverseCoords?coords.slice().reverse():coords;let match;if(rubric.correct.match==="congruent"){const angles=_.map([guess,correct],function(coords){if(!coords){return false}const angle=getClockwiseAngle(coords,allowReflexAngles);return angle});match=approximateEqual(...angles);}else {match=approximateDeepEqual(guess[1],correct[1])&&collinear(correct[1],correct[0],guess[0])&&collinear(correct[1],correct[2],guess[2]);}if(match){return {type:"points",earned:1,total:1,message:null}}}}if(!hasValue||_.isEqual(userInput,rubric.graph)){return {type:"invalid",message:null}}return {type:"points",earned:0,total:1,message:null}}
|
|
30
30
|
|
|
31
31
|
function scoreLabelImageMarker(userInput,rubric){const score={hasAnswers:false,isCorrect:false};if(userInput&&userInput.length>0){score.hasAnswers=true;}if(rubric.length>0){if(userInput&&userInput.length===rubric.length){score.isCorrect=userInput.every(choice=>rubric.includes(choice));}}else if(!userInput||userInput.length===0){score.isCorrect=true;}return score}function scoreLabelImage(userInput,rubric){if(userInput==null){return {type:"invalid",message:null}}let numCorrect=0;for(let i=0;i<userInput.markers.length;i++){const score=scoreLabelImageMarker(userInput.markers[i].selected,rubric.markers[i].answers);if(score.isCorrect){numCorrect++;}}return {type:"points",earned:numCorrect===userInput.markers.length?1:0,total:1,message:null}}
|
|
32
32
|
|