diff --git a/src/activities/roman_numerals/ActivityInfo.qml b/src/activities/roman_numerals/ActivityInfo.qml index b23846937..5a545a3fe 100644 --- a/src/activities/roman_numerals/ActivityInfo.qml +++ b/src/activities/roman_numerals/ActivityInfo.qml @@ -1,40 +1,44 @@ /* GCompris - ActivityInfo.qml * * Copyright (C) 2016 Bruno Coudoin < bruno.coudoin@gcompris.net> * * 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: "roman_numerals/RomanNumerals.qml" difficulty: 4 icon: "roman_numerals/roman_numerals.svg" author: "Bruno Coudoin <bruno.coudoin@gcompris.net>" demo: true //: Activity title title: qsTr("Roman numerals") //: Help title description: "" //intro: "Learn and practice roman to arabic numerals conversion" //: Help goal goal: "" //: Help prerequisite prerequisite: "" //: Help manual - manual: qsTr("A Roman numeral is the name for a number when it is written in the way the Romans used to write numbers. Roman numerals are not used very often today in the west. They are used to write the names of kings and queens, or popes. For example: Queen Elizabeth II. They may be used to write the year a book or movie was made.") + manual: qsTr("A Roman numeral is the name for a number when it is written in the way the Romans used to write numbers. Roman numerals are not used very often today in the west. They are used to write the names of kings and queens, or popes. For example: Queen Elizabeth II. They may be used to write the year a book or movie was made.\n + The building of the roman numbers is made up by an agglutination of numbers (the thousands, joined with the hundreds, joined with the tens, joined with the units → just like the arab decimal system). This agglutination of numbers is interpreted as the sum of these particular numbers (again → just like the arab decimal system: you add up thousands+hundreds+tens+units, and you write the respective figures combined).\n + Examples:\n + 2394: we got a sum of 2000 (MM), 300 (CCC), ninety (XC) and 4 units (IV), so we write this combined: MMCCCXCIV\n + MMMCMXLIX: we got first thousands (MMM=3000), then we got hundreds (CM=1000–100=900), then we got tens (XL=50–10=40), and at last we got units (IX=10–1=9), so we write this in the decimal system: 3949).") credit: "" section: "math" createdInVersion: 7000 } diff --git a/src/activities/roman_numerals/RomanNumerals.qml b/src/activities/roman_numerals/RomanNumerals.qml index e208c19c0..c289edcb8 100644 --- a/src/activities/roman_numerals/RomanNumerals.qml +++ b/src/activities/roman_numerals/RomanNumerals.qml @@ -1,530 +1,530 @@ /* GCompris - roman_numerals.qml * * Copyright (C) 2016 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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" ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background source: items.toArabic ? "qrc:/gcompris/src/activities/roman_numerals/resource/arcs.svg" : "qrc:/gcompris/src/activities/roman_numerals/resource/torrazzo-crema.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: Math.max(parent.width, parent.height) signal start signal stop 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 alias score: score property alias textInput: textInput property bool toArabic: dataset[currentLevel].toArabic property string questionText: dataset[currentLevel].question property string instruction: dataset[currentLevel].instruction property string questionValue property int currentLevel: 0 property int numberOfLevel: dataset.length property var dataset: [ { values: ['I', 'V', 'X', 'L', 'C', 'D', 'M'], - instruction: qsTr("Roman numerals, are based on seven symbols: \nI = 1, V = 5\nX = 10, L = 50\nC = 100, D = 500\nM = 1000"), + instruction: qsTr("The roman numbers are all built out of these 7 numbers:\nI and V (units, 1 and 5)\nX and L (tens, 10 and 50)\nC and D (hundreds, 100 and 500)\n and M (1000)\n An interesting observation here is that the roman numeric system lacks the number 0."), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { values: [1, 5, 10, 50, 100, 500, 1000], - instruction: qsTr("Roman numerals, are based on seven symbols: \nI = 1, V = 5\nX = 10, L = 50\nC = 100, D = 500\nM = 1000"), + instruction: qsTr("The roman numbers are all built out of these 7 numbers:\nI and V (units, 1 and 5)\nX and L (tens, 10 and 50)\nC and D (hundreds, 100 and 500)\n and M (1000)\n An interesting observation here is that the roman numeric system lacks the number 0."), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { - values: ['II', 'III', 'VI', 'XX', 'XXII', 'XXX', 'LX', 'CC', 'CCL', 'MM', 'CLX', 'DCCLI', 'CCCLXVI'], - instruction: qsTr("Several symbols create a larger number like:\nII = 2\nXX = 20\nXI = 11"), + values: ['II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'], + instruction: qsTr("All the units except 4 and 9 are built using sums of I and V:\nI, II, III, V, VI, VII, VIII.\n The 4 and the 9 units are built using differences:\nIV (5 – 1) and IX (10 – 1)"), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { - values: [2, 3, 6, 20, 22, 30, 60, 200, 250, 2000, 160, 651, 361], - instruction: qsTr("Several symbols create a larger number like:\nII = 2\nXX = 20\nXI = 11"), + values: [2, 3, 4, 5, 6, 7, 8, 9], + instruction: qsTr("All the units except 4 and 9 are built using sums of I and V:\nI, II, III, V, VI, VII, VIII\n The 4 and the 9 units are built using differences:\nIV (5 – 1) and IX (10 – 1)"), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { - values: ['IV', 'IX', 'XL', 'XC', 'CD', 'CM'], - instruction: qsTr("If a lower value symbol is before a higher value one, it is subtracted. Otherwise it is added. So:\n'IV' is '4'\n'VI' is '6'\n'IX' is '9'"), + values: ['XX', 'XXX', 'XL', 'LX', 'LXX', 'LXXX', 'XC'], + instruction: qsTr("All the tens except 40 and 90 are built using sums of X and L:\nX, XX, XXX, L, LX, LXX, LXXX\nThe 40 and the 90 tens are built using differences:\nXL (10 taken from 50) and XC (10 taken from 100)\n "), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { - values: [4, 9, 40, 90, 400, 900], - instruction: qsTr("If a lower value symbol is before a higher value one, it is subtracted. Otherwise it is added. So:\n'IV' is '4'\n'VI' is '6'\n'IX' is '9'"), + values: [20, 30, 40, 60, 70, 80, 90], + instruction: qsTr("All the tens except 40 and 90 are built using sums of X and L:\nX, XX, XXX, L, LX, LXX, LXXX\nThe 40 and the 90 tens are built using differences:\nXL (10 taken from 50) and XC (10 taken from 100)\n "), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { - values: ['VIII', 'LXXX', 'CDLXXX', 'DCCC', 'MCCC'], - instruction: qsTr("Only one number is subtracted, not two. So 8 is always VIII and never IIX."), + values: ['CC', 'CCC', 'CD', 'DC', 'DCC', 'DCCC', 'CM', ], + instruction: qsTr("All the hundreds except 400 and 900 are built using sums of C and D:\nC, CC, CCC, D, DC, DCC, DCCC\nThe 400 and the 900 hundreds are built using differences:\nCD (100 taken from 500) and CM (100 taken from 1000)"), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { - values: [8, 70, 480, 800, 1300], - instruction: qsTr("Only one number is subtracted, not two. So 8 is always VIII and never IIX."), + values: [200, 300, 400, 600, 700, 800, 900], + instruction: qsTr("All the hundreds except 400 and 900 are built using sums of C and D:\nC, CC, CCC, D, DC, DCC, DCCC\nThe 400 and the 900 hundreds are built using differences:\nCD (100 taken from 500) and CM (100 taken from 1000)"), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { - values: ['XLIX', 'CMXC', 'XCV', 'XLVIII', 'CMXCIX', 'XLVI', 'CXCII', 'CDLXXV'], - instruction: qsTr("Proper form is to subtract only a value with the next lower power of 10. Thus, 900 is written CM, but 990 would not be XM - properly, it is CM for the 900 portion and XC for the 90 portion, or CMXC. Similarly, 999 would not be IM but rather CMXCIX - CM for the 900 portion, XC for the 90 portion, and IX for the 9 portion. Only values starting in 1's are ever used to subtract; 45 is properly XLV, not VL."), + values: ['MM', 'MMM'], + instruction: qsTr("Sums of M are used for building thousands: M, MM, MMM.\nNotice that you cannot join more than three identical symbols. The first implication of this rule is that you cannot use just sums for building all possible units, tens or hundreds, you must use differences too. On the other hand, it limits the maximum roman number to 3999 (MMMCMXCIX).\n"), question: qsTr("Convert the roman number %1 in arabic."), toArabic: true }, { - values: [48, 991, 96, 49, 998, 47, 193, 472, 1092], - instruction: qsTr("Proper form is to subtract only a value with the next lower power of 10. Thus, 900 is written CM, but 990 would not be XM - properly, it is CM for the 900 portion and XC for the 90 portion, or CMXC. Similarly, 999 would not be IM but rather CMXCIX - CM for the 900 portion, XC for the 90 portion, and IX for the 9 portion. Only values starting in 1's are ever used to subtract; 45 is properly XLV, not VL."), + values: [2000, 3000], + instruction: qsTr("Sums of M are used for building thousands: M, MM, MMM.\nNotice that you cannot join more than three identical symbols. The first implication of this rule is that you cannot use just sums for building all possible units, tens or hundreds, you must use differences too. On the other hand, it limits the maximum roman number to 3999 (MMMCMXCIX).\n"), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['_random_', 50 /* up to this number */ , 10 /* sublevels */], - instruction: qsTr("Now you know the rules, you can read and write any numbers in roman numerals."), + instruction: qsTr("Now you know the rules, you can read and write any number in roman numerals."), question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['_random_', 100, 10], instruction: '', question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['_random_', 500, 10], instruction: '', question: qsTr("Convert the arabic number %1 in roman."), toArabic: false }, { values: ['_random_', 1000, 10], instruction: '', question: qsTr("Convert the arabic number %1 in roman."), toArabic: false } ] onQuestionValueChanged: { textInput.text = '' romanConverter.arabic = 0 if(toArabic) keyboard.populateArabic() else keyboard.populateRoman() } function start() { if (!ApplicationInfo.isMobile) textInput.forceActiveFocus(); items.currentLevel = 0 initLevel() } function initLevel() { score.currentSubLevel = 1 initSubLevel() } function initSubLevel() { if(dataset[currentLevel].values[0] == '_random_') { questionValue = Math.round(Math.random() * dataset[currentLevel].values[1] + 1) score.numberOfSubLevels = dataset[currentLevel].values[2] } else { questionValue = dataset[currentLevel].values[score.currentSubLevel - 1] score.numberOfSubLevels = dataset[currentLevel].values.length } } function nextLevel() { if(numberOfLevel - 1 == currentLevel ) { currentLevel = 0 } else { currentLevel++ } initLevel(); } function previousLevel() { if(currentLevel == 0) { currentLevel = numberOfLevel - 1 } else { currentLevel-- } initLevel(); } function nextSubLevel() { if(++score.currentSubLevel > score.numberOfSubLevels) { nextLevel() } else { initSubLevel(); } } property bool isChecking: false function check() { if(isChecking) { return } isChecking = true if(feedback.value == items.questionValue) { bonus.good('tux') } else { bonus.bad('tux') } } } onStart: { items.start() } onStop: { } Keys.onPressed: { if ((event.key === Qt.Key_Enter) || (event.key === Qt.Key_Return)) { items.check() } } QtObject { id: romanConverter property int arabic property string roman // conversion code copied from: // http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter function arabic2Roman(num) { if (!+num) return ''; var digits = String(+num).split(""), key = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM", "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC", "","I","II","III","IV","V","VI","VII","VIII","IX"], roman = "", i = 3; while (i--) roman = (key[+digits.pop() + (i * 10)] || "") + roman; return new Array(+digits.join("") + 1).join("M") + roman; } function roman2Arabic(str) { var str = str.toUpperCase(), validator = /^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/, token = /[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, key = {M:1000,CM:900,D:500,CD:400,C:100,XC:90,L:50,XL:40,X:10,IX:9,V:5,IV:4,I:1}, num = 0, m; if (!(str && validator.test(str))) return false; while (m = token.exec(str)) num += key[m[0]]; return num; } onArabicChanged: roman = arabic2Roman(arabic) onRomanChanged: arabic = roman2Arabic(roman) } Column { id: column anchors.fill: parent anchors.topMargin: 10 GCText { id: questionLabel anchors.horizontalCenter: parent.horizontalCenter wrapMode: TextEdit.WordWrap text: items.questionValue ? items.questionText.arg(items.questionValue) : '' color: 'white' width: parent.width * 0.9 horizontalAlignment: Text.AlignHCenter Rectangle { z: -1 border.color: 'black' border.width: 1 anchors.centerIn: parent width: parent.width * 1.1 height: parent.height opacity: 0.8 gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } radius: 10 } } Item { // Just a margin width: 1 height: 5 * ApplicationInfo.ratio } TextInput { id: textInput x: parent.width / 2 width: 60 * ApplicationInfo.ratio color: 'white' text: '' maximumLength: items.toArabic ? ('' + romanConverter.roman2Arabic(items.questionValue)).length + 1 : romanConverter.arabic2Roman(items.questionValue).length + 1 horizontalAlignment: Text.AlignHCenter verticalAlignment: TextInput.AlignVCenter anchors.horizontalCenter: parent.horizontalCenter font.pointSize: questionLabel.pointSize font.weight: Font.DemiBold font.family: GCSingletonFontLoader.fontLoader.name font.capitalization: ApplicationSettings.fontCapitalization font.letterSpacing: ApplicationSettings.fontLetterSpacing cursorVisible: true validator: RegExpValidator{regExp: items.toArabic ? /[0-9]+/ : /[ivxlcdmIVXLCDM]*/} onTextChanged: if(text) { text = text.toUpperCase(); if(items.toArabic) romanConverter.arabic = parseInt(text) else romanConverter.roman = text } Rectangle { z: -1 opacity: 0.8 gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } radius: 10 border.color: 'black' border.width: 1 anchors.horizontalCenter: parent.horizontalCenter width: column.width * 0.7 height: parent.height } function appendText(car) { if(car === keyboard.backspace) { if(text && cursorPosition > 0) { var oldPos = cursorPosition text = text.substring(0, cursorPosition - 1) + text.substring(cursorPosition) cursorPosition = oldPos - 1 } return } var oldPos = cursorPosition text = text.substring(0, cursorPosition) + car + text.substring(cursorPosition) cursorPosition = oldPos + 1 } } Item { // Just a margin width: 1 height: 5 * ApplicationInfo.ratio } GCText { id: feedback anchors.horizontalCenter: parent.horizontalCenter text: items.toArabic ? qsTr("Roman value: %1").arg(value) : qsTr('Arabic value: %1').arg(value) color: 'white' Rectangle { z: -1 opacity: 0.8 gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } radius: 10 border.color: 'black' border.width: 1 anchors.centerIn: parent width: parent.width * 1.1 height: parent.height } property string value: items.toArabic ? romanConverter.roman : romanConverter.arabic ? romanConverter.arabic : '' } Item { // Just a margin width: 1 height: 5 * ApplicationInfo.ratio } Rectangle { color: "transparent" width: parent.width height: (background.height - (y + bar.height + okButton.height + keyboard.height) * 1.1 ) Flickable { width: parent.width height: parent.height contentWidth: parent.width contentHeight: instructionContainer.height anchors.fill: parent flickableDirection: Flickable.VerticalFlick clip: true GCText { id: instruction visible: items.instruction != '' wrapMode: TextEdit.WordWrap fontSize: tinySize anchors.horizontalCenter: parent.horizontalCenter width: parent.width * 0.95 text: items.instruction horizontalAlignment: Text.AlignHCenter color: 'white' Rectangle { id: instructionContainer z: -1 opacity: 0.8 gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } radius: 10 border.color: 'black' border.width: 1 anchors.centerIn: parent width: parent.width height: parent.contentHeight } } } } } Score { id: score anchors.bottom: bar.top currentSubLevel: 0 numberOfSubLevels: 1 } DialogHelp { id: dialogHelp onClose: home() } VirtualKeyboard { id: keyboard anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter function populateArabic() { layout = [ [ { label: "0" }, { label: "1" }, { label: "2" }, { label: "3" }, { label: "4" }, { label: "5" }, { label: "6" }, { label: "7" }, { label: "8" }, { label: "9" }, { label: keyboard.backspace } ] ] } function populateRoman() { layout = [ [ { label: "I" }, { label: "V" }, { label: "X" }, { label: "L" }, { label: "C" }, { label: "D" }, { label: "M" }, { label: keyboard.backspace } ] ] } onKeypress: textInput.appendText(text) onError: console.log("VirtualKeyboard error: " + msg); } Bar { id: bar anchors.bottom: keyboard.top content: BarEnumContent { value: help | home | level | hint } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: items.previousLevel() onNextLevelClicked: items.nextLevel() onHomeClicked: activity.home() level: items.currentLevel + 1 onHintClicked: feedback.visible = !feedback.visible } BarButton { id: okButton source: "qrc:/gcompris/src/core/resource/bar_ok.svg"; sourceSize.width: 60 * ApplicationInfo.ratio visible: true anchors { verticalCenter: score.verticalCenter right: score.left rightMargin: 10 * ApplicationInfo.ratio bottomMargin: 10 * ApplicationInfo.ratio } onClicked: items.check() } Bonus { id: bonus Component.onCompleted: win.connect(items.nextSubLevel) onWin: items.isChecking = false onLoose: items.isChecking = false } } }