diff --git a/src/activities/programmingMaze/Dataset.qml b/src/activities/programmingMaze/Dataset.qml new file mode 100644 index 000000000..8cdf62b11 --- /dev/null +++ b/src/activities/programmingMaze/Dataset.qml @@ -0,0 +1,248 @@ +/* GCompris - Dataset.qml + * + * Copyright (C) 2018 Aman Kumar Gupta + * + * Authors: + * Aman Kumar Gupta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +import QtQuick 2.6 + +QtObject { + property var levels: [ + // Level one + { + "map": [ + {'x': 1, 'y': 2}, + {'x': 2, 'y': 2}, + {'x': 3, 'y': 2} + ], + "fish": {'x': 3, 'y': 2}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right" + ], + "maxNumberOfInstructions": 4 + }, + // Level two + { + "map": [ + {'x': 1, 'y': 3}, + {'x': 2, 'y': 3}, + {'x': 2, 'y': 2}, + {'x': 2, 'y': 1}, + {'x': 3, 'y': 1} + ], + "fish": {'x': 3, 'y': 1}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right" + ], + "maxNumberOfInstructions": 8 + }, + // Level three + { + "map": [ + {'x': 0, 'y': 3}, + {'x': 0, 'y': 2}, + {'x': 0, 'y': 1}, + {'x': 1, 'y': 1}, + {'x': 2, 'y': 1}, + {'x': 3, 'y': 1}, + {'x': 3, 'y': 2}, + {'x': 3, 'y': 3}, + {'x': 2, 'y': 3} + ], + "fish": {'x': 2, 'y': 3}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right" + ], + "maxNumberOfInstructions": 14 + }, + // Level four + { + "map": [ + {'x': 0, 'y': 1}, + {'x': 1, 'y': 1}, + {'x': 1, 'y': 0}, + {'x': 2, 'y': 0}, + {'x': 3, 'y': 0}, + {'x': 4, 'y': 0}, + {'x': 1, 'y': 2}, + {'x': 1, 'y': 3}, + {'x': 2, 'y': 3}, + {'x': 3, 'y': 3}, + {'x': 4, 'y': 3}, + {'x': 4, 'y': 2} + ], + "fish": {'x': 4, 'y': 2}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right" + ], + "maxNumberOfInstructions": 14 + }, + // Level five + { + "map": [ + {'x': 0, 'y': 1}, + {'x': 0, 'y': 0}, + {'x': 1, 'y': 0}, + {'x': 2, 'y': 0}, + {'x': 3, 'y': 0}, + {'x': 4, 'y': 0}, + {'x': 0, 'y': 2}, + {'x': 0, 'y': 3}, + {'x': 1, 'y': 3}, + {'x': 2, 'y': 3}, + {'x': 3, 'y': 3}, + {'x': 4, 'y': 3}, + {'x': 2, 'y': 1}, + {'x': 2, 'y': 2}, + {'x': 4, 'y': 2} + ], + "fish": {'x': 4, 'y': 2}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right" + ], + "maxNumberOfInstructions": 15 + }, + // Level six + { + "map": [ + {'x': 1, 'y': 1}, + {'x': 2, 'y': 1}, + {'x': 3, 'y': 1}, + {'x': 3, 'y': 2}, + {'x': 3, 'y': 3}, + {'x': 2, 'y': 3}, + {'x': 1, 'y': 3} + ], + "fish": {'x': 1, 'y': 3}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right", + "call-procedure" + ], + "maxNumberOfInstructions": 7 + }, + // Level seven + { + "map": [ + {'x': 0, 'y': 3}, + {'x': 1, 'y': 3}, + {'x': 2, 'y': 3}, + {'x': 2, 'y': 2}, + {'x': 2, 'y': 1}, + {'x': 3, 'y': 1}, + {'x': 4, 'y': 1}, + {'x': 4, 'y': 2}, + {'x': 4, 'y': 3} + ], + "fish": {'x': 4, 'y': 3}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right", + "call-procedure" + ], + "maxNumberOfInstructions": 10 + }, + // Level eight + { + "map": [ + {'x': 0, 'y': 3}, + {'x': 1, 'y': 3}, + {'x': 1, 'y': 2}, + {'x': 2, 'y': 2}, + {'x': 2, 'y': 1}, + {'x': 3, 'y': 1}, + {'x': 3, 'y': 0}, + {'x': 4, 'y': 0} + ], + "fish": {'x': 4, 'y': 0}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right", + "call-procedure" + ], + "maxNumberOfInstructions": 12 + }, + // Level nine + { + "map": [ + {'x': 1, 'y': 1}, + {'x': 0, 'y': 0}, + {'x': 1, 'y': 0}, + {'x': 2, 'y': 0}, + {'x': 2, 'y': 1}, + {'x': 3, 'y': 0}, + {'x': 4, 'y': 0}, + {'x': 4, 'y': 1}, + {'x': 4, 'y': 2}, + {'x': 4, 'y': 3}, + {'x': 3, 'y': 3}, + {'x': 2, 'y': 3}, + {'x': 1, 'y': 3}, + {'x': 0, 'y': 3}, + {'x': 0, 'y': 2} + ], + "fish": {'x': 0, 'y': 2}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right", + "call-procedure" + ], + "maxNumberOfInstructions": 14 + }, + // Level ten + { + "map": [ + {'x': 0, 'y': 3}, + {'x': 0, 'y': 2}, + {'x': 0, 'y': 1}, + {'x': 0, 'y': 0}, + {'x': 1, 'y': 0}, + {'x': 2, 'y': 0}, + {'x': 2, 'y': 1}, + {'x': 2, 'y': 2}, + {'x': 2, 'y': 3}, + {'x': 3, 'y': 3}, + {'x': 4, 'y': 3}, + {'x': 4, 'y': 2}, + {'x': 4, 'y': 1}, + {'x': 4, 'y': 0} + ], + "fish": {'x': 4, 'y': 0}, + "instructions": [ + "move-forward", + "turn-left", + "turn-right", + "call-procedure" + ], + "maxNumberOfInstructions": 15 + } + ] +} diff --git a/src/activities/programmingMaze/ProgrammingMaze.qml b/src/activities/programmingMaze/ProgrammingMaze.qml index 9b7e56596..0e80f3293 100644 --- a/src/activities/programmingMaze/ProgrammingMaze.qml +++ b/src/activities/programmingMaze/ProgrammingMaze.qml @@ -1,406 +1,410 @@ /* GCompris - ProgrammingMaze.qml * * Copyright (C) 2015 Siddhesh Suthar * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Siddhesh Suthar * Aman Kumar Gupta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import GCompris 1.0 import "../../core" import "programmingMaze.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} property int oldWidth: width onWidthChanged: { Activity.repositionObjectsOnWidthChanged(width / oldWidth) oldWidth = width } property int oldHeight: height onHeightChanged: { Activity.repositionObjectsOnHeightChanged(height / oldHeight) oldHeight = height } property bool keyboardNavigationVisible: false + property string mode: "basic" + property string datasetUrl: "qrc:/gcompris/src/activities/programmingMaze/Dataset.qml" pageComponent: Image { id: background source: "qrc:/gcompris/src/activities/programmingMaze/resource/background.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: parent.width signal start signal stop property bool insertIntoMain: true property alias items: items property int buttonWidth: background.width / 10 property int buttonHeight: background.height / 15.3 Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property GCAudio audioEffects: activity.audioEffects property alias mazeModel: mazeModel property alias instructionModel: instructionModel property alias mainFunctionModel: mainFunctionModel property alias mainFunctionCodeArea: mainFunctionCodeArea property alias procedureModel: procedureModel property alias procedureCodeArea: procedureCodeArea property alias instructionArea: instructionArea property alias player: player property alias constraintInstruction: constraintInstruction property alias tutorialImage: tutorialImage property alias fish: fish + property alias dataset: dataset property bool isRunCodeEnabled: true property bool isTuxMouseAreaEnabled: false property bool currentLevelContainsProcedure property int maxNumberOfInstructionsAllowed property int numberOfInstructionsAdded } // This function catches the signal emitted after completion of movement of Tux after executing each instruction. - function currentInstructionExecutionComplete() { + function checkSuccessAndExecuteNextInstruction() { Activity.checkSuccessAndExecuteNextInstruction() } // This function catches the signal emitted after finding a dead-end in any of the executing instruction. function deadEnd() { Activity.deadEnd() } - onStart: { - Activity.start(items) + Loader { + id: dataset } + + onStart: { Activity.start(items, mode, datasetUrl) } onStop: { Activity.stop() } property var areaWithKeyboardInput: instructionArea onAreaWithKeyboardInputChanged: activeCodeAreaIndicator.changeActiveCodeAreaIndicator(areaWithKeyboardInput) Keys.enabled: items.isTuxMouseAreaEnabled || items.isRunCodeEnabled Keys.onPressed: { activity.keyboardNavigationVisible = true if(event.key === Qt.Key_Left) areaWithKeyboardInput.moveCurrentIndexLeft() if(event.key === Qt.Key_Right) areaWithKeyboardInput.moveCurrentIndexRight() if(event.key === Qt.Key_Up) areaWithKeyboardInput.moveCurrentIndexUp() if(event.key === Qt.Key_Down) areaWithKeyboardInput.moveCurrentIndexDown() if(event.key === Qt.Key_Space) areaWithKeyboardInput.spaceKeyPressed() if(event.key === Qt.Key_Enter || event.key === Qt.Key_Return) runCodeOrResetTux() if(event.key === Qt.Key_Tab) areaWithKeyboardInput.tabKeyPressed() if(event.key === Qt.Key_Delete && activeCodeAreaIndicator.top != instructionArea.top) { areaWithKeyboardInput.deleteKeyPressed() } } function runCodeOrResetTux() { if(!Activity.deadEndPoint) runCodeMouseArea.executeCode() else Activity.initLevel() } ListModel { id: instructionModel } ListModel { id: mainFunctionModel } ListModel { id: procedureModel } Rectangle { id: constraintInstruction anchors.left: parent.left anchors.bottom: runCode.top width: parent.width / 2.3 height: parent.height / 8.9 radius: 10 z: 3 color: "#E8E8E8" //paper white border.width: 3 * ApplicationInfo.ratio border.color: "#87A6DD" //light blue Behavior on opacity { PropertyAnimation { duration: 200 } } function changeConstraintInstructionOpacity() { if(opacity) constraintInstruction.hide() else constraintInstruction.show() } function show() { if(instructionText.text) opacity = 0.8 } function hide() { opacity = 0 } GCText { id: instructionText anchors.fill: parent anchors.margins: parent.border.width horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter fontSizeMode: Text.Fit wrapMode: Text.WordWrap readonly property string resetTuxInstructionText: qsTr("Click on Tux or press Enter key to reset it or RELOAD button to reload the level.") readonly property string constraintInstructionText: qsTr("Reach the fish in less than %1 instructions.").arg(items.maxNumberOfInstructionsAllowed) text: items.isTuxMouseAreaEnabled ? resetTuxInstructionText : constraintInstructionText } } MouseArea { anchors.fill: parent onClicked: constraintInstruction.changeConstraintInstructionOpacity() } Repeater { id: mazeModel - model: Activity.mazeBlocks[0] anchors.left: parent.left anchors.top: parent.top Image { x: modelData.x * width y: modelData.y * height width: background.width / 10 height: (background.height - background.height / 10) / 10 source: Activity.reverseCountUrl + "iceblock.svg" } } Image { id: fish sourceSize.width: background.width / 12 source: Activity.reverseCountUrl + "blue-fish.svg" } Image { id: player source: "qrc:/gcompris/src/activities/maze/resource/tux_top_south.svg" sourceSize.width: background.width / 12 z: 1 property int duration: 1000 readonly property real playerCenterX: x + width / 2 readonly property real playerCenterY: y + height / 2 MouseArea { id: tuxMouseArea anchors.fill: parent enabled: items.isTuxMouseAreaEnabled onClicked: { Activity.initLevel() } } } Rectangle { id: activeCodeAreaIndicator opacity: 0.5 visible: activity.keyboardNavigationVisible function changeActiveCodeAreaIndicator(activeArea) { anchors.top = activeArea.top anchors.fill = activeArea } } InstructionArea { id: instructionArea } HeaderArea { id: mainFunctionHeader headerText: qsTr("Main") headerOpacity: background.insertIntoMain ? 1 : 0.5 onClicked: background.insertIntoMain = true anchors.top: parent.top anchors.right: parent.right } CodeArea { id: mainFunctionCodeArea currentModel: mainFunctionModel anchors.right: parent.right anchors.top: mainFunctionHeader.bottom onTabKeyPressed: { mainFunctionCodeArea.currentIndex = -1 if(!items.currentLevelContainsProcedure) { background.areaWithKeyboardInput = instructionArea instructionArea.currentIndex = 0 } else { background.areaWithKeyboardInput = procedureCodeArea background.insertIntoMain = false } } } HeaderArea { id: procedureHeader headerText: qsTr("Procedure") headerOpacity: !background.insertIntoMain ? 1 : 0.5 visible: procedureCodeArea.visible onClicked: background.insertIntoMain = false anchors.top: mainFunctionCodeArea.bottom anchors.right: parent.right } CodeArea { id: procedureCodeArea currentModel: procedureModel anchors.right: parent.right anchors.top: procedureHeader.bottom visible: items.currentLevelContainsProcedure property alias procedureIterator: procedureCodeArea.currentIndex onTabKeyPressed: { procedureCodeArea.currentIndex = -1 background.areaWithKeyboardInput = instructionArea instructionArea.currentIndex = 0 background.insertIntoMain = true } } Image { id: runCode width: background.width / 10 height: background.height / 10 anchors.right: instructionArea.right anchors.bottom: bar.top anchors.margins: 10 * ApplicationInfo.ratio source:"qrc:/gcompris/src/core/resource/bar_ok.svg" fillMode: Image.PreserveAspectFit MouseArea { id: runCodeMouseArea anchors.fill: parent hoverEnabled: ApplicationInfo.isMobile ? false : (!items.isRunCodeEnabled ? false : true) enabled: items.isRunCodeEnabled signal executeCode onEntered: runCode.scale = 1.1 onExecuteCode: { if(mainFunctionModel.count) startCodeExecution() } onClicked: executeCode() onExited: runCode.scale = 1 function startCodeExecution() { runCodeClickAnimation.start() Activity.resetCodeAreasIndices() if(constraintInstruction.opacity) constraintInstruction.hide() Activity.runCode() } } SequentialAnimation { id: runCodeClickAnimation NumberAnimation { target: runCode; property: "scale"; to: 0.8; duration: 100 } NumberAnimation { target: runCode; property: "scale"; to: 1.0; duration: 100 } } } Image { id: tutorialImage source: "qrc:/gcompris/src/activities/guesscount/resource/backgroundW01.svg" anchors.fill: parent z: 5 visible: true property bool shownProcedureTutorialInstructions: false Tutorial { id:tutorialSection tutorialDetails: bar.level <= 2 ? Activity.mainTutorialInstructions : Activity.procedureTutorialInstructions onSkipPressed: { Activity.initLevel() tutorialImage.visible = false tutorialNumber = 0 } } onVisibleChanged: { if(tutorialImage.visible && tutorialImage.shownProcedureTutorialInstructions) tutorialSection.visible = true } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: tutorialImage.visible ? help | home : help | home | level | reload } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: Activity.reloadLevel() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/programmingMaze/instructions/Procedure.qml b/src/activities/programmingMaze/instructions/Procedure.qml index 31d8d628d..db891c272 100644 --- a/src/activities/programmingMaze/instructions/Procedure.qml +++ b/src/activities/programmingMaze/instructions/Procedure.qml @@ -1,67 +1,67 @@ /* GCompris - Procedure.qml * * Copyright (C) 2018 Aman Kumar Gupta * * Author: * Aman Kumar Gupta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import GCompris 1.0 import "../programmingMaze.js" as Activity Instruction { id: callProcedure property alias procedureCode: procedureCode //Stores the list of instructions to be executed in procedure code area ListModel { id: procedureCode } function checkAndExecuteMovement() { if(!Activity.deadEndPoint && parent.items.procedureCodeArea.procedureIterator < callProcedure.procedureCode.count - 1) { parent.items.procedureCodeArea.procedureIterator++ var currentInstruction = procedureCode.get(parent.items.procedureCodeArea.procedureIterator).name - Activity.procedureCode[currentInstruction].checkAndExecuteMovement() + Activity.procedureInstructionObjects[currentInstruction].checkAndExecuteMovement() } else { parent.items.procedureCodeArea.procedureIterator = -1 executionComplete() } } function deadEnd() { foundDeadEnd() } function checkSuccessAndExecuteNextInstruction() { var fishX = Activity.mazeBlocks[Activity.currentLevel].fish.x var fishY = Activity.mazeBlocks[Activity.currentLevel].fish.y var tuxX = Math.floor(Activity.items.player.playerCenterX / Activity.stepX) var tuxY = Math.floor(Activity.items.player.playerCenterY / Activity.stepY) if(tuxX === fishX && tuxY === fishY) { Activity.codeIterator = 0 parent.items.bonus.good("tux") } else { checkAndExecuteMovement() } } } diff --git a/src/activities/programmingMaze/programmingMaze.js b/src/activities/programmingMaze/programmingMaze.js index fa9ca6789..7bc6a9cae 100644 --- a/src/activities/programmingMaze/programmingMaze.js +++ b/src/activities/programmingMaze/programmingMaze.js @@ -1,603 +1,358 @@ /* GCompris - programmingMaze.js * * Copyright (C) 2015 Siddhesh Suthar * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Siddhesh Suthar * Aman Kumar Gupta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ .pragma library .import QtQuick 2.6 as Quick .import GCompris 1.0 as GCompris //for ApplicationInfo // possible instructions var MOVE_FORWARD = "move-forward" var TURN_LEFT = "turn-left" var TURN_RIGHT = "turn-right" var CALL_PROCEDURE = "call-procedure" -var mazeBlocks = [ - //level one - { - "map": [ - {'x': 1, 'y': 2}, - {'x': 2, 'y': 2}, - {'x': 3, 'y': 2} - ], - "fish": {'x': 3, 'y': 2}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT - ], - "maxNumberOfInstructions": 4 - }, - //level two - { - "map": [ - {'x': 1, 'y': 3}, - {'x': 2, 'y': 3}, - {'x': 2, 'y': 2}, - {'x': 2, 'y': 1}, - {'x': 3, 'y': 1} - ], - "fish": {'x': 3, 'y': 1}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT - ], - "maxNumberOfInstructions": 8 - }, - //level three - { - "map": [ - {'x': 0, 'y': 3}, - {'x': 0, 'y': 2}, - {'x': 0, 'y': 1}, - {'x': 1, 'y': 1}, - {'x': 2, 'y': 1}, - {'x': 3, 'y': 1}, - {'x': 3, 'y': 2}, - {'x': 3, 'y': 3}, - {'x': 2, 'y': 3} - ], - "fish": {'x': 2, 'y': 3}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT - ], - "maxNumberOfInstructions": 14 - }, - //level four - { - "map": [ - {'x': 0, 'y': 1}, - {'x': 1, 'y': 1}, - {'x': 1, 'y': 0}, - {'x': 2, 'y': 0}, - {'x': 3, 'y': 0}, - {'x': 4, 'y': 0}, - {'x': 1, 'y': 2}, - {'x': 1, 'y': 3}, - {'x': 2, 'y': 3}, - {'x': 3, 'y': 3}, - {'x': 4, 'y': 3}, - {'x': 4, 'y': 2} - ], - "fish": {'x': 4, 'y': 2}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT - ], - "maxNumberOfInstructions": 14 - }, - //level five - { - "map": [ - {'x': 0, 'y': 1}, - {'x': 0, 'y': 0}, - {'x': 1, 'y': 0}, - {'x': 2, 'y': 0}, - {'x': 3, 'y': 0}, - {'x': 4, 'y': 0}, - {'x': 0, 'y': 2}, - {'x': 0, 'y': 3}, - {'x': 1, 'y': 3}, - {'x': 2, 'y': 3}, - {'x': 3, 'y': 3}, - {'x': 4, 'y': 3}, - {'x': 2, 'y': 1}, - {'x': 2, 'y': 2}, - {'x': 4, 'y': 2} - ], - "fish": {'x': 4, 'y': 2}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT - ], - "maxNumberOfInstructions": 15 - }, - //level six - { - "map": [ - {'x': 1, 'y': 1}, - {'x': 2, 'y': 1}, - {'x': 3, 'y': 1}, - {'x': 3, 'y': 2}, - {'x': 3, 'y': 3}, - {'x': 2, 'y': 3}, - {'x': 1, 'y': 3} - ], - "fish": {'x': 1, 'y': 3}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT, - CALL_PROCEDURE - ], - "maxNumberOfInstructions": 7 - }, - //level seven - { - "map": [ - {'x': 0, 'y': 3}, - {'x': 1, 'y': 3}, - {'x': 2, 'y': 3}, - {'x': 2, 'y': 2}, - {'x': 2, 'y': 1}, - {'x': 3, 'y': 1}, - {'x': 4, 'y': 1}, - {'x': 4, 'y': 2}, - {'x': 4, 'y': 3} - ], - "fish": {'x': 4, 'y': 3}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT, - CALL_PROCEDURE - ], - "maxNumberOfInstructions": 10 - }, - //level eight - { - "map": [ - {'x': 0, 'y': 3}, - {'x': 1, 'y': 3}, - {'x': 1, 'y': 2}, - {'x': 2, 'y': 2}, - {'x': 2, 'y': 1}, - {'x': 3, 'y': 1}, - {'x': 3, 'y': 0}, - {'x': 4, 'y': 0} - ], - "fish": {'x': 4, 'y': 0}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT, - CALL_PROCEDURE - ], - "maxNumberOfInstructions": 12 - }, - //level nine - { - "map": [ - {'x': 1, 'y': 1}, - {'x': 0, 'y': 0}, - {'x': 1, 'y': 0}, - {'x': 2, 'y': 0}, - {'x': 2, 'y': 1}, - {'x': 3, 'y': 0}, - {'x': 4, 'y': 0}, - {'x': 4, 'y': 1}, - {'x': 4, 'y': 2}, - {'x': 4, 'y': 3}, - {'x': 3, 'y': 3}, - {'x': 2, 'y': 3}, - {'x': 1, 'y': 3}, - {'x': 0, 'y': 3}, - {'x': 0, 'y': 2} - ], - "fish": {'x': 0, 'y': 2}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT, - CALL_PROCEDURE - ], - "maxNumberOfInstructions": 14 - }, - //level ten - { - "map": [ - {'x': 0, 'y': 3}, - {'x': 0, 'y': 2}, - {'x': 0, 'y': 1}, - {'x': 0, 'y': 0}, - {'x': 1, 'y': 0}, - {'x': 2, 'y': 0}, - {'x': 2, 'y': 1}, - {'x': 2, 'y': 2}, - {'x': 2, 'y': 3}, - {'x': 3, 'y': 3}, - {'x': 4, 'y': 3}, - {'x': 4, 'y': 2}, - {'x': 4, 'y': 1}, - {'x': 4, 'y': 0} - ], - "fish": {'x': 4, 'y': 0}, - "instructions": [ - MOVE_FORWARD, - TURN_LEFT, - TURN_RIGHT, - CALL_PROCEDURE - ], - "maxNumberOfInstructions": 15 - } - ] +var mazeBlocks // Length of 1 step along x-axis var stepX // Length of 1 step along y-axis var stepY -// List of commands to be executed -var mainFunctionCode = [] -var procedureCode = [] +/** + * Lookup tables of instruction objects for main and procedure areas which will be stored here on creation and can be + * accessed when required to execute. + */ +var mainInstructionObjects = [] +var procedureInstructionObjects = [] // New rotation of Tux on turning. var changedRotation // Indicates if there is a dead-end var deadEndPoint = false -// Stores the index of mainFunctionCode[] which is going to be processed +// Stores the index of mainInstructionObjects[] which is going to be processed var codeIterator = 0 /** * Stores if the reset is done only when Tux is clicked. * * If resetTux is true, initLevel() is called and the instruction areas are not cleared. * * Else, it means that initLevel() is called to reset the entire level and the instruction areas are cleared as well. */ var resetTux = false // Duration of movement of highlight in the execution area. var moveAnimDuration //Stores the currrent instruction which is going to be processed var currentInstruction var url = "qrc:/gcompris/src/activities/programmingMaze/resource/" var reverseCountUrl = "qrc:/gcompris/src/activities/reversecount/resource/" var currentLevel = 0 var numberOfLevel var items var NORTH = 0 var WEST = 90 var SOUTH = 180 var EAST = 270 /** * Stores the qml file components of all the instructions used in the activity. * - * To add a new instruction, add its component here, create its object in initLevel() along with the other instructions - * for procedure area, and call its destroy function in destroyInstructionObjects(). + * To add a new instruction, add its component here and add the instruction name in "instructionList" inside createInstructionObjects() along with the other instructions. */ var instructionComponents = { "move-forward": Qt.createComponent("qrc:/gcompris/src/activities/programmingMaze/instructions/MoveForward.qml"), "turn-left": Qt.createComponent("qrc:/gcompris/src/activities/programmingMaze/instructions/TurnLeftOrRight.qml"), "turn-right": Qt.createComponent("qrc:/gcompris/src/activities/programmingMaze/instructions/TurnLeftOrRight.qml"), "call-procedure": Qt.createComponent("qrc:/gcompris/src/activities/programmingMaze/instructions/Procedure.qml") } -/** - * The procedure function's object. This is a centralised procedure object and will be linked to all its instructions with signal and slot. - * - * Since the signals of instructions created in procedureCode need to be connected to the procedure file, - * hence only one object for procedure function is sufficient and this same - * procedure object will be re-used to push into the main code whenever the procedure call is made. - */ -var procedureObject - var mainTutorialInstructions = [ { "instruction": qsTr("Instruction Area:" + "There are 3 instructions which you have to use to code and make Tux reach the fish:" + "
  • 1. Move forward: Moves Tux one step forward in the direction it is facing.
  • " + "
  • 2. Turn left: Turns Tux in the left direction from where it is facing.
  • " + "
  • 3. Turn right: Turns Tux in the right direction from where it is facing.
  • "), "instructionImage": "qrc:/gcompris/src/activities/programmingMaze/resource/tutorial1.png" }, { "instruction": qsTr("Main Function:" + "
  • -The execution of code starts here on running.
  • " + "
  • -Click on any instruction in the instruction area to add them to the Main Function
  • " + "
  • -The instructions will execute in order until there's none left, dead-end or Tux reaches the fish.
  • "), "instructionImage": "qrc:/gcompris/src/activities/programmingMaze/resource/tutorial2.png" }, ] var procedureTutorialInstructions = [ { "instruction": qsTr("Procedure:" + "
  • -Procedure is a reusable set of instructions which can be used in a code by calling it where needed.
  • " + "
  • -To switch between the Procedure area and Main Function area to add your code, click on the label Procedure or Main Function.
  • "), "instructionImage": "qrc:/gcompris/src/activities/programmingMaze/resource/tutorial3.png" }, ] -function start(items_) { +// Mode of the activity: basic or loop +var activityMode + +function start(items_, mode_, datasetUrl_) { items = items_ + items.dataset.source = datasetUrl_ + activityMode = mode_ currentLevel = 0 + mazeBlocks = items.dataset.item.levels numberOfLevel = mazeBlocks.length resetTux = false initLevel() } function stop() { destroyInstructionObjects() } /** - * This function creates a single centralised and re-usable set of procedureObject and the procedure instructions. - * The procedure instructions are then connected to the procedureObject (object of the procedure file). + * This function creates and populate instruction objects for main as well as procedure area. + * + * These are stored in the lookup table, provided in the parameter as "instructionObjects". + * The instructions are then connected to the slots of their code area (main or procedure), provided as "instructionCodeArea" in the parameter. * - * These can be accessed wherever required to execute the procedure and saves the process of re-creating all the instruction objets, - * connecting them to the procedure's slot and destroying them everytime for each function call which will be very redundant - * and quite memory consuming on devices with less RAM, weak processing power and slow performance. + * The instructions can now be obtained from the look-up tables and executed when called. * - * Hence these centralised objects will be created and destroyed only once in each level (depending on the need) and can be accessed when needed. + * This saves the process of re-creating all the instruction objets, connecting them to their parent's slot and destroying + * them everytime for each instruction call which will be very redundant and quite memory consuming on devices with + * less RAM, weak processing power and slow performance specially for "loops" mode. + * + * Hence these look-up table objects will be created and destroyed only once in each level (depending on the need) and can be accessed when needed. */ -function createProcedureObjects() { - procedureObject = instructionComponents[CALL_PROCEDURE].createObject(items.background) - procedureCode[MOVE_FORWARD] = instructionComponents[MOVE_FORWARD].createObject(procedureObject) - procedureCode[TURN_LEFT] = instructionComponents[TURN_LEFT].createObject(procedureObject, { "turnDirection": "turn-left" }) - procedureCode[TURN_RIGHT] = instructionComponents[TURN_RIGHT].createObject(procedureObject, { "turnDirection": "turn-right" }) - - procedureObject.foundDeadEnd.connect(items.background.deadEnd) - procedureObject.executionComplete.connect(items.background.currentInstructionExecutionComplete) +function createInstructionObjects(instructionObjects, instructionCodeArea) { + var instructionList = [MOVE_FORWARD, TURN_LEFT, TURN_RIGHT] + for(var i = 0; i < instructionList.length; i++) + createInstruction(instructionObjects, instructionList[i], instructionCodeArea) +} - var procedureInstructions = Object.keys(procedureCode) +function createInstruction(instructionObjects, instructionName, instructionCodeArea) { + if(instructionName == TURN_LEFT || instructionName == TURN_RIGHT) + instructionObjects[instructionName] = instructionComponents[instructionName].createObject(instructionCodeArea, { "turnDirection": instructionName }) + else + instructionObjects[instructionName] = instructionComponents[instructionName].createObject(instructionCodeArea) - for(var i = 0; i < procedureInstructions.length; i++) { - procedureCode[procedureInstructions[i]].foundDeadEnd.connect(procedureObject.deadEnd) - procedureCode[procedureInstructions[i]].executionComplete.connect(procedureObject.checkSuccessAndExecuteNextInstruction) - } + instructionObjects[instructionName].foundDeadEnd.connect(instructionCodeArea.deadEnd) + instructionObjects[instructionName].executionComplete.connect(instructionCodeArea.checkSuccessAndExecuteNextInstruction) } +// Destroy instruction objects from the look-up tables function destroyInstructionObjects() { - for(var i = 0; i < mainFunctionCode.length; i++) { - mainFunctionCode[i].destroy() - } - if(procedureCode[MOVE_FORWARD] && procedureCode[TURN_LEFT] && procedureCode[TURN_RIGHT]) { - procedureCode[MOVE_FORWARD].destroy() - procedureCode[TURN_LEFT].destroy() - procedureCode[TURN_RIGHT].destroy() - procedureObject.destroy() - } + var instructionList = Object.keys(mainInstructionObjects) + for(var i = 0; i < instructionList.length; i++) + mainInstructionObjects[instructionList[i]].destroy() + + instructionList = Object.keys(procedureInstructionObjects) + for(var i = 0; i < instructionList.length; i++) + procedureInstructionObjects[instructionList[i]].destroy() - mainFunctionCode = [] - procedureCode = [] + mainInstructionObjects = [] + procedureInstructionObjects = [] } function initLevel() { if(!items || !items.bar) - return; + return items.bar.level = currentLevel + 1 destroyInstructionObjects() var levelInstructions = mazeBlocks[currentLevel].instructions if(levelInstructions.indexOf(CALL_PROCEDURE) != -1) items.currentLevelContainsProcedure = true else items.currentLevelContainsProcedure = false - //In the levels where there are procedure code area, create instructions for it and connect the instructions' signals to procedureObject's slots. + // Create, populate and connect signals of instructions for main function code area and store them in mainInstructionObjects. + createInstructionObjects(mainInstructionObjects, items.background) + if(items.currentLevelContainsProcedure) { if(!items.tutorialImage.shownProcedureTutorialInstructions) { items.tutorialImage.shownProcedureTutorialInstructions = true items.tutorialImage.visible = true } - createProcedureObjects() + + // Create procedure object in the main look-up table ,if the level has procedure/loop, to execute it for procedure/loop calls from the main code area. + createInstruction(mainInstructionObjects, CALL_PROCEDURE, items.background) + + // Create, populate and connect signals of instructions for procedure code area if the level has procedure/loop. + createInstructionObjects(procedureInstructionObjects, mainInstructionObjects[CALL_PROCEDURE]) } // Stores the co-ordinates of the tile blocks in the current level var currentLevelBlocksCoordinates = mazeBlocks[currentLevel].map items.mazeModel.model = currentLevelBlocksCoordinates if(!resetTux) { items.mainFunctionModel.clear() items.procedureModel.clear() items.numberOfInstructionsAdded = 0 } stepX = items.mazeModel.itemAt(0).width stepY = items.mazeModel.itemAt(0).height items.instructionModel.clear() - for (var i = 0; i < levelInstructions.length; i++) { - items.instructionModel.append({"name":levelInstructions[i]}); - } + for (var i = 0; i < levelInstructions.length; i++) + items.instructionModel.append({"name":levelInstructions[i]}) // Center Tux in its first case items.player.x = currentLevelBlocksCoordinates[0].x * stepX + (stepX - items.player.width) / 2 items.player.y = currentLevelBlocksCoordinates[0].y * stepY + (stepY - items.player.height) / 2 items.player.rotation = EAST // Center fish at it's co-ordinate items.fish.x = mazeBlocks[currentLevel].fish.x * stepX + (stepX - items.fish.width) / 2 items.fish.y = mazeBlocks[currentLevel].fish.y * stepY + (stepY - items.fish.height) / 2 changedRotation = EAST deadEndPoint = false moveAnimDuration = 1000 items.background.insertIntoMain = true items.mainFunctionCodeArea.highlightMoveDuration = moveAnimDuration / 2 items.procedureCodeArea.highlightMoveDuration = moveAnimDuration / 2 items.isTuxMouseAreaEnabled = false items.isRunCodeEnabled = true items.maxNumberOfInstructionsAllowed = mazeBlocks[currentLevel].maxNumberOfInstructions items.constraintInstruction.show() items.mainFunctionCodeArea.resetEditingValues() items.procedureCodeArea.resetEditingValues() items.background.areaWithKeyboardInput = items.instructionArea resetCodeAreasIndices() resetTux = false codeIterator = 0 } function resetCodeAreasIndices() { items.instructionArea.currentIndex = -1 items.mainFunctionCodeArea.currentIndex = -1 items.procedureCodeArea.currentIndex = -1 items.instructionArea.instructionToInsert = '' } function getPlayerRotation() { return ((changedRotation % 360) + 360) % 360 } -// Store all the instructions from main and procedure areas in their respective instruction lists. function runCode() { items.mainFunctionCodeArea.resetEditingValues() items.procedureCodeArea.resetEditingValues() - mainFunctionCode = [] - var instructionName - var instructionObject - - /** - * Create and append objects of all the instructions in the main area code. - * - * If the instruction is call-procedure, append the procedureObject, and it will in turn execute the centralised - * instructions created in procedureCode which are linked to this object. - */ - for(var i = 0; i < items.mainFunctionModel.count; i++) { - instructionName = items.mainFunctionModel.get([i]).name - if(instructionName != CALL_PROCEDURE) { - if(instructionName === TURN_LEFT) - instructionObject = instructionComponents[instructionName].createObject(items.background, { "turnDirection": "turn-left" }) - else if(instructionName === TURN_RIGHT) - instructionObject = instructionComponents[instructionName].createObject(items.background, { "turnDirection": "turn-right" }) - else - instructionObject = instructionComponents[instructionName].createObject(items.background) - - instructionObject.foundDeadEnd.connect(items.background.deadEnd) - instructionObject.executionComplete.connect(items.background.currentInstructionExecutionComplete) - - mainFunctionCode.push(instructionObject) - } - else { - mainFunctionCode.push(procedureObject) - } - } - // Append all the instructions from the procedure area to the procedureObject. + // Append all the procedure instructions to the procedure area object. for(var j = 0; j < items.procedureModel.count; j++) { - instructionName = items.procedureModel.get([j]).name - procedureObject.procedureCode.append({ "name" : instructionName }) + instructionName = items.procedureModel.get(j).name + mainInstructionObjects[CALL_PROCEDURE].procedureCode.append({ "name" : instructionName }) } items.isRunCodeEnabled = false - if(mainFunctionCode.length > 0) + if(items.mainFunctionModel.count > 0) executeNextInstruction() else deadEnd() } function executeNextInstruction() { - if(codeIterator < mainFunctionCode.length && !deadEndPoint) { + if((codeIterator < items.mainFunctionModel.count) && !deadEndPoint) { items.mainFunctionCodeArea.currentIndex += 1 - mainFunctionCode[codeIterator].checkAndExecuteMovement() + var instructionToExecute = items.mainFunctionModel.get(codeIterator).name + mainInstructionObjects[instructionToExecute].checkAndExecuteMovement() } } function deadEnd() { deadEndPoint = true resetTux = true items.isTuxMouseAreaEnabled = true items.constraintInstruction.show() items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/brick.wav") items.bonus.bad("tux") } function checkSuccessAndExecuteNextInstruction() { var fishX = mazeBlocks[currentLevel].fish.x var fishY = mazeBlocks[currentLevel].fish.y var tuxX = Math.floor(items.player.playerCenterX / stepX) var tuxY = Math.floor(items.player.playerCenterY / stepY) if(tuxX === fishX && tuxY === fishY) { codeIterator = 0 items.bonus.good("tux") } - else if(codeIterator === mainFunctionCode.length - 1) { + else if(codeIterator === (items.mainFunctionModel.count - 1)) { deadEnd() } else { codeIterator++ executeNextInstruction() } } function nextLevel() { resetTux = false if(numberOfLevel <= ++currentLevel) { currentLevel = 0 } initLevel(); } function previousLevel() { resetTux = false if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); } function repositionObjectsOnWidthChanged(factor) { resetTux = true if(items) initLevel() } function repositionObjectsOnHeightChanged(factor) { resetTux = true if(items) initLevel() } function reloadLevel() { resetTux = false initLevel() }