- 2 lives
- show
solution
on last incorrect - show
correct
on correct attempt - show
incorrect_1st_attempt
on incorrect when life is 2
getDynamicBlock
function getDynamicBlock(lives, inputs, is_correct, global_context_variables) {
const s3 = "name:incorrect_1st_attempt"; // 1st incorrect
const s2 = "solution"; // last incorrect
const s1 = "name:correct"; // correct
let storyId = "";
if (is_correct) storyId = s1;
if (lives == 2 && !is_correct) {
storyId = s3;
}
if (lives == 1 && !is_correct) {
storyId = s2;
}
if (storyId) {
return {
type: "manual_story",
storyId,
};
}
return null;
}
- 3 lives
- show
solution
on last incorrect - show
correct
on correct attempt - show
incorrect_1st_attempt
on incorrect when life is 3 - show
incorrect_2nd_attempt
on incorrect when life is 2 - show
incorrect_3rd_attempt
on incorrect when life is 1
getDynamicBlock
function getDynamicBlock(lives, inputs, is_correct, global_context_variables) {
const s5 = "name:incorrect_3rd_attempt"; // 1st incorrect
const s4 = "name:incorrect_2nd_attempt"; // 1st incorrect
const s3 = "name:incorrect_1st_attempt"; // 1st incorrect
const s2 = "solution"; // last incorrect
const s1 = "name:correct"; // correct
let storyId = "";
if (is_correct) storyId = s1;
if (lives == 5 && !is_correct) {
storyId = s3;
}
if (lives == 4 && !is_correct) {
storyId = s2;
}
if (lives == 3 && !is_correct) {
storyId = s1;
}
if (lives == 1 && !is_correct) {
storyId = s2;
}
if (storyId) {
return {
type: "manual_story",
storyId,
};
}
return null;
}
- Correctness based
- On correct goto ->
C1B2
- default ->
next
getNextBlock
function getNextBlock(history, next, global_context_variables) {
const curr = history[history.length - 1];
if (curr.submission.is_correct) return "C1B2";
return next;
}
Update Feedback Text
function updateFeedbackText(feedbackPool, global_context_variables) {
function replaceValuesInTemplate(template, valueMap) {
function replace(obj) {
if (Array.isArray(obj)) {
return obj.map((item) => replace(item));
} else if (typeof obj === "object" && obj !== null) {
const replacedObj = {};
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === "string") {
const replacedValue = replaceVariablesInString(
obj[key],
valueMap,
);
replacedObj[key] = replacedValue;
} else {
replacedObj[key] = replace(obj[key]);
}
});
return replacedObj;
} else if (typeof obj === "string") {
return replaceVariablesInString(obj, valueMap);
} else {
return obj;
}
}
function replaceVariablesInString(str, valueMap) {
const regex = /@@@(.*?)@@@/g;
const parsedText = str.replace(regex, (match, variableName) => {
const val =
valueMap[variableName] !== undefined
? valueMap[variableName]
: match;
return typeof val == "object" ? JSON.stringify(val) : val;
});
try {
const text = JSON.parse(parsedText);
return typeof text === "number" ? `${text}` : text;
} catch {
return parsedText;
}
}
return [...replace(template)];
}
return replaceValuesInTemplate(feedbackPool, global_context_variables);
}
Update Feedback Text ( with original/parsed )
function updateFeedbackPool(tableData, startFeedback, global_context_variables) {
function replaceValuesInTemplate(template, valueMap) {
function replace(obj) {
if (Array.isArray(obj)) {
return obj.map(item => replace(item));
} else if (typeof obj === 'object' && obj !== null) {
const replacedObj = {};
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'string') {
const replacedValue = replaceVariablesInString(obj[key], valueMap);
replacedObj[key] = replacedValue;
} else {
replacedObj[key] = replace(obj[key]);
}
});
return replacedObj;
} else if (typeof obj === 'string') {
return replaceVariablesInString(obj, valueMap);
} else {
return obj;
}
}
function replaceVariablesInString(str, valueMap) {
const regex = /@@@(.*?)@@@/g;
const parsedText = str.replace(regex, (match, variableName) => {
const val = valueMap[variableName] !== undefined ? valueMap[variableName] : match;
return typeof val == 'object' ? JSON.stringify(val) : val;
});
try {
const text = JSON.parse(parsedText);
return typeof text === 'number' ? `${text}` : text;
} catch {
return parsedText;
}
}
return replace(template);
}
let feedback = startFeedback;
const isObject = typeof startFeedback == 'object' && !Array.isArray(startFeedback) && startFeedback['original'];
if (isObject) {
feedback = startFeedback['original'] ?? [];
}
let output = replaceValuesInTemplate(feedback, global_context_variables);
return { original: feedback, parsed: output };
}
evaluate
function evaluate(params) {
const { tableData, helpers } = params;
let is_correct = true;
let cell_level_is_correct = [];
tableData.cells.forEach((row, rowIndex) => {
cell_level_is_correct.push([]);
row.forEach((cell, columnIndex) => {
const { cellInputValue, cellIsInput, cellIsLatexBox } =
helpers.getCellInputValue(cell);
console.log(
"evaluate---------",
rowIndex,
columnIndex,
cellIsLatexBox,
cell,
);
if (cellIsInput) {
const cellCorrectValueArr = helpers.getCellCorrectValues(cell);
if (cellIsLatexBox) {
console.log(
"cellIsLatexBox------",
cellCorrectValueArr,
cellInputValue,
);
let correctionState = [];
cellInputValue.forEach((inputValue, iterIndex) => {
// Using `find` to search for a matching element after converting both sides to strings
const isCorrect =
cellCorrectValueArr[iterIndex].find(
(item) => String(item) === String(inputValue),
) !== undefined;
correctionState.push(isCorrect);
});
cell_level_is_correct[rowIndex][columnIndex] =
correctionState;
} else {
if (
cellCorrectValueArr.find(
(corrVal) => corrVal === cellInputValue,
)
) {
cell_level_is_correct[rowIndex][columnIndex] = true;
} else {
cell_level_is_correct[rowIndex][columnIndex] = false;
is_correct = false;
}
}
}
});
});
return {
is_correct,
cell_level_is_correct,
};
}
Reorder evaluate
function checkReorder(params) {
const { tableData } = params;
const correctOrder = [0, 5, 2, 3, 1, 4]; // Predefined correct order of indices
let is_correct = true;
let cell_level_is_correct = [];
// Check if the length of the reorderValue matches the correctOrder to prevent out of bounds errors
if (tableData.reorderValue.length !== correctOrder.length) {
is_correct = false;
} else {
// Iterate and compare each element in the reorderValue array to the correctOrder array
for (let i = 0; i < correctOrder.length; i++) {
if (tableData.reorderValue[i] !== correctOrder[i]) {
is_correct = false;
break; // Exit the loop early if a mismatch is found
}
}
}
return {
is_correct,
cell_level_is_correct
};
}
Tappable evaluate
function evaluate(params) {
const { tableData, helpers } = params;
let is_correct = true;
let cell_level_is_correct = [];
tableData.cells.forEach((row, rowIndex) => {
cell_level_is_correct.push([]);
row.forEach((cell, columnIndex) => {
const { cellIsClick, cellClickValue } =
helpers.getCellInputValue(cell);
const cellCorrectValueArr = helpers.getCellCorrectValues(cell);
console.log(
"evaluate---------",
rowIndex,
columnIndex,
cell,
cellIsClick,
cellClickValue,
cellCorrectValueArr
);
if(cellIsClick){
if (
cellCorrectValueArr.find(
(corrVal) => corrVal === cellClickValue,
)
) {
cell_level_is_correct[rowIndex][columnIndex] = true;
} else {
cell_level_is_correct[rowIndex][columnIndex] = false;
is_correct = false;
}
}
});
});
return {
is_correct,
cell_level_is_correct,
};
}
feedbackFunction
function feedbackFunction(params) {
const { tableData, tableCorrectnessState, helpers, prefix, pixiData } = params;
const totalLives = pixiData.globalContext.parent_variables?.find(i => i.name.includes('total_lives'));
const totalLivesVal = totalLives.default ?? totalLives.value;
let soundArray;
const tableStartSound = `question_audio_checking`;
let tableEndSound = '';
if (tableCorrectnessState.is_correct) {
tableEndSound = `question_audio_correct`;
} else {
if (totalLivesVal == 2) tableEndSound = 'question_audio_incorrect_attempt1';
if (totalLivesVal == 1) tableEndSound = 'question_audio_incorrect_last_attempt';
}
let obj = {};
obj = {
tableStartText: tableStartSound,
textDuring: `${prefix}_table_during_${tableCorrectnessState.is_correct ? 'correct' : 'incorrect'}`,
tableEndText: tableEndSound
};
soundArray = {
soundDuring: `${prefix}_table_during_${tableCorrectnessState.is_correct ? 'correct' : 'incorrect'}`
};
return {
tableStartSound,
tableEndSound,
soundArray,
...obj
};
}
tappable
function tappable(params) {
const { variable, frameId } = params;
const result = [];
let currentSound = "";
const selectedSound =
"https://cdn.homeworkapp.ai/sets-gamify-assets/dev/worksheet/audio/1710667396330.mp3";
const notSelectedSound =
"https://cdn.homeworkapp.ai/sets-gamify-assets/dev/worksheet/audio/1710667360304.mp3";
for (let i = 0; i < variable.length; i++) {
console.log(variable[i].selected, variable[i].frame_id);
const value = variable[i].selected == 1 ? "#FCF6D7" : "#FFFFFF";
const item = {
type: "UPDATE_FILL",
props: {
...variable[i],
color: { value },
},
};
if (variable[i].frame_id == frameId) {
if (variable[i].selected) {
currentSound = selectedSound;
} else {
currentSound = notSelectedSound;
}
}
result.push(item);
}
const sound = {
type: "PLAY_SOUND",
props: {
url: currentSound,
},
};
result.push(sound);
return result;
}
Adv Tappable
function tappable(params) {
const { variable, frameId, defaults } = params;
const { tableData } = defaults;
const splitArray = frameId.split(':');
const arrayLength = splitArray.length;
const columnIndex = splitArray[arrayLength - 1];
const rowIndex = splitArray[arrayLength - 2];
const result = [];
let currentSound = '';
const selectedSound = 'https://cdn.homeworkapp.ai/sets-gamify-assets/dev/worksheet/audio/1711036651467.mp3';
const notSelectedSound = 'https://cdn.homeworkapp.ai/sets-gamify-assets/dev/worksheet/audio/1711036672317.mp3';
const cellVariable = variable.find(v => v.frame_id == frameId);
const cellValue = Number(tableData.cells[Number(rowIndex)][Number(columnIndex)].text.value.default);
const text = Number(tableData.cells[Number(rowIndex)][Number(columnIndex)].text.value.default) + 1;
const tappable = tableData.cells[Number(rowIndex)][Number(columnIndex)].tappable;
console.log(tappable,'this is tappable')
const currentTap = tappable.tap_count.default ?? 0;
function updateNextCell(colIndex){
if(colIndex != 0){
const nextColumnIndex = Number(colIndex) - 1;
const nextCellValue = Number(tableData.cells[Number(rowIndex)][nextColumnIndex].text.value.default);
const nextText = nextCellValue + 1;
const nextFrameId = splitArray.slice(0,arrayLength - 1).join(':') + ':' + nextColumnIndex;
const nextCellVariable = variable.find(v => v.frame_id == nextFrameId);
if(nextCellValue == 9){
const item = {
type: 'UPDATE_TEXT',
props: {
...nextCellVariable,
text: "0"
}
};
result.push(item);
updateNextCell(nextColumnIndex);
}else {
const item = {
type: 'UPDATE_TEXT',
props: {
...nextCellVariable,
text: nextText,
color: "#1B34FF"
}
};
result.push(item);
}
}
}
if(cellValue == 0 && tappable.tap_count.default != 0){
}else if (text >= 10) {
const item = {
type: 'UPDATE_TEXT',
props: {
...cellVariable,
text: "0"
}
};
updateNextCell(columnIndex);
result.push(item);
currentSound = selectedSound;
} else {
const item = {
type: 'UPDATE_TEXT',
props: {
...cellVariable,
text,
color: "#1B34FF"
}
};
result.push(item);
const count = {
type:"UPDATE_TAP_COUNT",
props:{
...cellVariable,
count: currentTap + 1
}
}
result.push(count);
currentSound = selectedSound;
}
const sound = {
type: 'PLAY_SOUND',
props: {
url: currentSound
}
};
result.push(sound);
return result;
}
Sound
function updateStartAudio(
is_correct,
componentsList,
global_context_variables,
) {
return "question_audio_checking";
}
function updateEndAudio(
is_correct,
componentsList,
pixiData,
global_context_variables,
) {
const tempList = Array.isArray(componentsList)
? componentsList
: JSON.parse(componentsList);
if (["true", true, 1, "1"].includes(is_correct)) {
return "question_audio_correct";
}
const totalLives = pixiData.globalContext.parent_variables?.find((i) =>
i.name.includes("total_lives"),
);
const totalLivesVal = totalLives.default ?? totalLives.value;
if (totalLivesVal == 2 && !is_correct) {
return "question_audio_incorrect_attempt1";
}
if (totalLivesVal == 1 && !is_correct) {
return "question_audio_incorrect_last_attempt";
}
}
Sound
function updateAttemptAudio(index, componentsList, groqResponse, pixiData) {
const totalLives = pixiData.globalContext.parent_variables?.find((i) =>
i.name.includes("total_lives"),
);
const totalLivesVal = totalLives.default ?? totalLives.value;
if (totalLivesVal == 1) {
switch (groqResponse?.toLowerCase()) {
case 'incorrect':
return 'question_audio_part2_incorrect_last_attempt';
break;
case 'incomplete':
return 'question_audio_part2_incomplete_last_attempt';
break;
case 'idk':
return 'question_audio_part2_idk_last_attempt';
break;
case 'gibberish':
return 'question_audio_part2_gibberish_last_attempt';
break;
default:
break;
}
}
switch (groqResponse?.toLowerCase()) {
case 'correct':
return 'question_audio_part2_correct';
break;
case "incorrect":
return "question_audio_part2_incorrect_attempt1"
break;
case "incomplete":
return "question_audio_part2_incomplete_attempt1"
break
case "idk":
return "question_audio_part2_idk_attempt1"
break
case "gibberish":
return "question_audio_part2_gibberish_attempt1"
break
default:
break;
}
return
}
Condition animation
{"currTopicEventEffectsMap":[{"event":"condition_sound_ended","computeFunction":{"output":"function conditionSoundEnded(variables) {\n return [\n {\n \"type\": \"PLAY_ANIMATION\",\n \"props\": {\n \"component_index\": 1,\n \"frame_id\": \"component1:2397:444:d0pnvs0y7lb48l:0-table\",\n \"type\": \"PROGRESS\",\n \"direction\": \"LTR\", // left to right\n \"color\": \"#fcf6d7\",\n \"duration\": 3000,\n\"component_type\": \"table\"\n }\n }\n ]\n}"}}]}
- Total lives
const totalLives = pixiData.globalContext.parent_variables?.find((i) =>
i.name.includes("total_lives"),
);
const totalLivesVal = totalLives.default ?? totalLives.value;
if (totalLivesVal == 1 && !is_correct) {
return "question_audio_incorrect_attempt2";
}
- Table
a. Drag drop value path via cell
const value =
cell.dragDrop?.value ??
cell.dragDrop?.droppedSource?.props?.drop_value?.value;
b. Helper functions
- To use following create a variable
tableData
add it in variable link
num_1
function nums(obj) {
const key = '1';
function getValue(name) {
try {
return JSON.parse(obj[name]);
} catch {
return obj[name];
}
}
function getStringToNumberMap(tableCells) {
const obj = {};
for (let row = 0; row < tableCells.length; row++) {
for (let col = 0; col < tableCells[row].length; col++) {
const cell = tableCells[row][col];
// Check if the cell contains an indicator string
if (typeof cell === 'string' && cell.includes('Number')) {
let numberKey = cell;
// Determine the direction of the indicator
if (cell.includes('ππ½')) {
// Collect all numbers directly below the indicator
let rowBelow = row + 1;
let numberString = '';
while (rowBelow < tableCells.length && tableCells[rowBelow][col] !== "") {
numberString += tableCells[rowBelow][col];
rowBelow++;
}
if (numberString) {
obj[numberKey] = numberString;
}
} else if (cell.includes('ππ½')) {
// Collect all numbers directly to the right of the indicator
let colRight = col + 1;
let numberString = '';
while (colRight < tableCells[row].length && tableCells[row][colRight] !== "") {
numberString += tableCells[row][colRight];
colRight++;
}
if (numberString) {
obj[numberKey] = numberString;
}
}
}
}
}
return obj;
}
const tableData = getValue('tableData')?.cells;
const nums = getStringToNumberMap((tableData ?? []).map(i => i.map(j => j.value ?? j.text?.value?.default ?? '')));
return nums[Object.keys(nums).find(i => i.includes(key))];
}
number1
function number_1(obj) {
const variable = 'num_1';
function getVal(key) {
try {
return JSON.parse(obj[key]);
} catch {
return obj[key];
}
}
function test(n) {
if (n < 0) return false;
const single_digit = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine'];
const double_digit = [
'Ten',
'Eleven',
'Twelve',
'Thirteen',
'Fourteen',
'Fifteen',
'Sixteen',
'Seventeen',
'Eighteen',
'Nineteen'
];
const below_hundred = ['Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety'];
if (n === 0) return 'Zero';
function translate(n) {
let word = '';
if (n < 10) {
word = single_digit[n] + ' ';
} else if (n < 20) {
word = double_digit[n - 10] + ' ';
} else if (n < 100) {
let rem = translate(n % 10);
word = below_hundred[(n - (n % 10)) / 10 - 2] + ' ' + rem;
} else if (n < 1000) {
word = single_digit[Math.trunc(n / 100)] + ' Hundred ' + translate(n % 100);
} else if (n < 1000000) {
word = translate(parseInt(n / 1000)).trim() + ' Thousand ' + translate(n % 1000);
} else if (n < 1000000000) {
word = translate(parseInt(n / 1000000)).trim() + ' Million ' + translate(n % 1000000);
} else {
word = translate(parseInt(n / 1000000000)).trim() + ' Billion ' + translate(n % 1000000000);
}
return word;
}
let result = translate(n);
return result.trim() + '.';
}
const num = (String(getVal(variable) ?? 0) || '0').replace(/\D/g, '') || 0;
return test(parseInt(String(num).replace(/\D/g, '')));
}
- Component Index
if (pixiData.originalJson.componentIndex == 1) {
//
}
`