VatsalPandya47 / README.md

Hi there 👋, I'm Vatsal 🏢 Aspiring Software Engineer @University of Nebraska-Lincoln currently in Lincoln, NE 📫 How to reach me: vatsalpandya84@gmail.com
0 stars 0 forks source link

MazeFinder Project #2

Closed VatsalPandya47 closed 7 months ago

VatsalPandya47 commented 7 months ago

Project Overview: MazeFinder is an advanced C++ application that utilizes genetic algorithms and parallel processing to find optimal solutions for mazes of size 20x20. The project incorporates dynamic maze generation, sophisticated fitness evaluation, advanced selection, crossover, and mutation techniques, as well as multi-threading for improved performance.

Features and Components: Dynamic Maze Generation:

Randomly generates mazes of size 20x20 for each individual in the population. Uses algorithms such as randomized depth-first search (DFS) or Prim's algorithm for maze generation. Genetic Algorithm:

Implements a genetic algorithm to evolve solutions for maze traversal. Population size: 100 individuals. Number of generations: 1000. Individual Representation:

Each individual is represented by a genome, which consists of a sequence of movements (UP, DOWN, LEFT, RIGHT, STAND) to navigate the maze. Genome size: 400 (20x20 maze). Fitness Evaluation:

Evaluates the fitness of each individual based on maze traversal. Fitness function considers factors such as path length, dead ends, loops, and path completeness. Uses maze simulation to determine the fitness score of individuals. Selection Strategies:

Incorporates elitism to preserve the best individuals from each generation. Utilizes advanced selection methods such as roulette wheel selection, tournament selection, or rank-based selection to choose parents for crossover. Crossover and Mutation Operators:

Implements advanced crossover techniques including uniform crossover, arithmetic crossover, or simulated binary crossover. Mutation operators randomly insert, delete, or modify genes in the genome to maintain genetic diversity. Parallel Processing:

Utilizes multi-threading to parallelize fitness evaluation and other computationally intensive tasks. Implements dynamic thread pool management to adjust the number of worker threads based on system resources and workload. Thread Safety and Synchronization:

Ensures thread safety using mutexes and condition variables to prevent data races and ensure proper synchronization among threads. Visualization:

Provides visualization capabilities to display maze generation, path exploration, and algorithm progress in real-time using OpenGL or SDL libraries. Parameter Tuning:

Allows for experimentation with parameters such as population size, mutation rate, crossover rate, and selection pressure to optimize algorithm performance. Conclusion: The MazeFinder project offers a comprehensive solution for solving complex mazes using genetic algorithms and parallel processing techniques. By incorporating dynamic maze generation, advanced fitness evaluation, selection, crossover, and mutation strategies, as well as parallel processing, the project achieves efficient maze traversal and finds optimal solutions for mazes of size 20x20. Additionally, the project provides flexibility for parameter tuning and visualization, making it a versatile tool for maze-solving applications.

VatsalPandya47 commented 7 months ago

// Include necessary headers

include

include

include

include

include

include

include

include

include

using namespace std;

// Define constants for the maze dimensions const int MAZE_SIZE = 20; const int POPULATION_SIZE = 100; const int NUM_GENERATIONS = 1000; const double MUTATION_RATE = 0.1; const double CROSSOVER_RATE = 0.8;

// Define directions for movement in the maze enum class Direction { UP, DOWN, LEFT, RIGHT, STAND };

// Define a structure for representing an individual in the population struct Individual { vector genome; int fitness; };

// Mutex and condition variables for thread synchronization mutex mtx; condition_variable cv; bool ready = false;

// Define a thread-safe queue for the work items queue workQueue;

// Function to generate random directions for an individual's genome vector generateGenome() { vector genome; static random_device rd; static mt19937 gen(rd()); uniform_int_distribution<> dis(0, 4);

for (int i = 0; i < MAZE_SIZE * MAZE_SIZE; ++i) {
    genome.push_back(static_cast<Direction>(dis(gen)));
}
return genome;

}

// Function to evaluate the fitness of an individual int evaluateFitness(const vector& genome) { // Your fitness evaluation logic here (e.g., simulating maze traversal) // Return a fitness score return 0; }

// Function to perform crossover between two individuals Individual crossover(const Individual& parent1, const Individual& parent2) { Individual child; child.genome.reserve(parent1.genome.size()); static random_device rd; static mt19937 gen(rd()); uniform_real_distribution dis(0.0, 1.0);

for (size_t i = 0; i < parent1.genome.size(); ++i) {
    if (dis(gen) < CROSSOVER_RATE) {
        child.genome.push_back(parent1.genome[i]);
    } else {
        child.genome.push_back(parent2.genome[i]);
    }
}
return child;

}

// Function to perform mutation on an individual's genome void mutate(Individual& individual) { static random_device rd; static mt19937 gen(rd()); uniform_real_distribution dis(0.0, 1.0);

for (auto& gene : individual.genome) {
    if (dis(gen) < MUTATION_RATE) {
        gene = static_cast<Direction>((gene + 1) % 5); // Mutate the gene
    }
}

}

// Function for the worker thread void workerThread(vector& population) { while (true) { unique_lock lck(mtx); cv.wait(lck, []{ return !workQueue.empty(); }); int index = workQueue.front(); workQueue.pop(); lck.unlock();

    // Perform work for the individual at the given index
    population[index].fitness = evaluateFitness(population[index].genome);
}

}

// Function to visualize the maze and path void visualizeMaze(const vector& path) { // Your visualization logic here }

int main() { // Create a pool of worker threads vector threads; vector population(POPULATION_SIZE);

for (int i = 0; i < thread::hardware_concurrency(); ++i) {
    threads.emplace_back(workerThread, ref(population));
}

// Main loop for genetic algorithm
for (int generation = 0; generation < NUM_GENERATIONS; ++generation) {
    // Create and evaluate initial population
    for (int i = 0; i < POPULATION_SIZE; ++i) {
        population[i] = {generateGenome(), 0};
        workQueue.push(i);
    }

    // Notify worker threads to start processing
    {
        lock_guard<mutex> lck(mtx);
        ready = true;
        cv.notify_all();
    }

    // Wait for all work to be finished
    {
        unique_lock<mutex> lck(mtx);
        cv.wait(lck, []{ return workQueue.empty(); });
    }

    // Sort population based on fitness
    sort(population.begin(), population.end(), [](const Individual& a, const Individual& b) {
        return a.fitness > b.fitness;
    });

    // Perform selection, crossover, and mutation
    vector<Individual> newPopulation;
    newPopulation.reserve(POPULATION_SIZE);

    for (int i = 0; i < POPULATION_SIZE; ++i) {
        Individual parent1 = population[i];
        Individual parent2 = population[i < POPULATION_SIZE - 1 ? i + 1 : i - 1]; // Cyclic crossover

        Individual child = crossover(parent1, parent2);
        mutate(child);
        newPopulation.push_back(child);
    }

    // Replace the old population with the new one
    population = move(newPopulation);

    // Output the best individual of the current generation
    cout << "Generation " << generation << ": Best fitness = " << population[0].fitness << endl;

    // Visualize the best path
    visualizeMaze(population[0].genome);
}

// Join worker threads
for (auto& t : threads) {
    t.join();
}

return 0;

}