From 0167a046095efbb516f15a6db107a631fbcc3ae8 Mon Sep 17 00:00:00 2001 From: Ryan Yiu Date: Thu, 21 Mar 2019 13:34:11 -0700 Subject: [PATCH 1/6] + Basic setup for improved leader election --- AmoebotSim.pro | 2 + alg/improvedleaderelection.cpp | 600 +++++++++++++++++++++++++++++++++ alg/improvedleaderelection.h | 289 ++++++++++++++++ core/amoebotparticle.h | 3 +- 4 files changed, 892 insertions(+), 2 deletions(-) create mode 100644 alg/improvedleaderelection.cpp create mode 100644 alg/improvedleaderelection.h diff --git a/AmoebotSim.pro b/AmoebotSim.pro index 2eda0dfc..7572bbd4 100644 --- a/AmoebotSim.pro +++ b/AmoebotSim.pro @@ -13,6 +13,7 @@ HEADERS += \ alg/demo/pulldemo.h \ alg/demo/tokendemo.h \ alg/compression.h \ + alg/improvedleaderelection.h \ alg/infobjcoating.h \ alg/shapeformation.h \ core/amoebotparticle.h \ @@ -40,6 +41,7 @@ SOURCES += \ alg/demo/pulldemo.cpp \ alg/demo/tokendemo.cpp \ alg/compression.cpp \ + alg/improvedleaderelection.cpp \ alg/infobjcoating.cpp \ alg/shapeformation.cpp \ core/amoebotparticle.cpp \ diff --git a/alg/improvedleaderelection.cpp b/alg/improvedleaderelection.cpp new file mode 100644 index 00000000..04e188a2 --- /dev/null +++ b/alg/improvedleaderelection.cpp @@ -0,0 +1,600 @@ +#include +#include + +#include +#include + +#include "alg/improvedleaderelection.h" + +//----------------------------BEGIN PARTICLE CODE---------------------------- + +LeaderElectionParticle::LeaderElectionParticle(const Node head, + const int globalTailDir, + const int orientation, + AmoebotSystem& system, + State state) + : AmoebotParticle(head, globalTailDir, orientation, system), + state(state), + currentAgent(0) { + borderColorLabels.fill(-1); + borderPointColorLabels.fill(-1); +} + +void LeaderElectionParticle::activate() { + if (state == State::Idle) { + // Determine the number of neighbors of the current particle. + // If there are no neighbors, then that means the particle is the only + // one in the system and should declare itself as the leader. + // If it is surrounded by 6 neighbors, then it cannot participate in + // leader election. + // Otherwise, the particle may participate in leader election and must + // generate agents to do so. + int numNbrs = getNumberOfNbrs(); + if (numNbrs == 0) { + state = State::Leader; + return; + } else if (numNbrs == 6) { + state = State::Finished; + } else { + int agentId = 0; + for (int dir = 0; dir < 6; dir++) { + if (!hasNbrAtLabel(dir) && hasNbrAtLabel((dir + 1) % 6)) { + Q_ASSERT(agentId < 3); + + LeaderElectionAgent* agent = new LeaderElectionAgent(); + agent->candidateParticle = this; + agent->localId = agentId + 1; + agent->agentDir = dir; + agent->nextAgentDir = getNextAgentDir(dir); + agent->prevAgentDir = getPrevAgentDir(dir); + agent->agentState = State::Candidate; + agent->subPhase = LeaderElectionAgent::SubPhase::SegmentSetup; + agent->setStateColor(); + + agent->paintBackSegment(0x696969); + agent->paintFrontSegment(0x696969); + + agents.push_back(agent); + agentId++; + } + } + state = State::Candidate; + return; + } + } else if (state == State::Candidate) { + agents.at(currentAgent)->activate(); + currentAgent = (currentAgent + 1) % agents.size(); + + // The following is used by a particle in the candidate state to determine + // whether or not to declare itself as the Leader or declare itself to be in + // the Finished state depending on the state of its agents. + bool allFinished = true; + for (unsigned i = 0; i < agents.size(); i++) { + LeaderElectionAgent* agent = agents.at(i); + if (agent->agentState != State::Finished) { + allFinished = false; + } + if (agent->agentState == State::Leader) { + state = State::Leader; + return; + } + } + + if (allFinished) { + state = State::Finished; + } + } + + return; +} + +int LeaderElectionParticle::headMarkColor() const { + if (state == State::Leader) { + return 0x00ff00; + } + + return -1; +} + +int LeaderElectionParticle::headMarkDir() const { + return -1; +} + +int LeaderElectionParticle::tailMarkColor() const { + return headMarkColor(); +} + +QString LeaderElectionParticle::inspectionText() const { + QString text; + text += "head: (" + QString::number(head.x) + ", " + QString::number(head.y) + + ")\n"; + text += "orientation: " + QString::number(orientation) + "\n"; + text += "globalTailDir: " + QString::number(globalTailDir) + "\n"; + text += "state: "; + text += [this](){ + switch(state) { + case State::Idle: return "idle"; + case State::Candidate: return "candidate"; + case State::SoleCandidate: return "sole candidate"; + case State::Demoted: return "demoted"; + case State::Leader: return "leader"; + case State::Finished: return "finished"; + default: return "no state"; + } + }(); + text += "\n"; + text += "number of agents: " + QString::number(agents.size()) + "\n"; + for (LeaderElectionAgent* agent : agents) { + text += [agent](){ + switch(agent->agentState) { + case State::Demoted: return " demoted\n "; + case State::Candidate: + switch(agent->subPhase) { + case LeaderElectionParticle::LeaderElectionAgent:: + SubPhase::SegmentSetup: return " segment setup\n "; + case LeaderElectionParticle::LeaderElectionAgent:: + SubPhase::IdentifierSetup: return " identifier setup\n "; + case LeaderElectionParticle::LeaderElectionAgent:: + SubPhase::IdentifierComparison: + return " identifer comparison\n "; + case LeaderElectionParticle::LeaderElectionAgent:: + SubPhase::SolitudeVerification: + return " solitude verification\n "; + } + case State::SoleCandidate: return " sole candidate\n "; + default: return "invalid\n"; + } + }(); + text += " agent dir: " + QString::number(agent->agentDir) + "\n "; + text += " next agent dir: " + QString::number(agent->nextAgentDir) + + "\n "; + text += " prev agent dir: " + QString::number(agent->prevAgentDir) + + "\n"; + } + text += "has leader election tokens: " + + QString::number(hasToken()) + "\n"; + text += "has solitude active token: " + + QString::number(hasToken()) + "\n"; + text += "has " + QString::number(countTokens()) + + " positive x tokens\n"; + text += "has " + QString::number(countTokens()) + + " negative x tokens\n"; + text += "has " + QString::number(countTokens()) + + " positive y tokens\n"; + text += "has " + QString::number(countTokens()) + + " negative y tokens\n"; + text += "\n"; + + return text; +} + +std::array LeaderElectionParticle::borderColors() const { + return borderColorLabels; +} + +std::array LeaderElectionParticle::borderPointColors() const { + return borderPointColorLabels; +} + +LeaderElectionParticle& LeaderElectionParticle::nbrAtLabel(int label) const { + return AmoebotParticle::nbrAtLabel(label); +} + +int LeaderElectionParticle::getNextAgentDir(const int agentDir) const { + Q_ASSERT(!hasNbrAtLabel(agentDir)); + + for (int dir = 1; dir < 6; dir++) { + if (hasNbrAtLabel((agentDir - dir + 6) % 6)) { + return (agentDir - dir + 6) % 6; + } + } + + Q_ASSERT(false); + return -1; +} + +int LeaderElectionParticle::getPrevAgentDir(const int agentDir) const { + Q_ASSERT(!hasNbrAtLabel(agentDir)); + for (int dir = 1; dir < 6; dir++) { + if (hasNbrAtLabel((agentDir + dir) % 6)) { + return (agentDir + dir) % 6; + } + } + + Q_ASSERT(false); + return -1; +} + +int LeaderElectionParticle::getNumberOfNbrs() const { + int count = 0; + for (int dir = 0; dir < 6; dir++) { + if (hasNbrAtLabel(dir)) { + count++; + } + } + return count; +} +//----------------------------END PARTICLE CODE---------------------------- + +//----------------------------BEGIN AGENT CODE---------------------------- + +LeaderElectionParticle::LeaderElectionAgent::LeaderElectionAgent() : + localId(-1), + agentDir(-1), + nextAgentDir(-1), + prevAgentDir(-1), + agentState(State::Idle), + candidateParticle(nullptr) +{} + +void LeaderElectionParticle::LeaderElectionAgent::activate() { + +} + +std::pair LeaderElectionParticle::LeaderElectionAgent:: +augmentDirVector(std::pair vector) { + unsigned int offset = (nextAgentDir - ((prevAgentDir + 3) % 6) + 6) % 6; + const std::array, 6> vectors = + { std::make_pair(1, 0), std::make_pair(0, 1), std::make_pair(-1, 1), + std::make_pair(-1, 0), std::make_pair(0, -1), std::make_pair(1, -1) }; + + for (unsigned i = 0; i < vectors.size(); ++i) { + if (vector == vectors.at(i)) { + return vectors.at((i + offset) % 6); + } + } + + Q_ASSERT(false); + return std::make_pair(0, 0); +} + +void LeaderElectionParticle::LeaderElectionAgent:: +generateSolitudeVectorTokens(std::pair vector) { + // We initialize the solitude vector tokens with an origin direction + // of nextAgentDir because this is the only direction from which these + // tokens can come from, so it does not matter whether or not these tokens + // were generated by the agent or if they were passed to the agent. + switch(vector.first) { + case -1: + candidateParticle->putToken + (std::make_shared(nextAgentDir, false)); + break; + case 0: + break; + case 1: + candidateParticle->putToken + (std::make_shared(nextAgentDir, false)); + break; + default: + Q_ASSERT(false); + break; + } + switch(vector.second) { + case -1: + candidateParticle->putToken + (std::make_shared(nextAgentDir, false)); + break; + case 0: + break; + case 1: + candidateParticle->putToken + (std::make_shared(nextAgentDir, false)); + break; + default: + Q_ASSERT(false); + break; + } +} + +int LeaderElectionParticle::LeaderElectionAgent::checkSolitudeXTokens() const { + if (hasAgentToken(nextAgentDir) && + hasAgentToken(nextAgentDir)) { + if (peekAgentToken(nextAgentDir)->isSettled && + peekAgentToken(nextAgentDir)->isSettled) { + return 2; + } else { + return 1; + } + } else if (hasAgentToken(nextAgentDir)) { + if (peekAgentToken(nextAgentDir)->isSettled) { + return 0; + } else { + return 1; + } + } else if (hasAgentToken(nextAgentDir)) { + if (peekAgentToken(nextAgentDir)->isSettled) { + return 0; + } else { + return 1; + } + } else { + return 2; + } +} + +int LeaderElectionParticle::LeaderElectionAgent::checkSolitudeYTokens() const { + if (hasAgentToken(nextAgentDir) && + hasAgentToken(nextAgentDir)) { + if (peekAgentToken(nextAgentDir)->isSettled && + peekAgentToken(nextAgentDir)->isSettled) { + return 2; + } else { + return 1; + } + } else if (hasAgentToken(nextAgentDir)) { + if (peekAgentToken(nextAgentDir)->isSettled) { + return 0; + } else { + return 1; + } + } else if (hasAgentToken(nextAgentDir)) { + if (peekAgentToken(nextAgentDir)->isSettled) { + return 0; + } else { + return 1; + } + } else { + return 2; + } +} + +void LeaderElectionParticle::LeaderElectionAgent:: +cleanSolitudeVerificationTokens() { + if (hasAgentToken(nextAgentDir)) { + takeAgentToken(nextAgentDir); + } + if (hasAgentToken(nextAgentDir)) { + takeAgentToken(nextAgentDir); + } + if (hasAgentToken(nextAgentDir)) { + takeAgentToken(nextAgentDir); + } + if (hasAgentToken(nextAgentDir)) { + takeAgentToken(nextAgentDir); + } + paintFrontSegment(0x696969); + paintBackSegment(0x696969); + hasGeneratedTokens = false; +} + +int LeaderElectionParticle::LeaderElectionAgent:: +addNextBorder(int currentSum) const { + // adjust offset in modulo 6 to be compatible with modulo 5 computations + int offsetMod6 = (prevAgentDir + 3) % 6 - nextAgentDir; + if(4 <= offsetMod6 && offsetMod6 <= 5) { + offsetMod6 -= 6; + } else if(-5 <= offsetMod6 && offsetMod6 <= -3) { + offsetMod6 += 6; + } + + return (currentSum + offsetMod6 + 5) % 5; +} + +template +bool LeaderElectionParticle::LeaderElectionAgent:: +hasAgentToken(int agentDir) const{ + auto prop = [agentDir](const std::shared_ptr token) { + return token->origin == agentDir; + }; + return candidateParticle->hasToken(prop); +} + +template +std::shared_ptr +LeaderElectionParticle::LeaderElectionAgent:: +peekAgentToken(int agentDir) const { + auto prop = [agentDir](const std::shared_ptr token) { + return token->origin == agentDir; + }; + return candidateParticle->peekAtToken(prop); +} + +template +std::shared_ptr +LeaderElectionParticle::LeaderElectionAgent::takeAgentToken(int agentDir) { + auto prop = [agentDir](const std::shared_ptr token) { + return token->origin == agentDir; + }; + return candidateParticle->takeToken(prop); +} + +template +void LeaderElectionParticle::LeaderElectionAgent:: +passAgentToken(int agentDir, std::shared_ptr token) { + LeaderElectionParticle* nbr = &candidateParticle->nbrAtLabel(agentDir); + int origin = -1; + for (int i = 0; i < 6; i++) { + if (nbr->hasNbrAtLabel(i) && &nbr->nbrAtLabel(i) == candidateParticle) { + origin = i; + break; + } + } + Q_ASSERT(origin != -1); + token->origin = origin; + nbr->putToken(token); +} + +LeaderElectionParticle::LeaderElectionAgent* +LeaderElectionParticle::LeaderElectionAgent::nextAgent() const { + LeaderElectionParticle* nextNbr = + &candidateParticle->nbrAtLabel(nextAgentDir); + int originLabel = -1; + for (int i = 0; i < 6; i++) { + if (nextNbr->hasNbrAtLabel(i) && + &nextNbr->nbrAtLabel(i) == candidateParticle) { + originLabel = i; + break; + } + } + Q_ASSERT(originLabel != -1); + for (LeaderElectionAgent* agent : nextNbr->agents) { + if (agent->prevAgentDir == originLabel) { + return agent; + } + } + Q_ASSERT(nextNbr->agents.size() == 0); + return nullptr; +} + +LeaderElectionParticle::LeaderElectionAgent* +LeaderElectionParticle::LeaderElectionAgent::prevAgent() const { + LeaderElectionParticle* prevNbr = + &candidateParticle->nbrAtLabel(prevAgentDir); + int originLabel = -1; + for (int i = 0; i < 6; i++) { + if (prevNbr->hasNbrAtLabel(i) && + &prevNbr->nbrAtLabel(i) == candidateParticle) { + originLabel = i; + break; + } + } + Q_ASSERT(originLabel != -1); + for (LeaderElectionAgent* agent : prevNbr->agents) { + if (agent->nextAgentDir == originLabel) { + return agent; + } + } + Q_ASSERT(prevNbr->agents.size() == 0); + return nullptr; +} + +void LeaderElectionParticle::LeaderElectionAgent::setStateColor() { + int globalizedDir = candidateParticle->localToGlobalDir(agentDir); + switch (agentState) { + case State::Candidate: + setSubPhaseColor(); + break; + case State::Demoted: + candidateParticle->borderPointColorLabels.at(globalizedDir) = 0x696969; + break; + case State::SoleCandidate: + candidateParticle->borderPointColorLabels.at(globalizedDir) = 0x00ff00; + break; + case State::Leader: + candidateParticle->borderPointColorLabels.at(globalizedDir) = -1; + break; + case State::Finished: + candidateParticle->borderPointColorLabels.at(globalizedDir) = -1; + break; + default: + break; + } +} + +void LeaderElectionParticle::LeaderElectionAgent::setSubPhaseColor() { + int globalizedDir = candidateParticle->localToGlobalDir(agentDir); + switch (subPhase) { + case SubPhase::SegmentSetup: + candidateParticle->borderPointColorLabels.at(globalizedDir) = 0xff0000; + break; + case SubPhase::IdentifierSetup: + candidateParticle->borderPointColorLabels.at(globalizedDir) = 0xffa500; + break; + case SubPhase::IdentifierComparison: + candidateParticle->borderPointColorLabels.at(globalizedDir) = 0xffff00; + break; + case SubPhase::SolitudeVerification: + candidateParticle->borderPointColorLabels.at(globalizedDir) = 0x00bfff; + break; + default: + Q_ASSERT(false); + break; + } +} + +void LeaderElectionParticle::LeaderElectionAgent::paintFrontSegment( + const int color) { + // Must use localToGlobalDir method to reconcile the difference between the + // local orientation of the particle and the global orientation used by + // drawing + int tempDir = candidateParticle->localToGlobalDir(agentDir); + int tempNextDir = candidateParticle->localToGlobalDir(nextAgentDir); + while (tempDir != (tempNextDir + 1) % 6) { + if ((tempDir + 5) % 6 != tempNextDir) { + candidateParticle->borderColorLabels.at((3 * tempDir + 17) % 18) = + color; + } + tempDir = (tempDir + 5) % 6; + } +} + +void LeaderElectionParticle::LeaderElectionAgent::paintBackSegment( + const int color) { + // Must use localToGlobalDir method to reconcile the difference between the + // local orientation of the particle and the global orientation used by + // drawing + candidateParticle->borderColorLabels.at( + 3 * candidateParticle->localToGlobalDir(agentDir) + 1) = color; +} + +//----------------------------END AGENT CODE---------------------------- + +//----------------------------BEGIN SYSTEM CODE---------------------------- + +LeaderElectionSystem::LeaderElectionSystem(int numParticles, double holeProb) { + Q_ASSERT(numParticles > 0); + Q_ASSERT(0 <= holeProb && holeProb <= 1); + + // Insert the seed at (0,0). + insert(new LeaderElectionParticle(Node(0, 0), -1, randDir(), *this, + LeaderElectionParticle::State::Idle)); + std::set occupied; + occupied.insert(Node(0, 0)); + + std::set candidates; + for (int i = 0; i < 6; ++i) { + candidates.insert(Node(0, 0).nodeInDir(i)); + } + + // Add inactive particles. + int numNonStaticParticles = 0; + while (numNonStaticParticles < numParticles && !candidates.empty()) { + // Pick random candidate. + int randIndex = randInt(0, candidates.size()); + Node randomCandidate; + for (auto it = candidates.begin(); it != candidates.end(); ++it) { + if (randIndex == 0) { + randomCandidate = *it; + candidates.erase(it); + break; + } else { + randIndex--; + } + } + + occupied.insert(randomCandidate); + + // Add this candidate as a particle if not a hole. + if (randBool(1.0 - holeProb)) { + insert(new LeaderElectionParticle(randomCandidate, -1, randDir(), *this, + LeaderElectionParticle::State::Idle)); + ++numNonStaticParticles; + + // Add new candidates. + for (int i = 0; i < 6; ++i) { + auto neighbor = randomCandidate.nodeInDir(i); + if (occupied.find(neighbor) == occupied.end()) { + candidates.insert(neighbor); + } + } + } + } +} + +bool LeaderElectionSystem::hasTerminated() const { + #ifdef QT_DEBUG + if (!isConnected(particles)) { + return true; + } + #endif + + for (auto p : particles) { + auto hp = dynamic_cast(p); + if (hp->state != LeaderElectionParticle::State::Leader && + hp->state != LeaderElectionParticle::State::Finished) { + return false; + } + } + + return true; +} diff --git a/alg/improvedleaderelection.h b/alg/improvedleaderelection.h new file mode 100644 index 00000000..cba87688 --- /dev/null +++ b/alg/improvedleaderelection.h @@ -0,0 +1,289 @@ +// Defines the particle system and composing particles for the General +// Leader Election Algorithm as alluded to in 'Leader Election and Shape +// Formation with Self-Organizing Programmable Matter' +// [https://arxiv.org/abs/1503.07991]. +// +// Please note that this distributed implementation of the algorithm described +// in the above paper has a chance of not working (which is related to the +// Segment Comparison subphase); however, the centralized algorithm described in +// the paper (which this distributed implementation is based on) is correct. + +#ifndef AMOEBOTSIM_ALG_IMPROVEDLEADERELECTION_H +#define AMOEBOTSIM_ALG_IMPROVEDLEADERELECTION_H + +#include "core/amoebotparticle.h" +#include "core/amoebotsystem.h" + +#include +#include + +class LeaderElectionParticle : public AmoebotParticle { + public: + enum class State { + Idle, + Candidate, + SoleCandidate, + Demoted, + Leader, + Finished + }; + + // Constructs a new particle with a node position for its head, a global + // compass direction from its head to its tail (-1 if contracted), an offset + // for its local compass, a system which it belongs to, an initial state, a + // signal for determining turning directions (currently for vertex triangle + // and square construction), and a string to determine what shape to form. + LeaderElectionParticle(const Node head, const int globalTailDir, + const int orientation, AmoebotSystem& system, + State state); + + // Executes one particle activation. + virtual void activate(); + + // Functions for altering a particle's cosmetic appearance; headMarkColor + // (respectively, tailMarkColor) returns the color to be used for the ring + // drawn around the head (respectively, tail) node. Tail color is not shown + // when the particle is contracted. headMarkDir returns the label of the port + // on which the black head marker is drawn. + virtual int headMarkColor() const; + virtual int headMarkDir() const; + virtual int tailMarkColor() const; + + // Returns the string to be displayed when this particle is inspected; used + // to snapshot the current values of this particle's memory at runtime. + virtual QString inspectionText() const; + + // Returns the borderColors and borderPointColors arrays associated with the + // particle to draw the boundaries for leader election. + virtual std::array borderColors() const; + virtual std::array borderPointColors() const; + + // Gets a reference to the neighboring particle incident to the specified port + // label. Crashes if no such particle exists at this label; consider using + // hasNbrAtLabel() first if unsure. + LeaderElectionParticle& nbrAtLabel(int label) const; + + // Returns the label associated with the direction which the next (resp. + // previous) agent is according to the cycle that the agent is on (which is + // determined by the provided agentDir parameter) + int getNextAgentDir(const int agentDir) const; + int getPrevAgentDir(const int agentDir) const; + + // Returns a count of the number of particle neighbors surrounding the calling + // particle. + int getNumberOfNbrs() const; + + protected: + // The LeaderElectionToken struct provides a general framework of what a + // token under the General Leader Election algorithm behaves. + // origin is used to define the direction (label) from which a + // LeaderElectionToken has been sent from. + struct LeaderElectionToken : public Token { + int origin; + }; + + // Tokens for Solitude Verification + struct SolitudeActiveToken : public LeaderElectionToken { + bool isSoleCandidate; + std::pair vector; + int local_id; + SolitudeActiveToken(int origin = -1, + std::pair vector = std::make_pair(1, 0), + int local_id = -1, + bool isSole = true) { + this->origin = origin; + this->vector = vector; + this->local_id = local_id; + this->isSoleCandidate = isSole; + } + }; + struct SolitudeVectorToken : public LeaderElectionToken { + bool isSettled; + }; + + struct SolitudePositiveXToken : public SolitudeVectorToken { + SolitudePositiveXToken(int origin = -1, bool settled = false) { + this->origin = origin; + this->isSettled = settled; + } + }; + struct SolitudePositiveYToken : public SolitudeVectorToken { + SolitudePositiveYToken(int origin = -1, bool settled = false) { + this->origin = origin; + this->isSettled = settled; + } + }; + struct SolitudeNegativeXToken : public SolitudeVectorToken { + SolitudeNegativeXToken(int origin = -1, bool settled = false) { + this->origin = origin; + this->isSettled = settled; + } + }; + struct SolitudeNegativeYToken : public SolitudeVectorToken { + SolitudeNegativeYToken(int origin = -1, bool settled = false) { + this->origin = origin; + this->isSettled = settled; + } + }; + + // Token for Border Testing + struct BorderTestToken : public LeaderElectionToken { + int borderSum; + BorderTestToken(int origin = -1, int borderSum = -1) { + this->origin = origin; + this->borderSum = borderSum; + } + }; + private: + friend class LeaderElectionSystem; + + // The nested class LeaderElectionAgent is used to define the behavior for the + // agents as described in the paper + class LeaderElectionAgent { + public: + enum class SubPhase { + SegmentSetup = 0, + IdentifierSetup, + IdentifierComparison, + SolitudeVerification + }; + + LeaderElectionAgent(); + + // localId is an int which stores which id an agent has according to the + // particle which is associated with it. This localId value lies in [1,3] + int localId; + // agentDir stores the direction/label of the current agent according to the + // labelling scheme used by the particle that owns the current agent. + // nextAgentDir and prevAgentDir are store the direction/label of the next + // and previous (respectively) agents of the current agent according to the + // particle that owns the current agent and the direction of the boundary + // that the agent is on. + int agentDir, nextAgentDir, prevAgentDir; + + State agentState; + SubPhase subPhase; + LeaderElectionParticle* candidateParticle; + + // passedTokensDir is an int which will be used to determine whether or not + // an agent will pass tokens in the prevAgentDir or nextAgentDir. + // This is done to maintain the rule that particles can only pass tokens to 1 + // other particle in a single activation. + // 0 --> tokens should be passed forwards in the cycle (nextAgentDir) + // 1 --> tokens should be passed backwards in the cycle (prevAgentDir) + int passTokensDir = -1; + + // Variables for Solitude Verification + // createdLead is a boolean which determines whether or not the current agent + // has generated a solitude active token and passed it along its front + // segment. + // hasGeneratedTokens is a boolean which determines whether or not the + // current agent has generated solitude vector tokens using the solitude + // active token. This is necessary since there are cases where different + // agents on the same particle might have the same nextAgentDir as the + // other's prevAgentDir, so an incorrect token pass is avoided using this + // boolean variable. + bool createdLead = false; + bool hasGeneratedTokens = false; + + // Variables for Boundary Testing + // testingBorder is a boolean used to determine whether or not the current + // sole candidate has generated a border testing token to determine what + // boundary it is on. + bool testingBorder = false; + + // The activate function is the LeaderElectionAgent equivalent of an + // Amoebot Particle's activate function + void activate(); + + // Solitude Verification Methods + // augmentDirVector takes a pair as a parameter, which represents + // the current vector stored in the solitude active token. This function then + // generates the next vector according to a local coordinate system (which is + // determined when a candidate agent in the Solitude Verification subphase + // generates the solitude active token) based on the vector stored in the + // solitude active token. + std::pair augmentDirVector(std::pair vector); + + // generateSolitudeVectorTokens generates the solitude vector tokens + // (SolitudePositiveXToken, SolitudeNegativeXToken, etc.) based on the given + // parameter vector. + void generateSolitudeVectorTokens(std::pair vector); + + // The checkSolitudeXTokens and checkSolitudeYTokens are used to determine + // the condition of the solitude vector tokens that an agent might own. + // The functions will return a value contained in [0,2] depending on what + // condition the solitude vector tokens are in: + // 0 --> tokens are settled and there is a mismatch, i.e., the agent might + // have a positive x token (which as settled), but no corresponding negative + // x token. + // 1 --> at least one of the tokens is not settled + // 2 --> tokens are settled and there is a match or neither tokens are + // present on the current agent. + int checkSolitudeXTokens() const; + int checkSolitudeYTokens() const; + + // The cleanSolitudeVerificationTokens function will clean the solitude + // vector tokens owned by a particular agent as well as paint the + // front and back segments gray + void cleanSolitudeVerificationTokens(); + + // Boundary Testing methods + int addNextBorder(int currentSum) const; + + // Methods for passing, taking, and checking the ownership of tokens at the + // agent level + template + bool hasAgentToken(int agentDir) const; + template + std::shared_ptr peekAgentToken(int agentDir) const; + template + std::shared_ptr takeAgentToken(int agentDir); + template + void passAgentToken(int agentDir, std::shared_ptr token); + LeaderElectionAgent* nextAgent() const; + LeaderElectionAgent* prevAgent() const; + + // Methods responsible for rendering the agents onto the simulator with their + // colors changing based on the state and the subphase of the current agent + // Red --> Candidate agent in Segment Comparison Subphase + // Yellow --> Candidate agent in Coin Flipping Subphase + // Blue --> Candidate agent in Solitude Verification Subphase + // Grey --> Demoted agent + // Green --> Sole candidate + void setStateColor(); + void setSubPhaseColor(); + + // Methods responsible for painting the borders which will act as physical + // representations of the cycle for leader election + // Red --> Segment Comparison Phase + // Yellow --> Coin Flipping Phase + // Blue --> Solitude Verification Phase + // Grey --> No phase (or, alternatively, active phase of Segment Comparison + // phase + void paintFrontSegment(const int color); + void paintBackSegment(const int color); + }; + + protected: + State state; + unsigned currentAgent; + std::vector agents; + std::array borderColorLabels; + std::array borderPointColorLabels; +}; + +class LeaderElectionSystem : public AmoebotSystem { + public: + // Constructs a system of LeaderElectionParticles with an optionally specified + // size (#particles), hole probability, and shape to form. holeProb in [0,1] + // controls how "spread out" the system is; closer to 0 is more compressed, + // closer to 1 is more expanded. + LeaderElectionSystem(int numParticles = 100, double holeProb = 0.2); + + // Checks whether or not the system's run of the Leader Election algorithm has + // terminated (all particles in state Finished or Leader). + bool hasTerminated() const override; +}; + +#endif // AMOEBOTSIM_ALG_IMPROVEDLEADERELECTION_H diff --git a/core/amoebotparticle.h b/core/amoebotparticle.h index 62caae0e..24953c51 100644 --- a/core/amoebotparticle.h +++ b/core/amoebotparticle.h @@ -130,8 +130,7 @@ class AmoebotParticle : public LocalParticle, public RandomNumberGenerator { // for that must satisfy a particular property requirement template std::shared_ptr peekAtToken( - std::function)> - propertyCheck) const; + std::function)> propertyCheck) const; template std::shared_ptr takeToken( std::function)> propertyCheck); From 945885cfef972fb015bda33edcd8e20dee9965b3 Mon Sep 17 00:00:00 2001 From: Ryan Yiu Date: Thu, 21 Mar 2019 14:21:07 -0700 Subject: [PATCH 2/6] + Ported over solitude verification and boundary testing + Began working on identifier setup phase --- alg/improvedleaderelection.cpp | 234 +++++++++++++++++++++++++++++++++ alg/improvedleaderelection.h | 42 ++++++ 2 files changed, 276 insertions(+) diff --git a/alg/improvedleaderelection.cpp b/alg/improvedleaderelection.cpp index 04e188a2..a1ad4913 100644 --- a/alg/improvedleaderelection.cpp +++ b/alg/improvedleaderelection.cpp @@ -228,7 +228,241 @@ LeaderElectionParticle::LeaderElectionAgent::LeaderElectionAgent() : {} void LeaderElectionParticle::LeaderElectionAgent::activate() { + passTokensDir = randInt(0, 2); + if (agentState == State::Candidate) { + + + // Solitude Verification + // Here we check whether or not the SolitudeActiveToken has a set local_id + // to avoid the a possibility that the current agent might incorrectly + // assume that the SolitudeActiveToken owned by the particle is meant for + // the agent to pass back (and set the local_id value for); for example, in + // the case where a candidate agent's nextAgentDir and prevAgentDir are the + // same. + if (passTokensDir == 1 && + hasAgentToken(prevAgentDir) && + peekAgentToken(prevAgentDir)->local_id == -1) { + peekAgentToken(prevAgentDir)->local_id = localId; + passAgentToken + (prevAgentDir, takeAgentToken(prevAgentDir)); + paintBackSegment(0x696969); + } + + if (hasAgentToken(nextAgentDir)) { + peekAgentToken(nextAgentDir)->isSettled = true; + } + + if (hasAgentToken(nextAgentDir)) { + peekAgentToken(nextAgentDir)->isSettled = true; + } + + if (hasAgentToken(nextAgentDir)) { + peekAgentToken(nextAgentDir)->isSettled = true; + } + + if (hasAgentToken(nextAgentDir)) { + peekAgentToken(nextAgentDir)->isSettled = true; + } + + if (subPhase == SubPhase::SegmentSetup) { + bool coinFlip = randBool(); + if (coinFlip) { + subPhase = SubPhase::IdentifierSetup; + } else { + agentState = State::Demoted; + } + } else if (subPhase == SubPhase::IdentifierSetup) { + if (!hasGeneratedSetupToken) { + passAgentToken(nextAgentDir, + std::make_shared()); + idValue = randInt(0,1); + hasGeneratedSetupToken = true; + } else { + + } + } else if (subPhase == SubPhase::IdentifierComparison) { + + } else if (subPhase == SubPhase::SolitudeVerification) { + if (!createdLead && passTokensDir == 0) { + passAgentToken + (nextAgentDir, std::make_shared()); + candidateParticle->putToken + (std::make_shared(nextAgentDir, true)); + paintFrontSegment(0x00bfff); + createdLead = true; + hasGeneratedTokens = true; + } else if (hasAgentToken(nextAgentDir) && + peekAgentToken + (nextAgentDir)->local_id != -1) { + int checkX = checkSolitudeXTokens(); + int checkY = checkSolitudeYTokens(); + bool isSole = + peekAgentToken(nextAgentDir)->isSoleCandidate; + int id = peekAgentToken(nextAgentDir)->local_id; + if (isSole && localId == id && checkX == 2 && checkY == 2) { + agentState = State::SoleCandidate; + } else if (checkX == 1 || checkY == 1) { + // It should never reach this state since all solitude vector tokens + // that reach a candidate agent are settled + Q_ASSERT(false); + return; + } else { + subPhase = SubPhase::IdentifierComparison; + } + takeAgentToken(nextAgentDir); + createdLead = false; + cleanSolitudeVerificationTokens(); + setStateColor(); + return; + } + } + } else if (agentState == State::Demoted) { + LeaderElectionAgent* next = nextAgent(); + LeaderElectionAgent* prev = prevAgent(); + + if (passTokensDir == 0 && + hasAgentToken(prevAgentDir) && + peekAgentToken(prevAgentDir)->initialize) { + + } + // Solitude Verification Tokens + if (passTokensDir == 0 && + hasAgentToken(prevAgentDir) && + peekAgentToken(prevAgentDir)->local_id == -1 && + !hasGeneratedTokens) { + std::shared_ptr token = + takeAgentToken(prevAgentDir); + std::pair generatedPair = augmentDirVector(token->vector); + generateSolitudeVectorTokens(generatedPair); + token->vector = generatedPair; + paintBackSegment(0x00bfff); + paintFrontSegment(0x00bfff); + passAgentToken(nextAgentDir, token); + hasGeneratedTokens = true; + } else if (passTokensDir == 1 && + hasAgentToken(nextAgentDir) && + peekAgentToken + (nextAgentDir)->local_id != -1 && + hasGeneratedTokens) { + int checkX = checkSolitudeXTokens(); + int checkY = checkSolitudeYTokens(); + if ((checkX == 2 && checkY == 2) || + !peekAgentToken(nextAgentDir)->isSoleCandidate) { + passAgentToken + (prevAgentDir, takeAgentToken(nextAgentDir)); + cleanSolitudeVerificationTokens(); + } else if (checkX == 0 || checkY == 0) { + std::shared_ptr token = + takeAgentToken(nextAgentDir); + token->isSoleCandidate = false; + passAgentToken(prevAgentDir, token); + cleanSolitudeVerificationTokens(); + } + } + + if (hasAgentToken(nextAgentDir) && + !peekAgentToken(nextAgentDir)->isSettled && + hasGeneratedTokens) { + if (passTokensDir == 1 && prev != nullptr && + !prev->hasAgentToken(prev->nextAgentDir)) { + passAgentToken + (prevAgentDir, + takeAgentToken(nextAgentDir)); + } else if (prev != nullptr && + prev->hasAgentToken + (prev->nextAgentDir) && + prev->peekAgentToken + (prev->nextAgentDir)->isSettled) { + peekAgentToken(nextAgentDir)->isSettled = true; + } + } + + if (hasAgentToken(nextAgentDir) && + !peekAgentToken(nextAgentDir)->isSettled && + hasGeneratedTokens) { + if (passTokensDir == 1 && prev != nullptr && + !prev->hasAgentToken(prev->nextAgentDir)) { + passAgentToken + (prevAgentDir, + takeAgentToken(nextAgentDir)); + } else if (prev != nullptr && + prev->hasAgentToken + (prev->nextAgentDir) && + prev->peekAgentToken + (prev->nextAgentDir)->isSettled) { + peekAgentToken(nextAgentDir)->isSettled = true; + } + } + + if (hasAgentToken(nextAgentDir) && + !peekAgentToken(nextAgentDir)->isSettled && + hasGeneratedTokens) { + if (passTokensDir == 1 && prev != nullptr && + !prev->hasAgentToken(prev->nextAgentDir)) { + passAgentToken + (prevAgentDir, + takeAgentToken(nextAgentDir)); + } else if (prev != nullptr && + prev->hasAgentToken + (prev->nextAgentDir) && + prev->peekAgentToken + (prev->nextAgentDir)->isSettled) { + peekAgentToken(nextAgentDir)->isSettled = true; + } + } + + if (hasAgentToken(nextAgentDir) && + !peekAgentToken(nextAgentDir)->isSettled && + hasGeneratedTokens) { + if (passTokensDir == 1 && prev != nullptr && + !prev->hasAgentToken(prev->nextAgentDir)) { + passAgentToken + (prevAgentDir, + takeAgentToken(nextAgentDir)); + } else if (prev != nullptr && + prev->hasAgentToken + (prev->nextAgentDir) && + prev->peekAgentToken + (prev->nextAgentDir)->isSettled) { + peekAgentToken(nextAgentDir)->isSettled = true; + } + } + + if (passTokensDir == 0 && hasAgentToken(prevAgentDir)) { + std::shared_ptr token = + takeAgentToken(prevAgentDir); + token->borderSum = addNextBorder(token->borderSum); + passAgentToken(nextAgentDir, token); + paintBackSegment(-1); + paintFrontSegment(-1); + agentState = State::Finished; + setStateColor(); + } + + } else if (agentState == State::SoleCandidate) { + if (!testingBorder) { + std::shared_ptr token = + std::make_shared(prevAgentDir, addNextBorder(0)); + passAgentToken(nextAgentDir, token); + paintFrontSegment(-1); + testingBorder = true; + } else if (hasAgentToken(prevAgentDir) && + peekAgentToken(prevAgentDir)->borderSum != -1) { + int borderSum = takeAgentToken(prevAgentDir)->borderSum; + paintBackSegment(-1); + if (borderSum == 1) { + agentState = State::Leader; + } else if (borderSum == 4) { + agentState = State::Finished; + } else { + Q_ASSERT(false); + } + setStateColor(); + testingBorder = false; + return; + } + } } std::pair LeaderElectionParticle::LeaderElectionAgent:: diff --git a/alg/improvedleaderelection.h b/alg/improvedleaderelection.h index cba87688..b8d7a51b 100644 --- a/alg/improvedleaderelection.h +++ b/alg/improvedleaderelection.h @@ -82,6 +82,41 @@ class LeaderElectionParticle : public AmoebotParticle { int origin; }; + // Token for Identifier Setup + struct SetUpToken : public LeaderElectionToken { + bool initialize = true; + int reverseValue = -1; + SetUpToken(int origin = -1, bool initialize = true, int reverseVal = -1) { + this->origin = origin; + this->initialize = initialize; + this->reverseValue = reverseVal; + } + }; + + // Tokens for Identifier Comparison + struct DigitToken : public LeaderElectionToken { + int value = -1; + bool isActive = false; + DigitToken(int origin = -1, int value = -1, bool isActive = false) { + this->origin = origin; + this->value = value; + this->isActive = isActive; + } + }; + + struct DelimiterToken : public LeaderElectionToken { + int value = -1; + bool isActive = false; + bool isGreater = false; + DelimiterToken(int origin = -1, int value = -1, bool isActive = false, + bool isGreater = false) { + this->origin = origin; + this->value = value; + this->isActive = isActive; + this->isGreater = isGreater; + } + }; + // Tokens for Solitude Verification struct SolitudeActiveToken : public LeaderElectionToken { bool isSoleCandidate; @@ -173,6 +208,13 @@ class LeaderElectionParticle : public AmoebotParticle { // 1 --> tokens should be passed backwards in the cycle (prevAgentDir) int passTokensDir = -1; + // Keep track of whether or not the current agent was demoted due to identifier + // comparison + bool demotedFromComparison = false; + bool hasGeneratedSetupToken = false; + bool isActive = false; + int idValue = -1; + // Variables for Solitude Verification // createdLead is a boolean which determines whether or not the current agent // has generated a solitude active token and passed it along its front From f911d3aa2935d75db2a6eea635d97c1af47adb68 Mon Sep 17 00:00:00 2001 From: Ryan Yiu Date: Wed, 27 Mar 2019 17:54:32 -0700 Subject: [PATCH 3/6] + Identifier Setup Phase --- alg/improvedleaderelection.cpp | 89 ++++++++++++++++++++++++++++++---- alg/improvedleaderelection.h | 1 + 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/alg/improvedleaderelection.cpp b/alg/improvedleaderelection.cpp index a1ad4913..81dc30ed 100644 --- a/alg/improvedleaderelection.cpp +++ b/alg/improvedleaderelection.cpp @@ -230,8 +230,27 @@ LeaderElectionParticle::LeaderElectionAgent::LeaderElectionAgent() : void LeaderElectionParticle::LeaderElectionAgent::activate() { passTokensDir = randInt(0, 2); if (agentState == State::Candidate) { - - + if (passTokensDir == 1 && hasAgentToken(prevAgentDir)) { + peekAgentToken(prevAgentDir)->initialize = false; + passAgentToken(prevAgentDir, + takeAgentToken(prevAgentDir)); + } + + if (hasAgentToken(nextAgentDir) && !hasGeneratedReverseToken) { + std::shared_ptr token = + takeAgentToken(nextAgentDir); + if (nextAgent()->agentState == State::Candidate) { + candidateParticle->putToken( + std::make_shared(nextAgentDir, idValue)); + hasGeneratedReverseToken = true; + } else if (passTokensDir == 0) { + candidateParticle->putToken( + std::make_shared(nextAgentDir, token->reverseValue)); + token->reverseValue = idValue; + passAgentToken(nextAgentDir, token); + hasGeneratedReverseToken = true; + } + } // Solitude Verification // Here we check whether or not the SolitudeActiveToken has a set local_id // to avoid the a possibility that the current agent might incorrectly @@ -272,13 +291,11 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { agentState = State::Demoted; } } else if (subPhase == SubPhase::IdentifierSetup) { - if (!hasGeneratedSetupToken) { + if (!hasGeneratedSetupToken && passTokensDir == 0) { passAgentToken(nextAgentDir, std::make_shared()); - idValue = randInt(0,1); + idValue = randInt(0,2); hasGeneratedSetupToken = true; - } else { - } } else if (subPhase == SubPhase::IdentifierComparison) { @@ -320,10 +337,64 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { LeaderElectionAgent* next = nextAgent(); LeaderElectionAgent* prev = prevAgent(); - if (passTokensDir == 0 && - hasAgentToken(prevAgentDir) && - peekAgentToken(prevAgentDir)->initialize) { + // Identifier Setup + if (passTokensDir == 1 && hasAgentToken(prevAgentDir) && + peekAgentToken(prevAgentDir)->initialize && + idValue == -1) { + idValue = randInt(0, 2); + passAgentToken(nextAgentDir, + takeAgentToken(prevAgentDir)); + } + + if (hasAgentToken(nextAgentDir)) { + std::shared_ptr token = + takeAgentToken(nextAgentDir); + if (prev != nullptr && prev->hasGeneratedTokens) { + if (next != nullptr && next->hasGeneratedTokens) { + candidateParticle->putToken( + std::make_shared(nextAgentDir, idValue)); + return; + } else if (next != nullptr && passTokensDir == 0) { + candidateParticle->putToken( + std::make_shared(nextAgentDir, + token->reverseValue)); + token->reverseValue = idValue; + passAgentToken(nextAgentDir, token); + } + hasGeneratedReverseToken = true; + return; + } else if (passTokensDir == 1 && prev != nullptr) { + if (next != nullptr && (next->agentState == State::Candidate || + next->hasGeneratedReverseToken)) { + token->reverseValue = idValue; + } + passAgentToken(prevAgentDir, token); + } + } + if (hasAgentToken(prevAgentDir) && + !peekAgentToken(prevAgentDir)->initialize) { + std::shared_ptr token = + takeAgentToken(prevAgentDir); + if (next != nullptr && (next->agentState == State::Candidate || + next->hasGeneratedReverseToken)) { + if (next->agentState == State::Candidate) { + candidateParticle->putToken( + std::make_shared(nextAgentDir, + token->reverseValue)); + } else { + candidateParticle->putToken( + std::make_shared(nextAgentDir, + token->reverseValue)); + } + hasGeneratedReverseToken = true; + if (prev != nullptr && !prev->hasGeneratedReverseToken && + passTokensDir == 1) { + passAgentToken(prevAgentDir, token); + } + } else if (next != nullptr && passTokensDir == 0) { + passAgentToken(nextAgentDir, token); + } } // Solitude Verification Tokens diff --git a/alg/improvedleaderelection.h b/alg/improvedleaderelection.h index b8d7a51b..6cbdaae4 100644 --- a/alg/improvedleaderelection.h +++ b/alg/improvedleaderelection.h @@ -212,6 +212,7 @@ class LeaderElectionParticle : public AmoebotParticle { // comparison bool demotedFromComparison = false; bool hasGeneratedSetupToken = false; + bool hasGeneratedReverseToken = false; bool isActive = false; int idValue = -1; From b36d2a11bc21aec0b2ae1319e0a856c26d1c019e Mon Sep 17 00:00:00 2001 From: Ryan Yiu Date: Thu, 4 Apr 2019 10:49:57 -0700 Subject: [PATCH 4/6] + WIP changes towards finishing identifier comparison --- alg/improvedleaderelection.cpp | 116 ++++++++++++++++++++++++++++++--- alg/improvedleaderelection.h | 11 ++++ core/amoebotparticle.h | 23 ++++++- 3 files changed, 138 insertions(+), 12 deletions(-) diff --git a/alg/improvedleaderelection.cpp b/alg/improvedleaderelection.cpp index 81dc30ed..553e1971 100644 --- a/alg/improvedleaderelection.cpp +++ b/alg/improvedleaderelection.cpp @@ -230,6 +230,8 @@ LeaderElectionParticle::LeaderElectionAgent::LeaderElectionAgent() : void LeaderElectionParticle::LeaderElectionAgent::activate() { passTokensDir = randInt(0, 2); if (agentState == State::Candidate) { + + // Identifier Setup if (passTokensDir == 1 && hasAgentToken(prevAgentDir)) { peekAgentToken(prevAgentDir)->initialize = false; passAgentToken(prevAgentDir, @@ -251,6 +253,42 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { hasGeneratedReverseToken = true; } } + + // Identifier Comparison + if (hasAgentToken(nextAgentDir)) { + if (isActive && peekAgentToken(nextAgentDir)->isActive) { + isActive = false; + peekAgentToken(nextAgentDir)->isActive = false; + demotedFromComparison = true; + } + if (canPassComparisonToken()) { + peekAgentToken(nextAgentDir)->isActive = true; + passAgentToken(prevAgentDir, + takeAgentToken(nextAgentDir)); + } + } + + if (hasAgentToken(nextAgentDir)) { + if (isActive && peekAgentToken(nextAgentDir)->isActive) { + int tokenValue = peekAgentToken(nextAgentDir)->value; + compareStatus = idValue - tokenValue; + isActive = false; + peekAgentToken(nextAgentDir)->isActive = false; + if (compareStatus == -1) { + demotedFromComparison = true; + } else if (compareStatus == 0) { + subPhase = SubPhase::SolitudeVerification; + } + } + if (canPassComparisonToken()) { + peekAgentToken(nextAgentDir)->isActive = true; + passAgentToken(prevAgentDir, + takeAgentToken( + nextAgentDir)); + isActive = true; + } + } + // Solitude Verification // Here we check whether or not the SolitudeActiveToken has a set local_id // to avoid the a possibility that the current agent might incorrectly @@ -296,9 +334,16 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { std::make_shared()); idValue = randInt(0,2); hasGeneratedSetupToken = true; + return; + } else if (hasGeneratedReverseToken) { + subPhase = SubPhase::IdentifierComparison; + return; } } else if (subPhase == SubPhase::IdentifierComparison) { - + if (demotedFromComparison) { + agentState = State::Demoted; + return; + } } else if (subPhase == SubPhase::SolitudeVerification) { if (!createdLead && passTokensDir == 0) { passAgentToken @@ -323,8 +368,10 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { // that reach a candidate agent are settled Q_ASSERT(false); return; - } else { + } else if (!demotedFromComparison) { subPhase = SubPhase::IdentifierComparison; + } else if (demotedFromComparison) { + agentState = State::Demoted; } takeAgentToken(nextAgentDir); createdLead = false; @@ -338,12 +385,18 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { LeaderElectionAgent* prev = prevAgent(); // Identifier Setup - if (passTokensDir == 1 && hasAgentToken(prevAgentDir) && - peekAgentToken(prevAgentDir)->initialize && - idValue == -1) { - idValue = randInt(0, 2); - passAgentToken(nextAgentDir, - takeAgentToken(prevAgentDir)); + if (hasAgentToken(prevAgentDir) && + peekAgentToken(prevAgentDir)->initialize) { + if (demotedFromComparison && passTokensDir == 1) { + peekAgentToken(prevAgentDir)->initialize = false; + passAgentToken(prevAgentDir, + takeAgentToken(prevAgentDir)); + } else if (passTokensDir == 0) { + Q_ASSERT(idValue != -1); + idValue = randInt(0, 2); + passAgentToken(nextAgentDir, + takeAgentToken(prevAgentDir)); + } } if (hasAgentToken(nextAgentDir)) { @@ -397,6 +450,27 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { } } + // Identifier Comparison + if (hasAgentToken(nextAgentDir)) { + if (demotedFromComparison) { + if (canPassComparisonToken()) { + peekAgentToken(nextAgentDir)->isActive = true; + passAgentToken(prevAgentDir, + takeAgentToken(nextAgentDir)); + } + } else { + + } + } + + if (hasAgentToken(nextAgentDir)) { + if (demotedFromComparison) { + + } else { + + } + } + // Solitude Verification Tokens if (passTokensDir == 0 && hasAgentToken(prevAgentDir) && @@ -515,7 +589,7 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { if (!testingBorder) { std::shared_ptr token = std::make_shared(prevAgentDir, addNextBorder(0)); - passAgentToken(nextAgentDir, token); + passAgentToken(nextAgentDir, token); paintFrontSegment(-1); testingBorder = true; } else if (hasAgentToken(prevAgentDir) && @@ -536,6 +610,21 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { } } +bool LeaderElectionParticle::LeaderElectionAgent:: +canPassComparisonToken() const { + LeaderElectionAgent* prev = prevAgent(); + if (prev == nullptr) { + return false; + } + int prevCountDigit = prev->countAgentTokens(prev->nextAgentDir); + if ((prevCountDigit <= 2 || + !prev->hasAgentToken(prev->nextAgentDir)) && + prev->hasGeneratedReverseToken && passTokensDir == 1) { + return true; + } + return false; +} + std::pair LeaderElectionParticle::LeaderElectionAgent:: augmentDirVector(std::pair vector) { unsigned int offset = (nextAgentDir - ((prevAgentDir + 3) % 6) + 6) % 6; @@ -719,6 +808,15 @@ passAgentToken(int agentDir, std::shared_ptr token) { nbr->putToken(token); } +template +int LeaderElectionParticle::LeaderElectionAgent:: +countAgentTokens(int agentDir) const { + auto prop = [agentDir](const std::shared_ptr token) { + return token->origin == agentDir; + }; + return candidateParticle->countTokens(prop); +} + LeaderElectionParticle::LeaderElectionAgent* LeaderElectionParticle::LeaderElectionAgent::nextAgent() const { LeaderElectionParticle* nextNbr = diff --git a/alg/improvedleaderelection.h b/alg/improvedleaderelection.h index 6cbdaae4..301553ce 100644 --- a/alg/improvedleaderelection.h +++ b/alg/improvedleaderelection.h @@ -214,6 +214,11 @@ class LeaderElectionParticle : public AmoebotParticle { bool hasGeneratedSetupToken = false; bool hasGeneratedReverseToken = false; bool isActive = false; + // compareStatus + // -1 --> lessThan + // 0 --> equal + // 1 --> greaterThan + int compareStatus = -1; int idValue = -1; // Variables for Solitude Verification @@ -235,6 +240,8 @@ class LeaderElectionParticle : public AmoebotParticle { // boundary it is on. bool testingBorder = false; + bool canPassComparisonToken() const; + // The activate function is the LeaderElectionAgent equivalent of an // Amoebot Particle's activate function void activate(); @@ -284,6 +291,10 @@ class LeaderElectionParticle : public AmoebotParticle { std::shared_ptr takeAgentToken(int agentDir); template void passAgentToken(int agentDir, std::shared_ptr token); + + template + int countAgentTokens(int agentDir) const; + LeaderElectionAgent* nextAgent() const; LeaderElectionAgent* prevAgent() const; diff --git a/core/amoebotparticle.h b/core/amoebotparticle.h index 24953c51..e8c648fd 100644 --- a/core/amoebotparticle.h +++ b/core/amoebotparticle.h @@ -144,12 +144,15 @@ class AmoebotParticle : public LocalParticle, public RandomNumberGenerator { template bool hasToken() const; - // An overloaded function of hasToken has been provided in case there is a - // specific token of type TokenType that is being searched for that must - // satisfy a particular property requirement + // Overloaded functinos for countTokens and hasToken have been provided in + // case we wish to check or count the existence(s) of a particular token of + // TokenType satisfying a property requirement. template bool hasToken(std::function)> propertyCheck) const; + template + int countTokens(std::function)> + propertyCheck) const; AmoebotSystem& system; private: @@ -251,6 +254,20 @@ int AmoebotParticle::countTokens() const { return count; } +template +int AmoebotParticle::countTokens( + std::function)> propertyCheck) const { + int count = 0; + for (unsigned int i = 0; i < tokens.size(); i++) { + std::shared_ptr token = + std::dynamic_pointer_cast(tokens[i]); + if (token != nullptr && propertyCheck(token)) { + count++; + } + } + return count; +} + template bool AmoebotParticle::hasToken() const { for (unsigned int i = 0; i < tokens.size(); i++) { From b6ef114f2bca842b25c8250b0682b9ec320ed4eb Mon Sep 17 00:00:00 2001 From: Ryan Yiu Date: Sat, 5 Oct 2019 11:19:09 -0700 Subject: [PATCH 5/6] + Add improved leader election to algorithm list ~ Fix the identifier setup phase so that SetUpTokens are not passed to different boundaries --- alg/improvedleaderelection.cpp | 335 +++++++++++++++++++++++---------- alg/improvedleaderelection.h | 69 +++++-- script/scriptinterface.cpp | 13 ++ script/scriptinterface.h | 2 + ui/alg.cpp | 5 + 5 files changed, 312 insertions(+), 112 deletions(-) diff --git a/alg/improvedleaderelection.cpp b/alg/improvedleaderelection.cpp index 553e1971..442766ac 100644 --- a/alg/improvedleaderelection.cpp +++ b/alg/improvedleaderelection.cpp @@ -83,6 +83,13 @@ void LeaderElectionParticle::activate() { if (allFinished) { state = State::Finished; } + } else if (state == State::Finished) { + for (unsigned i = 0; i < agents.size(); i++) { + LeaderElectionAgent* agent = agents.at(i); + agent->cleanAllTokens(); + agent->paintBackSegment(-1); + agent->paintFrontSegment(-1); + } } return; @@ -125,6 +132,8 @@ QString LeaderElectionParticle::inspectionText() const { text += "\n"; text += "number of agents: " + QString::number(agents.size()) + "\n"; for (LeaderElectionAgent* agent : agents) { + int nextAgentDir = agent->nextAgentDir; + int prevAgentDir = agent->prevAgentDir; text += [agent](){ switch(agent->agentState) { case State::Demoted: return " demoted\n "; @@ -146,10 +155,21 @@ QString LeaderElectionParticle::inspectionText() const { } }(); text += " agent dir: " + QString::number(agent->agentDir) + "\n "; - text += " next agent dir: " + QString::number(agent->nextAgentDir) + + text += " next agent dir: " + QString::number(nextAgentDir) + "\n "; + text += " prev agent dir: " + QString::number(prevAgentDir) + "\n "; + text += " number of digit tokens: " + + QString::number(agent->countAgentTokens(nextAgentDir)) + "\n "; - text += " prev agent dir: " + QString::number(agent->prevAgentDir) + - "\n"; + text += " number of delimiter tokens: " + + QString::number(agent->countAgentTokens(nextAgentDir)) + + "\n "; + text += " has generated reverse tokens: " + + QString::number(agent->hasGeneratedReverseToken) + "\n "; + text += " number of setup tokens: " + + QString::number(agent->countAgentTokens(nextAgentDir) + + agent->countAgentTokens(prevAgentDir)) + + "\n "; + text += " id value: " + QString::number(agent->idValue) + "\n"; } text += "has leader election tokens: " + QString::number(hasToken()) + "\n"; @@ -230,27 +250,54 @@ LeaderElectionParticle::LeaderElectionAgent::LeaderElectionAgent() : void LeaderElectionParticle::LeaderElectionAgent::activate() { passTokensDir = randInt(0, 2); if (agentState == State::Candidate) { + LeaderElectionAgent* next = nextAgent(); + LeaderElectionAgent* prev = prevAgent(); + + if (subPhase == SubPhase::SegmentSetup) { + bool coinFlip = randBool(); + if (coinFlip) { + subPhase = SubPhase::IdentifierSetup; + } else { + agentState = State::Demoted; + } + setStateColor(); + return; + } // Identifier Setup - if (passTokensDir == 1 && hasAgentToken(prevAgentDir)) { - peekAgentToken(prevAgentDir)->initialize = false; - passAgentToken(prevAgentDir, - takeAgentToken(prevAgentDir)); + if (passTokensDir == 1 && hasAgentToken(prevAgentDir) && + prev != nullptr && prev->idValue != -1 && + !prev->hasGeneratedReverseToken) { + takeAgentToken(prevAgentDir); + passAgentToken(prevAgentDir, + std::make_shared()); } - if (hasAgentToken(nextAgentDir) && !hasGeneratedReverseToken) { - std::shared_ptr token = - takeAgentToken(nextAgentDir); - if (nextAgent()->agentState == State::Candidate) { + if (hasAgentToken(nextAgentDir) && + !hasGeneratedReverseToken) { + if (next != nullptr && (next->agentState == State::Candidate || + next->demotedFromComparison)) { + takeAgentToken(nextAgentDir); candidateParticle->putToken( - std::make_shared(nextAgentDir, idValue)); + std::make_shared(nextAgentDir, comparisonColor, + idValue)); hasGeneratedReverseToken = true; - } else if (passTokensDir == 0) { + paintFrontSegment(comparisonColor); + paintBackSegment(0x696969); + } else if (passTokensDir == 0 && next != nullptr) { + Q_ASSERT(next->idValue != -1); + Q_ASSERT(!next->hasGeneratedReverseToken); + + std::shared_ptr token = + takeAgentToken(nextAgentDir); candidateParticle->putToken( - std::make_shared(nextAgentDir, token->reverseValue)); - token->reverseValue = idValue; - passAgentToken(nextAgentDir, token); + std::make_shared(nextAgentDir, token->val)); + std::shared_ptr nextToken = + std::make_shared(-1, idValue, comparisonColor); + passAgentToken(nextAgentDir, nextToken); hasGeneratedReverseToken = true; + paintFrontSegment(0x696969); + paintBackSegment(0x696969); } } @@ -261,7 +308,7 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { peekAgentToken(nextAgentDir)->isActive = false; demotedFromComparison = true; } - if (canPassComparisonToken()) { + if (canPassComparisonToken(false)) { peekAgentToken(nextAgentDir)->isActive = true; passAgentToken(prevAgentDir, takeAgentToken(nextAgentDir)); @@ -271,20 +318,31 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { if (hasAgentToken(nextAgentDir)) { if (isActive && peekAgentToken(nextAgentDir)->isActive) { int tokenValue = peekAgentToken(nextAgentDir)->value; + int status = peekAgentToken(nextAgentDir)->compare; compareStatus = idValue - tokenValue; isActive = false; peekAgentToken(nextAgentDir)->isActive = false; - if (compareStatus == -1) { + if (compareStatus == -1 || (status == -1 && compareStatus == 0)) { demotedFromComparison = true; - } else if (compareStatus == 0) { + } else if (compareStatus == 0 && status == 0) { subPhase = SubPhase::SolitudeVerification; + setStateColor(); } } - if (canPassComparisonToken()) { + if (canPassComparisonToken(true)) { + int color = + peekAgentToken(nextAgentDir)->comparisonColor; peekAgentToken(nextAgentDir)->isActive = true; passAgentToken(prevAgentDir, - takeAgentToken( - nextAgentDir)); + takeAgentToken + (nextAgentDir)); + if (prev->agentState == State::Candidate) { + prev->paintFrontSegment(color); + } else { + prev->paintFrontSegment(color); + prev->paintBackSegment(color); + } + paintFrontSegment(0x696969); isActive = true; } } @@ -321,27 +379,24 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { peekAgentToken(nextAgentDir)->isSettled = true; } - if (subPhase == SubPhase::SegmentSetup) { - bool coinFlip = randBool(); - if (coinFlip) { - subPhase = SubPhase::IdentifierSetup; - } else { - agentState = State::Demoted; - } - } else if (subPhase == SubPhase::IdentifierSetup) { + if (subPhase == SubPhase::IdentifierSetup) { if (!hasGeneratedSetupToken && passTokensDir == 0) { passAgentToken(nextAgentDir, - std::make_shared()); + std::make_shared + (comparisonColor)); idValue = randInt(0,2); hasGeneratedSetupToken = true; + paintFrontSegment(0xffa500); return; } else if (hasGeneratedReverseToken) { subPhase = SubPhase::IdentifierComparison; + setStateColor(); return; } } else if (subPhase == SubPhase::IdentifierComparison) { if (demotedFromComparison) { agentState = State::Demoted; + setStateColor(); return; } } else if (subPhase == SubPhase::SolitudeVerification) { @@ -370,7 +425,7 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { return; } else if (!demotedFromComparison) { subPhase = SubPhase::IdentifierComparison; - } else if (demotedFromComparison) { + } else { agentState = State::Demoted; } takeAgentToken(nextAgentDir); @@ -384,90 +439,165 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { LeaderElectionAgent* next = nextAgent(); LeaderElectionAgent* prev = prevAgent(); + if (passTokensDir == 0 && hasAgentToken(prevAgentDir)) { + std::shared_ptr token = + takeAgentToken(prevAgentDir); + token->borderSum = addNextBorder(token->borderSum); + passAgentToken(nextAgentDir, token); + cleanAllTokens(); + paintBackSegment(-1); + paintFrontSegment(-1); + agentState = State::Finished; + setStateColor(); + } + // Identifier Setup - if (hasAgentToken(prevAgentDir) && - peekAgentToken(prevAgentDir)->initialize) { - if (demotedFromComparison && passTokensDir == 1) { - peekAgentToken(prevAgentDir)->initialize = false; - passAgentToken(prevAgentDir, - takeAgentToken(prevAgentDir)); - } else if (passTokensDir == 0) { - Q_ASSERT(idValue != -1); + if (hasAgentToken(prevAgentDir)) { + if (demotedFromComparison && passTokensDir == 1 && prev != nullptr) { + Q_ASSERT(prev->idValue != -1); + Q_ASSERT(!prev->hasGeneratedReverseToken); + + takeAgentToken(prevAgentDir); + passAgentToken(prevAgentDir, + std::make_shared()); + } else if (!demotedFromComparison && passTokensDir == 0) { + Q_ASSERT(idValue == -1); + idValue = randInt(0, 2); passAgentToken(nextAgentDir, takeAgentToken(prevAgentDir)); + paintFrontSegment(0xffa500); + paintBackSegment(0xffa500); } } - if (hasAgentToken(nextAgentDir)) { - std::shared_ptr token = - takeAgentToken(nextAgentDir); - if (prev != nullptr && prev->hasGeneratedTokens) { - if (next != nullptr && next->hasGeneratedTokens) { + if (hasAgentToken(nextAgentDir) && + !hasGeneratedReverseToken) { + if (prev != nullptr && prev->hasGeneratedReverseToken) { + if (next != nullptr && next->hasGeneratedReverseToken) { + takeAgentToken(nextAgentDir); candidateParticle->putToken( std::make_shared(nextAgentDir, idValue)); - return; + hasGeneratedReverseToken = true; + paintFrontSegment(0x696969); + paintBackSegment(0x696969); } else if (next != nullptr && passTokensDir == 0) { + std::shared_ptr token = + takeAgentToken(nextAgentDir); candidateParticle->putToken( - std::make_shared(nextAgentDir, - token->reverseValue)); - token->reverseValue = idValue; - passAgentToken(nextAgentDir, token); + std::make_shared(nextAgentDir, token->val)); + std::shared_ptr nextToken = + std::make_shared(); + nextToken->val = idValue; + hasGeneratedReverseToken = true; + passAgentToken(nextAgentDir, nextToken); + paintFrontSegment(0x696969); + paintBackSegment(0x696969); } - hasGeneratedReverseToken = true; - return; } else if (passTokensDir == 1 && prev != nullptr) { + std::shared_ptr token = + takeAgentToken(nextAgentDir); if (next != nullptr && (next->agentState == State::Candidate || - next->hasGeneratedReverseToken)) { - token->reverseValue = idValue; + next->hasGeneratedReverseToken || + next->demotedFromComparison)) { + token->val = idValue; } - passAgentToken(prevAgentDir, token); + passAgentToken(prevAgentDir, token); } } - if (hasAgentToken(prevAgentDir) && - !peekAgentToken(prevAgentDir)->initialize) { - std::shared_ptr token = - takeAgentToken(prevAgentDir); - if (next != nullptr && (next->agentState == State::Candidate || - next->hasGeneratedReverseToken)) { - if (next->agentState == State::Candidate) { + if (hasAgentToken(prevAgentDir) && + !hasGeneratedReverseToken) { + if (next != nullptr && prev != nullptr && passTokensDir == 1 && + (next->agentState == State::Candidate || + next->hasGeneratedReverseToken || next->demotedFromComparison)) { + std::shared_ptr token = + takeAgentToken(prevAgentDir); + if (next->agentState == State::Candidate || + next->demotedFromComparison) { candidateParticle->putToken( std::make_shared(nextAgentDir, - token->reverseValue)); + token->comparisonColor, + token->val)); + paintFrontSegment(token->comparisonColor); + paintBackSegment(0x696969); } else { candidateParticle->putToken( - std::make_shared(nextAgentDir, - token->reverseValue)); + std::make_shared(nextAgentDir, token->val)); + paintFrontSegment(0x696969); + paintBackSegment(0x696969); } hasGeneratedReverseToken = true; - if (prev != nullptr && !prev->hasGeneratedReverseToken && - passTokensDir == 1) { - passAgentToken(prevAgentDir, token); + if (!prev->hasGeneratedReverseToken) { + passAgentToken(prevAgentDir, + std::make_shared()); } - } else if (next != nullptr && passTokensDir == 0) { - passAgentToken(nextAgentDir, token); + } else if (next != nullptr && passTokensDir == 0 && + !(next->agentState == State::Candidate || + next->hasGeneratedReverseToken || + next->demotedFromComparison)) { + passAgentToken(nextAgentDir, + takeAgentToken + (prevAgentDir)); } } // Identifier Comparison if (hasAgentToken(nextAgentDir)) { if (demotedFromComparison) { - if (canPassComparisonToken()) { + if (canPassComparisonToken(false)) { peekAgentToken(nextAgentDir)->isActive = true; passAgentToken(prevAgentDir, takeAgentToken(nextAgentDir)); } } else { - + if (isActive && peekAgentToken(nextAgentDir)->isActive) { + isActive = false; + peekAgentToken(nextAgentDir)->isActive = false; + int tokenValue = peekAgentToken(nextAgentDir)->value; + compareStatus = idValue - tokenValue; + } + if (canPassComparisonToken(false)) { + passAgentToken(prevAgentDir, + takeAgentToken(nextAgentDir)); + } } } if (hasAgentToken(nextAgentDir)) { if (demotedFromComparison) { - + if (canPassComparisonToken(true)) { + int color = + peekAgentToken(nextAgentDir)->comparisonColor; + peekAgentToken(nextAgentDir)->isActive = true; + passAgentToken(prevAgentDir, + takeAgentToken + (nextAgentDir)); + prev->paintFrontSegment(color); + prev->paintBackSegment(color); + paintFrontSegment(0x696969); + paintBackSegment(0x696969); + } } else { - + if (isActive && + peekAgentToken(nextAgentDir)->isActive) { + isActive = false; + peekAgentToken(nextAgentDir)->isActive = false; + } + peekAgentToken(nextAgentDir)->compare = compareStatus; + if (canPassComparisonToken(true)) { + int color = + peekAgentToken(nextAgentDir)->comparisonColor; + isActive = true; + passAgentToken(prevAgentDir, + takeAgentToken + (nextAgentDir)); + compareStatus = 0; + prev->paintFrontSegment(color); + prev->paintBackSegment(color); + paintFrontSegment(0x696969); + paintBackSegment(0x696969); + } } } @@ -574,17 +704,6 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { } } - if (passTokensDir == 0 && hasAgentToken(prevAgentDir)) { - std::shared_ptr token = - takeAgentToken(prevAgentDir); - token->borderSum = addNextBorder(token->borderSum); - passAgentToken(nextAgentDir, token); - paintBackSegment(-1); - paintFrontSegment(-1); - agentState = State::Finished; - setStateColor(); - } - } else if (agentState == State::SoleCandidate) { if (!testingBorder) { std::shared_ptr token = @@ -607,20 +726,42 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { testingBorder = false; return; } + } else if (agentState == State::Finished) { + cleanAllTokens(); + paintBackSegment(-1); + paintFrontSegment(-1); + } +} + +void LeaderElectionParticle::LeaderElectionAgent::cleanAllTokens() { + cleanSolitudeVerificationTokens(); + while (hasAgentToken(nextAgentDir)) { + takeAgentToken(nextAgentDir); + } + if (hasAgentToken(nextAgentDir)) { + takeAgentToken(nextAgentDir); } } bool LeaderElectionParticle::LeaderElectionAgent:: -canPassComparisonToken() const { +canPassComparisonToken(bool isDelimiter) const { LeaderElectionAgent* prev = prevAgent(); - if (prev == nullptr) { + if (prev == nullptr || !prev->hasGeneratedReverseToken || + passTokensDir != 1) { return false; } - int prevCountDigit = prev->countAgentTokens(prev->nextAgentDir); - if ((prevCountDigit <= 2 || - !prev->hasAgentToken(prev->nextAgentDir)) && - prev->hasGeneratedReverseToken && passTokensDir == 1) { - return true; + int prevNextAgentDir = prev->nextAgentDir; + if (isDelimiter) { + if (!prev->hasAgentToken(prevNextAgentDir) && + !prev->hasAgentToken(prevNextAgentDir)) { + return true; + } + } else { + int prevCountDigit = prev->countAgentTokens(prevNextAgentDir); + if (prevCountDigit < 2 && + !prev->hasAgentToken(prevNextAgentDir)) { + return true; + } } return false; } @@ -874,7 +1015,7 @@ void LeaderElectionParticle::LeaderElectionAgent::setStateColor() { candidateParticle->borderPointColorLabels.at(globalizedDir) = 0x00ff00; break; case State::Leader: - candidateParticle->borderPointColorLabels.at(globalizedDir) = -1; + candidateParticle->borderPointColorLabels.at(globalizedDir) = 0x00ff00; break; case State::Finished: candidateParticle->borderPointColorLabels.at(globalizedDir) = -1; @@ -894,7 +1035,8 @@ void LeaderElectionParticle::LeaderElectionAgent::setSubPhaseColor() { candidateParticle->borderPointColorLabels.at(globalizedDir) = 0xffa500; break; case SubPhase::IdentifierComparison: - candidateParticle->borderPointColorLabels.at(globalizedDir) = 0xffff00; + candidateParticle->borderPointColorLabels.at(globalizedDir) = + comparisonColor; break; case SubPhase::SolitudeVerification: candidateParticle->borderPointColorLabels.at(globalizedDir) = 0x00bfff; @@ -934,7 +1076,8 @@ void LeaderElectionParticle::LeaderElectionAgent::paintBackSegment( //----------------------------BEGIN SYSTEM CODE---------------------------- -LeaderElectionSystem::LeaderElectionSystem(int numParticles, double holeProb) { +ImprovedLeaderElectionSystem::ImprovedLeaderElectionSystem(int numParticles, + double holeProb) { Q_ASSERT(numParticles > 0); Q_ASSERT(0 <= holeProb && holeProb <= 1); @@ -984,7 +1127,7 @@ LeaderElectionSystem::LeaderElectionSystem(int numParticles, double holeProb) { } } -bool LeaderElectionSystem::hasTerminated() const { +bool ImprovedLeaderElectionSystem::hasTerminated() const { #ifdef QT_DEBUG if (!isConnected(particles)) { return true; diff --git a/alg/improvedleaderelection.h b/alg/improvedleaderelection.h index 301553ce..85609a2c 100644 --- a/alg/improvedleaderelection.h +++ b/alg/improvedleaderelection.h @@ -8,6 +8,19 @@ // Segment Comparison subphase); however, the centralized algorithm described in // the paper (which this distributed implementation is based on) is correct. +/** + TODO: + 1) Check whether or not the template class is really necessary --> I think + I could definitely replace it with just a general Token class decl since + all of my tokens technically inherit from it + 2) Randomly assign colors to agents + delimiter tokens when they enter + identifier comparison --> going to have to store this somewhere locally in + the agent 'cause it might alternate between solitude verification and + identifier comparison + 3) Identifier comparison is messed up somewhere, somehow (and the coloring + scheme also seems to be incorrect + */ + #ifndef AMOEBOTSIM_ALG_IMPROVEDLEADERELECTION_H #define AMOEBOTSIM_ALG_IMPROVEDLEADERELECTION_H @@ -16,6 +29,7 @@ #include #include +#include class LeaderElectionParticle : public AmoebotParticle { public: @@ -82,14 +96,30 @@ class LeaderElectionParticle : public AmoebotParticle { int origin; }; - // Token for Identifier Setup + // Tokens for Identifier Setup struct SetUpToken : public LeaderElectionToken { - bool initialize = true; - int reverseValue = -1; - SetUpToken(int origin = -1, bool initialize = true, int reverseVal = -1) { + int comparisonColor = -1; + SetUpToken(int comparisonColor = -1, int origin = -1) { + this->comparisonColor = comparisonColor; + this->origin = origin; + } + }; + + struct NextIDPassToken : public LeaderElectionToken { + int val = -1; + int comparisonColor = -1; + NextIDPassToken(int origin = -1, int val = -1, int comparisonColor = -1) { + this->origin = origin; + this->val = val; + this->comparisonColor = comparisonColor; + } + }; + + struct PrevIDPassToken : public LeaderElectionToken { + int val = -1; + PrevIDPassToken(int origin = -1, int val = -1) { this->origin = origin; - this->initialize = initialize; - this->reverseValue = reverseVal; + this->val = val; } }; @@ -104,16 +134,21 @@ class LeaderElectionParticle : public AmoebotParticle { } }; + // -1 --> Lesser + // 0 --> Equal + // 1 --> Greater struct DelimiterToken : public LeaderElectionToken { - int value = -1; + int value = 0; bool isActive = false; - bool isGreater = false; - DelimiterToken(int origin = -1, int value = -1, bool isActive = false, - bool isGreater = false) { + int compare = -1; + int comparisonColor = -1; + DelimiterToken(int origin = -1, int comparisonColor = -1, + int value = -1, bool isActive = false, int compare = 0) { this->origin = origin; + this->comparisonColor = comparisonColor; this->value = value; this->isActive = isActive; - this->isGreater = isGreater; + this->compare = compare; } }; @@ -170,7 +205,7 @@ class LeaderElectionParticle : public AmoebotParticle { } }; private: - friend class LeaderElectionSystem; + friend class ImprovedLeaderElectionSystem; // The nested class LeaderElectionAgent is used to define the behavior for the // agents as described in the paper @@ -220,6 +255,7 @@ class LeaderElectionParticle : public AmoebotParticle { // 1 --> greaterThan int compareStatus = -1; int idValue = -1; + int comparisonColor = randInt(0, 16777216); // Variables for Solitude Verification // createdLead is a boolean which determines whether or not the current agent @@ -240,12 +276,13 @@ class LeaderElectionParticle : public AmoebotParticle { // boundary it is on. bool testingBorder = false; - bool canPassComparisonToken() const; - // The activate function is the LeaderElectionAgent equivalent of an // Amoebot Particle's activate function void activate(); + void cleanAllTokens(); + bool canPassComparisonToken(bool isDelimiter) const; + // Solitude Verification Methods // augmentDirVector takes a pair as a parameter, which represents // the current vector stored in the solitude active token. This function then @@ -327,13 +364,13 @@ class LeaderElectionParticle : public AmoebotParticle { std::array borderPointColorLabels; }; -class LeaderElectionSystem : public AmoebotSystem { +class ImprovedLeaderElectionSystem : public AmoebotSystem { public: // Constructs a system of LeaderElectionParticles with an optionally specified // size (#particles), hole probability, and shape to form. holeProb in [0,1] // controls how "spread out" the system is; closer to 0 is more compressed, // closer to 1 is more expanded. - LeaderElectionSystem(int numParticles = 100, double holeProb = 0.2); + ImprovedLeaderElectionSystem(int numParticles = 100, double holeProb = 0.2); // Checks whether or not the system's run of the Leader Election algorithm has // terminated (all particles in state Finished or Leader). diff --git a/script/scriptinterface.cpp b/script/scriptinterface.cpp index adb605e4..dc00abbb 100644 --- a/script/scriptinterface.cpp +++ b/script/scriptinterface.cpp @@ -11,6 +11,7 @@ #include "alg/demo/pulldemo.h" #include "alg/demo/tokendemo.h" #include "alg/compression.h" +#include "alg/improvedleaderelection.h" #include "alg/infobjcoating.h" #include "alg/leaderelection.h" #include "alg/shapeformation.h" @@ -155,6 +156,18 @@ void ScriptInterface::compression(const int numParticles, const double lambda) { } } +void ScriptInterface::improvedleaderelection(const int numParticles, + const double holeProb) { + if (numParticles <= 0) { + log("# particles must be > 0", true); + } else if (holeProb < 0 || holeProb > 1) { + log("holeProb in [0,1] required", true); + } else { + sim.setSystem(std::make_shared(numParticles, + holeProb)); + } +} + void ScriptInterface::infobjcoating(const int numParticles, const double holeProb) { if (numParticles <= 0) { diff --git a/script/scriptinterface.h b/script/scriptinterface.h index 19d90ec2..d8c59f9c 100644 --- a/script/scriptinterface.h +++ b/script/scriptinterface.h @@ -71,6 +71,8 @@ class ScriptInterface : public QObject { // Algorithm instance commands. Documentation for foo() can be found in // alg/foo.h. void compression(const int numParticles = 100, const double lambda = 4.0); + void improvedleaderelection(const int numParticles = 100, + const double holeProb = 0.2); void infobjcoating(const int numParticles = 100, const double holeProb = 0.2); void leaderelection(const int numParticles = 100, const double holeProb = 0.2); diff --git a/ui/alg.cpp b/ui/alg.cpp index ecd0cb58..2fb32e36 100644 --- a/ui/alg.cpp +++ b/ui/alg.cpp @@ -69,6 +69,11 @@ AlgorithmList::AlgorithmList() { _algorithms.back()->addParameter("# Particles", "100"); _algorithms.back()->addParameter("Lambda", "4.0"); + // Improved Leader Election. + _algorithms.push_back(new Algorithm("Improved Leader Election", "improvedleaderelection")); + _algorithms.back()->addParameter("# Particles", "100"); + _algorithms.back()->addParameter("Hole Prob.", "0.2"); + // Infinite Object Coating. _algorithms.push_back(new Algorithm("Infinite Object Coating", "infobjcoating")); _algorithms.back()->addParameter("# Particles", "100"); From 0d40da01a6f673313402dbb7863725f9c59052b2 Mon Sep 17 00:00:00 2001 From: Ryan Yiu Date: Mon, 28 Oct 2019 02:12:11 -0700 Subject: [PATCH 6/6] ~ Update token removal and clean + document code --- alg/improvedleaderelection.cpp | 98 +++++++++++------ alg/improvedleaderelection.h | 185 +++++++++++++++++---------------- core/amoebotparticle.h | 6 +- 3 files changed, 167 insertions(+), 122 deletions(-) diff --git a/alg/improvedleaderelection.cpp b/alg/improvedleaderelection.cpp index 442766ac..51e33b95 100644 --- a/alg/improvedleaderelection.cpp +++ b/alg/improvedleaderelection.cpp @@ -18,9 +18,29 @@ LeaderElectionParticle::LeaderElectionParticle(const Node head, currentAgent(0) { borderColorLabels.fill(-1); borderPointColorLabels.fill(-1); + leaderSelected = false; } void LeaderElectionParticle::activate() { + if (leaderSelected) { + if (state != State::Finished && state != State::Leader) { + for (unsigned i = 0; i < agents.size(); i++) { + LeaderElectionAgent* agent = agents.at(i); + agent->agentState = State::Finished; + agent->setStateColor(); + agent->cleanAllTokens(); + agent->paintBackSegment(-1); + agent->paintFrontSegment(-1); + } + state = State::Finished; + } + for (int i = 0; i < 6; i++) { + if (hasNbrAtLabel(i)) { + nbrAtLabel(i).leaderSelected = true; + } + } + } + if (state == State::Idle) { // Determine the number of neighbors of the current particle. // If there are no neighbors, then that means the particle is the only @@ -90,6 +110,19 @@ void LeaderElectionParticle::activate() { agent->paintBackSegment(-1); agent->paintFrontSegment(-1); } + } else if (state == State::Leader) { + for (int i = 0; i < 6; i++) { + if (hasNbrAtLabel(i)) { + nbrAtLabel(i).leaderSelected = true; + } + } + leaderSelected = true; + for (unsigned i = 0; i < agents.size(); i++) { + LeaderElectionAgent* agent = agents.at(i); + agent->cleanAllTokens(); + agent->paintBackSegment(-1); + agent->paintFrontSegment(-1); + } } return; @@ -103,16 +136,13 @@ int LeaderElectionParticle::headMarkColor() const { return -1; } -int LeaderElectionParticle::headMarkDir() const { - return -1; -} - int LeaderElectionParticle::tailMarkColor() const { return headMarkColor(); } QString LeaderElectionParticle::inspectionText() const { QString text; + QString indent = " "; text += "head: (" + QString::number(head.x) + ", " + QString::number(head.y) + ")\n"; text += "orientation: " + QString::number(orientation) + "\n"; @@ -134,42 +164,48 @@ QString LeaderElectionParticle::inspectionText() const { for (LeaderElectionAgent* agent : agents) { int nextAgentDir = agent->nextAgentDir; int prevAgentDir = agent->prevAgentDir; - text += [agent](){ + text += [agent, indent](){ switch(agent->agentState) { - case State::Demoted: return " demoted\n "; + case State::Demoted: return indent + "demoted\n"; case State::Candidate: switch(agent->subPhase) { case LeaderElectionParticle::LeaderElectionAgent:: - SubPhase::SegmentSetup: return " segment setup\n "; + SubPhase::SegmentSetup: return indent + "segment setup\n"; case LeaderElectionParticle::LeaderElectionAgent:: - SubPhase::IdentifierSetup: return " identifier setup\n "; + SubPhase::IdentifierSetup: return indent + "identifier setup\n"; case LeaderElectionParticle::LeaderElectionAgent:: SubPhase::IdentifierComparison: - return " identifer comparison\n "; + return indent + "identifer comparison\n"; case LeaderElectionParticle::LeaderElectionAgent:: SubPhase::SolitudeVerification: - return " solitude verification\n "; + return indent + "solitude verification\n"; } - case State::SoleCandidate: return " sole candidate\n "; - default: return "invalid\n"; + case State::SoleCandidate: return indent + "sole candidate\n"; + default: return indent + "invalid\n"; } }(); - text += " agent dir: " + QString::number(agent->agentDir) + "\n "; - text += " next agent dir: " + QString::number(nextAgentDir) + "\n "; - text += " prev agent dir: " + QString::number(prevAgentDir) + "\n "; - text += " number of digit tokens: " + + text += indent + indent + "agent dir: " + QString::number(agent->agentDir) + + "\n"; + text += indent + indent + "next agent dir: " + + QString::number(nextAgentDir) + "\n"; + text += indent + indent + "prev agent dir: " + + QString::number(prevAgentDir) + "\n"; + text += indent + indent + "number of digit tokens: " + QString::number(agent->countAgentTokens(nextAgentDir)) + - "\n "; - text += " number of delimiter tokens: " + + "\n"; + text += indent + indent + "number of delimiter tokens: " + QString::number(agent->countAgentTokens(nextAgentDir)) + - "\n "; - text += " has generated reverse tokens: " + - QString::number(agent->hasGeneratedReverseToken) + "\n "; - text += " number of setup tokens: " + + "\n"; + text += indent + indent + "has generated reverse tokens: " + + QString::number(agent->hasGeneratedReverseToken) + "\n"; + text += indent + indent + "number of setup tokens: " + QString::number(agent->countAgentTokens(nextAgentDir) + agent->countAgentTokens(prevAgentDir)) + - "\n "; - text += " id value: " + QString::number(agent->idValue) + "\n"; + "\n"; + text += indent + indent + "id value: " + QString::number(agent->idValue) + + "\n"; + text += indent + indent + "compare status: " + + QString::number(agent->compareStatus) + "\n"; } text += "has leader election tokens: " + QString::number(hasToken()) + "\n"; @@ -183,6 +219,7 @@ QString LeaderElectionParticle::inspectionText() const { " positive y tokens\n"; text += "has " + QString::number(countTokens()) + " negative y tokens\n"; + text += "leader has been selected: " + QString::number(leaderSelected) + "\n"; text += "\n"; return text; @@ -473,6 +510,8 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { if (hasAgentToken(nextAgentDir) && !hasGeneratedReverseToken) { + Q_ASSERT(!demotedFromComparison); + if (prev != nullptr && prev->hasGeneratedReverseToken) { if (next != nullptr && next->hasGeneratedReverseToken) { takeAgentToken(nextAgentDir); @@ -497,9 +536,7 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { } else if (passTokensDir == 1 && prev != nullptr) { std::shared_ptr token = takeAgentToken(nextAgentDir); - if (next != nullptr && (next->agentState == State::Candidate || - next->hasGeneratedReverseToken || - next->demotedFromComparison)) { + if (token->val == -1) { token->val = idValue; } passAgentToken(prevAgentDir, token); @@ -570,6 +607,7 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { int color = peekAgentToken(nextAgentDir)->comparisonColor; peekAgentToken(nextAgentDir)->isActive = true; + peekAgentToken(nextAgentDir)->compare = 0; passAgentToken(prevAgentDir, takeAgentToken (nextAgentDir)); @@ -584,7 +622,9 @@ void LeaderElectionParticle::LeaderElectionAgent::activate() { isActive = false; peekAgentToken(nextAgentDir)->isActive = false; } - peekAgentToken(nextAgentDir)->compare = compareStatus; + if (compareStatus != 0) { + peekAgentToken(nextAgentDir)->compare = compareStatus; + } if (canPassComparisonToken(true)) { int color = peekAgentToken(nextAgentDir)->comparisonColor; @@ -1015,7 +1055,7 @@ void LeaderElectionParticle::LeaderElectionAgent::setStateColor() { candidateParticle->borderPointColorLabels.at(globalizedDir) = 0x00ff00; break; case State::Leader: - candidateParticle->borderPointColorLabels.at(globalizedDir) = 0x00ff00; + candidateParticle->borderPointColorLabels.at(globalizedDir) = -1; break; case State::Finished: candidateParticle->borderPointColorLabels.at(globalizedDir) = -1; diff --git a/alg/improvedleaderelection.h b/alg/improvedleaderelection.h index 85609a2c..f38c71a0 100644 --- a/alg/improvedleaderelection.h +++ b/alg/improvedleaderelection.h @@ -1,25 +1,17 @@ -// Defines the particle system and composing particles for the General -// Leader Election Algorithm as alluded to in 'Leader Election and Shape -// Formation with Self-Organizing Programmable Matter' -// [https://arxiv.org/abs/1503.07991]. +// Defines the particle system and composing particles for the Improved +// Leader Election Algorithm as alluded to in 'Improved Leader Election for +// Self-Organizing Programmable Matter' +// [https://arxiv.org/abs/1701.03616]. // -// Please note that this distributed implementation of the algorithm described -// in the above paper has a chance of not working (which is related to the -// Segment Comparison subphase); however, the centralized algorithm described in -// the paper (which this distributed implementation is based on) is correct. - -/** - TODO: - 1) Check whether or not the template class is really necessary --> I think - I could definitely replace it with just a general Token class decl since - all of my tokens technically inherit from it - 2) Randomly assign colors to agents + delimiter tokens when they enter - identifier comparison --> going to have to store this somewhere locally in - the agent 'cause it might alternate between solitude verification and - identifier comparison - 3) Identifier comparison is messed up somewhere, somehow (and the coloring - scheme also seems to be incorrect - */ +// A side remark about the algorithm is about an additional condition that may +// cause the algorithm to fail to elect a leader (apart from the probability +// that all agents elect to demote themselves in the Segment Setup phase): +// In the Segment Setup phase, if the agents determine their agent states in +// such a way that each candidate agent is either 1 demoted agent away from +// one another, or directly next to each other, the algorithm will not progress +// beyond the Identifier Comparison phase and fail to elect a leader. This may +// be observed in cycles of smaller lengths (such as ones in the inner +// boundary). #ifndef AMOEBOTSIM_ALG_IMPROVEDLEADERELECTION_H #define AMOEBOTSIM_ALG_IMPROVEDLEADERELECTION_H @@ -43,10 +35,8 @@ class LeaderElectionParticle : public AmoebotParticle { }; // Constructs a new particle with a node position for its head, a global - // compass direction from its head to its tail (-1 if contracted), an offset - // for its local compass, a system which it belongs to, an initial state, a - // signal for determining turning directions (currently for vertex triangle - // and square construction), and a string to determine what shape to form. + // compass direction from its head to its tail (-1 if contracted), an offset + // for its local compass, and a system which it belongs to. LeaderElectionParticle(const Node head, const int globalTailDir, const int orientation, AmoebotSystem& system, State state); @@ -57,10 +47,8 @@ class LeaderElectionParticle : public AmoebotParticle { // Functions for altering a particle's cosmetic appearance; headMarkColor // (respectively, tailMarkColor) returns the color to be used for the ring // drawn around the head (respectively, tail) node. Tail color is not shown - // when the particle is contracted. headMarkDir returns the label of the port - // on which the black head marker is drawn. + // when the particle is contracted. virtual int headMarkColor() const; - virtual int headMarkDir() const; virtual int tailMarkColor() const; // Returns the string to be displayed when this particle is inspected; used @@ -89,10 +77,10 @@ class LeaderElectionParticle : public AmoebotParticle { protected: // The LeaderElectionToken struct provides a general framework of what a - // token under the General Leader Election algorithm behaves. - // origin is used to define the direction (label) from which a - // LeaderElectionToken has been sent from. + // token under the Improved Leader Election algorithm behaves. struct LeaderElectionToken : public Token { + // origin is used to define the direction (label) from which a + // LeaderElectionToken has been sent from. int origin; }; @@ -134,13 +122,14 @@ class LeaderElectionParticle : public AmoebotParticle { } }; - // -1 --> Lesser - // 0 --> Equal - // 1 --> Greater + // DelimiterToken carries the comparison values from the previous comparisons + // of agents with DigitTokens to gauge the compare the id segments of the + // different candidate agnets. The value stored in compare follows the same + // guidelines as in compareStatus (see below). struct DelimiterToken : public LeaderElectionToken { int value = 0; bool isActive = false; - int compare = -1; + int compare = 0; int comparisonColor = -1; DelimiterToken(int origin = -1, int comparisonColor = -1, int value = -1, bool isActive = false, int compare = 0) { @@ -220,76 +209,89 @@ class LeaderElectionParticle : public AmoebotParticle { LeaderElectionAgent(); - // localId is an int which stores which id an agent has according to the - // particle which is associated with it. This localId value lies in [1,3] + // General variables in agent memory: + // The particle emulating this agent assigns it a localId in [1,3] to + // distinguish it from the other agents it may be emulating. From the + // particle's perspective, this agent is in local direction/label agentDir. + // The neighboring particle emulating the next (respectively, previous) + // agent on this agent's boundary is in local direction nextAgentDir + // (respectively, prevAgentDir). passTokensDir is used to determine if the + // agent should pass tokens toward nextAgentDir (if 0) or prevAgentDir (if + // 1). This is done to maintain the rule from direct write communication + // that a particle can only write into the memory of one of its neighbors in + // a single activation. demotedFromComparison is used to determine whether or + // not the current agent was originally a candidate, which may impact its + // behavior for phases such as Identifier Comparison. int localId; - // agentDir stores the direction/label of the current agent according to the - // labelling scheme used by the particle that owns the current agent. - // nextAgentDir and prevAgentDir are store the direction/label of the next - // and previous (respectively) agents of the current agent according to the - // particle that owns the current agent and the direction of the boundary - // that the agent is on. int agentDir, nextAgentDir, prevAgentDir; + int passTokensDir = -1; + bool demotedFromComparison = false; State agentState; SubPhase subPhase; LeaderElectionParticle* candidateParticle; - // passedTokensDir is an int which will be used to determine whether or not - // an agent will pass tokens in the prevAgentDir or nextAgentDir. - // This is done to maintain the rule that particles can only pass tokens to 1 - // other particle in a single activation. - // 0 --> tokens should be passed forwards in the cycle (nextAgentDir) - // 1 --> tokens should be passed backwards in the cycle (prevAgentDir) - int passTokensDir = -1; - - // Keep track of whether or not the current agent was demoted due to identifier - // comparison - bool demotedFromComparison = false; + // Variables for Identifier Setup: + // hasGeneratedSetupToken is used to determine whether or not the candidate + // agent has generated a SetupToken. + // hasGeneratedReverseToken is used to determine whether or not the agent + // has generated an id token for the corresponding candidate's segment. bool hasGeneratedSetupToken = false; bool hasGeneratedReverseToken = false; - bool isActive = false; - // compareStatus - // -1 --> lessThan - // 0 --> equal - // 1 --> greaterThan + + // Variables for Identifier Comparison: + // compareStatus is used to store the difference between the current agent + // and the DigitToken that it has matched with. compareStatus is computed as + // the difference between the agent's digit value and the DigitToken's value, + // i.e., compareStatus = idValue - DigitToken.value, with the values + // representing the following relationships: + // -1 --> agent value less than token value + // 0 --> agent value equal to token value + // 1 --> agent value greater than token value + // idValue is used to store the agent's id value generated from the + // Identifier Setup Phase. + // comparisonColor is used to store the agent's identifying color for the + // Identifier Comparison Phase. + // isActive is used to determine whether or not the agent has matched with a + // DigitToken. int compareStatus = -1; int idValue = -1; int comparisonColor = randInt(0, 16777216); + bool isActive = false; + + // canPassComparisonToken is a helper functino for the Identifier Comparison + // phase to determine whether or not the current agent may pass the + // Identifier Comparison token (Digit Token or Delimiter Token, determined + // by the boolean parameter). + bool canPassComparisonToken(bool isDelimiter) const; // Variables for Solitude Verification - // createdLead is a boolean which determines whether or not the current agent - // has generated a solitude active token and passed it along its front - // segment. - // hasGeneratedTokens is a boolean which determines whether or not the - // current agent has generated solitude vector tokens using the solitude - // active token. This is necessary since there are cases where different - // agents on the same particle might have the same nextAgentDir as the - // other's prevAgentDir, so an incorrect token pass is avoided using this - // boolean variable. + // createdLead is true if this agent generated a solitude active token and + // passed it forward during Solitude Verification. + // hasGeneratedTokens is true if this agent generated solitude vector tokens + // using the solitude active token. This is used to avoid incorrectly mixing + // tokens of different agents on the same particle in Solitude Verification. bool createdLead = false; bool hasGeneratedTokens = false; // Variables for Boundary Testing - // testingBorder is a boolean used to determine whether or not the current - // sole candidate has generated a border testing token to determine what - // boundary it is on. + // testingBorder is true if this agent is the sole candidate and has begun + // the Boundary Testing subphase. bool testingBorder = false; // The activate function is the LeaderElectionAgent equivalent of an - // Amoebot Particle's activate function + // Amoebot Particle's activate function. void activate(); void cleanAllTokens(); - bool canPassComparisonToken(bool isDelimiter) const; // Solitude Verification Methods // augmentDirVector takes a pair as a parameter, which represents - // the current vector stored in the solitude active token. This function then - // generates the next vector according to a local coordinate system (which is - // determined when a candidate agent in the Solitude Verification subphase - // generates the solitude active token) based on the vector stored in the - // solitude active token. + // the current vector stored in the solitude active token. This function + // then generates the next vector according to a local coordinate system + // (which is determined when a candidate agent in the Solitude Verification + // subphase generates the solitude active token) based on the vector stored + // in the solitude active token. std::pair augmentDirVector(std::pair vector); // generateSolitudeVectorTokens generates the solitude vector tokens @@ -312,14 +314,14 @@ class LeaderElectionParticle : public AmoebotParticle { // The cleanSolitudeVerificationTokens function will clean the solitude // vector tokens owned by a particular agent as well as paint the - // front and back segments gray + // front and back segments gray. void cleanSolitudeVerificationTokens(); // Boundary Testing methods int addNextBorder(int currentSum) const; // Methods for passing, taking, and checking the ownership of tokens at the - // agent level + // agent level. template bool hasAgentToken(int agentDir) const; template @@ -329,6 +331,7 @@ class LeaderElectionParticle : public AmoebotParticle { template void passAgentToken(int agentDir, std::shared_ptr token); + // Method for counting the number of tokens at the agent level. template int countAgentTokens(int agentDir) const; @@ -336,22 +339,22 @@ class LeaderElectionParticle : public AmoebotParticle { LeaderElectionAgent* prevAgent() const; // Methods responsible for rendering the agents onto the simulator with their - // colors changing based on the state and the subphase of the current agent - // Red --> Candidate agent in Segment Comparison Subphase - // Yellow --> Candidate agent in Coin Flipping Subphase - // Blue --> Candidate agent in Solitude Verification Subphase + // colors changing based on the state and the subphase of the current agent. + // Yellow --> Identifier Setup + // Random Color (determined by comparisonColor) --> Identifier Comparison + // Blue --> Solitude Verification Subphase // Grey --> Demoted agent // Green --> Sole candidate void setStateColor(); void setSubPhaseColor(); // Methods responsible for painting the borders which will act as physical - // representations of the cycle for leader election - // Red --> Segment Comparison Phase - // Yellow --> Coin Flipping Phase + // representations of the cycle for leader election. + // Yellow --> Identifier Setup + // Random Color (determined by comparisonColor) --> DelimiterToken for + // Identifier Comparison // Blue --> Solitude Verification Phase - // Grey --> No phase (or, alternatively, active phase of Segment Comparison - // phase + // Grey --> No specific phase void paintFrontSegment(const int color); void paintBackSegment(const int color); }; @@ -362,6 +365,10 @@ class LeaderElectionParticle : public AmoebotParticle { std::vector agents; std::array borderColorLabels; std::array borderPointColorLabels; + + // leaderSelected is used to act as a signal for when a leader is selected to + // set all of the remaining particles to finished. + bool leaderSelected; }; class ImprovedLeaderElectionSystem : public AmoebotSystem { diff --git a/core/amoebotparticle.h b/core/amoebotparticle.h index e8c648fd..8920903d 100644 --- a/core/amoebotparticle.h +++ b/core/amoebotparticle.h @@ -205,8 +205,7 @@ std::shared_ptr AmoebotParticle::takeToken() { std::shared_ptr token = std::dynamic_pointer_cast(tokens[i]); if (token != nullptr) { - std::swap(tokens[0], tokens[i]); - tokens.pop_front(); + tokens.erase(tokens.begin() + i); return token; } } @@ -233,8 +232,7 @@ std::shared_ptr AmoebotParticle::takeToken( std::shared_ptr token = std::dynamic_pointer_cast(tokens[i]); if (token != nullptr && propertyCheck(token)) { - std::swap(tokens[0], tokens[i]); - tokens.pop_front(); + tokens.erase(tokens.begin() + i); return token; } }