9.8 KiB
Algorithmes de calcul — Minuteur Dragodinde
Ce document décrit tous les algorithmes utilisés dans l'application, expliqués simplement.
1. Système de tiers des jauges
Chaque jauge (baffeur, caresseur, foudroyeur, abreuvoir, dragofesse, mangeoire) a un niveau entre 0 et 100 000. Ce niveau détermine un tier qui fixe la vitesse de conversion :
| Niveau de jauge | Tier | Taux (pts / 10 sec) |
|---|---|---|
| 0 – 40 000 | 1 | 10 |
| 40 001 – 70 000 | 2 | 20 |
| 70 001 – 90 000 | 3 | 30 |
| 90 001 – 100 000 | 4 | 40 |
Principe : La jauge se vide en donnant des points à la DD. Plus la jauge est haute, plus elle se vide vite (tier élevé = plus de points par tick).
Un "tick" = 10 secondes de timer.
2. Calcul des points gagnés dans le temps — gainedIn(lvl, sec)
Question : "Si ma jauge démarre au niveau lvl et tourne pendant sec secondes, combien de points ma DD gagne-t-elle ?"
Algorithme :
- On part du haut (tier 4) et on descend
- Pour chaque palier, on calcule combien de ticks on peut consommer avant de passer au palier en-dessous
- On additionne les points de chaque palier traversé
Exemple : Jauge à 95 000, timer 60 sec (= 6 ticks)
- Tier 4 (90k–100k) : 5 000 pts dispo ÷ 40/tick = 125 ticks possibles → on en utilise 6 → gain = 6 × 40 = 240 pts
- Jauge restante : 95 000 - 240 = 94 760
La jauge descend au fur et à mesure qu'elle donne des points. Quand elle passe sous un seuil de tier, le taux ralentit.
3. Temps pour gagner X points — timeToGain(lvl, pts)
Question : "Combien de temps faut-il pour gagner pts points depuis une jauge au niveau lvl ?"
C'est l'inverse de gainedIn. On parcourt les paliers du haut vers le bas :
- Pour chaque tier, combien de points peut-on donner avant de descendre au palier suivant ?
- On prend le minimum entre les points disponibles et les points restants à donner
- On calcule le temps correspondant :
ceil(points / taux) × 10 sec
Si la jauge se vide complètement avant d'atteindre l'objectif → retourne Infinity (impossible).
4. Niveau de jauge après X secondes — gaugeAfter(lvl, sec)
Question : "Si ma jauge démarre à lvl et tourne sec secondes, à quel niveau sera-t-elle ?"
Même logique que gainedIn, mais au lieu de compter les points donnés, on soustrait directement du niveau de la jauge.
5. Temps écoulé — elapsed(enc)
Calcule les secondes écoulées depuis le démarrage du timer d'un enclos, en excluant les pauses :
Si en cours : (maintenant - démarrage - temps_en_pause) / 1000
Si en pause : (moment_pause - démarrage - temps_en_pause) / 1000
Si non démarré : 0
6. Calcul unifié d'une jauge — computeGaugeLive(enc, dd, gid, el, started)
C'est le cœur du système. Pour chaque DD et chaque jauge active, il calcule en temps réel :
- La stat estimée actuelle
- Si la cible est atteinte
- Le % de progression
- Le countdown restant
Pour les jauges normales (sérénité, endurance, maturité, amour) :
- On récupère le snapshot de la jauge et de la stat au moment du démarrage du timer
- On calcule les points gagnés depuis le snapshot :
gainedIn(snapshotJauge, tempsÉcoulé) - On applique la direction (
dir) :dir = +1(caresseur, foudroyeur, abreuvoir, dragofesse) : stat montedir = -1(baffeur) : stat descend
- On clamp la stat dans ses bornes (ex: sérénité entre -5000 et +5000)
- On calcule le temps total et le countdown via
timeToGain
Pour la mangeoire (XP) — modèle spécial :
L'XP utilise un modèle linéaire constant car le joueur est supposé recharger la jauge manuellement :
- On détermine le taux fixe =
tierRate(snapshotJauge)au démarrage - XP gagnée =
nombre_de_ticks × taux(pas de dégression) - XP nécessaire =
xpForLevel(cible) - xpForLevel(départ) - Countdown =
ceil(xp_restante / taux) × 10 sec
Pourquoi un modèle différent ? Parce que pour les stats, la jauge se vide naturellement. Pour l'XP, le joueur remet des croquettes (recharge la mangeoire), donc le taux reste constant.
7. Table d'XP et niveaux
Table XP_RAW
Dictionnaire de 200 entrées : niveau → XP cumulatif total.
Exemples :
- Niveau 1 → 0 XP
- Niveau 10 → 809 XP
- Niveau 50 → 34 365 XP
- Niveau 100 → 172 668 XP
- Niveau 200 → 867 582 XP
Les valeurs sont cumulatives (pas incrémentales).
xpForLevel(lvl) : Retourne l'XP cumulatif pour atteindre le niveau lvl.
levelFromXp(xp) : Retourne le plus haut niveau atteint avec xp points d'XP cumulatifs. Parcourt la table de 200 à 1 pour trouver le seuil.
8. ETA Sérénité — calcSerenEta / calcSerenEtaLive
Version statique (calcSerenEta)
- Calcule
diff = cible - sérénité actuelle - Si
diff > 0→ besoin du caresseur (monte la sérénité) - Si
diff < 0→ besoin du baffeur (baisse la sérénité) - Temps =
timeToGain(niveauJauge, |diff|)
Version live (calcSerenEtaLive)
- Utilise
computeGaugeLivepour obtenir la sérénité estimée en temps réel - Recalcule le diff depuis cette estimation
- Calcule le temps restant avec les points encore à parcourir
9. ETA Niveau — calcLevelEta / calcLevelEtaLive
Version statique (calcLevelEta)
- XP nécessaire =
xpForLevel(cible) - xpForLevel(niveauActuel) - Taux fixe =
tierRate(niveauMangeoire) - Temps =
ceil(xpNécessaire / taux) × 10 sec
Version live (calcLevelEtaLive)
- Utilise
computeGaugeLivepour obtenir le niveau estimé actuel - XP restante =
xpForLevel(cible) - xpForLevel(niveauEstimé) - Taux fixe =
tierRate(snapshotMangeoire) - Countdown =
ceil(xpRestante / taux) × 10 sec
10. Countdown global d'un enclos — enclosGlobalState(enc)
Question : "Dans combien de temps TOUTES les DD de cet enclos auront atteint TOUTES leurs cibles ?"
- Pour chaque DD × chaque jauge active → appeler
computeGaugeLive - Prendre le maximum de tous les countdowns = le plus long à terminer
- Compter combien de DD ont TOUTES leurs cibles atteintes (
ddDone) allDone = truesi toutes les DD sont terminées
11. Arbre de réapprovisionnement — calcAppro()
Question : "Pour produire Q exemplaires de la race X, de quelles races et en quelles quantités ai-je besoin ?"
Principe : décomposition récursive par génération
Chaque race de génération ≥ 2 est produite par le croisement de 2 races parentes (table BREEDING_RECIPES).
Algorithme :
- On part de la race cible et de la quantité voulue
- Pour chaque génération (de la plus haute à gen 2) :
- Pour chaque race nécessaire à cette génération :
- Calculer le nombre de couples nécessaires (voir §11.1)
- Ajouter les parents nécessaires (chacun × nombre de couples) dans le pool
- Pour chaque race nécessaire à cette génération :
- Les races gen 1 restantes = matières premières
11.1 Mécanisme du reproducteur
Un reproducteur est une DD réutilisable : elle peut faire plusieurs bébés.
Si 2×R ≥ Q → couples = ceil(Q / 2)
Sinon → couples = Q - R
Où R = nombre de reproducteurs, Q = quantité nécessaire.
Explication :
- Si on a assez de reproducteurs (≥ moitié de Q), chaque reproducteur fait 2 bébés → on divise par 2
- Sinon, chaque reproducteur fait sa part et les autres sont des "one-shot"
12. Calcul d'inventaire avec contraintes ♂/♀ — calcInventaire()
Question : "Avec mon stock actuel de DD (mâles et femelles), quels croisements puis-je réaliser ?"
Modèle de données
Chaque race dans l'inventaire : { m: mâles, f: femelles, n: neutres }
metf= stock réel renseigné par le joueurn= bébés produits par le calcul (sexe inconnu)
Contrainte fondamentale
Un croisement nécessite 1 mâle + 1 femelle. On ne peut pas croiser ♂+♂ ou ♀+♀.
Algorithme : round-robin par génération
Pour chaque génération (2 → 10) :
- Lister tous les croisements possibles à cette génération
- Boucle round-robin : tant qu'au moins un croisement est possible :
- Pour chaque croisement [Parent A + Parent B → Bébé] :
- Vérifier qu'on a 1 mâle-capable chez A ET 1 femelle-capable chez B (ou l'inverse)
- Si même race (A = B) : vérifier qu'on a ≥ 2 individus dont au moins 1 ♂ et 1 ♀
- Consommer les parents (priorité au stock réel m/f, puis les neutres n)
- Ajouter 1 neutre (
n++) à la race du bébé produit
- Pour chaque croisement [Parent A + Parent B → Bébé] :
- Les bébés produits (neutres) sont disponibles pour les générations suivantes
Priorité de consommation
takeMale : si m > 0 → m--, sinon n--
takeFemale : si f > 0 → f--, sinon n--
Les neutres (n) peuvent jouer le rôle de ♂ ou ♀ selon le besoin.
Pourquoi round-robin ?
Pour répartir équitablement les ressources entre les croisements possibles, plutôt que de tout donner au premier croisement trouvé.
13. Constantes des stats
| Stat | Min | Max | Jauge(s) associée(s) |
|---|---|---|---|
| Sérénité | -5 000 | 5 000 | Baffeur (↓), Caresseur (↑) |
| Endurance | 0 | 20 000 | Foudroyeur (↑) |
| Maturité | 0 | 20 000 | Abreuvoir (↑) |
| Amour | 0 | 20 000 | Dragofesse (↑) |
| Niveau/XP | 1 | 200 | Mangeoire (↑) |
14. Système de snapshots
Quand le timer démarre, on prend un snapshot de l'état :
snapGauges: niveaux de toutes les jauges au moment du démarragesnapStats[dd.id]: stats de chaque DD au moment du démarrage
Tous les calculs utilisent ces snapshots comme point de départ, pas l'état en temps réel. Cela évite les dérives de calcul si les valeurs sont modifiées pendant que le timer tourne.