File indexing completed on 2024-05-05 15:53:11
0001 /* GCompris - learn_decimals.js 0002 * 0003 * SPDX-FileCopyrightText: 2021 Mariam Fahmy <mariamfahmy66@gmail.com> 0004 * 0005 * Authors: 0006 * Mariam Fahmy <mariamfahmy66@gmail.com> 0007 * Timothée Giet <animtim@gmail.com> 0008 * 0009 * SPDX-License-Identifier: GPL-3.0-or-later 0010 * 0011 */ 0012 .pragma library 0013 .import QtQuick 2.12 as Quick 0014 .import GCompris 1.0 as GCompris 0015 .import "qrc:/gcompris/src/core/core.js" as Core 0016 0017 var numberOfLevel; 0018 var items; 0019 var dataset; 0020 var generatedNumber; 0021 var minimumValue; 0022 var maximumValue; 0023 var firstNumber; 0024 var secondNumber = 0; 0025 var squaresNumber = 10; 0026 var correctAnswer; 0027 var lastBarSquareUnits; 0028 var firstNumberList; 0029 var numberOfPossibleQuestions; 0030 0031 var tutorialInstructions = [ 0032 { 0033 "instruction": qsTr("A decimal number is displayed. The bar with the arrow represents a full unit, and each square in it represents one tenth of this unit."), 0034 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial1.qml" 0035 }, 0036 { 0037 "instruction": qsTr("Drag the arrow to select a part of the bar, and drag the selected part of the bar to the empty area. Repeat these steps until the number of dropped bars corresponds to the displayed decimal number. Then click on the OK button to validate your answer."), 0038 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial2.qml" 0039 } 0040 ]; 0041 0042 var subtractionInstructions = [ 0043 { 0044 "instruction": qsTr("A subtraction with two decimal numbers is displayed. Below it, the first number from the subtraction is represented with bars. One bar represents a full unit, and each square in it represents one tenth of this unit."), 0045 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial3.qml" 0046 }, 0047 { 0048 "instruction": qsTr("Click on the squares to subtract them and display the result of the operation, and click on the OK button to validate your answer."), 0049 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial4.qml" 0050 }, 0051 { 0052 "instruction": qsTr("If the answer is correct, type the corresponding result, and click on the OK button to validate your answer."), 0053 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial5.qml" 0054 } 0055 ]; 0056 0057 var additionInstructions = [ 0058 { 0059 "instruction": qsTr("An addition with two decimal numbers is displayed. The bar with the arrow represents a full unit, and each square in it represents one tenth of this unit."), 0060 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial6.qml" 0061 }, 0062 { 0063 "instruction": qsTr("Drag the arrow to select a part of the bar, and drag the selected part of the bar to the empty area. Repeat these steps until the number of dropped bars corresponds to the result of the addition, and click on the OK button to validate your answer."), 0064 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial7.qml" 0065 }, 0066 { 0067 "instruction": qsTr("If the answer is correct, type the corresponding result, and click on the OK button to validate your answer."), 0068 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial8.qml" 0069 } 0070 ]; 0071 0072 var quantityInstructions = [ 0073 { 0074 "instruction": qsTr("A quantity is requested. The arrow allows to select up to 10 oranges."), 0075 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial1.qml" 0076 }, 0077 { 0078 "instruction": qsTr("Drag the arrow to select a number of oranges, and drag the selected oranges to the empty area. Repeat these steps until the number of oranges corresponds to the requested quantity. Then click on the OK button to validate your answer."), 0079 "instructionQml": "qrc:/gcompris/src/activities/learn_decimals/resource/tutorial2.qml" 0080 } 0081 ]; 0082 0083 function start(items_) { 0084 items = items_; 0085 dataset = items.levels; 0086 numberOfLevel = dataset.length; 0087 items.currentLevel = Core.getInitialLevel(numberOfLevel); 0088 items.score.currentSubLevel = 0; 0089 firstNumberList = []; 0090 0091 if(!items.tutorialImage.visible) { 0092 initLevel(); 0093 } 0094 } 0095 0096 function stop() { 0097 } 0098 0099 function initLevel() { 0100 items.errorRectangle.resetState(); 0101 var data = dataset[items.currentLevel]; 0102 items.score.numberOfSubLevels = data.numberOfSubLevels; 0103 items.draggedItems.clear(); 0104 items.droppedItems.clear(); 0105 items.largestNumberRepresentation.clear(); 0106 items.typeResult = false; 0107 minimumValue = dataset[items.currentLevel].minValue; 0108 maximumValue = dataset[items.currentLevel].maxValue; 0109 0110 checkAvailableQuestions(); 0111 0112 displayDecimalNumberQuestion() 0113 0114 if(!items.isSubtractionMode) { 0115 //resetting the selected bar to 0.1 (the least draggable part) 0116 items.draggedItems.append({"selectedSquareNumbers" : 1 }); 0117 0118 if(items.background.horizontalLayout) { 0119 items.scrollBar.arrowX = items.scrollBar.arrowOrigin; 0120 } 0121 else { 0122 items.scrollBar.arrowY = items.scrollBar.arrowOrigin; 0123 } 0124 items.scrollBar.currentStep = 0; 0125 } 0126 else { 0127 var largestNumber = firstNumber * squaresNumber; 0128 while(largestNumber > 0) { 0129 if(largestNumber > squaresNumber) { 0130 items.largestNumberRepresentation.append({"selectedSquareNumbers" : squaresNumber }); 0131 } 0132 else { 0133 items.largestNumberRepresentation.append({"selectedSquareNumbers" : largestNumber }); 0134 lastBarSquareUnits = largestNumber; 0135 } 0136 largestNumber -= squaresNumber; 0137 } 0138 } 0139 items.buttonsBlocked = false; 0140 } 0141 0142 function nextLevel() { 0143 items.score.stopWinAnimation(); 0144 items.currentLevel = Core.getNextLevel(items.currentLevel, numberOfLevel); 0145 items.score.currentSubLevel = 0; 0146 initLevel(); 0147 } 0148 0149 function previousLevel() { 0150 items.score.stopWinAnimation(); 0151 items.currentLevel = Core.getPreviousLevel(items.currentLevel, numberOfLevel); 0152 items.score.currentSubLevel = 0; 0153 initLevel(); 0154 } 0155 0156 function nextSubLevel() { 0157 if(items.score.currentSubLevel >= items.score.numberOfSubLevels) { 0158 items.bonus.good('flower'); 0159 } else { 0160 items.droppedItems.clear(); 0161 0162 // In case number of sublevels are greater than the number of possibilities of the current level. 0163 checkAvailableQuestions(); 0164 0165 displayDecimalNumberQuestion(); 0166 items.buttonsBlocked = false; 0167 } 0168 } 0169 0170 function checkAvailableQuestions() { 0171 // In case all possible values from the provided range in the dataset are all displayed. 0172 var isAllDisplayed = true; 0173 numberOfPossibleQuestions = 0; 0174 0175 for(var i = minimumValue; i <= maximumValue; i += items.unit) { 0176 numberOfPossibleQuestions += 1; 0177 // i += items.unit can sometimes return weird numbers like 0.30000000000000004 or 0.7999999999999999 0178 // so rounding it is needed for a safe check 0179 var j = Math.round(i * 10) / 10; 0180 if(firstNumberList.indexOf(j) == -1) { 0181 isAllDisplayed = false; 0182 } 0183 } 0184 0185 if(isAllDisplayed) { 0186 firstNumberList = []; 0187 } 0188 } 0189 0190 function organizeDroppedBars() { 0191 var totalSquareUnits = 0; 0192 for(var i = 0; i < items.droppedItems.count; i++) { 0193 totalSquareUnits += items.droppedItems.get(i).selectedSquareNumbers; 0194 } 0195 0196 items.droppedItems.clear(); 0197 0198 while(totalSquareUnits > 0) { 0199 if(totalSquareUnits >= squaresNumber) { 0200 items.droppedItems.append({"selectedSquareNumbers" : squaresNumber }); 0201 } 0202 else { 0203 items.droppedItems.append({"selectedSquareNumbers" : totalSquareUnits }); 0204 } 0205 totalSquareUnits -= squaresNumber; 0206 } 0207 } 0208 0209 function generateFirstNumber() { 0210 if(items.isAdditionMode) { 0211 maximumValue -= minimumValue; 0212 } 0213 generatedNumber = generateDecimalNumbers(minimumValue, maximumValue); 0214 var loopCheck = 0; 0215 // if the number has already been asked, try to get a new one 0216 while(firstNumberList.indexOf(generatedNumber) !== -1) { 0217 generatedNumber = generateDecimalNumbers(minimumValue, maximumValue); 0218 //safety check to avoid stuck loop in case of js bugs 0219 loopCheck += 1; 0220 if(loopCheck > numberOfPossibleQuestions) 0221 firstNumberList = [] 0222 } 0223 0224 return generatedNumber; 0225 } 0226 0227 function generateSecondNumber() { 0228 if(items.isAdditionMode) { 0229 maximumValue = dataset[items.currentLevel].maxValue; 0230 maximumValue -= generatedNumber; 0231 } 0232 0233 do { 0234 generatedNumber = generateDecimalNumbers(minimumValue, maximumValue); 0235 } 0236 while(generatedNumber === firstNumber && items.isSubtractionMode); 0237 0238 return generatedNumber; 0239 } 0240 0241 function displayDecimalNumberQuestion() { 0242 firstNumber = generateFirstNumber(); 0243 0244 if(items.isAdditionMode || items.isSubtractionMode) 0245 secondNumber = generateSecondNumber(); 0246 0247 // The first number must be greater than the second number to avoid having negative results. 0248 if(items.isSubtractionMode) { 0249 if(firstNumber < secondNumber) { 0250 var temp = firstNumber; 0251 firstNumber = secondNumber; 0252 secondNumber = temp; 0253 } 0254 } 0255 0256 // Storing the first decimal number in a list to avoid displaying the same number again for the rest of the levels. 0257 firstNumberList.push(firstNumber); 0258 0259 if(items.isQuantityMode) 0260 items.largestNumber = firstNumber.toString(); 0261 else { 0262 items.largestNumber = toDecimalLocaleNumber(firstNumber); 0263 items.smallestNumber = toDecimalLocaleNumber(secondNumber); 0264 } 0265 } 0266 0267 function verifyNumberRepresentation() { 0268 items.buttonsBlocked = true; 0269 var i; 0270 var sum = 0; 0271 0272 //calculating the correct answer. 0273 calculateCorrectAnswer(); 0274 0275 //calculating the displayed units in the answerZone. 0276 if(!items.isSubtractionMode) { 0277 for(i = 0; i < items.droppedItems.count; i++) { 0278 sum += items.droppedItems.get(i).selectedSquareNumbers; 0279 } 0280 } 0281 else { 0282 for(i = 0; i < items.largestNumberRepresentation.count; i++) { 0283 sum += items.largestNumberRepresentation.get(i).selectedSquareNumbers; 0284 } 0285 } 0286 0287 if(!items.isQuantityMode) 0288 sum /= squaresNumber; 0289 0290 if(sum === correctAnswer) { 0291 if(items.isSubtractionMode || items.isAdditionMode) { 0292 items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/completetask.wav"); 0293 items.numpad.resetText(); 0294 items.typeResult = true; 0295 items.buttonsBlocked = false; 0296 } 0297 else { 0298 items.score.currentSubLevel += 1; 0299 items.score.playWinAnimation(); 0300 items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/completetask.wav"); 0301 } 0302 } 0303 else { 0304 items.errorRectangle.startAnimation(); 0305 items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav"); 0306 } 0307 } 0308 0309 function calculateCorrectAnswer() { 0310 if(items.isSubtractionMode) { 0311 correctAnswer = (firstNumber * squaresNumber - secondNumber * squaresNumber) / squaresNumber; 0312 } 0313 else if(items.isAdditionMode) { 0314 correctAnswer = (firstNumber * squaresNumber + secondNumber * squaresNumber) / squaresNumber; 0315 } 0316 else { 0317 correctAnswer = firstNumber; 0318 } 0319 } 0320 0321 function verifyNumberTyping(typedAnswer) { 0322 items.buttonsBlocked = true; 0323 typedAnswer = typedAnswer.replace("," , "."); 0324 if(parseFloat(typedAnswer) === parseFloat(correctAnswer)) { 0325 items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/completetask.wav"); 0326 items.bonus.good('flower') 0327 } 0328 else { 0329 items.errorRectangle.startAnimation(); 0330 items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav"); 0331 } 0332 } 0333 0334 function changeSingleBarVisibility(currentSquareNumber) { 0335 items.draggedItems.setProperty(0, "selectedSquareNumbers", currentSquareNumber); 0336 } 0337 0338 function changeMultiBarVisibility(barIndex, currentSquareNumber) { 0339 items.largestNumberRepresentation.setProperty(barIndex, "selectedSquareNumbers", currentSquareNumber); 0340 } 0341 0342 function generateDecimalNumbers(minValue, maxValue) { 0343 var generatedNumber = Math.random() * (maxValue - minValue) + minValue; 0344 if(items.isQuantityMode) 0345 return Math.round(generatedNumber); 0346 else 0347 return Math.round((generatedNumber + Number.EPSILON) * squaresNumber) / squaresNumber; 0348 } 0349 0350 function toDecimalLocaleNumber(decimalNumber) { 0351 var locale = GCompris.ApplicationSettings.locale; 0352 if(locale === "system") { 0353 locale = Qt.locale().name === "C" ? "en_US" : Qt.locale().name; 0354 } 0355 var decimalLocale = Core.convertNumberToLocaleString(decimalNumber, locale, 'f', 1); 0356 return decimalLocale; 0357 }