File GraphBuilder.hpp
File List > Agents > GP > GraphBuilder.hpp
Go to the documentation of this file
#pragma once
#include "CGPGenotype.hpp"
#include "Graph.hpp"
namespace cowboys {
class GraphBuilder {
public:
GraphBuilder() = default;
~GraphBuilder() = default;
std::unique_ptr<Graph> CartesianGraph(const CGPGenotype &genotype, const std::vector<InnerFunction> &function_set,
const cse491::AgentBase *agent = nullptr) {
auto decision_graph = std::make_unique<Graph>();
//
// Add all the nodes
//
// Input layer
GraphLayer input_layer;
for (size_t i = 0; i < genotype.GetNumInputs(); ++i) {
input_layer.emplace_back(std::make_shared<GraphNode>(0));
}
decision_graph->AddLayer(input_layer);
// Middle Layers
for (size_t i = 0; i < genotype.GetNumLayers(); ++i) {
GraphLayer layer;
for (size_t j = 0; j < genotype.GetNumNodesPerLayer(); ++j) {
layer.emplace_back(std::make_shared<GraphNode>(0));
}
decision_graph->AddLayer(layer);
}
// Action layer
GraphLayer output_layer;
for (size_t i = 0; i < genotype.GetNumOutputs(); ++i) {
output_layer.emplace_back(std::make_shared<GraphNode>(0));
}
decision_graph->AddLayer(output_layer);
//
// Configure graph based on genotype
//
auto functional_nodes = decision_graph->GetFunctionalNodes();
auto all_nodes = decision_graph->GetNodes();
auto nodes_it = functional_nodes.cbegin();
auto genes_it = genotype.cbegin();
// Iterator distances should be the same
assert(std::distance(functional_nodes.cend(), functional_nodes.cbegin()) ==
std::distance(genotype.cend(), genotype.cbegin()));
// Get the iterator of all nodes and move it to the start of the first functional node
auto all_nodes_it = all_nodes.cbegin() + genotype.GetNumInputs();
for (; nodes_it != functional_nodes.end() && genes_it != genotype.cend(); ++nodes_it, ++genes_it) {
// Advance the all nodes iterator if we are at the start of a new layer
auto dist = std::distance(functional_nodes.cbegin(), nodes_it);
if (dist != 0 && dist % genotype.GetNumNodesPerLayer() == 0) {
std::advance(all_nodes_it, genotype.GetNumNodesPerLayer());
}
auto &[connections, function_idx, output] = *genes_it;
(*nodes_it)->SetFunctionPointer(NodeFunction{function_set.at(function_idx), agent});
(*nodes_it)->SetDefaultOutput(output);
// Copy the all nodes iterator and move it backwards by the number of connections
auto nodes_it_copy = all_nodes_it;
std::advance(nodes_it_copy, -connections.size());
// Add the inputs to the node
for (auto &connection : connections) {
if (connection != '0') {
(*nodes_it)->AddInput(*nodes_it_copy);
}
++nodes_it_copy;
}
}
return decision_graph;
}
std::unique_ptr<Graph> VerticalPacer() {
auto decision_graph = std::make_unique<Graph>();
GraphLayer input_layer;
std::shared_ptr<GraphNode> prev_action = std::make_shared<GraphNode>(0);
std::shared_ptr<GraphNode> current_state = std::make_shared<GraphNode>(0);
std::shared_ptr<GraphNode> above_state = std::make_shared<GraphNode>(0);
std::shared_ptr<GraphNode> below_state = std::make_shared<GraphNode>(0);
std::shared_ptr<GraphNode> left_state = std::make_shared<GraphNode>(0);
std::shared_ptr<GraphNode> right_state = std::make_shared<GraphNode>(0);
input_layer.insert(input_layer.end(),
{prev_action, current_state, above_state, below_state, left_state, right_state});
decision_graph->AddLayer(input_layer);
// state == 1 => floor which is walkable
GraphLayer obstruction_layer;
std::shared_ptr<GraphNode> up_not_blocked = std::make_shared<GraphNode>(AnyEq);
up_not_blocked->AddInputs(GraphLayer{above_state, std::make_shared<GraphNode>(1)});
std::shared_ptr<GraphNode> down_not_blocked = std::make_shared<GraphNode>(AnyEq);
down_not_blocked->AddInputs(GraphLayer{below_state, std::make_shared<GraphNode>(1)});
obstruction_layer.insert(obstruction_layer.end(), {up_not_blocked, down_not_blocked});
decision_graph->AddLayer(obstruction_layer);
// Separate previous action into up and down nodes
GraphLayer prev_action_layer;
std::shared_ptr<GraphNode> up_prev_action = std::make_shared<GraphNode>(AnyEq);
up_prev_action->AddInputs(GraphLayer{prev_action, std::make_shared<GraphNode>(1)});
std::shared_ptr<GraphNode> down_prev_action = std::make_shared<GraphNode>(AnyEq);
down_prev_action->AddInputs(GraphLayer{prev_action, std::make_shared<GraphNode>(2)});
prev_action_layer.insert(prev_action_layer.end(), {up_prev_action, down_prev_action});
decision_graph->AddLayer(prev_action_layer);
GraphLayer moving_layer;
// If up_not_blocked and up_prev_action ? return 1 : return 0
// If down_not_blocked and down_prev_action ? return 1 : return 0
std::shared_ptr<GraphNode> keep_up = std::make_shared<GraphNode>(And);
keep_up->AddInputs(GraphLayer{up_not_blocked, up_prev_action});
std::shared_ptr<GraphNode> keep_down = std::make_shared<GraphNode>(And);
keep_down->AddInputs(GraphLayer{down_not_blocked, down_prev_action});
moving_layer.insert(moving_layer.end(), {keep_up, keep_down});
decision_graph->AddLayer(moving_layer);
// If down_blocked, turn_up
// If up_blocked, turn_down
GraphLayer turn_layer;
std::shared_ptr<GraphNode> turn_up = std::make_shared<GraphNode>(Not);
turn_up->AddInputs(GraphLayer{down_not_blocked});
std::shared_ptr<GraphNode> turn_down = std::make_shared<GraphNode>(Not);
turn_down->AddInputs(GraphLayer{up_not_blocked});
turn_layer.insert(turn_layer.end(), {turn_up, turn_down});
decision_graph->AddLayer(turn_layer);
// Output layer, up, down, left, right
GraphLayer action_layer;
// move up = keep_up + turn_up,
// move down = keep_down + turn_down,
std::shared_ptr<GraphNode> up = std::make_shared<GraphNode>(Sum);
up->AddInputs(GraphLayer{keep_up, turn_up});
std::shared_ptr<GraphNode> down = std::make_shared<GraphNode>(Sum);
down->AddInputs(GraphLayer{keep_down, turn_down});
std::shared_ptr<GraphNode> left = std::make_shared<GraphNode>(0);
std::shared_ptr<GraphNode> right = std::make_shared<GraphNode>(0);
action_layer.insert(action_layer.end(), {up, down, left, right});
decision_graph->AddLayer(action_layer);
return decision_graph;
}
};
} // namespace cowboys