Created
December 2, 2025 10:39
-
-
Save naholyr/35eab4c0f4cdbd01576a3a60209664db to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const leD12blanc = [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, "*", "*"]; | |
| const leD12noir = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, "*", "*"]; | |
| const leD6noir = [0, 0, 0, 2, "x", "*"]; | |
| const leD6bleu = [0, 0, 1, 1, 2, "*"]; | |
| const leD6rouge = [1, 1, 2, 2, "*", "*"]; | |
| const leD6jaune = [0, 1, 2, 3, 3, "*"]; | |
| const lancer = (dé, nbLancers = 1) => { | |
| const résultats = []; | |
| for (let i = 0; i < nbLancers; i++) { | |
| const i = Math.floor(Math.random() * dé.length); | |
| résultats.push(dé[i]); | |
| } | |
| return résultats; | |
| }; | |
| const lancerD12 = (dé, nbLancers = 1, valeurSpéciale = 0) => { | |
| const résultats = lancer(dé, nbLancers); | |
| return résultats.reduce( | |
| (total, face) => | |
| total + (face === "*" ? valeurSpéciale : face === 1 ? 1 : 0), | |
| 0 | |
| ); | |
| }; | |
| const lancerAttaque = ({ nbDésRouges, nbDésJaunes, nbDégatsSpéciale = 0 }) => { | |
| const résultatsRouges = lancer(leD6rouge, nbDésRouges); | |
| const résultatsJaune = lancer(leD6jaune, nbDésJaunes); | |
| return résultatsRouges | |
| .concat(résultatsJaune) | |
| .reduce( | |
| (total, face) => total + (face === "*" ? nbDégatsSpéciale : face), | |
| 0 | |
| ); | |
| }; | |
| const lancerDéfense = ({ | |
| nbDésBleus, | |
| nbDésNoirs, | |
| nbBoucliersSpéciale = 0, | |
| esquiveSiSpéciale = false, | |
| }) => { | |
| const résultatsEsquive = lancer(leD6noir, nbDésNoirs); | |
| if ( | |
| résultatsEsquive.includes("x") || | |
| (esquiveSiSpéciale && résultatsEsquive.includes("*")) | |
| ) { | |
| return "esquive"; | |
| } | |
| const boucliersDésNoirs = résultatsEsquive.reduce( | |
| (total, face) => total + (face === "*" || face === "x" ? 0 : face), | |
| 0 | |
| ); | |
| const résultatsBleus = lancer(leD6bleu, nbDésBleus); | |
| const boucliersDésBleus = résultatsBleus.reduce( | |
| (total, face) => total + (face === "*" ? nbBoucliersSpéciale : face), | |
| 0 | |
| ); | |
| return boucliersDésBleus - boucliersDésNoirs; | |
| }; | |
| const résolutionAttaque = ({ | |
| attaque: { | |
| nbDésRouges = 0, | |
| nbDésJaunes = 0, | |
| nbDégatsSpéciale = 0, | |
| bonusDégats = 0, | |
| }, | |
| défense: { | |
| nbDésBleus = 0, | |
| nbDésNoirs = 0, | |
| nbBoucliersSpéciale = 0, | |
| esquiveSiSpéciale = false, | |
| bonusBoucliers = 0, | |
| }, | |
| }) => { | |
| const dégats = | |
| lancerAttaque({ nbDésRouges, nbDésJaunes, nbDégatsSpéciale }) + bonusDégats; | |
| const défense = lancerDéfense({ | |
| nbDésBleus, | |
| nbDésNoirs, | |
| nbBoucliersSpéciale, | |
| esquiveSiSpéciale, | |
| }); | |
| if (défense === "esquive") { | |
| return "annulé"; | |
| } | |
| const défenseFinale = défense + bonusBoucliers; | |
| if (défenseFinale > dégats) { | |
| return "annulé"; | |
| } | |
| return dégats - défenseFinale; | |
| }; | |
| const stopOuEncore = ({ | |
| nbD12blanc, | |
| nbSuccèsSpécial, | |
| nbD12noir, | |
| nbStopSpécial, | |
| nbMaxStop, | |
| }) => { | |
| let nbSuccès = 0; | |
| let nbStops = 0; | |
| while (nbStops < nbMaxStop) { | |
| nbSuccès += lancerD12(leD12blanc, nbD12blanc, nbSuccèsSpécial); | |
| nbStops += lancerD12(leD12noir, nbD12noir, nbStopSpécial); | |
| } | |
| if (nbStops > nbMaxStop) { | |
| return 0; | |
| } | |
| return nbSuccès; | |
| }; | |
| const dardDeFeu = (niveau = 1, nbBoucliersAuratiques = 0) => | |
| stopOuEncore({ | |
| nbD12blanc: niveau, | |
| nbSuccèsSpécial: 1, | |
| nbD12noir: 2, | |
| nbStopSpécial: nbBoucliersAuratiques, | |
| nbMaxStop: 2, | |
| }); | |
| const annulé = (résultat) => résultat === "annulé"; | |
| const simulation = (titre, itération, nbItérations = 10000) => { | |
| const résultats = []; | |
| for (let i = 0; i < nbItérations; i++) { | |
| résultats.push(itération()); | |
| } | |
| const nbAnnulés = résultats.filter(annulé).length; | |
| const nbÉchecs = résultats.filter((r) => annulé(r) || r === 0).length; | |
| const succès = résultats.filter((r) => !annulé(r) && r > 0); | |
| const max = Math.max(...succès); | |
| const min = Math.min(...succès); | |
| const mean = succès.reduce((acc, curr) => acc + curr, 0) / succès.length; | |
| const median = succès.sort((a, b) => a - b)[Math.floor(succès.length / 2)]; | |
| const p90 = succès.sort((a, b) => a - b)[Math.floor(succès.length * 0.9)]; | |
| console.log(`${titre}: | |
| Total échecs: ${(nbÉchecs / nbItérations) * 100}%${ | |
| nbAnnulés > 0 ? ` (${(nbAnnulés / nbItérations) * 100}% annulés)` : "" | |
| } | |
| Max succès: ${max} (${ | |
| (succès.filter((r) => r === max).length / nbItérations) * 100 | |
| }% des succès) | |
| Min succès: ${min} (${ | |
| (succès.filter((r) => r === min).length / nbItérations) * 100 | |
| }% des succès) | |
| Moyenne des succès: ${mean} | |
| Médiane des succès: ${median} | |
| P90 des succès: ${p90} (90% des succès sont inférieurs à cette valeur) | |
| `); | |
| }; | |
| simulation("dardDeFeu niveau 1, max 6 PA, sans bouclier auratique", () => | |
| dardDeFeu(1, 0) | |
| ); | |
| simulation("dardDeFeu niveau 2, max 6 PA, sans bouclier auratique", () => | |
| dardDeFeu(2, 0) | |
| ); | |
| simulation("dardDeFeu niveau 3, max 6 PA, sans bouclier auratique", () => | |
| dardDeFeu(3, 0) | |
| ); | |
| simulation("dardDeFeu niveau 1, max 6 PA, avec 1 bouclier auratique", () => | |
| dardDeFeu(1, 1) | |
| ); | |
| simulation("dardDeFeu niveau 2, max 6 PA, avec 1 bouclier auratique", () => | |
| dardDeFeu(2, 1) | |
| ); | |
| simulation("dardDeFeu niveau 3, max 6 PA, avec 1 bouclier auratique", () => | |
| dardDeFeu(3, 1) | |
| ); | |
| simulation("dardDeFeu niveau 1, max 6 PA, avec 2 boucliers auratiques", () => | |
| dardDeFeu(1, 2) | |
| ); | |
| simulation("dardDeFeu niveau 2, max 6 PA, avec 2 boucliers auratiques", () => | |
| dardDeFeu(2, 2) | |
| ); | |
| simulation("dardDeFeu niveau 3, max 6 PA, avec 2 boucliers auratiques", () => | |
| dardDeFeu(3, 2) | |
| ); | |
| simulation( | |
| "Lance (niveau 2) contre molosse (2 dés rouges + 2 dégats par face spéciale vs 2 dés noirs + esquive si face spéciale)", | |
| () => | |
| résolutionAttaque({ | |
| attaque: { nbDésRouges: 2, nbDégatsSpéciale: 2 }, | |
| défense: { | |
| nbDésNoirs: 2, | |
| esquiveSiSpéciale: true, | |
| }, | |
| }) | |
| ); | |
| simulation( | |
| "Épée (niveau 1) contre molosse (2 dés jaunes vs 2 dés noirs + esquive si face spéciale)", | |
| () => | |
| résolutionAttaque({ | |
| attaque: { nbDésJaunes: 2 }, | |
| défense: { | |
| nbDésNoirs: 2, | |
| esquiveSiSpéciale: true, | |
| }, | |
| }) | |
| ); | |
| simulation( | |
| "Épée (niveau 2) contre molosse (3 dés jaunes vs 2 dés noirs + esquive si face spéciale)", | |
| () => | |
| résolutionAttaque({ | |
| attaque: { nbDésJaunes: 3 }, | |
| défense: { | |
| nbDésNoirs: 2, | |
| esquiveSiSpéciale: true, | |
| }, | |
| }) | |
| ); | |
| simulation( | |
| "Molosse contre Ughnal pas très bien couvert (1 dé rouge + 1 choc vs 1 dé bleu + 1 bouclier par face spéciale + 1 bouclier de base", | |
| () => | |
| résolutionAttaque({ | |
| attaque: { nbDésRouges: 1, bonusDégats: 1 }, | |
| défense: { | |
| nbDésBleus: 1, | |
| nbBoucliersSpéciale: 1, | |
| bonusBoucliers: 1, | |
| }, | |
| }) | |
| ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment