Warning, /education/gcompris/src/activities/guess24/Guess24.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - guess24.qml
0002 *
0003 * SPDX-FileCopyrightText: 2023 Bruno ANSELME <be.root@free.fr>
0004 *
0005 * Authors:
0006 * Bruno ANSELME <be.root@free.fr> (Qt Quick native)
0007 * Timothée Giet <animtim@gmail.com> (Graphics and layout refactoring)
0008 *
0009 * SPDX-License-Identifier: GPL-3.0-or-later
0010 *
0011 * References :
0012 * https://www.4nums.com/game/difficulties/
0013 * https://www.4nums.com/solutions/allsolvables/ (2023-07-08 datas)
0014 * https://fr.y8.com/games/make_24
0015 */
0016 import QtQuick 2.12
0017 import GCompris 1.0
0018
0019 import "../../core"
0020 import "qrc:/gcompris/src/core/core.js" as Core
0021 import "guess24.js" as Activity
0022
0023 ActivityBase {
0024 id: activity
0025
0026 onStart: focus = true
0027 onStop: {}
0028
0029 pageComponent: Image {
0030 id: background
0031 source: "qrc:/gcompris/src/activities/guesscount/resource/backgroundW01.svg"
0032 sourceSize.width: width
0033 sourceSize.height: height
0034 fillMode: Image.PreserveAspectCrop
0035 anchors.fill: parent
0036 signal start
0037 signal stop
0038
0039 property int baseMargins: 5 * ApplicationInfo.ratio
0040 property int baseRadius: 2 * ApplicationInfo.ratio
0041 property string baseColor: "#A1CBD9"
0042 property string selectionColor: "#4B9BB5"
0043 property int selectionWidth: 4 * ApplicationInfo.ratio
0044
0045 Component.onCompleted: {
0046 dialogActivityConfig.initialize()
0047 activity.start.connect(start)
0048 activity.stop.connect(stop)
0049 }
0050
0051 // Add here the QML items you need to access in javascript
0052 QtObject {
0053 id: items
0054 property Item main: activity.main
0055 property alias background: background
0056 property int currentLevel: activity.currentLevel
0057 property alias bonus: bonus
0058 property alias score: score
0059 property alias errorRectangle: errorRectangle
0060 property var levels: activity.datasetLoader.data
0061 property GCSfx audioEffects: activity.audioEffects
0062 property alias jsonParser: jsonParser
0063 property int currentValue: 0
0064 property int currentOperator: -1
0065 property int operatorsCount: 4
0066 property int subLevelCount: 0
0067 property bool keysOnValues: true // True when focus is on values false if focus is on operators
0068 property alias cardsModel: cardsModel
0069 property alias cardsBoard: cardsBoard
0070 property alias operators: operators
0071 property alias cancelButton: cancelButton
0072 property alias hintButton: hintButton
0073 property alias animationCard: animationCard
0074 property alias steps: steps
0075 property alias solution: solution
0076 property alias solutionRect: solutionRect
0077 property alias animSol: animSol
0078 property bool keyboardNavigation: false
0079 property bool buttonsBlocked: false
0080 }
0081
0082 onStart: { Activity.start(items) }
0083 onStop: { Activity.stop() }
0084
0085 JsonParser { id: jsonParser }
0086
0087 ListModel {
0088 id: cardsModel
0089 function randPosition() { return (Math.floor(Math.random() * count)) } // choose a random position
0090 function shuffleModel() { for (var i = 0 ; i < count; i++) { move(randPosition(), randPosition(), 1) } } // shuffle elements
0091 }
0092
0093 Rectangle {
0094 id: captionBg
0095 width: caption.paintedWidth + background.baseMargins * 2
0096 height: caption.paintedHeight + background.baseMargins
0097 color: "#80FFFFFF"
0098 radius: background.baseRadius
0099 anchors.horizontalCenter: parent.horizontalCenter
0100 anchors.top: parent.top
0101 anchors.topMargin: background.baseMargins
0102 GCText {
0103 id: caption
0104 width: background.width - background.baseMargins * 4
0105 anchors.centerIn: captionBg
0106 text: qsTr("Use the four numbers with given operators to find 24.")
0107 verticalAlignment: Text.AlignVCenter
0108 horizontalAlignment: Text.AlignHCenter
0109 wrapMode: Text.WordWrap
0110 }
0111 MouseArea {
0112 anchors.fill: parent
0113 onClicked: captionBg.opacity = 0.0
0114 }
0115 }
0116
0117 Item {
0118 id: layoutArea
0119 anchors.top: captionBg.bottom
0120 anchors.bottom: score.top
0121 anchors.horizontalCenter: background.horizontalCenter
0122 anchors.margins: background.baseMargins
0123 width: Math.min(background.width - 2 * background.baseMargins,
0124 400 * ApplicationInfo.ratio)
0125 }
0126 // Main section
0127 Rectangle { // Values
0128 id: valuesArea
0129 anchors.top: layoutArea.top
0130 anchors.left: layoutArea.left
0131 width: layoutArea.width * 0.66
0132 height: Math.min(250 * ApplicationInfo.ratio, layoutArea.height * 0.75)
0133 color: "#80FFFFFF"
0134 radius: background.baseRadius
0135 GridView {
0136 id: cardsBoard
0137 anchors.fill: parent
0138 cellWidth: (parent.width - background.baseMargins) * 0.5
0139 cellHeight: (parent.height - background.baseMargins) * 0.5
0140 highlightFollowsCurrentItem: false
0141 boundsBehavior: Flickable.StopAtBounds
0142 model: cardsModel
0143 delegate: Item { // Display a card with a number inside
0144 id: cardNumber
0145 property int value: value_
0146 width: cardsBoard.cellWidth
0147 height: cardsBoard.cellHeight
0148 Rectangle {
0149 id: cardRect
0150 width: parent.width - background.baseMargins
0151 height: parent.height - background.baseMargins
0152 anchors.top: parent.top
0153 anchors.left: parent.left
0154 anchors.margins: background.baseMargins
0155 visible: true
0156 color: (items.currentValue === index) ? background.baseColor : "#F0F0F0"
0157 border.width: (items.keyboardNavigation && items.keysOnValues && (cardsBoard.currentIndex === index)) ? background.selectionWidth : ApplicationInfo.ratio
0158 border.color: (items.keyboardNavigation && items.keysOnValues && (cardsBoard.currentIndex === index)) ? background.selectionColor : background.baseColor
0159 radius: background.baseMargins
0160 GCText {
0161 anchors.fill: parent
0162 horizontalAlignment: Text.AlignHCenter
0163 verticalAlignment: Text.AlignVCenter
0164 fontSize: hugeSize
0165 fontSizeMode: Text.Fit
0166 text: value
0167 }
0168 MouseArea {
0169 id: boardArea
0170 anchors.fill: parent
0171 enabled: animationCard.state === "" && !items.buttonsBlocked
0172 onClicked: Activity.valueClicked(index)
0173 }
0174 }
0175 }
0176 }
0177
0178 ErrorRectangle {
0179 id: errorRectangle
0180 anchors.fill: valuesArea
0181 radius: valuesArea.radius
0182 imageSize: Math.min(width, height) * 0.5
0183 function releaseControls() { items.buttonsBlocked = false; }
0184 }
0185
0186 Item { // Animation card visible during animations
0187 id: animationCard
0188 property string value: ""
0189 property string action: Activity.animActions[0]
0190 width: cardsBoard.cellWidth
0191 height: cardsBoard.cellHeight
0192 visible: false
0193 Rectangle {
0194 id: animationCardBg
0195 width: parent.width - background.baseMargins
0196 height: parent.height - background.baseMargins
0197 color: background.baseColor
0198 radius: background.baseMargins
0199 border.color: background.baseColor
0200 border.width: background.selectionWidth
0201 anchors.top: parent.top
0202 anchors.left: parent.left
0203 anchors.margins: background.baseMargins
0204 }
0205 GCText {
0206 anchors.fill: parent
0207 horizontalAlignment: Text.AlignHCenter
0208 verticalAlignment: Text.AlignVCenter
0209 fontSize: hugeSize
0210 fontSizeMode: Text.Fit
0211 text: animationCard.value
0212 }
0213 states: [
0214 State {
0215 name: "moveto"
0216 PropertyChanges {
0217 target: animationCard
0218 visible: true
0219 x: items.cardsBoard.currentItem.x
0220 y: items.cardsBoard.currentItem.y
0221 }
0222 },
0223 State {
0224 name: "wait"
0225 PropertyChanges {
0226 target: animationCard
0227 visible: true
0228 }
0229 PropertyChanges {
0230 target: animationCardBg
0231 color: "tomato"
0232 border.color: "tomato"
0233 }
0234 }
0235 ]
0236 transitions: [
0237 Transition {
0238 to: "moveto"
0239 SequentialAnimation {
0240 alwaysRunToEnd: true
0241 NumberAnimation { properties: "x,y"; duration: 300 }
0242 ScriptAction {
0243 script: {
0244 animationCard.state = ""
0245 if (animationCard.action === "forward")
0246 Activity.checkResult()
0247 else if (animationCard.action === "backward")
0248 Activity.endPopOperation()
0249 }
0250 }
0251 }
0252 },
0253 Transition {
0254 to: "wait"
0255 SequentialAnimation {
0256 alwaysRunToEnd: true
0257 PauseAnimation { duration: 800 }
0258 ScriptAction { script: { audioEffects.play('qrc:/gcompris/src/core/resource/sounds/crash.wav') } }
0259 PauseAnimation { duration: 800 }
0260 ScriptAction { script: { Activity.popOperation() }}
0261 }
0262 }
0263 ]
0264 }
0265 }
0266
0267 Rectangle { // Operators
0268 id: operatorsArea
0269 anchors.top: valuesArea.bottom
0270 anchors.topMargin: background.baseMargins
0271 anchors.left: valuesArea.left
0272 anchors.right: valuesArea.right
0273 height: Math.min(layoutArea.height - valuesArea.height - background.baseMargins,
0274 80 * ApplicationInfo.ratio)
0275 radius: background.baseRadius
0276 color: "#80FFFFFF"
0277 enabled: ((items.currentValue !== -1) && (animationCard.state === ""))
0278 ListView {
0279 id: operators
0280 anchors.fill: parent
0281 orientation: ListView.Horizontal
0282 boundsBehavior: Flickable.StopAtBounds
0283 model: [ "+", "−", "×", "÷" ]
0284 delegate: Item {
0285 width: (operatorsArea.width - background.baseMargins) * 0.25
0286 height: operatorsArea.height
0287 Rectangle { // Display an operator button
0288 id: opRect
0289 width: parent.width - background.baseMargins
0290 height: parent.height - background.baseMargins * 2
0291 anchors.top: parent.top
0292 anchors.left: parent.left
0293 anchors.margins: background.baseMargins
0294 visible: index < items.operatorsCount
0295 color: (items.currentOperator === index) ? background.baseColor : "#F0F0F0"
0296 opacity: (items.currentValue !== -1) ? 1.0 : 0.5
0297 border.width: (items.keyboardNavigation && !items.keysOnValues && (operators.currentIndex === index) && (items.currentValue !== -1)) ? background.selectionWidth : ApplicationInfo.ratio
0298 border.color: (items.keyboardNavigation && !items.keysOnValues && (operators.currentIndex === index) && (items.currentValue !== -1)) ? background.selectionColor : background.baseColor
0299 radius: background.baseMargins
0300 GCText {
0301 anchors.fill: parent
0302 horizontalAlignment: Text.AlignHCenter
0303 verticalAlignment: Text.AlignVCenter
0304 text: operators.model[index]
0305 fontSize: hugeSize
0306 fontSizeMode: Text.Fit
0307 }
0308 MouseArea {
0309 id: opArea
0310 anchors.fill: parent
0311 enabled: !items.buttonsBlocked
0312 onClicked: Activity.operatorClicked(index)
0313 }
0314 }
0315 }
0316 }
0317 }
0318
0319 Rectangle {
0320 id: stepsRect
0321 width: layoutArea.width - valuesArea.width - background.baseMargins
0322 height: Math.min(80 * ApplicationInfo.ratio,
0323 (layoutArea.height - background.baseMargins) * 0.5)
0324 anchors.top: layoutArea.top
0325 anchors.right: layoutArea.right
0326 color: "#F0F0F0"
0327 radius: background.baseRadius
0328 GCText {
0329 id: steps
0330 anchors.fill: parent
0331 anchors.leftMargin: background.baseMargins
0332 fontSize: tinySize
0333 text: ""
0334 }
0335 }
0336
0337 Rectangle {
0338 id: solutionRect
0339 width: stepsRect.width
0340 height: stepsRect.height
0341 color: "#F0F0F0"
0342 radius: background.baseRadius
0343 anchors.top: stepsRect.bottom
0344 anchors.topMargin: background.baseMargins
0345 anchors.right: layoutArea.right
0346 opacity: 0.0
0347 GCText {
0348 id: solution
0349 anchors.fill: parent
0350 anchors.leftMargin: background.baseMargins
0351 fontSize: tinySize
0352 opacity: 0.5
0353 text: ""
0354 }
0355 NumberAnimation on opacity {
0356 id: animSol
0357 property bool firstTime: true
0358 alwaysRunToEnd: true
0359 to: 0.0
0360 duration: 1000
0361 onStarted: cancelButton.visible = false
0362 onStopped: {
0363 if (!firstTime) { // animation is triggered on activity start
0364 Activity.unstack = true
0365 Activity.popOperation()
0366 }
0367 firstTime = false
0368 cardsBoard.enabled = true
0369 operators.enabled = true
0370 }
0371 }
0372 }
0373
0374 Image {
0375 id: cancelButton
0376 source: "qrc:/gcompris/src/activities/chess/resource/undo.svg"
0377 height: score.height
0378 width: height
0379 sourceSize.width: width
0380 sourceSize.height: height
0381 anchors.top: operatorsArea.bottom
0382 anchors.topMargin: background.baseMargins
0383 anchors.left: operatorsArea.left
0384 enabled: animationCard.state === ""
0385 MouseArea {
0386 anchors.fill: parent
0387 enabled: !items.buttonsBlocked
0388 onClicked: {
0389 if (solutionRect.opacity !== 0.0) {
0390 animSol.start()
0391 } else {
0392 animSol.stop()
0393 Activity.unstack = false
0394 Activity.popOperation()
0395 }
0396 }
0397 }
0398 }
0399
0400 Image {
0401 id: hintButton
0402 source: "qrc:/gcompris/src/core/resource/bar_hint.svg"
0403 height: score.height
0404 width: height
0405 sourceSize.width: width
0406 sourceSize.height: height
0407 anchors.right: operatorsArea.right
0408 anchors.top: operatorsArea.bottom
0409 anchors.topMargin: background.baseMargins
0410 enabled: animationCard.state === ""
0411 MouseArea {
0412 anchors.fill: parent
0413 enabled: !items.buttonsBlocked
0414 onClicked: {
0415 if (Activity.helpCount < 4)
0416 Activity.helpCount++
0417 solution.text = Activity.splittedSolution.slice(0, Activity.helpCount).join("\n")
0418 solutionRect.opacity = 1.0
0419 cardsBoard.enabled = false
0420 operators.enabled = false
0421 hintButton.visible = false
0422 }
0423 }
0424 }
0425
0426 DialogChooseLevel {
0427 id: dialogActivityConfig
0428 currentActivity: activity.activityInfo
0429
0430 onSaveData: {
0431 levelFolder = dialogActivityConfig.chosenLevels
0432 currentActivity.currentLevels = dialogActivityConfig.chosenLevels
0433 ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels)
0434 }
0435 onClose: {
0436 home()
0437 }
0438 onStartActivity: {
0439 background.stop()
0440 background.start()
0441 }
0442 }
0443
0444 DialogHelp {
0445 id: dialogHelp
0446 onClose: home()
0447 }
0448
0449 Score {
0450 id: score
0451 numberOfSubLevels: items.subLevelCount
0452 currentSubLevel: 0
0453 anchors.top: undefined
0454 anchors.right: background.right
0455 anchors.rightMargin: background.baseMargins
0456 anchors.left: undefined
0457 anchors.bottom: background.bottom
0458 anchors.bottomMargin: bar.height * 1.5
0459 onStop: Activity.nextSubLevel()
0460 }
0461
0462 Bar {
0463 id: bar
0464 level: items.currentLevel + 1
0465 content: BarEnumContent { value: help | home | level | activityConfig }
0466 onHelpClicked: {
0467 displayDialog(dialogHelp)
0468 }
0469 onActivityConfigClicked: {
0470 displayDialog(dialogActivityConfig)
0471 }
0472 onPreviousLevelClicked: Activity.previousLevel()
0473 onNextLevelClicked: Activity.nextLevel()
0474 onHomeClicked: activity.home()
0475 }
0476
0477 Bonus {
0478 id: bonus
0479 Component.onCompleted: win.connect(Activity.nextLevel)
0480 }
0481
0482 Keys.onPressed: Activity.handleKeys(event)
0483 }
0484 }