Jeu de la vie

Description

Une implémentation en C# du jeu de la vie, illustrant certaines méthodes permettant de gagner en rapidité d'exécution :
- Utilisation du ThreadPooling pour traiter en parallèle de grandes quantités de données.
- Création de graphisme directement à partir d'un tampon d'octets.
Cette implémentation permet aussi de charger ses propres "formes" et d'observer leur comportement.

Divers :
- La vitesse est limité à 100 génération / secondes.
- Si une cellule vivante est entourée par 2 ou 3 cellules vivantes elle survie, sinon elle meurt.
- Si une cellule morte est entourée par 2 cellules vivantes, elle revit.
- Le tiers central est laissé vide pour laisser place à une image Bitmap choisie par l'utilisateur.

Source / Exemple :


//-----------------------
        // Prend le tampon pour appliquer les règles de développement, Paramètre : l'indice du thread
        //-----------------------
        static private void Génération(object Numéro)
        {
            //Détermine sur quelle partie du buffer on travail
            int NuméroThread = (int)Numéro;
            int CellulesTotales = Programme.Fenetre.Hauteur * Programme.Fenetre.Largeur;
            int NombreDeCellules = CellulesTotales / Programme.Threads;
            int OffsetDépart = NuméroThread * NombreDeCellules;
            int TailleLigne = Programme.Fenetre.Largeur;
            byte[] Tampon = Programme.TamponModèle;

            //Remplissage
            for (int Indice = 0; Indice < NombreDeCellules; Indice++)
            {
                //Offset des cellules voisines
                int HautGauche = OffsetDépart - TailleLigne - 1;
                int HautMilieu = OffsetDépart - TailleLigne;
                int HautDroite = OffsetDépart - TailleLigne + 1;

                int MilieuGauche = OffsetDépart - 1;
                int MilieuMilieu = OffsetDépart;
                int MilieuDroite = OffsetDépart + 1;

                int BasGauche = OffsetDépart + TailleLigne - 1;
                int BasMilieu = OffsetDépart + TailleLigne;
                int BasDroite = OffsetDépart + TailleLigne + 1;

                //Valeur des cellules voisines à partir du tampon modèle
                //Système d'élimination des bords approximatif, mais plus rapide
                byte CelulleHautGauche;
                byte CelulleHautMilieu;
                byte CelulleHautDroite;
                byte CelulleMilieuGauche;

                if (HautGauche > 0)
                {
                    CelulleHautGauche = Tampon[HautGauche];
                    CelulleHautMilieu = Tampon[HautMilieu];
                    CelulleHautDroite = Tampon[HautDroite];
                    CelulleMilieuGauche = Tampon[MilieuGauche];                    
                }
                else
                {
                    CelulleHautGauche = 255;
                    CelulleHautMilieu = 255;
                    CelulleHautDroite = 255;
                    CelulleMilieuGauche = 255;
                }

                byte CelulleMilieuMilieu = Tampon[MilieuMilieu];

                byte CelulleMilieuDroite;
                byte CelulleBasGauche;
                byte CelulleBasMilieu;
                byte CelulleBasDroite;
                if (BasDroite < Programme.TamponTravail.Length)
                {
                    CelulleMilieuDroite = Tampon[MilieuDroite];
                    CelulleBasGauche = Tampon[BasGauche];
                    CelulleBasMilieu = Tampon[BasMilieu];
                    CelulleBasDroite = Tampon[BasDroite];
                }
                else
                {
                    CelulleMilieuDroite = 255;
                    CelulleBasGauche = 255;
                    CelulleBasMilieu = 255;
                    CelulleBasDroite = 255;
                }

                int Total = 0;
                if (CelulleHautGauche == 0) Total++;
                if (CelulleHautMilieu == 0) Total++;
                if (CelulleHautDroite == 0) Total++;
                if (CelulleMilieuGauche == 0) Total++;
                if (CelulleMilieuDroite == 0) Total++;
                if (CelulleBasGauche == 0) Total++;
                if (CelulleBasMilieu == 0) Total++;
                if (CelulleBasDroite == 0) Total++;

                //Case actuelle noire : prend la 'bonne' couleur si il y en 3 autres à coté
                if (CelulleMilieuMilieu != 0)
                {
                    //Résultat :
                    if (Total == 3) Programme.TamponTravail[MilieuMilieu] = 0;
                    else Programme.TamponTravail[MilieuMilieu] = 255;
                }                
                //Case actuelle pleine : reste en vie seulement si 2 ou 3 voisines
                else
                {
                    //Résultat :
                    if (Total == 3 || Total == 2) Programme.TamponTravail[MilieuMilieu] = 0;
                    else Programme.TamponTravail[MilieuMilieu] = 255;
                }

                OffsetDépart++;
            }
            
            //Indique qu'une opération est fini
            lock (Programme.Verrou) { Programme.Complétion++; }
        }

Conclusion :


// Mise à jour 06/11/2011 :

- Modification de la classe 'Pixel' en classe 'Couleur', plus succincte.
- La création de l'image est désormais elle-aussi accélérée par ThreadPooling.
- Factorisation du code déterminant la vie ou la mort d'une cellule.
- Utilisation d'une référence directe au tampon contenant l'image précédente pour éviter l'appel (lent) à l'accesseur.

- L'IHM affiche désormais le temps de conversion 'Cellules -> Image' en plus du temps de génération.
- L'IHM utilise désormais des timers pour l'affichage CPU/Mémoire au lieu de l'afficher à chaque génération.
- Le chargement d'image s'adapte désormais aux dimensions (levée de la contrainte de 64x64 pixels).
- Changement du système qui vérifiait la fin des ThreadPool : chaque thread a désormais sa propre 'case', ce qui évite les problèmes de concurrence.

-----------------------------------------------------------------------------

N'hésitez pas à poser vos questions / remarques / conseils / etc ...

Merci ;-)

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.