Partage
  • Partager sur Facebook
  • Partager sur Twitter

Activité 2 Ruby: jeu de combat

Aide

Sujet résolu
    21 juin 2019 à 17:26:13

    Bonjour,

    J'ai commencé le cours de Ruby ce matin et j'en suis arrivé au stade de l'activité 2. Je le complète au fur et à mesure mais une erreur persiste sur la ligne 33 (@points_de_vie -= 10 #degats_recus)

    Si je garde le 10, le code marche parfaitement, mais si je le remplace par la variable degats_recus, ça plante avec comme erreur:

    jeu.rb:33:in `-': nil can't be coerced into Integer (TypeError)

    Je ne comprends pas pourquoi la variable est 'nil' alors quand je débug (de façon barbare) en mettant des "puts degats" ça m'affiche bien la valeur retournée par degats = rand(20..50) + @degats_bonus

    Ca fais plus de 3h que je suis dessus, j'ai essayé pas mal de truc mais rien ne va donc je poste mon code ici, parce que c'est en ayant le nez dedans qu'on ne voit pas ses erreurs les plus simples

    class Personne
        attr_accessor :nom, :points_de_vie, :en_vie
        
        def initialize(nom)
            @nom = nom
            @points_de_vie = 100
            @en_vie = true
        end
    
        def info
            # - Renvoie le nom et les points de vie si la personne est en vie
            # - Renvoie le nom et "vaincu" si la personne a été vaincue
            if points_de_vie > 0
                return nom + ":  #{@points_de_vie}/100 PV"
            else
                en_vie = false 
                return nom + ": vaincu"
            end
        end
        
        def attaque(personne)
            # - Fait subir des dégats à la personne passée en paramètre
            # - Affiche ce qu'il s'est passé
            puts "#{nom} attaque #{personne.nom}"
            personne.subit_attaque(degats)
        end
    
        def subit_attaque(degats_recus)
            # - Réduit les points de vie en fonction des dégats reçus
            # - Affiche ce qu'il s'est passé
            # - Détermine si la personne est toujours en_vie ou non
    
            @points_de_vie -= 10 #@degats_recus
    
            puts "#{nom} a subi #{degats_recus} de dégats, il lui reste #{points_de_vie} PV"
    
            if @points_de_vie <= 0
                @en_vie = false
                puts "#{nom} est mort"
            end
        end
    
    end
    
    class Joueur < Personne
        attr_accessor :degats_bonus
    
        def initialize(nom)
            # Par défaut le joueur n'a pas de dégats bonus
            @degats_bonus = 0
    
            # Appelle le "initialize" de la classe mère (Personne)
            super(nom)
        end
    
        def degats
            degats = rand(20..50) + @degats_bonus
            puts degats
            puts nom + " inflige #{degats} dégats"
            # - Calculer les dégats
            # - Affiche ce qu'il s'est passé
        end
    
        def soin
            # - Gagner de la vie
            # - Affiche ce qu'il s'est passé
            soin = rand(5..20)
            @points_de_vie += soin
            puts "#{nom} a gagné #{soin} points de vie"
        end
    
        def ameliorer_degats
            # - Augmenter les dégats bonus
            # - Affiche ce qu'il s'est passé
            @degats_bonus = rand(10..30)
            puts "#{nom} a chargé son attaque et bénéficie de #{@degats_bonus} dégats supplémentaire"
        end
    end
    
    class Ennemi < Personne
        def degats
            # - Calculer les dégats
            degats = rand(10..30)
        end
    end
    
    class Jeu
        def self.actions_possibles(monde)
            puts "ACTIONS POSSIBLES :"  
    
            puts "0 - Se soigner"
            puts "1 - Améliorer son attaque"
    
            # On commence à 2 car 0 et 1 sont réservés pour les actions
            # de soin et d'amélioration d'attaque
            i = 2
            monde.ennemis.each do |ennemi|
            puts "#{i} - Attaquer #{ennemi.info}"
            i = i + 1
            end
            puts "99 - Quitter"
        end
    
        def self.est_fini(joueur, monde)
            # - Déterminer la condition de fin du jeu
            adversaire = 0
            
            monde.ennemis.each do |ennemi|
                adversaire += 1 if ennemi.en_vie
            end
           
            if joueur.en_vie && adversaire > 0
                return false
                # Sinon, le jeu est terminé
            else
                return true
            end
        end
    end
    
    class Monde
        attr_accessor :ennemis
    
        def ennemis_en_vie
            # - Ne retourner que les ennemis en vie
            alive = []
            @ennemis.each do |ennemi|
                if ennemi.en_vie == true
                    alive << ennemi
                end
            end
            alive
        end
    end
    
    
    ##############
    
    # Initialisation du monde
    monde = Monde.new
    
    # Ajout des ennemis
    monde.ennemis = [
        Ennemi.new("Balrog"),
        Ennemi.new("Goblin"),
        Ennemi.new("Squelette")
    ]
    
    # Initialisation du joueur
    joueur = Joueur.new("Jean-Michel Paladin")
    
    # Message d'introduction. \n signifie "retour à la ligne"
    puts "\n\nAinsi débutent les aventures de #{joueur.nom}\n\n"
    
    # Boucle de jeu principale
    100.times do |tour|
        puts "\n------------------ Tour numéro #{tour} ------------------"
    
        # Affiche les différentes actions possibles
        Jeu.actions_possibles(monde)
    
        puts "\nQUELLE ACTION FAIRE ?"
        # On range dans la variable "choix" ce que l'utilisateur renseigne
        choix = gets.chomp.to_i
    
        # En fonction du choix on appelle différentes méthodes sur le joueur
        if choix == 0
            joueur.soin
        elsif choix == 1
            joueur.ameliorer_degats
        elsif choix == 99
            # On quitte la boucle de jeu si on a choisi
            # 99 qui veut dire "quitter"
            break
        else
            # Choix - 2 car nous avons commencé à compter à partir de 2
            # car les choix 0 et 1 étaient réservés pour le soin et
            # l'amélioration d'attaque
            ennemi_a_attaquer = monde.ennemis[choix - 2]
            joueur.attaque(ennemi_a_attaquer)
        end
    
        puts "\nLES ENNEMIS RIPOSTENT !"
        # Pour tous les ennemis en vie ...
        monde.ennemis_en_vie.each do |ennemi|
            # ... le héros subit une attaque.
            ennemi.attaque(joueur)
        end
    
        puts "\nEtat du héro: #{joueur.info}\n"
    
        # Si le jeu est fini, on interompt la boucle
        break if Jeu.est_fini(joueur, monde)
    end
    
    puts "\n Game Over \n"
    
    if joueur.en_vie
        puts "Vous avez gagné"
    else
        puts "Vous avez perdu"
    end



    -
    Edité par BaptisteII 21 juin 2019 à 17:32:33

    • Partager sur Facebook
    • Partager sur Twitter
      21 juin 2019 à 21:48:13

      Salut,

      Tes méthodes degats ne renvoient rien donc quand tu appelles degat à la ligne 25, c'est bien nil. Tes méthodes dégats devraient renvoyer le nombre de dégâts (et pas faire une affectation degats = .... Je rajoute quelques trucs.

      • Le nom devrait être en attr_reader et pas en attr_accessor, on ne veut pas donner accès en écriture au nom en dehors de la classe (on pourrait faire pareil pour les points de vie).
      • Plutôt que d'avoir une variable @en_vie, fais une méthode qui renvoie le booléen @points_de_vie &gt; 0.
      • Pareil, plutôt que d'avoir une méthode info, fais une méthode to_s, ça te permettra de faire directement puts personne.
      class Person
        attr_reader :name, :hp
      
        def initialize(name)
          @name = name
          @hp = 100
        end
      
        def to_s
          "#{@name} : #{@hp}/100 pv"
        end
      end
      
      p1 = Person.new("p1")
      p2 = Person.new("p2")
      puts p1
      puts p2
      
      • Tu peux faire utiliser la méthode select pour sélectionner les ennemis en vie. De même tu peux utiliser la méthode any? pour vérifier s'il y a un ennemi en vie. Pas la peine de les compter, tu veux juste savoir s'il en reste (d'ailleurs pour les compter tu pourrais utiliser la méthode count).

      Et je te conseille de lire ça pour avoir un code plus Ruby. Une des choses qu'on remarque c'est ton indentation de quatre espaces. Utilise plutôt deux espaces.

      • Partager sur Facebook
      • Partager sur Twitter
      Tutoriel Ruby - Bon tutoriel C - Tutoriel SDL 2 - Python avancé - Faîtes un zeste, devenez des zesteurs
        22 juin 2019 à 10:18:28

        Salut

        Déjà merci encore pour ton aide, un return que j'avais pas mis :/

        Ensuite merci pour ces conseils, malheureusement je ne connais pas encore le langage ruby et le squelette du code a été donné dans l'activité du cours :/ Mais je vais modifier mon code avec ces astuces :)

        Et pour l'indendation à 4 espaces, j'ai l'habitude de faire Tab, surtout quand je code en Python ou autres, et j'aime bien avoir de la lisibilité dans mon code ^^

        • Partager sur Facebook
        • Partager sur Twitter
          22 juin 2019 à 11:24:30

          C'est vrai que le cours est pas très complet et plutôt bof...

          Pour l'indentation, tu peux paramétrer ton éditeur pour qu'il mette deux espaces quand tu utilises la touche tabulation. Pour le coup, il s'agit de respecter les conventions utilisées par les gens qui codent en Ruby (tout comme en Python, la convention veut qu'on indente de quatre espaces). Là quelqu'un qui fait du Ruby aura du mal à lire ton code.

          • Partager sur Facebook
          • Partager sur Twitter
          Tutoriel Ruby - Bon tutoriel C - Tutoriel SDL 2 - Python avancé - Faîtes un zeste, devenez des zesteurs

          Activité 2 Ruby: jeu de combat

          × 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.
          • Editeur
          • Markdown