/* Implémentation en C++ du jeu de la vie pour tout mode dont les fonctions de configuration et d'affichage de pixels peuvent êtres fournies. cellmap stocke le compte de voisines pour chaque cellule ainsi que l'état de chaque cellule; ce qui permet de déterminer rapidement le prochain état. Les cotés boucles toujours dans cette implémentation. Testée avec Borland C++ en modèle large. Pour lancer ce programme, le Listing 17-2.cpp doit être inclus */ #include #include #include #include #include #include #include #include #define ON_COLOR 15 // couleur du pixel d'une cellule active #define OFF_COLOR 0 // couleur du pixel d'une cellule inactive #define MSG_LINE 10 // ligne des messages texte #define GENERATION_LINE 12 // colonne d'affichage de numéro de génération #define LIMIT_18_HZ 0 // à 1 pour un taux d'images maximal = 18Hz class cellmap { private: unsigned char *cells; unsigned char *temp_cells; unsigned int width; unsigned int height; unsigned int length_in_bytes; public: cellmap(unsigned int h, unsigned int v); ~cellmap(void); void set_cell(unsigned int x, unsigned int y); void clear_cell(unsigned int x, unsigned int y); int cell_state(int x, int y); int count_neighbors(int x, int y); void next_generation(void); void init(void); }; extern void enter_display_mode(void); extern void exit_display_mode(void); extern void draw_pixel(unsigned int X, unsigned int Y, unsigned int Color); extern void show_text(int x, int y, char *text); /* Contrôle la taille de la carte de cellule. Doit correspondre aux capacités du mode d'affichage et être limitée pour laisser de la place pour l'affichage du texte sur la droite. */ unsigned int cellmap_width = 96; unsigned int cellmap_height = 96; /* Largeur et hauteur de chaque cellule. */ unsigned int magnifier = 2; /* Valeur pour initialiser le nombre de générations pseudo-aléatoires */ unsigned int seed; void main() { unsigned long generation = 0; char gen_text[80]; long bios_time, start_bios_time; cellmap current_map(cellmap_height, cellmap_width); current_map.init(); // initialisation aléatoire de la carte de cellules enter_display_mode(); //Ne calcule et réaffiche une génération que lorsque une touche est pressée show_text(0, MSG_LINE, "Generation: "); start_bios_time = _bios_timeofday(_TIME_GETCLOCK, &bios_time); do { generation++; sprintf(gen_text, "%10lu", generation); show_text(1, GENERATION_LINE, gen_text); // Calcule et affiche la prochaine génération current_map.next_generation(); #if LIMIT_18_HZ // Limite à un maximum 18.2 images par seconde pour visualisation do { _bios_timeofday(_TIME_GETCLOCK, &bios_time); } while (start_bios_time == bios_time); start_bios_time = bios_time; #endif } while (!kbhit()); getch(); exit_display_mode(); cout << "Total de générations: " << generation << "\nValeur initiale: " << seed << "\n"; } /* Constructeur de l'objet cellmap . */ cellmap::cellmap(unsigned int h, unsigned int w) { width = w; height = h; length_in_bytes = w * h; cells = new unsigned char[length_in_bytes]; // stockage de cellules temp_cells = new unsigned char[length_in_bytes]; // stockage temporaire de cellules if ( (cells == NULL) || (temp_cells == NULL) ) { printf("plus de memoire disponible\n"); exit(1); } memset(cells, 0, length_in_bytes); // rend toutes les cellules inactives au démarrage } /* Destructeur de l'objet cellmap. */ cellmap::~cellmap(void) { delete[] cells; delete[] temp_cells; } /* Active une cellule inactive, augmentant le compte de cellules actives pour les huit cellules avoisinantes. */ void cellmap::set_cell(unsigned int x, unsigned int y) { unsigned int w = width, h = height; int xoleft, xoright, yoabove, yobelow; unsigned char *cell_ptr = cells + (y * w) + x; // Calcule les décalages des huit cellules avoisinantes, // utilisé pour le bouclage des cotés de la carte. if (x == 0) xoleft = w - 1; else xoleft = -1; if (y == 0) yoabove = length_in_bytes - w; else yoabove = -w; if (x == (w - 1)) xoright = -(w - 1); else xoright = 1; if (y == (h - 1)) yobelow = -(length_in_bytes - w); else yobelow = w; *(cell_ptr) |= 0x01; *(cell_ptr + yoabove + xoleft) += 2; *(cell_ptr + yoabove) += 2; *(cell_ptr + yoabove + xoright) += 2; *(cell_ptr + xoleft) += 2; *(cell_ptr + xoright) += 2; *(cell_ptr + yobelow + xoleft) += 2; *(cell_ptr + yobelow) += 2; *(cell_ptr + yobelow + xoright) += 2; } /* Désactive une cellule active, diminuant le compte des cellules actives pour les huit cellules avoisinantes. */ void cellmap::clear_cell(unsigned int x, unsigned int y) { unsigned int w = width, h = height; int xoleft, xoright, yoabove, yobelow; unsigned char *cell_ptr = cells + (y * w) + x; // Calcule les décalages des huit cellules avoisinantes, // utilisé pour le bouclage des cotés de la carte. if (x == 0) xoleft = w - 1; else xoleft = -1; if (y == 0) yoabove = length_in_bytes - w; else yoabove = -w; if (x == (w - 1)) xoright = -(w - 1); else xoright = 1; if (y == (h - 1)) yobelow = -(length_in_bytes - w); else yobelow = w; *(cell_ptr) &= ~0x01; *(cell_ptr + yoabove + xoleft) -= 2; *(cell_ptr + yoabove ) -= 2; *(cell_ptr + yoabove + xoright) -= 2; *(cell_ptr + xoleft) -= 2; *(cell_ptr + xoright) -= 2; *(cell_ptr + yobelow + xoleft) -= 2; *(cell_ptr + yobelow) -= 2; *(cell_ptr + yobelow + xoright) -= 2; } /* Retourne l'état d'une cellule (1=actif ou 0=inactif). */ int cellmap::cell_state(int x, int y) { unsigned char *cell_ptr; cell_ptr = cells + (y * width) + x; return *cell_ptr & 0x01; } /* Calcule et affiche la prochaine génération de current_map */ void cellmap::next_generation() { unsigned int x, y, count; unsigned int h = height, w = width; unsigned char *cell_ptr, *row_cell_ptr; // Copie la carte temporaire, nous avons donc une version inaltérée à // partir de laquelle nous pouvons travailler memcpy(temp_cells, cells, length_in_bytes); // Traite toutes les cellules dans la carte de cellules courante cell_ptr = temp_cells; // première cellule for (y=0; y= w) goto RowDone; } // a trouvé active soit une cellule soit sa voisine, // vérifions si son état ne doit pas être changé count = *cell_ptr >> 1; // nombre de voisines actives if (*cell_ptr & 0x01) { // La cellule est active; désactivons-la si elle n'a pas // 2 ou 3 voisines actives if ((count != 2) && (count != 3)) { clear_cell(x, y); draw_pixel(x, y, OFF_COLOR); } } else { // La cellule est inactive; activons-la si elle a exactement 3 voisines actives if (count == 3) { set_cell(x, y); draw_pixel(x, y, ON_COLOR); } } // Passons à la prochaine cellule cell_ptr++; // passons au prochain octet de cellule } while (++x < w); RowDone: } } /* Initialisation aléatoire de la carte d'environ 50 % pixels actifs */ { unsigned int x, y, init_length; // Récupère une valeur d'initialisation cout << "valeur d'initialisation (0 pour une valeur aléatoire): "; cin >> seed; if (seed == 0) seed = (unsigned) time(NULL); // Initialisation aléatoire de la première carte de cellules avec environ 50% pixels actifs // (en fait moins, car certaines coordonnées seront sélectionnées de manière aléatoire plus d'une fois) cout << "Initialisation..."; srand(seed); init_length = (height * width) / 2; do { x = random(width); y = random(height); if (cell_state(x, y) == 0) { set_cell(x, y); } } while (--init_length); }