256 lines
9.8 KiB
Markdown
256 lines
9.8 KiB
Markdown
# 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** :
|
||
1. On part du haut (tier 4) et on descend
|
||
2. Pour chaque palier, on calcule combien de ticks on peut consommer avant de passer au palier en-dessous
|
||
3. 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 :
|
||
1. Pour chaque tier, combien de points peut-on donner avant de descendre au palier suivant ?
|
||
2. On prend le minimum entre les points disponibles et les points restants à donner
|
||
3. 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) :
|
||
|
||
1. On récupère le **snapshot** de la jauge et de la stat au moment du démarrage du timer
|
||
2. On calcule les points gagnés depuis le snapshot : `gainedIn(snapshotJauge, tempsÉcoulé)`
|
||
3. On applique la direction (`dir`) :
|
||
- `dir = +1` (caresseur, foudroyeur, abreuvoir, dragofesse) : stat monte
|
||
- `dir = -1` (baffeur) : stat descend
|
||
4. On clamp la stat dans ses bornes (ex: sérénité entre -5000 et +5000)
|
||
5. 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 :
|
||
|
||
1. On détermine le taux fixe = `tierRate(snapshotJauge)` au démarrage
|
||
2. XP gagnée = `nombre_de_ticks × taux` (pas de dégression)
|
||
3. XP nécessaire = `xpForLevel(cible) - xpForLevel(départ)`
|
||
4. 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`)
|
||
1. Calcule `diff = cible - sérénité actuelle`
|
||
2. Si `diff > 0` → besoin du caresseur (monte la sérénité)
|
||
3. Si `diff < 0` → besoin du baffeur (baisse la sérénité)
|
||
4. Temps = `timeToGain(niveauJauge, |diff|)`
|
||
|
||
### Version live (`calcSerenEtaLive`)
|
||
1. Utilise `computeGaugeLive` pour obtenir la sérénité estimée en temps réel
|
||
2. Recalcule le diff depuis cette estimation
|
||
3. Calcule le temps restant avec les points encore à parcourir
|
||
|
||
---
|
||
|
||
## 9. ETA Niveau — `calcLevelEta` / `calcLevelEtaLive`
|
||
|
||
### Version statique (`calcLevelEta`)
|
||
1. XP nécessaire = `xpForLevel(cible) - xpForLevel(niveauActuel)`
|
||
2. Taux fixe = `tierRate(niveauMangeoire)`
|
||
3. Temps = `ceil(xpNécessaire / taux) × 10 sec`
|
||
|
||
### Version live (`calcLevelEtaLive`)
|
||
1. Utilise `computeGaugeLive` pour obtenir le niveau estimé actuel
|
||
2. XP restante = `xpForLevel(cible) - xpForLevel(niveauEstimé)`
|
||
3. Taux fixe = `tierRate(snapshotMangeoire)`
|
||
4. 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 ?"
|
||
|
||
1. Pour chaque DD × chaque jauge active → appeler `computeGaugeLive`
|
||
2. Prendre le **maximum** de tous les countdowns = le plus long à terminer
|
||
3. Compter combien de DD ont TOUTES leurs cibles atteintes (`ddDone`)
|
||
4. `allDone = true` si 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** :
|
||
1. On part de la race cible et de la quantité voulue
|
||
2. 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
|
||
3. 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 }`
|
||
- `m` et `f` = stock réel renseigné par le joueur
|
||
- `n` = 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) :
|
||
1. Lister tous les croisements possibles à cette génération
|
||
2. **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
|
||
3. 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émarrage
|
||
- `snapStats[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.
|