Je travail sur un programme pour attribuer des caractéristiques à des monstres.
Tout se passe bien à part à un endroit, où en théorie je conditionne l'obtention de la caractéristique "Spé 2" à l’obtention préalable de la compétence "Spé 1". Le programme me renvoi parfois des monstres sans la caractéristique "Spé 1" mais avec la caractéristique "Spé 2" (ce qui ne devrait pas être le cas).
Exemple:
La partie du code qui gère ça prend la forme d'une condition contenue dans une autre. Voici la partie en question:
tab_monstres = []
nb = 0
# Je peux tout regrouper dans un seul tableau?
compt_Cica = 0
compt_Spé_1 = 0
compt_Spé_2 = 0
compt_Petite = 0
compt_Grande = 0
compt_Discret = 0
compt_Craintif = 0
compt_Passif = 0
compt_Agressif = 0
compt_Enrage = 0
100.times do
nb = nb + 1
### Boucle principale.
tab_monstres[0] = nb
## Cica
alea = rand(99)
if alea < 10
tab_monstres[1] = "Cicatrisant"
compt_Cica = compt_Cica + 1
else
tab_monstres[1] = "/"
end
## Spé
alea = rand(99)
if alea < 50
tab_monstres[2] = "Spé 1"
compt_Spé_1 = compt_Spé_1 + 1
alea = rand(99)
if alea < 50
tab_monstres[3] = "Spé 2"
compt_Spé_2 = compt_Spé_2 + 1
else
tab_monstres[3] = "/"
end
else
tab_monstres[2] = "/"
end
## Petite zone
alea = rand(99)
if alea < 50
tab_monstres[4] = "Petite"
compt_Petite = compt_Petite + 1
else
tab_monstres[4] = "/"
end
## Grande zone
alea = rand(99)
if alea < 25
tab_monstres[5] = "Grande"
compt_Grande = compt_Grande + 1
else
tab_monstres[5] = "/"
end
## Carcatère:
alea = rand(99)
case alea
when 0..5
tab_monstres[6] = "Discret"
compt_Discret = compt_Discret + 1
when 6..25
tab_monstres[6] = "Craintif"
compt_Craintif = compt_Craintif + 1
when 26..75
tab_monstres[6] = "Passif"
compt_Passif = compt_Passif + 1
when 75..95
tab_monstres[6] = "Agressif"
compt_Agressif = compt_Agressif + 1
when 95..99
tab_monstres[6] = "Enragé"
compt_Enrage = compt_Enrage + 1
end
## Résultat
puts "-------------- Le monstre #{tab_monstres[0]} --------------"
# J'affiche le tableau à partir du second objet.
puts tab_monstres[1, tab_monstres.size]
end
puts "--------------------------------------------"
puts "Il y a:"
puts " #{compt_Cica} monstres avec des facultés cicatrisantes."
puts " #{compt_Spé_1} monstres avec un type particulier."
puts " #{compt_Spé_2} monstres avec deux types particuliers."
puts " #{compt_Petite} monstres avec une petite attaque de zone."
puts " #{compt_Grande} monstres avec une grande attaque de zone."
puts "Dont:"
puts " #{compt_Discret} monstres discrets."
puts " #{compt_Craintif} monstres craintifs."
puts " #{compt_Passif} monstres passifs."
puts " #{compt_Agressif} monstres agressifs."
puts " #{compt_Enrage} monstres enragés."
Je n'ai jamais rencontré ce genre de souci et je ne comprend pas pourquoi ça ne fonctionne pas. Auriez-vous une idée pour m'aiguiller ?
Édit:
Pour préciser, j'ai l'impression que lorsque le problème survient, le 2eme tirage de la variable "alea" renvoi " ", ce que je suppose être un "nil". Et une condition sous cette forme (voir ci-dessous) ne résout pas le problème:
Il me manque des notions ici represente par des `??`, c'est un devoir en tant de developpeur que de comprendre ce que l'on cherche a faire, ici sans info complementaire je vais avoir du mal.
En revanche on a deja une bonne base de depart.
Au niveau des capacitee, un point important, si Spe depend d'une autre, alors il faut un lien dans le code qui le represente, ici c'est volatile.
Une structure comme celle-ci serais bien plus saine pour la suite de ton programme.
Attention, ce n'est que un pansement, comme dit dans ma resolution c'est la conception qui est a revoir (sans parler de la semantique du Ruby, la je parle vraiment du code en lui meme).
Je trouve ton code assez interessant, tu pourrais iterer un grand nombre de fois dessus en ayant le focus sur 1 point a la fois, cela serais vraiment un bon exercice.
Hesite pas a revenir avec une partie de ton code corrige pour qu'on puisse te faire des critiques dessus.
J'ai appris le Ruby (et la programmation) via des tutos. J'ai conscience que je passe à côté de quelque chose, mais pour dire vrai je ne comprend pas ce que tu essai de me dire.
J'ai essayé de prendre du recul, mais mon code me semble logique. Même si pas du tout optimisé j'en convient (et sûrement très moche pour des gens qui ont un plus gros niveau).
Lorsque tu parle itération, je suppose que tu veux dire que comme mes conditions se ressemblent je peux créer une fonction unique à appeler? Je pense pouvoir également réunir mes nombreux compteurs dans un tableau unique. Mes premiers jets de code sont toujours très basiques, je comprend mieux ce que je fais au moins.
@umfred: Tout à fait. Puisque j'affiche directement les caractéristiques du monstre en question, il ne me semble pas essentiel de stocker ce dernier non?
****
Quoi qu'il en soit, mon code fait bien ce que je lui demande. Il me sort les caractéristiques de 100 monstres ainsi qu'une synthèse, que j'enregistre dans un fichier texte.
Voici le code actuellement:
# Purge du fichier de sauvegarde.
fichier_listeMonstres = File.open("./listeMonstres", "w")
fichier_listeMonstres.puts("")
fichier_listeMonstres.close
tab_monstres = []
stats_monstre = []
tab_lvl_monstres = []
nb = 0
lvl_monstre = 0
# Je peux tout regrouper dans un seul tableau?
compt_Cica = 0
compt_Spé_1 = 0
compt_Spé_2 = 0
compt_Petite = 0
compt_Grande = 0
compt_Normal = 0
compt_Discret = 0
compt_Craintif = 0
compt_Passif = 0
compt_Agressif = 0
compt_Enrage = 0
100.times do
nb = nb + 1
# Reset pour pas avoir de Spé 2 sans Spé 1.
tab_monstres[3] = "/"
### Boucle principale.
tab_monstres[0] = nb
## Cica
alea = rand(99)
if alea < 10
tab_monstres[1] = "Cicatrisant"
compt_Cica = compt_Cica + 1
else
tab_monstres[1] = "/"
end
## Spé
alea = rand(99)
if alea < 50
tab_monstres[2] = "Spé 1"
compt_Spé_1 = compt_Spé_1 + 1
alea = rand(99)
if alea < 50
tab_monstres[3] = "Spé 2"
compt_Spé_2 = compt_Spé_2 + 1
else
tab_monstres[3] = "/"
end
else
tab_monstres[2] = "/"
end
## Petite zone
alea = rand(99)
if alea < 50
tab_monstres[4] = "Petite"
compt_Petite = compt_Petite + 1
else
tab_monstres[4] = "/"
end
## Grande zone
alea = rand(99)
if alea < 25
tab_monstres[5] = "Grande"
compt_Grande = compt_Grande + 1
else
tab_monstres[5] = "/"
end
## Monstre avec seulement une attaque normale.
if tab_monstres[1] == "/" && tab_monstres[2] == "/" && tab_monstres[3] == "/" && tab_monstres[4] == "/" && tab_monstres[5] == "/"
compt_Normal = compt_Normal + 1
end
## Caractère:
alea = rand(99)
case alea
when 0..5
tab_monstres[6] = "Discret"
compt_Discret = compt_Discret + 1
when 6..25
tab_monstres[6] = "Craintif"
compt_Craintif = compt_Craintif + 1
when 26..75
tab_monstres[6] = "Passif"
compt_Passif = compt_Passif + 1
when 75..95
tab_monstres[6] = "Agressif"
compt_Agressif = compt_Agressif + 1
when 95..99
tab_monstres[6] = "Enragé"
compt_Enrage = compt_Enrage + 1
end
## Statistiques: Calcul à ajuster !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# lvl
alea = rand (1..10)
lvl = alea
stats_monstre[0] = lvl
tab_lvl_monstres[lvl] = tab_lvl_monstres[lvl].to_i + 1
# vita
alea = rand (50..100)
stats_monstre[1] = alea*lvl
# force
alea = rand (10..50)
stats_monstre[2] = alea*lvl
# agilité
alea = rand (10..50)
stats_monstre[3] = alea*lvl
# intelligence
alea = rand (10..50)
stats_monstre[4] = alea*lvl
# constitution
alea = rand (10..50)
stats_monstre[5] = alea*lvl
# vita_max
stats_monstre[1] = stats_monstre[5]/2 + stats_monstre [1]
## Résultat
# Sauvegarde des caractéristiques de chaque monstres.
fichier_listeMonstres = File.open("./listeMonstres", "a")
fichier_listeMonstres.puts("-------------- Le monstre #{tab_monstres[0]} --------------")
fichier_listeMonstres.puts(tab_monstres[1, tab_monstres.size])
fichier_listeMonstres.puts("lvl #{stats_monstre[0]}.")
fichier_listeMonstres.puts("#{stats_monstre[1]} de vitalité max.")
fichier_listeMonstres.puts("#{stats_monstre[2]} de force.")
fichier_listeMonstres.puts("#{stats_monstre[3]} d'agilité.")
fichier_listeMonstres.puts("#{stats_monstre[4]} d'intelligence.")
fichier_listeMonstres.puts("#{stats_monstre[5]} de constitution.")
fichier_listeMonstres.close
end
# Affichage de la synthèse.
puts("--------------------------------------------")
puts("Il y a:")
puts(" #{compt_Normal} monstres avec seulement une attaque normal.")
puts(" #{compt_Cica} monstres avec des facultés cicatrisantes.")
puts(" #{compt_Spé_1} monstres avec un type particulier.")
puts(" #{compt_Spé_2} monstres avec deux types particuliers.")
puts(" #{compt_Petite} monstres avec une petite attaque de zone.")
puts(" #{compt_Grande} monstres avec une grande attaque de zone.")
puts("")
puts("Dont:")
puts(" #{compt_Discret} monstres discrets.")
puts(" #{compt_Craintif} monstres craintifs.")
puts(" #{compt_Passif} monstres passifs.")
puts(" #{compt_Agressif} monstres agressifs.")
puts(" #{compt_Enrage} monstres enragés.")
puts("")
puts("Ils sont:")
10.times do
lvl_monstre = lvl_monstre + 1
puts(" #{tab_lvl_monstres[lvl_monstre]} de lvl #{lvl_monstre}.")
end
# Sauvegarde de la synthèse.
fichier_listeMonstres = File.open("./listeMonstres", "a")
fichier_listeMonstres.puts("--------------------------------------------")
fichier_listeMonstres.puts("Il y a:")
fichier_listeMonstres.puts(" #{compt_Normal} monstres avec seulement une attaque normal.")
fichier_listeMonstres.puts(" #{compt_Cica} monstres avec des facultés cicatrisantes.")
fichier_listeMonstres.puts(" #{compt_Spé_1} monstres avec un type particulier.")
fichier_listeMonstres.puts(" #{compt_Spé_2} monstres avec deux types particuliers.")
fichier_listeMonstres.puts(" #{compt_Petite} monstres avec une petite attaque de zone.")
fichier_listeMonstres.puts(" #{compt_Grande} monstres avec une grande attaque de zone.")
fichier_listeMonstres.puts("")
fichier_listeMonstres.puts("Dont:")
fichier_listeMonstres.puts(" #{compt_Discret} monstres discrets.")
fichier_listeMonstres.puts(" #{compt_Craintif} monstres craintifs.")
fichier_listeMonstres.puts(" #{compt_Passif} monstres passifs.")
fichier_listeMonstres.puts(" #{compt_Agressif} monstres agressifs.")
fichier_listeMonstres.puts(" #{compt_Enrage} monstres enragés.")
fichier_listeMonstres.puts("")
fichier_listeMonstres.puts("Ils sont:")
# Purge de la variable lvl_monstre
lvl_monstre = 0
10.times do
lvl_monstre = lvl_monstre + 1
fichier_listeMonstres.puts(" #{tab_lvl_monstres[lvl_monstre]} de lvl #{lvl_monstre}.")
end
fichier_listeMonstres.close
Je serai ravi de progresser grâce à vos critiques et conseils. N'hésitez pas.
Par contre je vais surement vous faire détailler vos propos, car comme je vous l'ai dis je suis un peu à la masse niveau vocabulaire et concepte de programmation.
> Par contre je vais surement vous faire détailler vos propos, car comme je vous l'ai dis je suis un peu à la masse niveau vocabulaire et concepte de programmation.
Pas de probleme, je vais expliciter mon 1er message.
Je vais mettre pas mal de lien externe, considere mon message plus comme un mini-cours que tu vas devoir suivre plusieurs fois, et probablement par petites iterations
> Puisque j'affiche directement les caractéristiques du monstre en question, il ne me semble pas essentiel de stocker ce dernier non?
En fait le soucis n'est pas:
Vu que j'ecrit de suite dans un fichier, pas besoin d'enregistrer
Mais
Vu que j'ecrit de suite dans un fichier, mon code est pas modulable
En programmation on essaie de suivre quelques principes assez pratique.
L'un des plus important et des moins bien compris est SOLID.
Pour en revenir sur le stockage dans une variable, c'est bien mieux de stocker tes monstres dans un seul array
que tu vas ensuite passer a une unitee de code qui vas le parcourir et enregistrer dans un fichier.
Pourquoi separer dans une unitee de code distincte ?
Aujourd'hui tu enregistres dans un fichier, rien ne t'interdis demain d'enregistrer dans une base de donnee.
L'architecture logiciel a pour objectif de remmetre au plus tard les choix sans retours.
> Lorsque tu parle itération, je suppose que tu veux dire que comme mes conditions se ressemblent je peux créer une fonction unique à appeler?
Lorsque je parle d'iteration, c'est le nombre de fois ou tu vas revenir dessus.
Tu ne peux pas corriger tout les soucis d'une traite a ton niveau et c'est bien normal.
Justement c'est pour ca que c'est interessant pour toi de poster au fur et a messure ton code ici.
Les personnes de la communautee pourrons alors le critiquer et te faire avancer, c'est le seul moyen
de bien progresser en developpement.
> Je pense pouvoir également réunir mes nombreux compteurs dans un tableau unique.
> Mes premiers jets de code sont toujours très basiques, je comprend mieux ce que je fais au moins.
Les premiers bout de codes sont rarement les bons, sauf si tu te fait guider.
Il y a deux grand guide plutot pas mal.
Papier crayon
Tests automatiques
Pour le Papier crayon, c'est de poser a l'avance ton probleme et de verifier ses limites.
C'est un travail cerebral assez important en general, qui demande pas mal de competences en amons
Pour les tests en revanche c'est un des exercices les plus bete possible.
Tu vas ecrire une unitee de code qui vas dire "Je veux un monstre" et tu vas ensuite coder
une autre unitee de code qui vas creer ce monstre.
Je parle d'unitee de code pour eviter de dire fonction, classe ou methode car on vas y revenir plus tard.
En revanche le mot "unitee" est assez important, car il dit que le code est auto-suffisant.
Bon, reprenons des parties de mon message initial, le soucis vu que tu ne me dit pas ce que tu n'as
pas compris je vais devoir deviner
> ubiquitous language
C'est une sorte de dictionnaire de ton domaine.
On programme toujours pour un domaine donnee, ici ca a l'air d'etre le jeu de role.
On vas donc utiliser le vocabulaire du jeu de role.
Le gros plus de l'ubiquitous language, c'est que ton code vas utiliser exactement les meme termes
que ceux qui sont present dans celui-ci.
Ainsi, tu vas pouvoir partager ton code avec ton ubiquitous language a n'importe qui et si la conception est propre,
il devrais comprendre le code sous ses yeux sans forcement connaitre la programmation.
Il m'est arriver de pair-programmer avec une personne du metier grace au fait que l'on parlait
la meme langue, c'est l'une des fonctionnalitee avec le moins de bug que j'ai produit de ma vie
> Monstre : Entiee Malefique ayant un pouvoir cicatrisant, des spe, une zone et un caractere.
> Pouvoir Cicatrisant: ??
> Spe : ??
> Zone : ??, celle ci comprend deux valeurs possibles, [Petite, Grande]
Ici c'est assez important donc de combler les ?? histoire de pouvoir comprendre ce qu'est une Spe par exemple.
Je suis un joueur donc pas de probleme pour moi, en revanche ca ne sera pas le cas pour tout le monde,
Je remarque un nouveau concepte dans ton nouveau code, les statistiques, il faut donc les ajouters a l'UL.
Statistique: ??
Vu qu'aujourd'hui elles ne servent qu'a du stockage en db, je ne peux pas deviner a quoi servent les statistiques.
Bon, entrons dans le dur du sujet, ta sementique.
Elle decoulera naturellement de ton ubiquitous language.
Creons notre element central, le monstre
class Monstre
attr_reader :pouvoir, :spes, :zones, :caractere, :statistiques
def initialize(pouvoir, spes, zones, caractere, statistiques)
@pouvoir = pouvoir
@spes = spes
@zones = zones
@caractere = caractere
@statistiques = statistiques
end
def normal_attack?
pouvoir.nil? && spes.empty? && zones.empty?
end
end
Ici toutes les info de ton monstres sont centraliser dans une classe.
Elle utilise d'autres element sementique, alors il faut les creer aussi.
class Statistiques
attr_reader :lvl, :vita, :force, :agilite, :intelligence, :constitution, :vita_max
def initialize(lvl, vita, force, agilite, intelligence, constitution)
@lvl = lvl
@vita = vita
@force = force
@agilite = agilite
@intelligence = intelligence
@constitution = constitution
@vita_max = constitution / 2 + vita
end
end
Deja les statistiques, comme ca on sais ce que l'on manipule, je t'avoue que stats_monstre[5] n'est pas
parlant pour moi, et je pense que pour toi non plus dans quelques jours/mois.
Pour les autres, pour le moment je ne sais pas trop comment tu comptes les utiliser a l'avenir, on vas donc
seulement les utilisers comme simple string
En revanche j'aime bien les enums, et ruby n'en propose pas, donc je fait sans ici.
Dans un vrais projet j'utiliserais cette lib bien utile: https://dry-rb.org/gems/dry-types/1.2/
Comme ca je pourrais utiliser des Enum pour les zones et spe, mais aussi des Monades pour
la presence ou non d'un pouvoir.
Bon, il serais temps de creer un monstre tu en dis quoi?
Pour construire un objet un peu complexe on utilise ce qu'il s'appelle une factory.
Cette factory vas creer des stats par rapport a des lancer de des.
Il nous faut une classe pour ces lancer de des
class RandomDice
def roll(min: 0, max:)
rand(min..max)
end
end
Pourquoi avoir une classe pour les lancer de des?
Pour pouvoir tester ton code !
Comment tu peux tester ton code si le comportement change a chaque lancer.
Tu vas pouvoir faire un des comme suit
class TestDice
def initialize(suite)
@suite = suite
@current = 0
end
def roll(min: 0, max:)
@suite[current].tap { @current += 1 }
end
end
La ton test deviens predictible.
Viens ensuite la factory:
class MonstreFactory
def initialize(dices)
@dices = dices
end
def build
Monstre.new(
POUVOIRS[build_pouvoir],
build_spes.map { SPES[_1] },
build_zones.map { ZONES[_1] },
CARACTERES[build_caractere],
build_statistiques
)
end
private
def build_pouvoir
roll = @dices.roll(max: 99)
if roll < 10
:cicatrisant
end
end
def build_spes
spes = []
roll = @dices.roll(max: 99)
if roll < 50
spes << :spe_une
second_spe_roll = @dices.roll(max: 99)
if second_spe_roll < 50
spes << :spe_deux
end
end
spes
end
def build_zones
zones = []
petite_zone_roll = @dices.roll(max: 99)
if petite_zone_roll < 50
zones << :petite
end
grande_zone_roll = @dices.roll(max: 99)
if grande_zone_roll < 25
zones << :grande
end
zones
end
def build_caractere
roll = @dices.roll(max: 99)
case roll
when 0..5 then :discret
when 6..25 then :craintif
when 26..75 then :passif
when 75..95 then :agressif
when 95..99 then :enrage
end
end
def build_statistiques
Statistiques.new(
@dices.roll(min: 1, max: 10),
@dices.roll(min: 50, max: 100),
@dices.roll(min: 10, max: 50),
@dices.roll(min: 10, max: 50),
@dices.roll(min: 10, max: 50),
@dices.roll(min: 10, max: 50)
)
end
end
Ici on construit le monstre.
On vas pouvoir en constuire plein regarde
# On construit la factory
factory = MonstreFactory.new(RandomDice.new)
# On peux l'appeller autant de fois que l'on veux
monstres = 2.times.map { factory.build }
Bon, on a des monstres, faut les enregistrer, pour ca, il nous faut un moyen de persistance.
Un repository fera l'affaire, c'est une classe qui se charge des entree/sortie de ton programme avec le monde exterieur.
Comme dit plus haut, tu veux qu'une unitee de code s'en occupe pour pouvoir la changer au besoin.
Cependent, on veux cette classe la plus debile possible, et on veux enregistrer des monstres, mais aussi une synthese.
Si on donne notre array de monstre dans le repository, il vas devoir trouver combiens il y a de monstres normal par exemple.
C'est pas notre envie, on veux pas melanger une ecrire et un traitement, alors on vas faire appel encore a une autre classe.
Une collection de monstres c'est quoi ? Un bestiaire ?
Hop, un nouveau terme dans notre ubiquitous language.
Bestiaire : Catalogue de monstre
Et on construit la classe. Je vais le faire en 2 temps.
Dans un 1er temps je la declare comme enumerable, car ca reste un array de monstre
class Bestiaire
include Enumerable
def initialize(monstres)
@monstres = monstres
end
def each(&block)
if block_given?
@monstres.each(&block)
else
to_enum(:each)
end
end
end
Ensuite viens les counts !
class Bestiaire
include Enumerable
def initialize(monstres)
@monstres = monstres
end
def each(&block)
if block_given?
@monstres.each(&block)
else
to_enum(:each)
end
end
def count_normal
count { |monstre| monstre.normal_attack? }
end
def count_cica
count { |monstre| monstre.pouvoir == POUVOIRS[:cicatrisant] }
end
def count_spe(spe)
count { |monstre| monstre.spes.include?(SPES[spe]) }
end
def count_zone(zone)
count { |monstre| monstre.zones.include?(ZONES[zone]) }
end
def count_caractere(caractere)
count { |monstre| monstre.caractere == CARACTERES[caractere] }
end
def levels
map { |monstre| monstre.statistiques.lvl }.tally
end
end
La c'est les differentes stats.
Point important, on viens de depasser le nombre de lignes de ton code actuelle, et il manque encore la sauvegarde en
fichier. C'est tout a fait normal, on a un peu plus d'outil donc un peu plus de lignes de code.
En revanche on arrive enfin a la last step, le repository.
On vas lui donner un bestiaire, et il vas ecrire dans un fichier.
require 'stringio'
class BestiaireFicherRepository
def initialize(path)
@path = path
end
def store_bestiaire(bestiaire)
fichier = File.open(@path, 'a')
io = ::StringIO.new
bestiaire.each_with_index do |monstre, index|
io << <<~TEXT
-------------- Le monstre #{index} --------------"
#{monstre.pouvoir} #{monstre.spes.join(' ')} #{monstre.zones.join(' ')} #{monstre.caractere}
lvl #{monstre.statistiques.lvl}
#{monstre.statistiques.vita_max} de vitalité max
#{monstre.statistiques.force} de force
#{monstre.statistiques.agilite} d'agilité
#{monstre.statistiques.intelligence} d'intelligence
#{monstre.statistiques.constitution} de constitution
TEXT
end
io << <<~TEXT
--------------------------------------------
Il y a:
#{bestiaire.count_normal} monstres avec seulement une attaque normal.
#{bestiaire.count_cica} monstres avec des facultés cicatrisantes.
#{bestiaire.count_spe(:spe_une)} monstres avec un type particulier
#{bestiaire.count_spe(:spe_deux)} monstres avec deux types particuliers
#{bestiaire.count_zone(:petite)} monstres avec une petite attaque de zone
#{bestiaire.count_zone(:grande)} monstres avec une grande attaque de zone
Dont:
- #{bestiaire.count_caractere(:discret)} monstres discrets.
- #{bestiaire.count_caractere(:craintif)} monstres craintifs.
- #{bestiaire.count_caractere(:passif)} monstres passifs.
- #{bestiaire.count_caractere(:agressif)} monstres agressifs.
- #{bestiaire.count_caractere(:enrage)} monstres enragés.
Ils sont:
#{level_display(bestiaire).join("\n")}
TEXT
fichier.puts(io.string)
end
private def level_display(bestiaire)
bestiaire.levels.each_with_object([]) do |(level, count), acc|
acc[level - 1] = count
end.map.with_index { |count, level| "#{count} de level #{level + 1}" }
end
end
C'est un peu dense, possiblement un peu decousu car je l'ai ecrit au fur et a mesure.
Avec ce code, tu t'en sortira mieux pour ajouter des fonctionnalitee, et le tester.
Je te le met au complet ici, et bonne chance.
Hesite pas a la moindre question.
CARACTERES = {
discret: 'Discret',
craintif: 'Craintif',
passif: 'Passif',
agressif: 'Agressif',
enrage: 'Enrage',
}
ZONES = {
petite: 'Petite',
grande: 'Grande',
}
SPES = {
spe_une: 'Spe 1',
spe_deux: 'Spe 2'
}
POUVOIRS = {
cicatrisant: 'Cicatrisant'
}
class Monstre
attr_reader :pouvoir, :spes, :zones, :caractere, :statistiques
def initialize(pouvoir, spes, zones, caractere, statistiques)
@pouvoir = pouvoir
@spes = spes
@zones = zones
@caractere = caractere
@statistiques = statistiques
end
def normal_attack?
pouvoir.nil? && spes.empty? && zones.empty?
end
end
class Statistiques
attr_reader :lvl, :vita, :force, :agilite, :intelligence, :constitution, :vita_max
def initialize(lvl, vita, force, agilite, intelligence, constitution)
@lvl = lvl
@vita = vita
@force = force
@agilite = agilite
@intelligence = intelligence
@constitution = constitution
@vita_max = constitution / 2 + vita
end
end
class TestDice
def initialize(suite)
@suite = suite
@current = 0
end
def roll(min: 0, max:)
@suite[current].tap { @current += 1 }
end
end
class RandomDice
def roll(min: 0, max:)
rand(min..max)
end
end
class Bestiaire
include Enumerable
def initialize(monstres)
@monstres = monstres
end
def each(&block)
if block_given?
@monstres.each(&block)
else
to_enum(:each)
end
end
def count_normal
count { |monstre| monstre.normal_attack? }
end
def count_cica
count { |monstre| monstre.pouvoir == POUVOIRS[:cicatrisant] }
end
def count_spe(spe)
count { |monstre| monstre.spes.include?(SPES[spe]) }
end
def count_zone(zone)
count { |monstre| monstre.zones.include?(ZONES[zone]) }
end
def count_caractere(caractere)
count { |monstre| monstre.caractere == CARACTERES[caractere] }
end
def levels
map { |monstre| monstre.statistiques.lvl }.tally
end
end
class MonstreFactory
def initialize(dices)
@dices = dices
end
def build
Monstre.new(
POUVOIRS[build_pouvoir],
build_spes.map { SPES[_1] },
build_zones.map { ZONES[_1] },
CARACTERES[build_caractere],
build_statistiques
)
end
private
def build_pouvoir
roll = @dices.roll(max: 99)
if roll < 10
:cicatrisant
end
end
def build_spes
spes = []
roll = @dices.roll(max: 99)
if roll < 50
spes << :spe_une
second_spe_roll = @dices.roll(max: 99)
if second_spe_roll < 50
spes << :spe_deux
end
end
spes
end
def build_zones
zones = []
petite_zone_roll = @dices.roll(max: 99)
if petite_zone_roll < 50
zones << :petite
end
grande_zone_roll = @dices.roll(max: 99)
if grande_zone_roll < 25
zones << :grande
end
zones
end
def build_caractere
roll = @dices.roll(max: 99)
case roll
when 0..5 then :discret
when 6..25 then :craintif
when 26..75 then :passif
when 75..95 then :agressif
when 95..99 then :enrage
end
end
def build_statistiques
Statistiques.new(
@dices.roll(min: 1, max: 10),
@dices.roll(min: 50, max: 100),
@dices.roll(min: 10, max: 50),
@dices.roll(min: 10, max: 50),
@dices.roll(min: 10, max: 50),
@dices.roll(min: 10, max: 50)
)
end
end
require 'stringio'
class BestiaireFicherRepository
def initialize(path)
@path = path
end
def store_bestiaire(bestiaire)
fichier = File.open(@path, 'a')
io = ::StringIO.new
bestiaire.each_with_index do |monstre, index|
io << <<~TEXT
-------------- Le monstre #{index} --------------"
#{monstre.pouvoir} #{monstre.spes.join(' ')} #{monstre.zones.join(' ')} #{monstre.caractere}
lvl #{monstre.statistiques.lvl}
#{monstre.statistiques.vita_max} de vitalité max
#{monstre.statistiques.force} de force
#{monstre.statistiques.agilite} d'agilité
#{monstre.statistiques.intelligence} d'intelligence
#{monstre.statistiques.constitution} de constitution
TEXT
end
io << <<~TEXT
--------------------------------------------
Il y a:
#{bestiaire.count_normal} monstres avec seulement une attaque normal.
#{bestiaire.count_cica} monstres avec des facultés cicatrisantes.
#{bestiaire.count_spe(:spe_une)} monstres avec un type particulier
#{bestiaire.count_spe(:spe_deux)} monstres avec deux types particuliers
#{bestiaire.count_zone(:petite)} monstres avec une petite attaque de zone
#{bestiaire.count_zone(:grande)} monstres avec une grande attaque de zone
Dont:
- #{bestiaire.count_caractere(:discret)} monstres discrets.
- #{bestiaire.count_caractere(:craintif)} monstres craintifs.
- #{bestiaire.count_caractere(:passif)} monstres passifs.
- #{bestiaire.count_caractere(:agressif)} monstres agressifs.
- #{bestiaire.count_caractere(:enrage)} monstres enragés.
Ils sont:
#{level_display(bestiaire).join("\n")}
TEXT
fichier.puts(io.string)
end
private def level_display(bestiaire)
bestiaire.levels.each_with_object([]) do |(level, count), acc|
acc[level - 1] = count
end.map.with_index { |count, level| "#{count} de level #{level + 1}" }
end
end
# On construit la factory
factory = MonstreFactory.new(RandomDice.new)
# On peux l'appeller autant de fois que l'on veux
monstres = 50.times.map { factory.build }
bestiaire = Bestiaire.new(monstres)
repository = BestiaireFicherRepository.new("./listeMonstres")
repository.store_bestiaire(bestiaire)
Merci pour cet exposé détaillé et sourcé. Je suis dans le rush en ce moment alors je n'ai pour l'instant pas mis en application tous tes conseils (je viens de voir ton message pour dire vrai). Dès que j'ai un créneau pour l'ordi je m'attaque à tout ça.
Merci encore une fois.
ps: Pour pas flood le fofo, je passerai sûrement via mp pour la suite de nos échange.
[RUBY] Une condition dans une condition.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Architecte logiciel - Software craftsmanship convaincu.
Architecte logiciel - Software craftsmanship convaincu.
Architecte logiciel - Software craftsmanship convaincu.