Skip to content

Instantly share code, notes, and snippets.

@r-kohale9
Last active June 18, 2024 07:13
Show Gist options
  • Save r-kohale9/db87c087a5f547fdc7d8461b30cb5619 to your computer and use it in GitHub Desktop.
Save r-kohale9/db87c087a5f547fdc7d8461b30cb5619 to your computer and use it in GitHub Desktop.

Logic

Stories

Dynamic block

  1. 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;
}
  1. 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;
}

Chunk/Block Logic

Next block

  1. 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;
}

Feedback pool

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 };
}

Table

Evaluation

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,
    };
}

Feedback

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

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;
}

Tap & select

Sound

Start

function updateStartAudio(
    is_correct,
    componentsList,
    global_context_variables,
) {
    return "question_audio_checking";
}

End

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";
    }
}

Groq

Sound

End

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
  
}

Variable Snippets

  1. 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";
}
  1. 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, '')));
}
  1. Component Index
if (pixiData.originalJson.componentIndex == 1) {
    // 
}
`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment