Skip to content

Commit

Permalink
* Merge pull request #30 from SOPSLab/metrics-demo
Browse files Browse the repository at this point in the history
  • Loading branch information
jdaymude committed Apr 11, 2020
2 parents 949568b + dccbe25 commit 7798638
Show file tree
Hide file tree
Showing 18 changed files with 2,533 additions and 22 deletions.
2 changes: 2 additions & 0 deletions AmoebotSim.pro
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ win32:RC_FILE = res/AmoebotSim.rc

HEADERS += \
alg/demo/discodemo.h \
alg/demo/metricsdemo.h \
alg/demo/pulldemo.h \
alg/demo/tokendemo.h \
alg/compression.h \
Expand Down Expand Up @@ -38,6 +39,7 @@ HEADERS += \

SOURCES += \
alg/demo/discodemo.cpp \
alg/demo/metricsdemo.cpp \
alg/demo/pulldemo.cpp \
alg/demo/tokendemo.cpp \
alg/compression.cpp \
Expand Down
2 changes: 1 addition & 1 deletion alg/demo/discodemo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include "alg/demo/discodemo.h"

DiscoDemoParticle::DiscoDemoParticle(const Node head, const int globalTailDir,
DiscoDemoParticle::DiscoDemoParticle(const Node& head, const int globalTailDir,
const int orientation,
AmoebotSystem& system,
const int counterMax)
Expand Down
2 changes: 1 addition & 1 deletion alg/demo/discodemo.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class DiscoDemoParticle : public AmoebotParticle {
// compass direction from its head to its tail (-1 if contracted), an offset
// for its local compass, a system that it belongs to, and a maximum value for
// its counter.
DiscoDemoParticle(const Node head, const int globalTailDir,
DiscoDemoParticle(const Node& head, const int globalTailDir,
const int orientation, AmoebotSystem& system,
const int counterMax);

Expand Down
173 changes: 173 additions & 0 deletions alg/demo/metricsdemo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/* Copyright (C) 2019 Joshua J. Daymude, Robert Gmyr, and Kristian Hinnenthal.
* The full GNU GPLv3 can be found in the LICENSE file, and the full copyright
* notice can be found at the top of main/main.cpp. */

#include "alg/demo/metricsdemo.h"

#include <algorithm> // for std::max
#include <cmath> // for std::sqrt, std::pow

MetricsDemoParticle::MetricsDemoParticle(const Node& head,
const int globalTailDir,
const int orientation,
AmoebotSystem& system,
const int counterMax)
: AmoebotParticle(head, globalTailDir, orientation, system),
_counter(counterMax),
_counterMax(counterMax) {
_state = getRandColor();
}

void MetricsDemoParticle::activate() {
// First decrement the particle's counter. If it's zero, reset the counter and
// get a new color.
_counter--;
if (_counter == 0) {
_counter = _counterMax;
_state = getRandColor();
}

// Next, handle movement. If the particle is contracted, choose a random
// direction to try to expand towards, but only do so if the node in that
// direction is unoccupied. Otherwise, if the particle is expanded, simply
// contract its tail.
if (isContracted()) {
int expandDir = randDir();
if (canExpand(expandDir)) {
expand(expandDir);
} else if (hasObjectAtLabel(expandDir)) {
system.getCount("# Wall Bumps").record();
}
} else { // isExpanded().
contractTail();
}
}

int MetricsDemoParticle::headMarkColor() const {
switch(_state) {
case State::Red: return 0xff0000;
case State::Orange: return 0xff9000;
case State::Yellow: return 0xffff00;
case State::Green: return 0x00ff00;
case State::Blue: return 0x0000ff;
case State::Indigo: return 0x4b0082;
case State::Violet: return 0xbb00ff;
}

return -1;
}

int MetricsDemoParticle::tailMarkColor() const {
return headMarkColor();
}

QString MetricsDemoParticle::inspectionText() const {
QString text;
text += "Global Info:\n";
text += " head: (" + QString::number(head.x) + ", "
+ QString::number(head.y) + ")\n";
text += " orientation: " + QString::number(orientation) + "\n";
text += " globalTailDir: " + QString::number(globalTailDir) + "\n\n";
text += "Local Info:\n";
text += " state: ";
text += [this](){
switch(_state) {
case State::Red: return "red\n";
case State::Orange: return "orange\n";
case State::Yellow: return "yellow\n";
case State::Green: return "green\n";
case State::Blue: return "blue\n";
case State::Indigo: return "indigo\n";
case State::Violet: return "violet\n";
}
return "no state\n";
}();
text += " counter: " + QString::number(_counter);

return text;
}

MetricsDemoParticle::State MetricsDemoParticle::getRandColor() const {
// Randomly select an integer and return the corresponding state via casting.
return static_cast<State>(randInt(0, 7));
}

MetricsDemoSystem::MetricsDemoSystem(unsigned int numParticles, int counterMax) {
// In order to enclose an area that's roughly 3.7x the # of particles using a
// regular hexagon, the hexagon should have side length 1.4*sqrt(# particles).
int sideLen = static_cast<int>(std::round(1.4 * std::sqrt(numParticles)));
Node boundNode(0, 0);
for (int dir = 0; dir < 6; ++dir) {
for (int i = 0; i < sideLen; ++i) {
insert(new Object(boundNode));
boundNode = boundNode.nodeInDir(dir);
}
}

// Let s be the bounding hexagon side length. When the hexagon is created as
// above, the nodes (x,y) strictly within the hexagon have (i) -s < x < s,
// (ii) 0 < y < 2s, and (iii) 0 < x+y < 2s. Choose interior nodes at random to
// place particles, ensuring at most one particle is placed at each node.
std::set<Node> occupied;
while (occupied.size() < numParticles) {
// First, choose an x and y position at random from the (i) and (ii) bounds.
int x = randInt(-sideLen + 1, sideLen);
int y = randInt(1, 2 * sideLen);
Node node(x, y);

// If the node satisfies (iii) and is unoccupied, place a particle there.
if (0 < x + y && x + y < 2 * sideLen
&& occupied.find(node) == occupied.end()) {
insert(new MetricsDemoParticle(node, -1, randDir(), *this, counterMax));
occupied.insert(node);
}
}

// Set up metrics.
_counts.push_back(new Count("# Wall Bumps"));
_measures.push_back(new PercentRedMeasure("% Red", 1, *this));
_measures.push_back(new MaxDistanceMeasure("Max. Distance", 1, *this));
}

PercentRedMeasure::PercentRedMeasure(const QString name,
const unsigned int freq,
MetricsDemoSystem& system)
: Measure(name, freq),
_system(system) {}

double PercentRedMeasure::calculate() const {
int numRed = 0;

// Loop through all particles of the system.
for (const auto& p : _system.particles) {
// Convert the pointer to a MetricsDemoParticle so its color can be checked.
auto metr_p = dynamic_cast<MetricsDemoParticle*>(p);
if (metr_p->_state == MetricsDemoParticle::State::Red) {
numRed++;
}
}

return numRed / static_cast<double>(_system.size()) * 100;
}

MaxDistanceMeasure::MaxDistanceMeasure(const QString name,
const unsigned int freq,
MetricsDemoSystem& system)
: Measure(name, freq),
_system(system) {}

double MaxDistanceMeasure::calculate() const {
double maxDist = 0.0;
for (const auto& p1 : _system.particles) {
double x1 = p1->head.x + p1->head.y / 2.0;
double y1 = std::sqrt(3.0) / 2 * p1->head.y;
for (const auto& p2 : _system.particles) {
double x2 = p2->head.x + p2->head.y / 2.0;
double y2 = std::sqrt(3.0) / 2 * p2->head.y;
maxDist = std::max(std::sqrt(std::pow(x2 - x1, 2) + std::pow(y2 - y1, 2)),
maxDist);
}
}

return maxDist;
}
109 changes: 109 additions & 0 deletions alg/demo/metricsdemo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/* Copyright (C) 2020 Joshua J. Daymude, Robert Gmyr, and Kristian Hinnenthal.
* The full GNU GPLv3 can be found in the LICENSE file, and the full copyright
* notice can be found at the top of main/main.cpp. */

// Defines the particle system and composing particles for the MetricsDemo code
// tutorial. MetricsDemo demonstrates how to add custom "counts" and "measures"
// to an algorithm, enabling the capture of quantitative data. The pseudocode is
// available in the docs:
// [https://amoebotsim.rtfd.io/en/latest/tutorials/tutorials.html#metricsdemo-capturing-data].
//
// Run on the simulator command line using metricsdemo(#particles, counter max).

#ifndef AMOEBOTSIM_ALG_DEMO_METRICSDEMO_H_
#define AMOEBOTSIM_ALG_DEMO_METRICSDEMO_H_

#include <QString>

#include "core/amoebotparticle.h"
#include "core/amoebotsystem.h"

class MetricsDemoParticle : public AmoebotParticle {
friend class PercentRedMeasure;

public:
enum class State {
Red,
Orange,
Yellow,
Green,
Blue,
Indigo,
Violet
};

// 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 that it belongs to, and a maximum value for
// its counter.
MetricsDemoParticle(const Node& head, const int globalTailDir,
const int orientation, AmoebotSystem& system,
const int counterMax);

// Executes one particle activation.
void activate() override;

// Functions for altering the particle's color. headMarkColor() (resp.,
// tailMarkColor()) returns the color to be used for the ring drawn around the
// particle's head (resp., tail) node. In this demo, the tail color simply
// matches the head color.
int headMarkColor() const override;
int tailMarkColor() const override;

// Returns the string to be displayed when this particle is inspected; used to
// snapshot the current values of this particle's memory at runtime.
QString inspectionText() const override;

protected:
// Returns a random State.
State getRandColor() const;

// Member variables.
State _state;
int _counter;
const int _counterMax;

private:
friend class MetricsDemoSystem;
};

class MetricsDemoSystem : public AmoebotSystem {
friend class PercentRedMeasure;
friend class MaxDistanceMeasure;

public:
// Constructs a system of the specified number of MetricsDemoParticles
// enclosed by a hexagonal ring of objects.
MetricsDemoSystem(unsigned int numParticles = 30, int counterMax = 5);
};

class PercentRedMeasure : public Measure {
public:
// Constructs a PercentRedMeasure by using the parent constructor and adding a
// reference to the MetricsDemoSystem being measured.
PercentRedMeasure(const QString name, const unsigned int freq,
MetricsDemoSystem& system);

// Calculated the percentage of particles in the system in the Red state.
double calculate() const final;

protected:
MetricsDemoSystem& _system;
};

class MaxDistanceMeasure : public Measure {
public:
// Constructs a MaxDistanceMeasure by using the parent constructor and adding
// a reference to the MetricsDemoSystem being measured.
MaxDistanceMeasure(const QString name, const unsigned int freq,
MetricsDemoSystem& system);

// Calculates the largest Cartesian distance between any pair of particles in
// the system.
double calculate() const final;

protected:
MetricsDemoSystem& _system;
};

#endif // AMOEBOTSIM_ALG_DEMO_METRICSDEMO_H_
2 changes: 1 addition & 1 deletion alg/demo/tokendemo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include "alg/demo/tokendemo.h"

TokenDemoParticle::TokenDemoParticle(const Node head, const int globalTailDir,
TokenDemoParticle::TokenDemoParticle(const Node& head, const int globalTailDir,
const int orientation,
AmoebotSystem& system)
: AmoebotParticle(head, globalTailDir, orientation, system) {}
Expand Down
2 changes: 1 addition & 1 deletion alg/demo/tokendemo.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class TokenDemoParticle : 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, and a system which it belongs to.
TokenDemoParticle(const Node head, const int globalTailDir,
TokenDemoParticle(const Node& head, const int globalTailDir,
const int orientation, AmoebotSystem& system);

// Executes one particle activation.
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7798638

Please sign in to comment.