diff --git a/src/activities/activities.txt b/src/activities/activities.txt index 50e830e0c..ecdb52be8 100644 --- a/src/activities/activities.txt +++ b/src/activities/activities.txt @@ -1,154 +1,155 @@ # The list of activities that will be loaded at GCompris start. # Keep it sorted advanced_colors algebra_by algebra_div algebra_minus algebra_plus algorithm align4 align4-2players alphabet-sequence babymatch babyshapes baby_wordprocessor balancebox ballcatch bargame bargame_2players binary_bulb braille_alphabets braille_fun calendar canal_lock categorization checkers checkers_2players chess chess_2players chess_partyend chronos clickanddraw clickgame click_on_letter click_on_letter_up clockgame color_mix color_mix_light colors crane details digital_electricity drawletters drawnumbers enumerate erase erase_2clic erase_clic explore_farm_animals explore_monuments explore_world_animals explore_world_music family family_find_relative fifteen find_the_day followline football geo-country geography gletters gnumch-equality gnumch-factors gnumch-inequality gnumch-multiples gnumch-primes graph-coloring guesscount guessnumber hangman hanoi hanoi_real hexagon imagename instruments intro_gravity land_safe lang leftright letter-in-word lightsoff louis-braille magic-hat-minus magic-hat-plus maze mazeinvisible mazerelative melody memory memory-case-association memory-case-association-tux memory-enumerate memory-math-add memory-math-add-minus memory-math-add-minus-mult-div memory-math-add-minus-mult-div-tux memory-math-add-minus-tux memory-math-add-tux memory-math-div memory-math-div-tux memory-math-minus memory-math-minus-tux memory-math-mult memory-math-mult-div memory-math-mult-div-tux memory-math-mult-tux memory-sound memory-sound-tux memory-tux memory-wordnumber mining missing-letter money money_back money_back_cents money_cents mosaic nine_men_morris nine_men_morris_2players note_names number_sequence numbers-odd-even paintings penalty piano_composition photo_hunter planegame play_piano play_rhythm +programmingMaze railroad readingh readingv redraw redraw_symmetrical renewable_energy reversecount roman_numerals scalesboard scalesboard_weight scalesboard_weight_avoirdupois share simplepaint smallnumbers smallnumbers2 solar_system submarine sudoku superbrain tangram target tic_tac_toe tic_tac_toe_2players traffic watercycle wordsgame diff --git a/src/activities/programmingMaze/ActivityInfo.qml b/src/activities/programmingMaze/ActivityInfo.qml new file mode 100644 index 000000000..036c37556 --- /dev/null +++ b/src/activities/programmingMaze/ActivityInfo.qml @@ -0,0 +1,35 @@ +/* GCompris - ActivityInfo.qml + * + * Copyright (C) 2015 Siddhesh Suthar + * + * 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 GCompris 1.0 + +ActivityInfo { + name: "programmingMaze/ProgrammingMaze.qml" + difficulty: 3 + icon: "programmingMaze/programmingMaze.svg" + author: "Siddhesh Suthar <siddhesh.it@gmail.com>" + demo: false + title: qsTr("Programming Maze") + description: qsTr("This activity teaches the kid to program the Tux to its goal using +simple instructions like move forward, turn left etc") + goal: qsTr("Tux is hungry. Help him find fish by programming him to the correct ice spot.") + prerequisite: qsTr("Can read instructions. Thinking of the path logically") + manual: qsTr("Choose the instructions from given menu. Arrange the instruction in an +order so that they can make the tux reach to its goal") + credit: "" + section: "fun" +} diff --git a/src/activities/programmingMaze/AnswerSheet.qml b/src/activities/programmingMaze/AnswerSheet.qml new file mode 100644 index 000000000..46757c128 --- /dev/null +++ b/src/activities/programmingMaze/AnswerSheet.qml @@ -0,0 +1,292 @@ +/* GCompris - programmingMaze.js + * + * Copyright (C) 2015 Siddhesh Suthar + * + * Authors: + * "Siddhesh Suthar" (Qt Quick port) + * + * 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.2 +import GCompris 1.0 + +import "programmingMaze.js" as Activity + +GridView { + id: answerSheet + property Item background + property ListModel currentModel + property int buttonWidth: background.width / 10 + property int buttonHeight: background.height / 10 + + width: background.width * 0.4 + height: background.height * 0.4 - 5 * ApplicationInfo.ratio + cellWidth: background.buttonWidth + cellHeight: background.buttonHeight + + interactive: false + model: currentModel + clip: true + + highlight: Rectangle { + width: buttonWidth + height: buttonHeight + color: "lightsteelblue" + border.width: 5 * ApplicationInfo.ratio + border.color: "purple" + opacity: 0.5 + z: 11 + Behavior on x { SpringAnimation { spring: 2; damping: 0.2 } } + } + highlightFollowsCurrentItem: true + highlightMoveDuration: Activity.moveAnimDuration + keyNavigationWraps: false + focus: true + + property int draggedItemIndex: -1 + property int possibleDropIndex: -1 + property int possibleDropRemoveIndex: -1 + property int xCoordinateInPossibleDrop: -1 + property int yCoordinateInPossibleDrop: -1 + + Item { + id: dropPosIndicator + visible: false + height: answerSheet.cellHeight + width: 5 * ApplicationInfo.ratio + + Rectangle { + visible: parent.visible + anchors.centerIn: parent + width: 5 * ApplicationInfo.ratio + height: parent.height - 5 * ApplicationInfo.ratio + color: "lightsteelblue" + } + + states: [ + State { + name: "shown" + when: answerSheet.possibleDropIndex != -1 + PropertyChanges { + target: dropPosIndicator + visible: true + x: Math.floor(answerSheet.xCoordinateInPossibleDrop/answerSheet.cellWidth) * + answerSheet.cellWidth - 5 * ApplicationInfo.ratio + y: Math.floor(answerSheet.yCoordinateInPossibleDrop/answerSheet.cellHeight) * + answerSheet.cellHeight - 2 * ApplicationInfo.ratio + } + } + ] + } + Item { + id: dropPosRemoveIndicator + visible: false + width: background.buttonWidth + height: background.buttonHeight + + Rectangle { + visible: parent.visible + anchors.fill: parent + radius: 5 * ApplicationInfo.ratio + color: "transparent" + border.width: 5 * ApplicationInfo.ratio + border.color: "lightsteelblue" + opacity: 1 + } + + states: [ + State { + name: "shown" + when: answerSheet.possibleDropRemoveIndex != -1 + PropertyChanges { + target: dropPosRemoveIndicator + visible: true + x: Math.floor(answerSheet.xCoordinateInPossibleDrop/answerSheet.cellWidth) * + answerSheet.cellWidth - 5 * ApplicationInfo.ratio + y: Math.floor(answerSheet.yCoordinateInPossibleDrop/answerSheet.cellHeight) * + answerSheet.cellHeight - 2 * ApplicationInfo.ratio + } + } + ] + } + Item { + id: dndContainer + anchors.fill: parent + } + + MouseArea { + id: coords + anchors.fill: parent + onPressed: { + answerSheet.draggedItemIndex = answerSheet.indexAt(mouseX,mouseY) + } + onReleased: { + if(answerSheet.draggedItemIndex != -1) { + var draggedIndex = answerSheet.draggedItemIndex + var dropIndex = answerSheet.indexAt(mouseX,mouseY) + answerSheet.draggedItemIndex = -1 + var calculatedX = Math.floor(answerSheet.xCoordinateInPossibleDrop/answerSheet.cellWidth) * + answerSheet.cellWidth + var diff = Math.floor(mouseX - calculatedX) + var insertEnd = answerSheet.cellWidth / 2 + if(answerSheet.indexAt(mouseX,mouseY) == -1) + currentModel.remove(draggedIndex) + else { + if(diff <= insertEnd) { + if(dropIndex <= draggedIndex) { + //moving box from right to left + currentModel.move(draggedIndex, answerSheet.indexAt(mouseX,mouseY), 1) + } + else { + //moving box from left to right + currentModel.move(draggedIndex, answerSheet.indexAt(mouseX,mouseY)-1, 1) + } + } + else { + currentModel.set(dropIndex, currentModel.get(draggedIndex), 1) + currentModel.remove(draggedIndex) + } + } + answerSheet.possibleDropRemoveIndex = -1 + answerSheet.possibleDropIndex = -1 + } + } + onPositionChanged: { + var newPos = answerSheet.indexAt(mouseX, mouseY) + answerSheet.xCoordinateInPossibleDrop = mouseX + answerSheet.yCoordinateInPossibleDrop = mouseY + var calculatedX = Math.floor(answerSheet.xCoordinateInPossibleDrop/answerSheet.cellWidth) * + answerSheet.cellWidth + var diffX = Math.floor(mouseX - calculatedX) + var insertEndX = answerSheet.cellWidth / 2 + if(diffX <= insertEndX) { + answerSheet.possibleDropIndex = newPos + answerSheet.possibleDropRemoveIndex = -1 + } + else { + answerSheet.possibleDropRemoveIndex = newPos + answerSheet.possibleDropIndex = -1 + } + } + } + + + delegate: Column { + Item { + id: itemParent + width: background.buttonWidth + height: background.buttonHeight + + Rectangle { + id: circlePlaceholder + width: 30 * ApplicationInfo.ratio + height: width + radius: width + anchors.centerIn: parent + color: "#cecece" + opacity: 0 + } + + Item { + id: item + width: background.buttonWidth - 5 * ApplicationInfo.ratio + height: background.buttonHeight - 5 * ApplicationInfo.ratio + state: "inactive" + opacity: 1 + + Behavior on width { NumberAnimation { duration: 300; easing.type: Easing.InOutQuad } } + Behavior on height { NumberAnimation { duration: 300; easing.type: Easing.InOutQuad } } + Behavior on opacity {NumberAnimation { duration: 300; easing.type: Easing.InOutQuad } } + + + states: [ + State { + name: "inDrag" + when: index == answerSheet.draggedItemIndex + PropertyChanges { target: circlePlaceholder; opacity: 1} + PropertyChanges { target: imageBorder; opacity: 1 } + PropertyChanges { target: item; parent: dndContainer } + PropertyChanges { target: item; width: background.buttonWidth * 0.80 } + PropertyChanges { target: item; height: background.buttonHeight * 0.80 } + PropertyChanges { target: item; anchors.centerIn: undefined } + PropertyChanges { target: item; x: coords.mouseX - item.width / 2 } + PropertyChanges { target: item; y: coords.mouseY - item.height / 2 } + }, + State { + name: "greyedOut" + when: (answerSheet.draggedItemIndex != -1) &&(answerSheet.draggedItemIndex != index) + PropertyChanges { target: item; opacity: 0.7 } + }, + State { + name: "inactive" + when: (answerSheet.draggedItemIndex == -1) || (answerSheet.draggedItemIndex == index) + PropertyChanges { target: item; opacity: 1.0 } + } + ] + + transitions: [ + Transition { + from: "inDrag" + to: "*" + PropertyAnimation { + target: item + properties: "scale, opacity" + easing.overshoot: 1.5 + easing.type: "OutBack" + from: 0.0 + to: 1.0 + duration: 500 + } + } + ] + + Rectangle { + id: answerRect + anchors.fill: parent + color: "#005B9A" + opacity: 1 + } + + Image { + source: "qrc:/gcompris/src/core/resource/button.svg" + sourceSize { height: parent.height; width: parent.width } + width: sourceSize.width + height: sourceSize.height + } + + Image { + id: answer + source: Activity.url + name + ".svg" + sourceSize { width: parent.width; height: parent.height } + width: sourceSize.width + height: sourceSize.height + anchors.centerIn: parent + smooth: false + + Rectangle { + id: imageBorder + width: background.buttonWidth + 5 * ApplicationInfo.ratio + height: background.buttonHeight + 5 * ApplicationInfo.ratio + anchors.fill: parent + radius: 5 * ApplicationInfo.ratio + color: "transparent" + border.width: 5 * ApplicationInfo.ratio + border.color: "#ffffff" + opacity: 0 + } + } + } + } + } +} diff --git a/src/activities/programmingMaze/CMakeLists.txt b/src/activities/programmingMaze/CMakeLists.txt new file mode 100644 index 000000000..6ab9f7d21 --- /dev/null +++ b/src/activities/programmingMaze/CMakeLists.txt @@ -0,0 +1 @@ +GCOMPRIS_ADD_RCC(activities/programmingMaze *.qml *.svg *.js resource/*) diff --git a/src/activities/programmingMaze/ProgrammingMaze.qml b/src/activities/programmingMaze/ProgrammingMaze.qml new file mode 100644 index 000000000..18a2079eb --- /dev/null +++ b/src/activities/programmingMaze/ProgrammingMaze.qml @@ -0,0 +1,513 @@ +/* GCompris - programmingMaze.qml + * + * Copyright (C) 2015 Siddhesh Suthar + * + * Authors: + * Siddhesh Suthar (Qt Quick port) + * + * 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.2 +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 + } + + pageComponent: Rectangle { + id: background + anchors.fill: parent + color: "#8C8984" + + signal start + signal stop + + property bool keyNavigation: false + property bool insertIntoMain: true + property bool insertIntoProcedure: false + + 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 answerModel: answerModel + property alias answerSheet: answerSheet + property alias procedureModel: procedureModel + property alias procedure: procedure + property alias player: player + property alias fish: fish + property alias runCodeImage: runCode.source + } + + onStart: { + Activity.start(items) + keyNavigation = false + } + onStop: { Activity.stop() } + + + Keys.onRightPressed: { + keyNavigation = true + instruction.moveCurrentIndexRight() + } + Keys.onLeftPressed: { + keyNavigation = true + instruction.moveCurrentIndexLeft() + } + Keys.onDownPressed: { + keyNavigation = true + instruction.moveCurrentIndexDown() + } + Keys.onUpPressed: { + keyNavigation = true + instruction.moveCurrentIndexUp() + } + Keys.onSpacePressed: { + keyNavigation = true + instruction.currentItem.mouseAreaInstruction.clicked() + } + Keys.onEnterPressed: { + keyNavigation = true + instruction.currentItem.mouseAreaInstruction.clicked() + } + Keys.onReturnPressed: { + keyNavigation = true + instruction.currentItem.mouseAreaInstruction.clicked() + } + + ListModel { + id: instructionModel + } + + ListModel { + id: answerModel + } + + ListModel { + id: procedureModel + } + + Repeater { + id: mazeModel + model: Activity.mazeBlocks[0] + + anchors.left: parent.left + anchors.top: parent.top + + Image { + x: modelData[0] * background.width / 10 + y: modelData[1] * (background.height - background.height/10) / 10 + width: background.width / 10 + height: background.height / 10 + source: Activity.reverseCountUrl + "iceblock.svg" + } + } + + Image { + id: player + source: "qrc:/gcompris/src/activities/maze/resource/tux_top_south.svg" + sourceSize.width: background.width / 12 + x: 0; y: 0; z: 11 + property int duration: 1000 + property bool tuxIsBusy: false + + signal init + + onInit: { + player.rotation = Activity.EAST + } + + onTuxIsBusyChanged: { + Activity.playerRunningChanged() + } + + Behavior on x { + SmoothedAnimation { + duration: player.duration + reversingMode: SmoothedAnimation.Immediate + onRunningChanged: { + player.tuxIsBusy = !player.tuxIsBusy + } + } + } + + Behavior on y { + SmoothedAnimation { + duration: player.duration + reversingMode: SmoothedAnimation.Immediate + onRunningChanged: { + player.tuxIsBusy = !player.tuxIsBusy + } + } + } + + Behavior on rotation { + RotationAnimation { + duration: player.duration / 2 + direction: RotationAnimation.Shortest + onRunningChanged: { + player.tuxIsBusy = !player.tuxIsBusy + } + } + } + + } + + Image { + id: fish + source: Activity.reverseCountUrl + "blue-fish.svg" + sourceSize.width: background.width / 12 + x: 0; y: 0; z: 5 + } + + property int buttonWidth: background.width / 10 + property int buttonHeight: background.height / 10 + + GridView { + id: instruction + width: parent.width * 0.5 + height: parent.height * 0.3 + 25 * ApplicationInfo.ratio + cellWidth: background.buttonWidth + cellHeight: background.buttonHeight + + anchors.left: parent.left + anchors.bottom: runCode.top + anchors.top: mazeModel.bottom + anchors.topMargin: background.buttonHeight * 4 + anchors.bottomMargin: runCode.height / 2 + + interactive: false + model: instructionModel + + header: instructionHeaderComponent + + highlight: Rectangle { + width: buttonWidth + height: buttonHeight + color: "lightsteelblue" + border.width: 3 + border.color: "black" + visible: background.keyNavigation + x: instruction.currentItem.x + Behavior on x { SpringAnimation { spring: 3; damping: 0.2 } } + } + highlightFollowsCurrentItem: false + focus: true + keyNavigationWraps: true + + delegate: Column { + spacing: 10 * ApplicationInfo.ratio + property alias mouseAreaInstruction: mouseAreaInstruction + + Item { + width: background.buttonWidth + height: background.buttonHeight + + Rectangle { + id: rect + width: parent.width + height: parent.height + anchors.fill: parent + gradient: Gradient { + GradientStop { position: 0.0; color: "#0191C8" } + GradientStop { position: 1.0; color: "#005B9A" } + } + opacity: 0.5 + } + + Image { + id: icon + source: Activity.url + name + ".svg" + sourceSize {width: parent.width; height: parent.height} + width: sourceSize.width + height: sourceSize.height + anchors.horizontalCenter: parent.horizontalCenter + } + + + Image { + source: "qrc:/gcompris/src/core/resource/button.svg" + sourceSize { height: parent.height; width: parent.width } + width: sourceSize.width + height: sourceSize.height + } + + MouseArea { + id: mouseAreaInstruction + anchors.fill: parent + signal clicked + onClicked: { + insertIntoModel() + } + onPressed: { + insertIntoModel() + } + function insertIntoModel() { + clickedAnim.start() + if(background.insertIntoProcedure && name != Activity.CALL_PROCEDURE) { + procedureModel.append({"name": name}) + } + if(background.insertIntoMain) { + answerModel.append({"name": name}) + } + } + } + SequentialAnimation { + id: clickedAnim + PropertyAnimation { + target: rect + property: "opacity" + to: "1" + duration: 300 + } + + PropertyAnimation { + target: rect + property: "opacity" + to: "0.5" + duration: 300 + } + } + } + } + } + + // insert data upon clicking the list items into this answerData + // and then process it to run the code + + AnswerSheet { + id: answerSheet + background: background + currentModel: answerModel + anchors.right: parent.right + anchors.top: answerHeaderComponent.bottom + } + + AnswerSheet { + id: procedure + background: background + currentModel: procedureModel + anchors.right: parent.right + anchors.bottom: parent.bottom + visible: bar.level > 2 + } + + Image { + id: runCode + width: background.width / 10 + height: background.height / 10 + anchors.right: instruction.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: true + onEntered: runCode.scale = 1.1 + onClicked: { + // todo add a condition to disable it if code is running + // either the execution hasn't started or stopped because of deadEndPoint + if(Activity.codeIterator == 0 || Activity.deadEndPoint) { + console.log(Activity.codeIterator +" value of codeIterator") + Activity.runCode() + } + } + onExited: runCode.scale = 1 + } + } + + + + Component { + id: instructionHeaderComponent + Rectangle { + id: headerRect + width: instruction.width + height: 25 * ApplicationInfo.ratio + color: "#005B9A" + + Image { + source: "qrc:/gcompris/src/core/resource/button.svg" + sourceSize { height: parent.height; width: parent.width } + width: sourceSize.width + height: sourceSize.height + } + + GCText { + id: headerText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignHCenter + width: parent.width + height: parent.height + fontSizeMode: Font.DemiBold + minimumPointSize: 7 + fontSize: mediumSize + wrapMode: Text.WordWrap + color: "white" + text: qsTr("Choose the instructions") + } + } + } + + Item { + id: answerHeaderComponent + width: answerSheet.width + height: 50 * ApplicationInfo.ratio + anchors.left: answerSheet.left + anchors.top: parent.top + + Rectangle { + id: answerHeaderRect + anchors.fill: parent + color: "#005B9A" + opacity: background.insertIntoMain ? 1 : 0.5 + + MouseArea { + anchors.fill: parent + onClicked: { + background.insertIntoMain = true + background.insertIntoProcedure = false + } + } + + Image { + source: "qrc:/gcompris/src/core/resource/button.svg" + sourceSize { height: parent.height; width: parent.width } + width: sourceSize.width + height: sourceSize.height + } + + GCText { + id: answerHeaderText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width + height: parent.height + fontSizeMode: Font.DemiBold + minimumPointSize: 7 + fontSize: mediumSize + wrapMode: Text.WordWrap + color: "white" + text: qsTr("Your Code") + } + } + } + + Item{ + id: procedureHeaderComponent + width: procedure.width + height: 50 * ApplicationInfo.ratio + anchors.left: procedure.left + anchors.bottom: procedure.top + visible: procedure.visible + Rectangle { + id: procedureHeaderRect + anchors.fill: parent + color: "#005B9A" + opacity: background.insertIntoProcedure ? 1 : 0.5 + + MouseArea { + anchors.fill: parent + onClicked: { + background.insertIntoMain = false + background.insertIntoProcedure = true + } + } + + Image { + source: "qrc:/gcompris/src/core/resource/button.svg" + sourceSize { height: parent.height; width: parent.width } + width: sourceSize.width + height: sourceSize.height + } + + GCText { + id: procedureHeaderText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width + height: parent.height + fontSizeMode: Font.DemiBold + minimumPointSize: 7 + fontSize: mediumSize + wrapMode: Text.WordWrap + color: "white" + text: qsTr("Your procedure") + } + } + } + + DialogHelp { + id: dialogHelp + onClose: home() + } + + Bar { + id: bar + content: BarEnumContent { value: 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/programmingMaze.js b/src/activities/programmingMaze/programmingMaze.js new file mode 100644 index 000000000..ee785d515 --- /dev/null +++ b/src/activities/programmingMaze/programmingMaze.js @@ -0,0 +1,358 @@ +/* GCompris - programmingMaze.js + * + * Copyright (C) 2015 Siddhesh Suthar + * + * Authors: + * "Siddhesh Suthar" (Qt Quick port) + * + * 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.0 as Quick +.import GCompris 1.0 as GCompris //for ApplicationInfo + +var mazeBlocks = [ + //level one + [ + //maze blocks + [[1,2],[2,2],[3,2]], + //fish index + [[3,2]], + //instruction set + ["move-forward", + "turn-left", + "turn-right"] + ], + //level two + [ + [[1,3],[2,3],[2,2],[2,1],[3,1]], + //fish index + [[3,1]], + //instruction set + ["move-forward", + "turn-left", + "turn-right"] + ], + //level three + [ + [[1,1],[2,1],[3,1],[3,2],[3,3],[2,3],[1,3]], + [[1,3]], + //instruction set + ["move-forward", + "turn-left", + "turn-right", + "call-procedure"] + ], + //level four + [ + [[0,3],[1,3],[1,2],[2,2],[2,1],[3,1]], + [[3,1]], + //instruction set + ["move-forward", + "turn-left", + "turn-right", + "call-procedure"] + ], + //level five + [ + [[0,3],[0,2],[0,1],[0,0],[1,0],[2,0],[2,1], + [2,2],[2,3],[3,3],[4,3],[4,2],[4,1],[4,0]], + [[4,0]], + //instruction set + ["move-forward", + "turn-left", + "turn-right", + "call-procedure"] + ] + ] +//[1,3],[2,3],[2,2],[2,1],[3,1] +//[1,1],[2,1],[3,1],[3,2],[3,3],[2,3],[1,3] +//[0,3],[1,3],[1,2],[2,2],[2,1],[3,1] +var countOfMazeBlocks +var stepX +var stepY +var playerCode = [] +var currentInstruction +var tuxIceBlockNumber +var changedX +var changedY +var currentRotation +var changedRotation +var deadEndPoint = false +var codeIterator = 0 +var reset = false +var procedureBlocks +var runningProcedure +var moveAnimDuration +var url = "qrc:/gcompris/src/activities/programmingMaze/resource/" +var reverseCountUrl = "qrc:/gcompris/src/activities/reversecount/resource/" +var okImage = "qrc:/gcompris/src/core/resource/bar_ok.svg" +var reloadImage = "qrc:/gcompris/src/core/resource/bar_reload.svg" +var currentLevel = 0 +var numberOfLevel +var items + +var NORTH = 0 +var WEST = 90 +var SOUTH = 180 +var EAST = 270 + +var BLOCKS_DATA_INDEX = 0 +var BLOCKS_FISH_INDEX = 1 +var BLOCKS_INSTRUCTION_INDEX = 2 + +var MOVE_FORWARD = "move-forward" +var TURN_LEFT = "turn-left" +var TURN_RIGHT = "turn-right" +var CALL_PROCEDURE = "call-procedure" +var END_PROCEDURE = "end-procedure" + +function start(items_) { + items = items_ + currentLevel = 0 + numberOfLevel = mazeBlocks.length + reset = false + initLevel() +} + +function stop() { +} + +function initLevel() { + if(!items || !items.bar) + return; + + items.bar.level = currentLevel + 1 + items.mazeModel.model = mazeBlocks[currentLevel][BLOCKS_DATA_INDEX] + + if(!reset && !deadEndPoint) { + items.answerModel.clear() + items.procedureModel.clear() + } + countOfMazeBlocks = mazeBlocks[currentLevel][BLOCKS_DATA_INDEX].length + + stepX = items.background.width / 10 + stepY = (items.background.height - items.background.height/10) / 10 + + items.instructionModel.clear() + var levelInstructions = mazeBlocks[currentLevel][BLOCKS_INSTRUCTION_INDEX] + for (var i = 0; i < levelInstructions.length ; i++) { + items.instructionModel.append({"name":levelInstructions[i]}); + } + + items.player.x = mazeBlocks[currentLevel][BLOCKS_DATA_INDEX][0][0] * stepX + items.player.y = mazeBlocks[currentLevel][BLOCKS_DATA_INDEX][0][1] * stepY + items.fish.x = mazeBlocks[currentLevel][BLOCKS_FISH_INDEX][0][0] * stepX + items.fish.y = mazeBlocks[currentLevel][BLOCKS_FISH_INDEX][0][1] * stepY + tuxIceBlockNumber = 0 + currentRotation = EAST + changedRotation = EAST + deadEndPoint = false + procedureBlocks = 0 + runningProcedure = false + moveAnimDuration = 1000 + items.background.insertIntoMain = true + items.background.insertIntoProcedure = false + items.answerSheet.currentIndex = -1 + items.procedure.currentIndex = -1 + items.answerSheet.highlightMoveDuration = moveAnimDuration + items.procedure.highlightMoveDuration = moveAnimDuration + items.runCodeImage = okImage + items.player.tuxIsBusy = false + codeIterator = 0 + playerCode = [] + + items.player.init() +} + +function getPlayerRotation() { + return ((changedRotation % 360) + 360) % 360 +} + +function runCode() { + if(items.runCodeImage == reloadImage) { + playerCode = [] + items.answerSheet.highlightFollowsCurrentItem = false + initLevel() + } + else { + items.answerSheet.highlightFollowsCurrentItem = true + //initialize code + playerCode = [] + items.player.tuxIsBusy = false + procedureBlocks = items.procedureModel.count + for(var i = 0; i < items.answerModel.count; i ++) { + if(items.answerModel.get([i]).name == CALL_PROCEDURE) { + playerCode.push("start-procedure") + for(var j = 0; j < items.procedureModel.count; j++) { + if(items.procedureModel.get([j]).name != END_PROCEDURE) + playerCode.push(items.procedureModel.get([j]).name) + } + playerCode.push("end-procedure") + } + else { + playerCode.push(items.answerModel.get([i]).name) + } + } + + if(!items.player.tuxIsBusy) { + executeNextInstruction() + } + } +} + + +function playerRunningChanged() { + if(!items.player.tuxIsBusy) { + if(deadEndPoint) { + console.log("it was a dead end") + } + else{ + executeNextInstruction() + } + } +} + +function executeNextInstruction() { + currentInstruction = playerCode[codeIterator] + + if(!items.player.tuxIsBusy && codeIterator < playerCode.length && !deadEndPoint + && currentInstruction != "start-procedure" && currentInstruction != "end-procedure") { + changedX = items.player.x + changedY = items.player.y + currentRotation = getPlayerRotation() + + var currentBlock = tuxIceBlockNumber + var nextBlock = tuxIceBlockNumber + 1 + + var currentX = mazeBlocks[currentLevel][BLOCKS_DATA_INDEX][currentBlock][0] + var currentY = mazeBlocks[currentLevel][BLOCKS_DATA_INDEX][currentBlock][1] + var nextX = mazeBlocks[currentLevel][BLOCKS_DATA_INDEX][nextBlock][0] + var nextY = mazeBlocks[currentLevel][BLOCKS_DATA_INDEX][nextBlock][1] + + if(currentInstruction == MOVE_FORWARD) { + ++tuxIceBlockNumber; + items.answerSheet.highlightMoveDuration = moveAnimDuration + items.procedure.highlightMoveDuration = moveAnimDuration + if (nextX - currentX > 0 && currentRotation == EAST) { + changedX = currentX * stepX + stepX + } + else if(nextX - currentX < 0 && currentRotation == WEST) { + changedX = currentX * stepX - stepX + } + else if(nextY - currentY < 0 && currentRotation == SOUTH) { + changedY = currentY * stepY - stepY + } + else if(nextY - currentY > 0 && currentRotation == NORTH) { + changedY = currentY * stepY + stepY + } + else { + // add an animation to indicate that its not possible + deadEndPoint = true + items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/brick.wav") + console.log("dead end") + deadEnd() + } + items.player.x = changedX + items.player.y = changedY + } + else if(currentInstruction == TURN_LEFT) { + changedRotation = (currentRotation - 90) % 360 + items.player.rotation = changedRotation + items.answerSheet.highlightMoveDuration = moveAnimDuration / 2 + items.procedure.highlightMoveDuration = moveAnimDuration / 2 + } + else if(currentInstruction == TURN_RIGHT) { + changedRotation = (currentRotation + 90) % 360 + items.player.rotation = changedRotation + items.answerSheet.highlightMoveDuration = moveAnimDuration / 2 + items.procedure.highlightMoveDuration = moveAnimDuration / 2 + } + + codeIterator = codeIterator + 1 + items.player.tuxIsBusy = true + if(runningProcedure && procedureBlocks > 0 + && currentInstruction != "start-procedure" && currentInstruction != "end-procedure") { + procedureBlocks-- + items.procedure.moveCurrentIndexRight() + } + if(!runningProcedure + && currentInstruction != "start-procedure" && currentInstruction != "end-procedure") { + items.answerSheet.moveCurrentIndexRight() + } + checkSuccess() + } + else if(currentInstruction == "start-procedure") { + runningProcedure = true + items.answerSheet.currentIndex += 1 + items.procedure.currentIndex = -1 + codeIterator = codeIterator + 1 + executeNextInstruction() + } + else if(currentInstruction == "end-procedure") { + runningProcedure = false + procedureBlocks = items.procedureModel.count + codeIterator = codeIterator + 1 + executeNextInstruction() + } +} + +function deadEnd() { + deadEndPoint = true + items.runCodeImage = reloadImage +} + +function checkSuccess() { + if(changedX === items.fish.x && changedY === items.fish.y) { + playerCode = [] + codeIterator = 0 + items.player.tuxIsBusy = false + items.bonus.good("smiley") + } +} + +function nextLevel() { + reset = false + if(numberOfLevel <= ++currentLevel) { + currentLevel = 0 + } + initLevel(); +} + +function previousLevel() { + reset = false + if(--currentLevel < 0) { + currentLevel = numberOfLevel - 1 + } + initLevel(); +} + +function repositionObjectsOnWidthChanged(factor) { + reset = true + if(items) + initLevel() +} + +function repositionObjectsOnHeightChanged(factor) { + reset = true + if(items) + initLevel() +} + +function reloadLevel() { + if(deadEndPoint) { + playerCode = [] + } + initLevel() +} diff --git a/src/activities/programmingMaze/programmingMaze.svg b/src/activities/programmingMaze/programmingMaze.svg new file mode 100644 index 000000000..b3dd8b1d1 --- /dev/null +++ b/src/activities/programmingMaze/programmingMaze.svg @@ -0,0 +1,146 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/call-procedure.svg b/src/activities/programmingMaze/resource/call-procedure.svg new file mode 100644 index 000000000..62358acbf --- /dev/null +++ b/src/activities/programmingMaze/resource/call-procedure.svg @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + CALL P + + + diff --git a/src/activities/programmingMaze/resource/move-forward.svg b/src/activities/programmingMaze/resource/move-forward.svg new file mode 100644 index 000000000..7d8f4989f --- /dev/null +++ b/src/activities/programmingMaze/resource/move-forward.svg @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + Indian road sign - Compulsory ahead only + 2012-09-10T00:52:00 + created by swecha developer and contributer Srujana. + https://openclipart.org/detail/172253/indian-road-sign---compulsory-ahead-only-by-ksrujana96-172253 + + + ksrujana96 + + + + + Compulsory ahead only + indian road sign + road sign + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/turn-left.svg b/src/activities/programmingMaze/resource/turn-left.svg new file mode 100644 index 000000000..c65e6b27e --- /dev/null +++ b/src/activities/programmingMaze/resource/turn-left.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2012-09-10T00:55:46 + created by swecha developer and contributer Srujana. + https://openclipart.org/detail/172256/indian-road-sign---compulsory-turn-left-by-ksrujana96-172256 + + + ksrujana96 + + + + + Compulsory turn left + indian road sign + road sign + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/turn-right.svg b/src/activities/programmingMaze/resource/turn-right.svg new file mode 100644 index 000000000..01a8fd3e2 --- /dev/null +++ b/src/activities/programmingMaze/resource/turn-right.svg @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + Indian road sign - Compulsory turn right + 2012-09-10T00:56:48 + created by swecha developer and contributer Srujana. + https://openclipart.org/detail/172257/indian-road-sign---compulsory-turn-right-by-ksrujana96-172257 + + + ksrujana96 + + + + + Compulsory turn right + indian road sign + road sign + + + + + + + + + + +