Avec un point d'arrêt, ça marche nickel, autrement c'est la cata [Résolu]

Signaler
Messages postés
10
Date d'inscription
mardi 29 juillet 2003
Statut
Membre
Dernière intervention
11 février 2010
-
Messages postés
10
Date d'inscription
mardi 29 juillet 2003
Statut
Membre
Dernière intervention
11 février 2010
-
Bonjour,
je m'entraîne sur C# depuis peu.
j'ai un bug depuis hier et je n'arrive pas à trouver la solution.
je dois faire un logiciel permettant de générer un lancé de dés.
j'ai créé une windows form application avec une fenêtre et deux classes.

Ma première classe est De, qui permet d'instancier un De et de lui changer sa face :
class De
    {
        // attributs
        private string faceApparente;
        private string cheminFace;
        private string nomFace;
        private string extensionFace;
        private int idDe;

        // constructeur
        public De()
        {
            cheminFace = "D:\\cours\\c#\";
            nomFace = "tp";
            extensionFace = ".jpg";
            this.faceApparente = null;
            idDe = 0;
        }

        // propriétés
        public int IdDe
        {
            get { return idDe; }
            set
            {
                idDe = value;
            }
        }

        public String FaceApparente
        {
            get { return faceApparente; }
            set {
                if (value != null)
                {
                    faceApparente = cheminFace + nomFace + value + extensionFace; 
                }
            }

        }

        // méthodes
        public void changerFaceApparente()
        {

            Random randomFaceApparente = new Random();
            IdDe = randomFaceApparente.Next(1,6);
            FaceApparente = System.Convert.ToString(IdDe);
            
        }

        public override string ToString()
        {
            return this.FaceApparente ;
        }
    }


ensuite j'ai une classe user qui me permet d'initialiser un nouveau joueur, avec son lancé de dés :
class user
    {
        // attributs
        private string nom;
        private string prenom;

        private De[] tabTotalDes;
        private De[] tabEquipageDes;
        private int nbDesEnJeu;
        private int nbLancees;

        public const int premier = 1;
        public const int deuxieme = 2;
        public const int troisieme = 3;
        

        // propriétés
        public string Nom
        {
            get { return nom; }
            set { nom = value; }
        }
        public string Prenom
        {
            get { return prenom; }
            set { prenom = value; }
        }
        public int NbLancees
        {
            get { return nbLancees ; }
            set { nbLancees = value; }
        }
        public int NbDesEnJeu
        {
            get { return nbDesEnJeu ; }
            set { nbDesEnJeu = value; }
        }
        public De[]  TabTotalDes
        {
            get { return tabTotalDes; }
            set { tabTotalDes = value; }
        }   

        // constructeur 
        public user(string nom, string prenom)
        {
            this.nom = nom;
            this.prenom = prenom;
            nbDesEnJeu = 6;
            tabTotalDes = new De[nbDesEnJeu]; 
            for (int i = 0; i < nbDesEnJeu; i++)
            {
                tabTotalDes[i] = new De();  
            }
            tabEquipageDes = new De[3];
        }

        // méthodes
        public void lancer()
        {
            nbLancees++; 
            
            for (int i = 0; i < nbDesEnJeu ; i++)
            {
                TabTotalDes[i].changerFaceApparente();
            }
        }

       
    }


j'ai ensuite une fenêtre.
Au chargement de cette dernière j'instancie un joueur.
ensuite j'ai un bouton "lancer !" qui permet de lancer la méthode lancer de la classe user :
 public partial class FormDemarrage : Form
    {
        user user1;
        De[] tabDes;
        System.Windows.Forms.GroupBox gbUser1;

        public FormDemarrage()
        {
            InitializeComponent();
        }

        public static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new FormDemarrage());
        }

        private void btnLancer_Click(object sender, EventArgs e)
        { 
            
            // lancer les dés
            tabDes = user1.TabTotalDes; 
            user1.lancer(); 

            // création d'un group box contenant les dés
            System.Windows.Forms.GroupBox gbLancer = new System.Windows.Forms.GroupBox();
            if (user1.NbLancees == user.premier)
            {
                gbLancer.Text = "Premier lancé";
                gbLancer.Location = new Point(6, 33);
                
            }
            else if(user1.NbLancees == user.deuxieme) 
            {
                gbLancer.Text = "Deuxième lancé";
                gbLancer.Location = new Point(6, 100);
            }
            else if (user1.NbLancees == user.troisieme)
            {
                gbLancer.Text = "Troisième lancé";
                gbLancer.Location = new Point(6, 167);
                btnLancer.Enabled = false;
            }

            gbLancer.Size = new Size(269, 60);
            this.gbUser1.Controls.Add(gbLancer);
            
            int x = 19;
            int y = 21;
            for (int i = 0; i < tabDes.Length  ; i++)
            {
                
                // création des controles images pour afficher les dés
                System.Windows.Forms.PictureBox image = new System.Windows.Forms.PictureBox ();
                image.Name = "de" + i.ToString();
                image.Location = new Point(x, y);
                image.Size = new Size(29,29);
                image.ImageLocation = tabDes[i].ToString();
                gbLancer.Controls.Add(image);
                x += 40; 
            }
            
  
            
        }

       

        private void FormDemarrage_Load(object sender, EventArgs e)
        {
            // instanciation d'un user
            user1 = new user("Tonton", "Elodie");
            // création d'une zone pour le joueur
            gbUser1 = new System.Windows.Forms.GroupBox();
            gbUser1.Text = user1.Nom + " " + user1.Prenom ;
            gbUser1.Location = new Point(12,12);
            gbUser1.Size = new Size(285,335);
            this.Controls.Add(gbUser1); 
            
        }
    }

et là gros bug, j'ai toujours la même valeur qui s'affiche sur chacun de mes dés.
Quand je met par contre un point d'arrêt sur la méthode lancer, cela fonctionne.

donc en bref, ce que ça devrait faire :

et voilà ce que ça fait :


j'espère que j'ai bien expliqué mon problème

si quelqu'un peut m'aider ça serait vraiment génial.

bonne journée

9 réponses

Messages postés
1860
Date d'inscription
lundi 28 novembre 2005
Statut
Modérateur
Dernière intervention
14 février 2015
42
Tu dois sortir le new Random() de ta boucle.
(en gros, tu dois mettre randomFaceApparente en attribut de ta classe et non variable de ta méthode, afin de n'appeler le new Random() qu'une seule fois)

En gros (mais alors vraiment pour simplifier), random c'est, en quelques sortes, une fonction qui associe un nombre à un indice :
0 => 57
1 => 18633
2 => 94237
3 => 4731
... (je mets n'importe quoi, hein)

Et quand tu crées un new Random(int graine), tu te places à l'indice "graine". Et à chaque fois que tu fais rand.Next(), il se déplace à l'indice suivant.

Donc dans mon exemple, si tu fais new Random(1)
rand.Next(); // 94327
rand.Next(); // 4731
...

Maintenant, si tu fais à chaque fois un new Random(1) :
rand = new Random(1);
rand.Next(); // 94327
rand = new Random(1);
rand.Next(); // 94327
rand = new Random(1);
rand.Next(); // 94327
...

Cependant, dans ton cas, tu ne précise effectivement pas la graine. Dans ce cas, il prend automatiquement comme graine une valeur dépendant du temps (un truc du genre le nombre de millisecondes passées depuis le 1er janvier 1970).

Résultat, quand tu fais ton new Random() tu n'as aucune idée du nombre qu'il te génèrera. Cependant, si tu fais un new Random() moins d'une milliseconde après, il créera un nouveau nombre aléatoire avec la même graine, donc qui te génèrera le même nombre.

C'est pour cette raison que ça marche quand tu fais un point d'arrêt (puisqu'il se passe plus d'une milliseconde entre tes 2 new Random()).

Pour résumer, un new Random() ne doit surtout pas être mis dans une boucle.

Krimog : while (!(succeed = try())) ;
- Nous ne sommes pas des décodeurs ambulants. Le style SMS est prohibé. -
Messages postés
1860
Date d'inscription
lundi 28 novembre 2005
Statut
Modérateur
Dernière intervention
14 février 2015
42
PS : quand tu lances quelque chose, tu effectue un "lancer", pas un lancé.

Krimog : while (!(succeed = try())) ;
- Nous ne sommes pas des décodeurs ambulants. Le style SMS est prohibé. -
Messages postés
10
Date d'inscription
mardi 29 juillet 2003
Statut
Membre
Dernière intervention
11 février 2010

Merci pour la réponse
je suis bien contente j'ai compris pas mal de choses
mais du coup il faudrait que j'instancie un random avant d'instancier mon tableau de dés ??
Messages postés
10
Date d'inscription
mardi 29 juillet 2003
Statut
Membre
Dernière intervention
11 février 2010

j'ai finalement changé mon code
en mettant un attribut de ma classe user, randomFaceApparente en tant que random et je l'instancie à l'instanciation de mon user
mais je trouve ça pas très propre quand meme.
il y a peut être une autre solution non ?
Messages postés
268
Date d'inscription
lundi 1 mars 2004
Statut
Membre
Dernière intervention
19 avril 2012
11
En fait, de suite il me vient deux solutions, tu as celle où tu génère un random pour chaque dé :

class De
    {
        // attributs
        private string faceApparente;
        private string cheminFace;
        private string nomFace;
        private string extensionFace;
        private int idDe;
        private Random randomFaceApparente;

        // constructeur
        public De()
        {
            cheminFace = "D:\\cours\\c#\";
            nomFace = "tp";
            extensionFace = ".jpg";
            this.faceApparente = null;
            idDe = 0;
            randomFaceApparente = new Random();
        }

        // propriétés
        public int IdDe
        {
            get { return idDe; }
            set
            {
                idDe = value;
            }
        }

        public String FaceApparente
        {
            get { return faceApparente; }
            set {
                if (value != null)
                {
                    faceApparente = cheminFace + nomFace + value + extensionFace; 
                }
            }

        }

        // méthodes
        public void changerFaceApparente()
        {
            IdDe = randomFaceApparente.Next(1,6);
            FaceApparente = System.Convert.ToString(IdDe);
        }

        public override string ToString()
        {
            return this.FaceApparente ;
        }
    }


Ou alors tu créer un seul et unique random dans la classe user et tu t'en sert en le placant dans la méthode changerFaceApparente.

Les deux classes deviendraient :

class De
    {
        // attributs
        private string faceApparente;
        private string cheminFace;
        private string nomFace;
        private string extensionFace;
        private int idDe;

        // constructeur
        public De()
        {
            cheminFace = "D:\\cours\\c#\";
            nomFace = "tp";
            extensionFace = ".jpg";
            this.faceApparente = null;
            idDe = 0;
        }

        // propriétés
        public int IdDe
        {
            get { return idDe; }
            set
            {
                idDe = value;
            }
        }

        public String FaceApparente
        {
            get { return faceApparente; }
            set {
                if (value != null)
                {
                    faceApparente = cheminFace + nomFace + value + extensionFace; 
                }
            }

        }

        // méthodes
        public void changerFaceApparente(Random rnd)
        {
            IdDe = rnd.Next(1,6);
            FaceApparente = System.Convert.ToString(IdDe);
        }

        public override string ToString()
        {
            return this.FaceApparente ;
        }
    }


et la classe user :

class user
    {
        // attributs
        private string nom;
        private string prenom;

        private De[] tabTotalDes;
        private De[] tabEquipageDes;
        private int nbDesEnJeu;
        private int nbLancees;

        private Random randomFaceApparente;

        public const int premier = 1;
        public const int deuxieme = 2;
        public const int troisieme = 3;
        

        // propriétés
        public string Nom
        {
            get { return nom; }
            set { nom = value; }
        }
        public string Prenom
        {
            get { return prenom; }
            set { prenom = value; }
        }
        public int NbLancees
        {
            get { return nbLancees ; }
            set { nbLancees = value; }
        }
        public int NbDesEnJeu
        {
            get { return nbDesEnJeu ; }
            set { nbDesEnJeu = value; }
        }
        public De[]  TabTotalDes
        {
            get { return tabTotalDes; }
            set { tabTotalDes = value; }
        }   

        // constructeur 
        public user(string nom, string prenom)
        {
            this.nom = nom;
            this.prenom = prenom;
            nbDesEnJeu = 6;
            randomFaceApparente = new Random();
            tabTotalDes = new De[nbDesEnJeu]; 
            for (int i = 0; i < nbDesEnJeu; i++)
            {
                tabTotalDes[i] = new De();  
            }
            tabEquipageDes = new De[3];
        }

        // méthodes
        public void lancer()
        {
            nbLancees++; 
            
            for (int i = 0; i < nbDesEnJeu ; i++)
            {
                TabTotalDes[i].changerFaceApparente(randomFaceApparente);
            }
        }

       
    }


Après techniquement, il vaudrait mieux la deuxième solution, elle est à mon sens plus logique et tu ne trimballe en mémoire qu'une seule instance de random au lieu de nbDesEnJeu...
Messages postés
1860
Date d'inscription
lundi 28 novembre 2005
Statut
Modérateur
Dernière intervention
14 février 2015
42
Les deux codes de MasterShadows sont bons, et propres.
Cependant, je vois encore d'autres solutions :

- Tu fais ton randomFaceApparente.Next() dans la méthode lancer() de user, et donc tu fournis une valeur bien précise à changerFaceApparente(). (c'est probablement cette méthode que j'utiliserais, personnellement)
En gros, ton code c'est
randomFaceApparente = new Random();
for(int i = 0; i < nbDesEnJeu; i++)
{
    int valeur = randomFaceApparente.Next(1, 6);
    TabTotalDes[i].changerFaceApparente(valeur);
}

- Tu définis ton objet randomFaceApparente en static dans ta classe De. Ainsi, il y en aura un seul (accessible via De.randomFaceApparente) pour tous tes dés, et non un par dé.

Krimog : while (!(succeed = try())) ;
- Nous ne sommes pas des décodeurs ambulants. Le style SMS est prohibé. -
Messages postés
10
Date d'inscription
mardi 29 juillet 2003
Statut
Membre
Dernière intervention
11 février 2010

merci MasterShadows mais ta première solution ne fonctionne pas, je l'ai testé.
la deuxième c'est celle que j'ai adopté mais je ne suis pas très contente puis que j'ai un random dans la classe user, alors que c'est un random lié à mon dé.
du coup, c'est pas très propre, je me suis dit que je pouvais éventuellement utiliser des threads pour générer pour chaque dé, un random.

quant à la solution krimog, ça me parait etre la meilleure en attendant de trouver mieux :p
merci beaucoup les gens, ça m'a bien aidée à avancer !!!
Messages postés
1860
Date d'inscription
lundi 28 novembre 2005
Statut
Modérateur
Dernière intervention
14 février 2015
42
Tu n'as pas 50000 solutions non plus.
- Soit tu définis 1 random par dé (que tu initialise dans le constructeur). - Soit tu définis 1 random dans le user, que tu transmets à ton dé
- Soit tu définis 1 random dans le user, dont tu transmets la valeur à ton dé
- Soit tu définis 1 random static dans la classe dé, qui correspond donc à la classe dé et non à un objet dé

Ces 4 solutions sont parfaitement propres (peut-être moins cependant pour la 2ème).

Krimog : while (!(succeed = try())) ;
- Nous ne sommes pas des décodeurs ambulants. Le style SMS est prohibé. -
Messages postés
10
Date d'inscription
mardi 29 juillet 2003
Statut
Membre
Dernière intervention
11 février 2010

j'y ai réfléchis et c'est vrai que c'est propre finalement
désolée