/* eslint-disable camelcase */
import { choice, contraindreValeur, lettreMinusculeDepuisChiffre, listeQuestionsToContenu, randint, sp } from '../../modules/outils.js'
import { point, rotation, similitude, texteParPoint, longueur, segment, homothetie, polygoneRegulierParCentreEtRayon, latexParCoordonneesBox, droite, latexParPoint, tracePoint, labelPoint, pointIntersectionDD } from '../../modules/2d.js'
import { create, all } from 'mathjs'
import { calculer } from '../../modules/outilsMathjs.js'
import Exercice from '../Exercice.js'
import { mathalea2d, colorToLatexOrHTML, vide2d, fixeBordures } from '../../modules/2dGeneralites.js'
import { ajouteChampTexteMathLive } from '../../modules/interactif/questionMathLive.js'
import { context } from '../../modules/context.js'
import * as pkg from '@cortex-js/compute-engine'
const { ComputeEngine } = pkg
export const interactifReady = true
export const interactifType = 'custom'
let engine
if (context.versionMathalea) engine = new ComputeEngine()
const math = create(all)
/**
* Travailler les tables de multiplication autrement
* @author Jean-Claude Lhote
* Référence 6C10-6
*/
export class Rose {
// operation = 'addition' | 'multiplication
// type = 'résultats' | 'valeurs' | 'can1' | 'can2' | 'solutions'
constructor ({ values = [], nombreDeValeurs, rayon = 2, operation = 'addition', type = 'résultats', typeDonnees = 'entiers', cellulesPreremplies = Array.from('abcdefghi'), valeurMax = 10, indexInconnue = 999 }) {
this.type = type
this.operation = operation
this.typeDonnees = typeDonnees
this.nombreDeValeurs = nombreDeValeurs
this.cellulesPreremplies = cellulesPreremplies
this.rayon = rayon
this.resultats = []
this.valeurMax = valeurMax
this.indexInconnue = indexInconnue
if (values === undefined || values.length === 0) {
while (this.valeurMax - 2 < this.nombreDeValeurs) {
this.valeurMax++
}
const den = randint(2, this.valeurMax)
for (let i = 0; i < this.nombreDeValeurs; i++) {
switch (this.typeDonnees) {
case 'entiers' :
values.push(randint(1, this.valeurMax, values))
this.rayon = 2
break
case 'entiers relatifs' :
values.push(randint(-this.valeurMax, this.valeurMax, [0, ...values]))
this.rayon = 2
break
case 'litteraux' :
values.push(calculer(`${randint(1, this.valeurMax)}x + ${randint(1, this.valeurMax)}`, null).printResult)
this.rayon = 4
break
case 'fractions dénominateurs multiples':
values.push(math.fraction(randint(1, this.valeurMax), den))
this.rayon = 3
break
case 'fractions positives dénominateurs premiers':
values.push(math.fraction(randint(1, this.valeurMax), choice([2, 3, 5, 7])))
this.rayon = 3
break
case 'fractions positives' :
values.push(math.fraction(randint(1, this.valeurMax), randint(2, this.valeurMax)))
this.rayon = 3
break
case 'fractions relatives' :
values.push(math.fraction(randint(-this.valeurMax, this.valeurMax, 0), randint(2, this.valeurMax)))
this.rayon = 3
break
}
}
} else { // si elles sont définies, on complète éventuellement la grille aléatoirement.
for (let i = this.values.length; i < this.nombreDeValeurs; i++) {
switch (this.typeDonnees) {
case 'entiers' :
values.push(randint(1, this.valeurMax, values))
break
case 'entiers relatifs' :
values.push(randint(-this.valeurMax, this.valeurMax, [0, ...values]))
break
case 'litteraux' :
values.push(calculer(`${randint(1, this.valeurMax)}x + ${randint(1, this.valeurMax)}`, null).printResult)
break
case 'fractions dénominateurs multiples':
values.push(math.fraction(randint(1, this.valeurMax), values[i - 1].d))
break
case 'fractions positives dénominateurs premiers':
values.push(math.fraction(randint(1, this.valeurMax), choice([2, 3, 5, 7])))
break
case 'fractions positives' :
values.push(math.fraction(randint(1, this.valeurMax), randint(2, this.valeurMax)))
break
case 'fractions relatives' :
values.push(math.fraction(randint(-this.valeurMax, this.valeurMax, 0), randint(2, this.valeurMax)))
break
}
}
}
this.values = values
this.calculeResultats()
}
// méthode qui calcule les résultats si on le veut (sinon on peut les renseigner dans this.resultats manuellement)
calculeResultats () {
for (let i = 0; i < this.nombreDeValeurs; i++) {
this.resultats[i] = this.operate(this.values[i], this.values[(i + 1) % this.nombreDeValeurs])
}
}
// fonction utilisée par calculeResultats
operate (a, b) {
switch (this.operation) {
case 'addition':
if (this.typeDonnees !== 'litteraux') {
if (this.typeDonnees.substring(0, 4) === 'frac') {
return math.fraction(math.add(a, b))
} else {
return math.add(a, b)
}
} else {
return calculer(`${a.toString()}+${b.toString()}`, null).printResult
}
case 'multiplication':
if (this.typeDonnees !== 'litteraux') {
if (this.typeDonnees.substring(0, 4) === 'frac') {
return math.fraction(math.multiply(a, b))
} else {
return math.multiply(a, b)
}
} else {
return calculer(`(${a.toString()})*(${b.toString()})`, null).printResult
}
}
}
representation () {
if (this.type === 'résultats') {
this.rayonBoite = 1
} else {
if (this.typeDonnees.substring(0, 4) === 'frac') this.rayonBoite = 1.5
else if (this.typeDonnees === 'litteraux') this.rayonBoite = 3.5
else this.rayonBoite = 1
}
const objets = []
const O = point(0, 0, '', '')
const A = rotation(point(this.rayon, 0, '', ''), O, 180 / this.nombreDeValeurs - 90, 'A')
for (let i = 0, bulle1, bulle2; i < this.nombreDeValeurs; i++) {
const M = rotation(A, O, 360 * i / this.nombreDeValeurs, 'M')
M.positionLabel = 'center'
const B = similitude(M, O, 180 / this.nombreDeValeurs, 1.3, 'B')
const D = similitude(M, O, -180 / this.nombreDeValeurs, 1.3, 'D')
const C = homothetie(M, O, 1.6, 'C')
const N = rotation(C, O, 360 / this.nombreDeValeurs, 'N')
const P = similitude(M, O, 180 / this.nombreDeValeurs, 2.5, 'P')
const s = segment(O, B, 'black')
const t = segment(B, C, 'black')
const u = segment(C, D, 'black')
const M2 = pointIntersectionDD(droite(B, D), droite(O, C), 'M2')
// objets.push(tracePoint(C, P, M2), labelPoint(C, P, M2), segment(O, C, 'red'), segment(B, D, 'green'))
const s1 = homothetie(segment(C, P), C, (longueur(C, P) - this.rayonBoite) / longueur(C, P))
s1.styleExtremites = '->'
s1.tailleExtremites = 2
s1.pointilles = 2
const s2 = homothetie(segment(N, P), N, (longueur(N, P) - this.rayonBoite) / longueur(N, P))
s2.styleExtremites = '->'
s2.tailleExtremites = 2
s2.pointilles = 2
if (this.type === 'can1') {
bulle1 = vide2d() // rotation(boite({??????}), M, 180 / this.nombreDeValeurs - 90)
} else {
bulle1 = vide2d()
}
objets.push(bulle1)
objets.push(s, t, u, s1, s2)
bulle2 = rotation(polygoneRegulierParCentreEtRayon(P, this.rayonBoite, this.nombreDeValeurs), P, 360 / this.nombreDeValeurs - 90)
if (this.type === 'résultats' || this.type === 'solutions' || this.type === 'can1' || this.type === 'can2') {
if (!(this.type === 'can1' && (this.indexInconnue === i || i === (this.indexInconnue - 1) % this.nombreDeValeurs || i === (this.indexInconnue + 1) % this.nombreDeValeurs))) {
if (!(this.type === 'can2' && (this.indexInconnue === i || i === (this.indexInconnue + 1) % this.nombreDeValeurs))) {
if (this.typeDonnees !== 'litteraux' && this.typeDonnees.substring(0, 4) !== 'frac') {
objets.push(texteParPoint(this.values[i].toString(), M, 'milieu', 'black', 1, 'middle', true))
} else {
if (this.typeDonnees !== 'litteraux') {
if (this.values[i].d === 1) {
objets.push(texteParPoint(this.values[i].toLatex().replace('frac', 'dfrac'), M, 'milieu', 'black', 1, 'middle', true))
} else {
objets.push(latexParPoint(this.values[i].toLatex().replace('frac', 'dfrac'), M, 'black', 20, 0, ''))
}
} else {
objets.push(latexParCoordonneesBox(this.values[i], M2.x, M2.y, 'black', 50, 12, '', 8, { anchor: 'center' }))
// objets.push(latexParPoint(this.values[i], M, 'black', 70, 12, '', 6))
}
}
}
}
if (this.type === 'can1' && this.indexInconnue === i) {
objets.push(texteParPoint(lettreMinusculeDepuisChiffre(i + 1), M))
}
}
if (this.type === 'solutions' || this.type === 'valeurs' || this.type === 'can1' || this.type === 'can2') { // on ajoute les produits
if (!(this.type === 'can2' && this.indexInconnue === i)) {
if (this.typeDonnees !== 'litteraux' && this.typeDonnees.substring(0, 4) !== 'frac') {
objets.push(texteParPoint((this.resultats[i]).toString(), P, 'milieu', 'black', 1, 'middle', true))
} else {
if (this.typeDonnees !== 'litteraux') {
if (this.resultats[i].d === 1) {
objets.push(texteParPoint(this.resultats[i].toLatex().replace('frac', 'dfrac'), P, 'milieu', 'black', 1, 'middle', true))
} else {
objets.push(latexParPoint(this.resultats[i].toLatex().replace('frac', 'dfrac'), P, 'black', 20, 0, ''))
}
} else {
objets.push(latexParCoordonneesBox(this.resultats[i], P.x, P.y, 'black', 50, 12, '', 8, { anchor: 'center', dy: (this.nombreDeValeurs === 3 ? '-35%' : '') }))
// objets.push(latexParPoint(this.resultats[i], P, 'black', 70, 10, ''))
}
}
}
if (this.type === 'can2' && this.indexInconnue === i) {
objets.push(texteParPoint(lettreMinusculeDepuisChiffre(i + 1), P))
} else {
bulle2.color = colorToLatexOrHTML('black')
if (this.type === 'valeurs') {
objets.push(texteParPoint(lettreMinusculeDepuisChiffre(i + 1), M))
}
}
} else {
objets.push(texteParPoint(this.cellulesPreremplies[i], P, 'milieu', 'black', 1, 'middle', true))
}
objets.push(bulle2)
}
return objets
}
}
export function ExoRose () {
Exercice.call(this)
this.spacing = 2
this.tailleDiaporama = 1
this.nbQuestions = 1
this.sup = 10
this.sup2 = 4
this.sup3 = 1
this.operation = 'multiplication'
this.type = 'résultats'
this.typeDonnees = 'entiers'
this.nombreDeValeurs = 4
this.valeurMax = 10
this.roses = []
this.indexInconnue = []
this.nouvelleVersion = function () {
this.listeQuestions = [] // Liste de questions
this.listeCorrections = [] // Liste de questions corrigées
this.valeurMax = contraindreValeur(10, 30, this.sup, 10)
this.nombreDeValeurs = contraindreValeur(3, 9, this.sup2, 5)
this.sup3 = contraindreValeur(1, 4, this.sup3, 1)
switch (this.sup3) {
case 1:
this.type = 'résultats'
if (this.typeDonnees.substring(0, 4) === 'frac' || this.typeDonnees === 'litteraux') {
if (this.nombreDeValeurs > 5) this.nombreDeValeurs = 5
}
break
case 2:
this.type = 'valeurs'
break
case 3:
if (this.typeDonnees.substring(0, 4) === 'frac' || this.typeDonnees === 'litteraux') {
if (this.nombreDeValeurs > 5) this.nombreDeValeurs = 5
}
this.type = 'can1'
break
case 4:
this.type = 'can2'
if (this.typeDonnees.substring(0, 4) === 'frac' || this.typeDonnees === 'litteraux') {
if (this.nombreDeValeurs > 5) this.nombreDeValeurs = 5
}
break
}
for (
let i = 0, objets, objetsCorr, texte, texteCorr, cpt = 0;
i < this.nbQuestions && cpt < 50;
) {
this.indexInconnue[i] = randint(0, this.nombreDeValeurs - 1)
if (this.operation === 'multiplication') {
this.introduction = 'Les nombres situés à l\'extrémité des flèches sont les produits des nombres dont les flèches sont issues.'
} else {
this.introduction = 'Les nombres situés à l\'extrémité des flèches sont les sommes des nombres dont les flèches sont issues.'
}
switch (this.type) {
case 'résultats':
if (this.operation === 'multiplication') {
this.consigne = 'Calculer les produits à l\'extrémité des flèches.'
} else {
this.consigne = 'Calculer les sommes à l\'extrémité des flèches.'
}
break
case 'valeurs':
if (this.operation === 'multiplication') {
this.consigne = 'Retrouver les facteurs à l\'origine des flèches.'
} else {
this.consigne = 'Retrouver les termes à l\'origine des flèches.'
}
break
case 'can1':
if (this.typeDonnees === 'nombres') {
this.consigne = `Trouver le nombre de la case ${lettreMinusculeDepuisChiffre(this.indexInconnue[i] + 1)}.`
} else {
this.consigne = `Trouver l'expression de la case ${lettreMinusculeDepuisChiffre(this.indexInconnue[i] + 1)}.`
}
break
case 'can2':
if (this.typeDonnees === 'nombres') {
this.consigne = `Trouver le nombre de la case ${lettreMinusculeDepuisChiffre(this.indexInconnue[i] + 1)}.`
} else {
this.consigne = `Trouver l'expression de la case ${lettreMinusculeDepuisChiffre(this.indexInconnue[i] + 1)}.`
}
break
}
this.roses[i] = new Rose({ nombreDeValeurs: this.nombreDeValeurs, type: this.type, operation: this.operation, valeurMax: this.valeurMax, typeDonnees: this.typeDonnees, indexInconnue: this.indexInconnue[i] })
objets = this.roses[i].representation()
this.roses[i].type = 'solutions'
objetsCorr = this.roses[i].representation()
texte = mathalea2d(Object.assign({ scale: 0.6 }, fixeBordures(objets)), objets)
if (this.interactif) {
if (this.type.substring(0, 3) === 'can') {
texte += ajouteChampTexteMathLive(this, i, 'nospacebefor', { texte: `${lettreMinusculeDepuisChiffre(this.indexInconnue[i] + 1)}=`, tailleExtensible: true })
} else {
for (let k = 0; k < this.nombreDeValeurs; k++) {
texte += ajouteChampTexteMathLive(this, i * this.nombreDeValeurs + k, 'nospacebefor', { texte: `${lettreMinusculeDepuisChiffre(k + 1)}=`, tailleExtensible: true })
texte += sp(6)
}
}
}
texteCorr = mathalea2d(Object.assign({ scale: 0.6 }, fixeBordures(objetsCorr)), objetsCorr)
if (this.questionJamaisPosee(i, ...this.roses[i].values)) {
// Si la question n'a jamais été posée, on en crée une autre
this.listeQuestions.push(texte)
this.listeCorrections.push(texteCorr)
i++
}
cpt++
}
listeQuestionsToContenu(this)
}
this.besoinFormulaireNumerique = ['Valeur maximale (entre 10 et 30) des facteurs', 30]
this.besoinFormulaire2Numerique = ['Nombre de facteur entre 3 et 9 (limité à 5 pour les valeurs fractionnaires ou littérales)']
this.besoinFormulaire3Numerique = ['Type de question', 4, '1 : Calculer les produits\n2 : Calculer les facteurs\n3 : Course aux nombres 1\n4 : Course aux nombres 2']
this.correctionInteractive = i => {
const taille = this.nombreDeValeurs
const champsTexte = []
const divFeedback = this.type.substring(0, 3) === 'can'
? document.querySelector(`#resultatCheckEx${this.numeroExercice}Q${i}`)
: document.querySelector(`#resultatCheckEx${this.numeroExercice}Q${(i + 1) * taille - 1}`)
const saisies = []
if (this.type.substring(0, 3) === 'can') {
champsTexte[0] = document.getElementById(`champTexteEx${this.numeroExercice}Q${i}`)
saisies[0] = champsTexte[0].value.replace(',', '.').replace(/\((\+?-?\d+)\)/, '$1')
} else {
for (let k = 0; k < taille; k++) {
champsTexte[k] = document.getElementById(`champTexteEx${this.numeroExercice}Q${i * taille + k}`)
saisies[k] = champsTexte[k].value.replace(',', '.').replace(/\((\+?-?\d+)\)/, '$1')
}
}
let resultat
if (this.saisieCoherente(saisies, taille, i)) {
divFeedback.innerHTML = '😎'
resultat = 'OK'
} else {
divFeedback.innerHTML = '☹️'
resultat = 'KO'
}
return resultat
}
this.saisieCoherente = function (saisies, taille, question) {
let resultatOK = true
if (this.type === 'can2') {
if (this.roses[question].typeDonnees.substring(0, 4) === 'frac') {
return engine.parse(this.roses[question].resultats[this.indexInconnue[question]].toLatex()).canonical.isSame(engine.parse(saisies[0].toLatex()).canonical)
} else {
return engine.parse(this.roses[question].resultats[this.indexInconnue[question]]).canonical.isSame(engine.parse(saisies[0].toString()).canonical)
}
} else if (this.type === 'can1') {
if (this.roses[question].typeDonnees.substring(0, 4) === 'frac') {
return engine.parse(saisies[0]).canonical.isSame(engine.parse(this.roses[question].values[this.indexInconnue[question]].toLatex()).canonical)
} else {
return engine.parse(saisies[0]).canonical.isSame(engine.parse(this.roses[question].values[this.indexInconnue[question]].toString()).canonical)
}
} else {
for (let i = 0; i < taille; i++) {
if (this.type === 'résultats') {
if (this.roses[question].typeDonnees.substring(0, 4) === 'frac') {
resultatOK = resultatOK && engine.parse(saisies[i]).canonical.isEqual(engine.parse(this.roses[question].resultats[i].toLatex()))
} else {
resultatOK = resultatOK && engine.parse(saisies[i]).canonical.isEqual(engine.parse(this.roses[question].resultats[i].toString()).canonical)
}
} else {
if (this.roses[question].typeDonnees.substring(0, 4) === 'frac') {
resultatOK = resultatOK && engine.parse(`${saisies[i]}${this.roses[question].operation === 'addition' ? '+' : '\\times'}${saisies[(i + 1) % this.nombreDeValeurs]}`).canonical.isEqual(engine.parse(this.roses[question].resultats[i].toLatex()))
} else {
resultatOK = resultatOK && engine.parse(this.roses[question].operate(saisies[i], saisies[(i + 1) % this.nombreDeValeurs])).canonical.isEqual(engine.parse(this.roses[question].resultats[i].toString()).canonical)
}
}
}
return resultatOK
}
}
}