diff --git a/metadata.json b/metadata.json index 52615a4..3faad7b 100644 --- a/metadata.json +++ b/metadata.json @@ -3,9 +3,9 @@ "uid": "gameOfLife", "title": "Conway's Game of Life", "version": 1, - "author": "Ethan Padden and Marcos Cardinot", - "description": "This implements Conway's Game Of Life cellular automaton.", - + "author": "Ethan Padden, Marcos Cardinot and Eleftheria Chatziargyriou", + "description": "This implements life-like cellular automata.", "supportedGraphs": ["squareGrid"], + "pluginAttributesScope": [ {"rules": "string"} ], "nodeAttributesScope": [ {"live": "bool"} ] } diff --git a/plugin.cpp b/plugin.cpp index 04a93f2..0ee810e 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -10,10 +10,78 @@ namespace evoplex { +QStringList GameOfLife::parseCmd(const QString &cmd) +{ + if (cmd.isEmpty()) + { + qWarning() << "The command cannot be empty"; + return QStringList(); + } + + QStringList _cmds; + QString _cmd1, _cmd2; + + bool isInt1 = false; + bool isInt2 = false; + + if (cmd.indexOf("/") == -1) + { + qWarning() << "Commands must be of the form B{number of neighbors for cell birth} / S{number of neighbors for cell survival"; + return QStringList(); + } + + _cmds = cmd.split("/"); + + if (!_cmds.at(0).startsWith("B") || !_cmds.at(1).startsWith("S")) + { + qWarning() << "Commands must be of the form B{number of neighbors for cell birth} / S{number of neighbors for cell survival"; + return QStringList(); + } + + _cmd1 = _cmds.at(0); + _cmd1.remove(0, 1); + _cmd2 = _cmds.at(1); + _cmd2.remove(0, 1); + + int intRule1 = _cmd2.toInt(&isInt1); + int intRule2 = _cmd1.toInt(&isInt2); + + // check if rules are integers + if (!isInt1 || !isInt2) + { + qWarning() << "Unable to parse command. Make sure you give a valid integer."; + return QStringList(); + } + // check if the rulestring has unique integers + if (QSet::fromList(_cmd1.split("")).count() != _cmd1.size() + 1 || QSet::fromList(_cmd2.split("")).count() != _cmd2.size() + 1) + { + qWarning() << "Integers can't appear more than once on each rule."; + return QStringList(); + } + return QStringList() << _cmd1 << _cmd2; +} + bool GameOfLife::init() { // gets the id of the `live` node's attribute, which is the same for all nodes m_liveAttrId = node(0).attrs().indexOf("live"); + + // parses the ruleset + if (attrExists("rules")){ + m_ruleset = attr("rules").toQString(); + } + else{ + qWarning() << "missing attributes."; + return false; + } + + m_rulesetLst = parseCmd(m_ruleset); + + if (m_rulesetLst.isEmpty()) + { + return false; + } + return m_liveAttrId >= 0; } @@ -31,20 +99,15 @@ bool GameOfLife::algorithmStep() } if (node.attr(m_liveAttrId).toBool()) { - if (liveNeighbourCount < 2) { // Dies due to underpopulation - nextStates.emplace_back(false); - } else if (liveNeighbourCount < 4) { // Lives to next state - nextStates.emplace_back(true); - } else { // Dies due to overpopulation - nextStates.emplace_back(false); - } + // If the node is alive, then it only survives if its number of neighbors is specified in the rulestring. + // Otherwise, it dies from under/overpopulation + nextStates.emplace_back(m_rulesetLst.at(1).contains(QString::number(liveNeighbourCount))); } else { - // Any dead node with exactly three live neighbors - // becomes a live node, as if by reproduction. - nextStates.emplace_back(liveNeighbourCount == 3); + // Any dead cell can become alive if its number of neighbors matches the one specified in the rulestring. + // Otherwise, it remains dead. + nextStates.emplace_back(m_rulesetLst.at(0).contains(QString::number(liveNeighbourCount))); } } - // For each node, load the next state into the current state size_t i = 0; for (Node node : nodes()) { diff --git a/plugin.h b/plugin.h index a04bb04..17add8b 100644 --- a/plugin.h +++ b/plugin.h @@ -17,9 +17,12 @@ class GameOfLife: public AbstractModel public: bool init() override; bool algorithmStep() override; + QStringList parseCmd(const QString &cmd); private: int m_liveAttrId; // the id of the 'live' node's attribute + QString m_ruleset; // the model's ruleset (B/S format) + QStringList m_rulesetLst; }; } // evoplex #endif // GAME_OF_LIFEL_H