From af7e23ab119eba7c0579796abd288c027edabfa9 Mon Sep 17 00:00:00 2001 From: Paul Oliver Date: Thu, 29 Feb 2024 19:20:22 +0100 Subject: Initial commit --- src/App.cpp | 538 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 538 insertions(+) create mode 100644 src/App.cpp (limited to 'src/App.cpp') diff --git a/src/App.cpp b/src/App.cpp new file mode 100644 index 0000000..c97061e --- /dev/null +++ b/src/App.cpp @@ -0,0 +1,538 @@ +#include +#include +#include +#include +#include +#include + +using namespace hn; +using namespace sf; +using namespace tbb; +using namespace std; + +int +App::startup() +{ + ifstream paramsFile("parameters"); + ifstream popFile("current.population"); + + if (!paramsFile) { + return EXIT_FAILURE; + } + + cout << "Type hours to run sim.: "; + cin >> _secondsToRun; + _secondsToRun *= (60 * 60); + + bool popLoaded = false; + + if (popFile) { + popLoaded = true; + + LoadFile lFile(popFile); + lFile.loadPopulation(_population); + + _popPrms = _population.getPopulationPrms(); + _doNoveltySearch = _population.isNoveltyMetricSet(); + } + + auto until = numeric_limits::max(); + + while (!paramsFile.eof()) { + paramsFile.ignore(until, '<'); + + string command; + getline(paramsFile, command, '>'); + + if (!popLoaded && command == "POPULATION_PARAMETERS") { + paramsFile.ignore(until, '<'); + + while (paramsFile.peek() != '/') { + string subCommand; + getline(paramsFile, subCommand, '>'); + + if (subCommand == "populationSize") { + paramsFile >> _popPrms._popSize; + } else if (subCommand == "seed") { + paramsFile >> _popPrms._seed; + } else if (subCommand == "weightRange") { + paramsFile >> _popPrms._weightRange; + } else if (subCommand == "c1Disjoint") { + paramsFile >> _popPrms._c1Disjoint; + } else if (subCommand == "c3WeightDifference") { + paramsFile >> _popPrms._c3WeightDifference; + } else if (subCommand == "populationSize") { + paramsFile >> _popPrms._popSize; + } else if (subCommand == "initialDistanceThreshold") { + paramsFile >> _popPrms._initialDistanceThreshold; + } else if (subCommand == "distanceThresholdShift") { + paramsFile >> _popPrms._distanceThresholdShift; + } else if (subCommand == "sexualReproductionRate") { + paramsFile >> _popPrms._sexualReproductionRate; + } else if (subCommand == "weightMutationRate") { + paramsFile >> _popPrms._weightMutationRate; + } else if (subCommand == "weightDeviation") { + paramsFile >> _popPrms._weightDeviation; + } else if (subCommand == "interspeciesMatingRate") { + paramsFile >> _popPrms._interspeciesMatingRate; + } else if (subCommand == "geneDisablingRatio") { + paramsFile >> _popPrms._geneDisablingRatio; + } else if (subCommand == "linkMutationRate") { + paramsFile >> _popPrms._linkMutationRate; + } else if (subCommand == "nodeMutationRate") { + paramsFile >> _popPrms._nodeMutationRate; + } else if (subCommand == "targetSpeciesCount") { + paramsFile >> _popPrms._targetSpeciesCount; + } else if (subCommand == "eligibilityRatio") { + paramsFile >> _popPrms._eligibilityRatio; + } else if (subCommand == "minimumLifetime") { + paramsFile >> _popPrms._minimumLifetime; + } else if (subCommand == "replBeforeReorganization") { + paramsFile >> _popPrms._replBeforeReorganization; + } + + paramsFile.ignore(until, '<'); + } + } else if (command == "NEURAL_NET_PARAMETERS") { + paramsFile.ignore(until, '<'); + + while (paramsFile.peek() != '/') { + string subCommand; + getline(paramsFile, subCommand, '>'); + + if (subCommand == "testGridLevel") { + paramsFile >> _nnPrms._testGridLevel; + } else if (subCommand == "maxQuadTreeLevel") { + paramsFile >> _nnPrms._maxQuadTreeLevel; + } else if (subCommand == "minQuadTreeLevel") { + paramsFile >> _nnPrms._minQuadTreeLevel; + } else if (subCommand == "bandPruningThreshold") { + paramsFile >> _nnPrms._bandPruningThreshold; + } else if (subCommand == "varianceThreshold") { + paramsFile >> _nnPrms._varianceThreshold; + } else if (subCommand == "divisionThreshold") { + paramsFile >> _nnPrms._divisionThreshold; + } else if (subCommand == "searchIterations") { + paramsFile >> _nnPrms._iterations; + } + + paramsFile.ignore(until, '<'); + } + } else if (command == "NOVELTY_SEARCH_PARAMETERS") { + paramsFile.ignore(until, '<'); + + while (paramsFile.peek() != '/') { + string subCommand; + getline(paramsFile, subCommand, '>'); + + if (subCommand == "doNoveltySearch") { + paramsFile >> _doNoveltySearch; + } else if (subCommand == "noveltyThreshold") { + paramsFile >> _noveltyPrms._noveltyThreshold; + } else if (subCommand == "referenceOrganisms") { + paramsFile >> _noveltyPrms._referenceOrganisms; + } + + _noveltyPrms._characterizationSize = 3; + + paramsFile.ignore(until, '<'); + } + } else if (command == "STAGE_PARAMETERS") { + paramsFile.ignore(until, '<'); + + while (paramsFile.peek() != '/') { + string subCommand; + getline(paramsFile, subCommand, '>'); + + if (subCommand == "file") { + string fileName; + paramsFile >> fileName; + + if (_stage.load(fileName) == EXIT_FAILURE) { + return EXIT_FAILURE; + } + } else if (subCommand == "draw") { + paramsFile >> _draw; + } + + paramsFile.ignore(until, '<'); + } + } + } + + if (!_draw) { + _isVSyncEnabled = false; + _isPaused = false; + } + + _nnPrms._inputMap = { + { -1.0, -0.5 }, + { -0.7, 0.2 }, + { 0.0, 0.5 }, + { 0.7, 0.2 }, + { 1.0, -0.5 } + }; + + _nnPrms._outputMap = { + { -0.5, -0.5 }, + { 0.0, 0.0 }, + { 0.5, -0.5 } + }; + + if (!popLoaded) { + _population.create(_popPrms, _nnPrms); + } + + if (_doNoveltySearch && !_population.isNoveltyMetricSet()) { + _population.setNoveltyMetric(_noveltyPrms); + } else if (!_doNoveltySearch && _population.isNoveltyMetricSet()) { + _population.clearNoveltyMetric(); + } + + _agents.resize(_popPrms._popSize, Agent()); + + for (size_t i = 0; i < _agents.size(); ++i) { + _agents[i]._organism = &_population.getOrganism(i); + _agents[i].create(_stage); + } + + // Agent::_currentLifetime = (_stage._checkPoints.back()._value / 1.75); + + _champ.setFillColor(Color::Transparent); + _champ.setOutlineColor(CHAMP_COLOR); + _champ.setOutlineThickness(2); + _champ.setRadius(AGENT_RAD + 2); + _champ.setOrigin(AGENT_RAD + 2, AGENT_RAD + 2); + + _selected = _champ; + _selected.setOutlineColor(SELECTED_COLOR); + + // Print NoveltyMarks + if (_population.isNoveltyMetricSet()) { + for (auto& i : _population.getNoveltyMetric().getArchive()) { + _noveltyMarks.emplace_back(); + _noveltyMarks.back().setFillColor({ 255, 255, 0, 10 }); + _noveltyMarks.back().setRadius(AGENT_RAD + 2); + _noveltyMarks.back().setOrigin(AGENT_RAD + 2, AGENT_RAD + 2); + _noveltyMarks.back().setPosition(i[0], i[1]); + } + } + + // Prepare text + if (!_font.loadFromFile("C:/Windows/Fonts/font_1.ttf")) { + return EXIT_FAILURE; + } + + _updates.setFont(_font); + _champFitness.setFont(_font); + _avergFitness.setFont(_font); + + _updates.setCharacterSize(11); + _champFitness.setCharacterSize(11); + _avergFitness.setCharacterSize(11); + + _updates.setPosition(5, 5); + _champFitness.setPosition(5, 18); + _avergFitness.setPosition(5, 31); + + _updates.setColor(TEXT_COLOR); + _champFitness.setColor(TEXT_COLOR); + _avergFitness.setColor(TEXT_COLOR); + + return EXIT_SUCCESS; +} + +int +App::execute() +{ + if (!_draw) { + hn::Thread runner([&]() { + while (_runningWOGfx) { + update(); + } + }); + + cin.get(); + cin.get(); + + _runningWOGfx = false; + runner.join(); + + return EXIT_SUCCESS; + } + + _window.create(VIDEO_MODE, WIN_TITLE, WIN_STYLE, WIN_SETTINGS); + + _clock.restart(); + _globalClock.restart(); + + while (_window.isOpen()) { + if (!_isPaused) { + update(); + } + + Event event; + + while (_window.pollEvent(event)) { + if (event.type == Event::Closed) { + _cppnEx.shutdown(); + _window.close(); + } else if (event.type == Event::KeyPressed) { + if (event.key.code == Keyboard::Space) { + _isPaused = !_isPaused; + } else if (event.key.code == Keyboard::V) { + _isVSyncEnabled = !_isVSyncEnabled; + + if (_isVSyncEnabled) { + _window.setVerticalSyncEnabled(true); + } else { + _window.setVerticalSyncEnabled(false); + } + } else if (event.key.code == Keyboard::L) { + if (_selectedIdx != -1) { + if (_population.isOrganismLocked(_selectedIdx)) { + _population.unlockOrganism(_selectedIdx); + } else { + _population.lockOrganism(_selectedIdx); + } + } + } else if (event.key.code == Keyboard::F) { + if (_selectedIdx != -1) { + if (_population.isOrganismFrozen(_selectedIdx)) { + _population.unfreezeOrganism(_selectedIdx); + } else { + _population.freezeOrganism(_selectedIdx); + } + } + } else if (event.key.code == Keyboard::C) { + if (_selectedIdx != -1) { + auto& genome = _population.getOrganism(_selectedIdx).getGenome(); + _cppnEx.run(genome, &_nnPrms); + } + } + } else if (event.type == Event::MouseButtonPressed && event.mouseButton.button == Mouse::Left) { + float cX = static_cast(Mouse::getPosition(_window).x); + float cY = static_cast(Mouse::getPosition(_window).y); + + b2AABB aabb; + aabb.lowerBound = { cX - 0.5f, cY - 0.5f }; + aabb.upperBound = { cX + 0.5f, cY + 0.5f }; + + QueryCallback qCallback; + + _stage._world.QueryAABB(&qCallback, aabb); + + if (qCallback._fixture) { + if (qCallback._fixture->GetFilterData().groupIndex == -1) { + Agent* agent = static_cast(qCallback._fixture->GetUserData()); + size_t agentIdx = 0; + + while (&_agents[agentIdx] != agent) { + ++agentIdx; + } + + _selectedIdx = agentIdx; + + size_t oldOrg = 0; + double averageDist = 0.0; + + for (auto& i : _agents) { + if (i._organism->isOld()) { + ++oldOrg; + averageDist += i._distance; + } + } + + averageDist /= static_cast(oldOrg); + + cout.setf(ios::boolalpha); + cout << endl; + cout << "completedCircuits : " << _agents[agentIdx]._completedCircuits << endl; + cout << "lifetime : " << _agents[agentIdx]._lifetime << endl; + cout << "distance : " << _agents[agentIdx]._distance << endl; + cout << "isOnTrap : " << _agents[agentIdx]._isOnTrap << endl; + cout << "isOld : " << _agents[agentIdx]._organism->isOld() << endl; + // cout << "noveltyScore : " << _agents[agentIdx]._organism->getBehavior().getNoveltyScore() << endl; + // cout << "criteriaReached : " << _agents[agentIdx]._organism->getBehavior()._criteriaReached << endl; + cout << "averageDist : " << averageDist << endl; + cout << endl; + } else { + _selectedIdx = -1; + } + } else { + _selectedIdx = -1; + } + } + } + + draw(); + } + + return EXIT_SUCCESS; +} + +void +App::update() +{ + parallel_for(size_t(0), _agents.size(), size_t(1), [&](size_t i) { + if (!_agents[i]._organism->isBeingGenerated()) { + _agents[i].update(_stage, _doNoveltySearch); + } + }); + + _population.update([&]() { + if (!_doNoveltySearch) { + for (auto& i : _agents) { + i.setPoints(_stage, true); + } + } else { + for (auto& i : _agents) { + i.setBehavior(_stage); + } + } + }, [&]() { + _agents[_population.getLastReplacement()->getIndex()].recreate(_stage, true); + }); + + if (_doNoveltySearch) { + size_t maxCircuits = 0; + + for (auto& i : _agents) { + maxCircuits = max(maxCircuits, i._completedCircuits); + } + + if (maxCircuits != _completedCircuits) { + _completedCircuits = maxCircuits; + size_t newLifetime = (_stage._checkPoints.back()._value / 1.75) * (_completedCircuits + 1); + + _population.setMinimumLifetime(newLifetime); + Agent::_currentLifetime = newLifetime; + } + } + + for (auto& i : _stage._traps) { + TrapCallback trapCallback; + _stage._world.QueryAABB(&trapCallback, i); + } + + if (_doNoveltySearch) { + auto& archive = _population.getNoveltyMetric().getArchive(); + + if (archive.size() > _noveltyMarks.size()) { + for (size_t i = _noveltyMarks.size(); i < archive.size(); ++i) { + _noveltyMarks.emplace_back(); + _noveltyMarks.back().setFillColor({ 255, 255, 0, 10 }); + _noveltyMarks.back().setRadius(AGENT_RAD + 2); + _noveltyMarks.back().setOrigin(AGENT_RAD + 2, AGENT_RAD + 2); + _noveltyMarks.back().setPosition(archive[i][0], archive[i][1]); + } + } + } + + _stage.update(); + + if (_population.getUpdates() % 10000 == 0) { + ofstream popFile("current.population"); + SaveFile sFile(popFile); + sFile.savePopulation(_population, true); + + using chrono::system_clock; + system_clock::time_point today = system_clock::now(); + + time_t tt = system_clock::to_time_t(today); + cout << "> Saved on : " << ctime(&tt); + cout << "> Champ Fitness : " << _population.getChampion()._fitness << endl; + cout << "> Comp. Circuits : " << _completedCircuits << endl; + + if (_population.isNoveltyMetricSet()) { + cout << "> Archive Size : " << _population.getNoveltyMetric().getArchive().size() << endl << endl; + } else { + cout << endl; + } + } + + if (_globalClock.getElapsedTime().asSeconds() > _secondsToRun) { + _window.close(); + _runningWOGfx = false; + } +} + +void +App::draw() +{ + if (!_isVSyncEnabled) { + if (_clock.getElapsedTime().asSeconds() < 1.0f / 60.0f) { + return; + } + + _clock.restart(); + } + + _window.clear(BG_COLOR); + + _stage.drawOn(_window); + + for (auto& i : _noveltyMarks) { + _window.draw(i); + } + + for (size_t i = 0, end = _agents.size(); i < end; ++i) { + if (!_population.isOrganismBeingGenerated(i)) { + _agents[i].drawOn(_window); + } + } + + // Highlight champ + double champFtn = _population.getChampion()._fitness; + ssize_t champIdx = _population.getChampion().getIndex(); + double averageFitness = _population.getAverageFitness(); + + if (champIdx != -1) { + auto cPos = _agents[champIdx]._body->GetPosition(); + _champ.setPosition(cPos.x, cPos.y); + _window.draw(_champ); + } + + // Highlight selected + if (_selectedIdx != -1) { + auto sPos = _agents[_selectedIdx]._body->GetPosition(); + _selected.setPosition(sPos.x, sPos.y); + _window.draw(_selected); + } + + // Print text + _updates.setString ("Simulation updates : " + toString(_population.getUpdates())); + _champFitness.setString("Champ's fitness : " + toString(champFtn)); + _avergFitness.setString("Average fitness : " + toString(averageFitness)); + + _window.draw(_updates); + _window.draw(_champFitness); + _window.draw(_avergFitness); + + _window.display(); +} + +void +App::shutdown() +{ + _population.shutdown(true); + + ofstream popFile("current.population"); + SaveFile sFile(popFile); + sFile.savePopulation(_population); + + cout << "SHUTTING DOWN!" << endl; +} + +int +main() +{ + App app; + + if (app.startup() == EXIT_FAILURE || app.execute() == EXIT_FAILURE) { + return EXIT_FAILURE; + } else { + app.shutdown(); + return EXIT_SUCCESS; + } +} -- cgit v1.2.1