Grid.cpp h - JulianKerignard/ProjetJeuDeLaVie_POO GitHub Wiki

Fichier Grid.h

#ifndef GRID_H
#define GRID_H

#include <vector>
#include "Cell.h"

// Enumération pour les différents motifs de cellules
enum class Pattern {
    GLIDER,  // Motif de planeur
    BLINKER, // Motif de clignotant
    BLOCK,   // Motif de bloc
    BEACON   // Motif de balise
};

// Classe représentant une grille de cellules pour le Jeu de la Vie
class Grid {
private:
    std::vector<std::vector<Cell>> cells;  // Grille de cellules
    int width;  // Largeur de la grille
    int height;  // Hauteur de la grille
    bool isToroidal;  // Mode torique (bords connectés)

    // Méthodes privées de gestion des indices et des voisins
    int getValidIndex(int index, int max) const;
    int countLiveNeighbors(int x, int y) const;

public:
    // Constructeur avec dimensions de la grille
    Grid(int width, int height);
    
    // Mise à jour des cellules de la grille
    void updateCells();
    
    // Accesseur pour une cellule (modifiable)
    Cell& getCellAt(int x, int y);
    
    // Accesseur pour une cellule (non-modifiable)
    const Cell& getCellAt(int x, int y) const;
    
    // Modificateur pour l'état d'une cellule
    void setCellAt(int x, int y, CellState state);
    
    // Obtient les voisins d'une cellule
    std::vector<std::pair<int, int>> getNeighbors(int x, int y) const;
    
    // Définit le mode torique de la grille
    void setToroidal(bool enabled) { isToroidal = enabled; }
    
    // Ajoute un obstacle à la grille
    void addObstacle(int x, int y);
    
    // Ajoute un motif à la grille
    void addPattern(Pattern pattern, int x, int y);
    
    // Accesseurs pour la largeur et la hauteur de la grille
    int getWidth() const { return width; }
    int getHeight() const { return height; }
    bool getToroidal() const { return isToroidal; }
};

#endif // GRID_H

Documentation des Membres

  • Enumération Pattern :

    • Définit les différents motifs de cellules :
      • GLIDER : Motif de planeur.
      • BLINKER : Motif de clignotant.
      • BLOCK : Motif de bloc.
      • BEACON : Motif de balise.
  • Classe Grid :

    • Représente une grille de cellules pour le Jeu de la Vie.

    • Membres privés :

      • std::vector<std::vector<Cell>> cells : Grille de cellules.
      • int width : Largeur de la grille.
      • int height : Hauteur de la grille.
      • bool isToroidal : Indique si la grille est en mode torique.
    • Membres publics :

      • Grid(int width, int height) :
        • Constructeur qui initialise la grille avec les dimensions spécifiées.
      • void updateCells() :
        • Met à jour l'état des cellules dans la grille.
      • Cell& getCellAt(int x, int y) :
        • Accesseur modifiable pour une cellule aux coordonnées spécifiées.
      • const Cell& getCellAt(int x, int y) const :
        • Accesseur non modifiable pour une cellule aux coordonnées spécifiées.
      • void setCellAt(int x, int y, CellState state) :
        • Modifie l'état de la cellule aux coordonnées spécifiées.
      • std::vector<std::pair<int, int>> getNeighbors(int x, int y) const :
        • Retourne les coordonnées des cellules voisines d'une cellule donnée.
      • void setToroidal(bool enabled) :
        • Active ou désactive le mode torique de la grille.
      • void addObstacle(int x, int y) :
        • Ajoute un obstacle à la grille aux coordonnées spécifiées.
      • void addPattern(Pattern pattern, int x, int y) :
        • Ajoute un motif de cellules à la grille aux coordonnées spécifiées.
      • int getWidth() const :
        • Retourne la largeur de la grille.
      • int getHeight() const :
        • Retourne la hauteur de la grille.
      • bool getToroidal() const :
        • Retourne l'état du mode torique de la grille.

Fichier Grid.cpp

Implémentation de la Classe Grid

Constructeur

  • Grid(int width, int height) :
    • Initialise une grille avec les dimensions spécifiées. Lance une exception si les dimensions ne sont pas positives.
Grid::Grid(int width, int height)
    : width(width), height(height), isToroidal(false) {
    if (width <= 0 || height <= 0) {
        throw std::invalid_argument("Grid dimensions must be positive");
    }
    cells.resize(height, std::vector<Cell>(width));
}

Méthodes

  • void setCellAt(int x, int y, CellState state) :
    • Modifie l'état de la cellule aux coordonnées spécifiées. Lance une exception si les coordonnées sont hors limites et que le mode torique est désactivé.
void Grid::setCellAt(int x, int y, CellState state) {
    int validX = x;
    int validY = y;

    if (isToroidal) {
        validX = ((x % width) + width) % width;
        validY = ((y % height) + height) % height;
    } else if (x < 0 || x >= width || y < 0 || y >= height) {
        throw std::out_of_range("Cell coordinates out of range");
    }

    cells[validY][validX] = Cell(state);
}
  • int getValidIndex(int index, int max) const :
    • Retourne l'index valide en tenant compte du mode torique.
int Grid::getValidIndex(int index, int max) const {
    if (isToroidal) {
        return ((index % max) + max) % max;
    }
    return index;
}
  • int countLiveNeighbors(int x, int y) const :
    • Compte le nombre de voisins vivants autour de la cellule aux coordonnées spécifiées.
int Grid::countLiveNeighbors(int x, int y) const {
    int count = 0;
    for (const auto& neighbor : getNeighbors(x, y)) {
        const Cell& neighborCell = getCellAt(neighbor.first, neighbor.second);
        if (!neighborCell.isObstacleCell() && neighborCell.getCurrentState() == CellState::ALIVE) {
            count++;
        }
    }
    return count;
}
  • void updateCells() :
    • Met à jour l'état des cellules de la grille. Utilise plusieurs threads pour améliorer les performances.
void Grid::updateCells() {
    auto start = std::chrono::high_resolution_clock::now();

    // Créer une copie de la grille pour les calculs
    auto newCells = cells;

    // Calculer le nombre optimal de threads
    int threadCount = std::min(static_cast<int>(std::thread::hardware_concurrency()), height);
    std::vector<std::thread> threads;
    threads.reserve(threadCount);

    // S'assurer qu'il y a au moins une ligne par thread
    const int rowsPerThread = std::max(1, height / threadCount);
    threadCount = (height + rowsPerThread - 1) / rowsPerThread;

    // Mise à jour des états
    for (int i = 0; i < threadCount; ++i) {
        int startRow = i * rowsPerThread;
        int endRow = std::min(startRow + rowsPerThread, height);

        threads.emplace_back([this, &newCells, startRow, endRow, i]() {
            std::cout << "Thread " << i << " traite les lignes " << startRow << " à " << endRow - 1 << std::endl;

            for (int y = startRow; y < endRow; y++) {
                for (int x = 0; x < width; x++) {
                    if (cells[y][x].isObstacleCell()) {
                        continue;
                    }

                    int liveNeighbors = countLiveNeighbors(x, y);
                    CellState currentState = cells[y][x].getCurrentState();

                    if (currentState == CellState::DEAD) {
                        if (liveNeighbors == 3) {
                            newCells[y][x] = Cell(CellState::ALIVE);
                        }
                    } else { // ALIVE
                        if (liveNeighbors < 2 || liveNeighbors > 3) {
                            newCells[y][x] = Cell(CellState::DEAD);
                        }
                    }
                }
            }
        });
    }

    // Attendre que tous les threads terminent
    for (auto& thread : threads) {
        thread.join();
    }

    // Mettre à jour la grille avec les nouveaux états
    cells = std::move(newCells);

    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "Mise à jour effectuée en " << duration.count()
              << " microsecondes avec " << threadCount << " threads" << std::endl;
}
  • Cell& getCellAt(int x, int y) :
    • Retourne une référence à la cellule aux coordonnées spécifiées.
Cell& Grid::getCellAt(int x, int y) {
    if (isToroidal) {
        x = ((x % width) + width) % width;
        y = ((y % height) + height) % height;
    } else if (x < 0 || x >= width || y < 0 || y >= height) {
        throw std::out_of_range("Cell coordinates out of range");
    }
    return cells[y][x];
}
  • const Cell& getCellAt(int x, int y) const :
    • Retourne une référence constante à la cellule aux coordonnées spécifiées.
const Cell& Grid::getCellAt(int x, int y) const {
    return const_cast<Grid*>(this)->getCellAt(x, y);
}
  • void addObstacle(int x, int y) :
    • Ajoute un obstacle à la grille aux coordonnées spécifiées.
void Grid::addObstacle(int x, int y) {
    if (isToroidal) {
        x = ((x % width) + width) % width;
        y = ((y % height) + height) % height;
    } else if (x < 0 || x >= width || y < 0 || y >= height) {
        throw std::out_of_range("Obstacle coordinates out of range");
    }
    cells[y][x].setObstacle(true);
}
  • std::vector<std::pair<int, int>> getNeighbors(int x, int y) const :
    • Retourne les coordonnées des cellules voisines d'une cellule donnée.
std::vector<std::pair<int, int>> Grid::getNeighbors(int x, int y) const {
    std::vector<std::pair<int, int>> neighbors;
    neighbors.reserve(8);

    for (int dy = -1; dy <= 1; dy++) {
        for (int dx = -1; dx <= 1; dx++) {
            if (dx == 0 && dy == 0) continue;

            int newX = x + dx;
            int newY = y + dy;

            if (isToroidal) {
                newX = ((newX % width) + width) % width;
                newY = ((newY % height) + height) % height;
                neighbors.emplace_back(newX, newY);
            } else if (newX >= 0 && newX < width && newY >= 0 && newY < height) {
                neighbors.emplace_back(newX, newY);
            }
        }
    }
    return neighbors;
}
  • void addPattern(Pattern pattern, int x, int y) :
    • Ajoute un motif de cellules à la grille aux coordonnées spécifiées.
void Grid::addPattern(Pattern pattern, int x, int y) {
    switch (pattern) {
        case Pattern::GLIDER:
            if (x + 2 < width && y + 2 < height) {
                setCellAt(x + 1, y, CellState::ALIVE);
                setCellAt(x + 2, y + 1, CellState::ALIVE);
                setCellAt(x, y + 2, CellState::ALIVE);
                setCellAt(x + 1, y + 2, CellState::ALIVE);
                setCellAt(x + 2, y + 2, CellState::ALIVE);
            }
            break;
        case Pattern::BLINKER:
            if (x + 2 < width && y < height) {
                setCellAt(x, y, CellState::ALIVE);
                setCellAt(x + 1, y, CellState::ALIVE);
                setCellAt(x + 2, y, CellState::ALIVE);
            }
            break;
        case Pattern::BLOCK:
            if (x + 1 < width && y + 1 < height) {
                setCellAt(x, y, CellState::ALIVE);
                setCellAt(x + 1, y, CellState::ALIVE);
                setCellAt(x, y + 1, CellState::ALIVE);
                setCellAt(x + 1, y + 1, CellState::ALIVE);
            }
            break;
        case Pattern::BEACON:
            if (x + 3 < width && y + 3 < height) {
                setCellAt(x, y, CellState::ALIVE);
                setCellAt(x + 1, y, CellState::ALIVE);
                setCellAt(x, y + 1, CellState::ALIVE);
                setCellAt(x + 3, y + 2, CellState::ALIVE);
                setCellAt(x + 2, y + 3, CellState::ALIVE);
                setCellAt(x + 3, y + 3, CellState::ALIVE);
            }
            break;
    }
}
⚠️ **GitHub.com Fallback** ⚠️