File indexing completed on 2024-05-05 15:52:56

0001 /* GCompris - algebra.js
0002  *
0003  * SPDX-FileCopyrightText: 2014 Aruna Sankaranarayanan and Bruno Coudoin
0004  *
0005  *   SPDX-License-Identifier: GPL-3.0-or-later
0006  */
0007 
0008 .pragma library
0009 .import QtQuick 2.12 as Quick
0010 .import "qrc:/gcompris/src/core/core.js" as Core
0011 
0012 var numberOfLevel
0013 var items
0014 var dataset
0015 var operand
0016 var secondOperandVal
0017 var firstOperandVal
0018 var subLevelData = []
0019 var speedSetting
0020 var operandText
0021 var OperandsEnum = {
0022     TIMES_SIGN : "\u00D7",
0023     PLUS_SIGN : "\u002B",
0024     MINUS_SIGN : "\u2212",
0025     DIVIDE_SIGN : "\u2215"
0026 }
0027 
0028 
0029 function start(items_, operand_, speedSetting_) {
0030     operand = operand_
0031     items = items_
0032     speedSetting = speedSetting_
0033     dataset = items.levels
0034     numberOfLevel = dataset.length
0035     items.currentLevel = Core.getInitialLevel(numberOfLevel)
0036     initLevel()
0037 }
0038 
0039 function stop() {
0040     items.balloon.stopBalloon()
0041 }
0042 
0043 // get appropriate operands depending on operator
0044 function getOperands(min, max)
0045 {
0046     switch(operand.text)
0047     {
0048     case OperandsEnum.TIMES_SIGN:
0049         return [randomInRange(min,max), randomInRange(min,max)]
0050 
0051     case OperandsEnum.PLUS_SIGN:
0052         return [randomInRange(min,max), randomInRange(min,max)]
0053 
0054     case OperandsEnum.MINUS_SIGN:
0055         // Left operand is always greater than right operand
0056         var minusLeftOperand = randomInRange(min, max)
0057         var minusRightOperand = randomInRange(min, minusLeftOperand)
0058         return [minusLeftOperand, minusRightOperand]
0059 
0060     case OperandsEnum.DIVIDE_SIGN:
0061         // Left operand is a multiple of right operand
0062         var divideRightOperand = randomInRange(min, max)
0063         var divideLeftOperand = divideRightOperand * randomInRange(min, max)
0064         return [divideLeftOperand, divideRightOperand]
0065     }
0066 }
0067 
0068 function initLevel() {
0069     items.score.visible = false
0070     items.okButton.visible = false
0071     items.score.currentSubLevel = 0
0072     subLevelData = []
0073 
0074     /**
0075      It exists two types of levels:
0076       - the operation table where all operands are defined in the dataset
0077       - the generated question, where the dataset specify constraits
0078      Leveltype is determined by checking what is the inner structure of the dataset
0079     */
0080     if("operands" in dataset[items.currentLevel]) {
0081         items.score.numberOfSubLevels = dataset[items.currentLevel].operands.length;
0082         for(var i = 0; i < items.score.numberOfSubLevels; i++)
0083             subLevelData.push([dataset[items.currentLevel].operands[i].first, dataset[items.currentLevel].operands[i].second])
0084     }
0085     else {
0086         // Generate random operations
0087         var minOperandValue = dataset[items.currentLevel].min
0088         var maxOperandValue = dataset[items.currentLevel].max
0089         var limit = dataset[items.currentLevel].limit
0090         items.score.numberOfSubLevels = 10;
0091         for(var i = 0; i < items.score.numberOfSubLevels; i++)
0092         {
0093             var leftOperand = 0;
0094             var rightOperand = 0;
0095             do
0096             {
0097                 [leftOperand, rightOperand] = getOperands(minOperandValue, maxOperandValue);
0098             } while(limit !== 0 && getAnswer(leftOperand, rightOperand) > limit);
0099             subLevelData.push([leftOperand, rightOperand])
0100         }
0101     }
0102 
0103     subLevelData = Core.shuffle(subLevelData)
0104     calculateOperands()
0105     items.iAmReady.visible = true
0106     items.firstOp.visible = false
0107     items.secondOp.visible = false
0108     items.balloon.stopMoving()
0109 }
0110 
0111 function randomInRange(min, max) {
0112     return Math.floor(min + Math.random() * (max - min + 1))
0113 }
0114 
0115 function circularShiftElements() {
0116     if(!validateAnswer(parseInt(items.numpad.answer)))
0117         subLevelData.push(subLevelData.shift())
0118     else
0119         subLevelData.shift()
0120 }
0121 
0122 function nextLevel() {
0123     items.currentLevel = Core.getNextLevel(items.currentLevel, numberOfLevel);
0124     initLevel();
0125 }
0126 
0127 function previousLevel() {
0128     items.currentLevel = Core.getPreviousLevel(items.currentLevel, numberOfLevel);
0129     initLevel();
0130 }
0131 
0132 function calculateOperands()
0133 {
0134     firstOperandVal = subLevelData[0][0]
0135     secondOperandVal = subLevelData[0][1]
0136 
0137     items.firstOp.text = firstOperandVal
0138     items.secondOp.text = secondOperandVal
0139 }
0140 
0141 // Return the expected answer
0142 function getAnswer(val1, val2) {
0143     switch(operand.text)
0144     {
0145     case OperandsEnum.TIMES_SIGN:
0146         return (val1 * val2)
0147 
0148     case OperandsEnum.PLUS_SIGN:
0149         return (val1 + val2)
0150 
0151     case OperandsEnum.MINUS_SIGN:
0152         return (val1 - val2)
0153 
0154     case OperandsEnum.DIVIDE_SIGN:
0155         return (val1 / val2)
0156     }
0157 }
0158 
0159 function validateAnswer(screenAnswer)
0160 {
0161     return (getAnswer(firstOperandVal, secondOperandVal) === screenAnswer)
0162 }
0163 
0164 function run() {
0165     circularShiftElements()
0166     calculateOperands()
0167     items.numpad.resetText()
0168     items.score.visible = true
0169     items.iAmReady.visible = false
0170     items.firstOp.visible = true
0171     items.secondOp.visible = true
0172     items.okButton.visible = true
0173     items.numpad.answerFlag = false
0174     items.result = getAnswer(firstOperandVal, secondOperandVal)
0175     items.balloon.startMoving(100000 / speedSetting)
0176     items.buttonsBlocked = false
0177 }
0178 
0179 function checkAnswer() {
0180     items.buttonsBlocked = true
0181     items.balloon.startMoving(100000)
0182     if(validateAnswer(parseInt(items.numpad.answer))) {
0183         items.score.currentSubLevel++
0184         items.score.playWinAnimation()
0185         items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/completetask.wav")
0186     } else {
0187         items.errorRectangle.startAnimation()
0188         items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav")
0189     }
0190 }
0191 
0192 function questionsLeft() {
0193     if(items.score.currentSubLevel >= items.score.numberOfSubLevels) {
0194         items.bonus.good("smiley")
0195     } else {
0196         run()
0197     }
0198 }