API Guide¶
This guide covers the core APIs for extending Game Graph Gym with new game types and solvers.
Adding New Game Types¶
Game Graph Gym uses X-macros to automatically generate graph types and utility functions. To add a new game type, you just need to define the vertex, edge, and graph properties and then call the DEFINE_GAME_GRAPH macro as follows.
// my_graph.hpp
#include <libggg/graphs/graph_utilities.hpp>
// Define vertex properties
#define VERTEX_FIELDS(F) \
F(std::string, name) \
F(int, player) \
F(int, custom_property)
// Define edge properties
#define EDGE_FIELDS(F) \
F(std::string, label)
// Define graph properties (if any)
#define GRAPH_FIELDS(F) \
F(std::string, title)
// Generate the complete graph type with utilities
DEFINE_GAME_GRAPH(VERTEX_FIELDS, EDGE_FIELDS, GRAPH_FIELDS)
// Optionally add utilities specific to your graph type
inline bool is_valid(const MyGraph &graph) {
return true;
}
This will generate (in the call-site namespace)
Graph- a Boost adjacency_list alias with the declared bundled propertiesVertex,Edge- descriptor type aliasesadd_vertex()andadd_edge()- convenience helpers whose parameter lists match the declared vertex/edge fieldsparse(std::istream&)andparse(const std::string&)- DOT parsers that returnstd::shared_ptr<Graph>on success (ornullptron failure)write(std::ostream&)andwrite(const std::string&)- DOT writers
To use the generated types and functions:
#include "my_graph.hpp"
// Create graph and add vertices with named parameters
Graph graph;
const auto v1 = add_vertex(graph, "vertex1", 0, 42);
const auto v2 = add_vertex(graph, "vertex2", 1, 24);
// Add edges with properties
add_edge(graph, v1, v2, "edge_label");
// Parse from file or stream
auto graph_ptr = parse("input.dot");
auto graph_ptr2 = parse(std::cin);
// Write to file or stream
write(*graph_ptr, "output.dot");
write(*graph_ptr, std::cout);
Adding New Solvers¶
Implementing a Solver¶
To implement a new solver, inherit from the unified Solver interface with the appropriate solution type and implement the solve() method. For example, implement a new solver for parity games, that operates on ggg::parity::graph::Graph and produces solutions which encode winning regions only (RSolution<ggg::parity::graph::Graph>) as follows.
#include "libggg/parity/graph.hpp"
#include "libggg/solvers/solver.hpp"
// Example solver providing winning regions and strategies
class YourSolver : public ggg::solvers::Solver<ggg::parity::graph::Graph, ggg::parity::RSolution> {
public:
ggg::parity::RSolution solve(const ggg::parity::graph::Graph &game) override {
ggg::parity::RSolution solution; // Default construct
const auto [vertices_begin, vertices_end] = boost::vertices(game);
for (auto it = vertices_begin; it != vertices_end; ++it) {
const auto vertex = *it;
const auto winner = computeWinner(game, vertex);
solution.setWinningPlayer(vertex, winner);
}
return solution;
}
std::string get_name() const override {
return "Your Custom Solver Name";
}
};
You can use the project macro to expose this solver as a CLI, following the pattern used by the shipped tools:
// inside the game-specific tools/ or main.cpp
GGG_GAME_SOLVER_MAIN(ggg::parity::graph::Graph, ggg::parity::graph::parse, YourSolver)
See the implementations of existing solvers under solvers/ as examples.
Solution Types¶
We define different solution capabilities via C++ concepts and inheritance.
Solution types are named XSolution, where the prefix X indicates what is encoded:
initial state only (X=I), winning regions (R), a winning strategy (S), and quantitative (vertex) values (Q).
For instance, RSSolution<graphs::ParityGraph>C++ is a type of solution operating on ParityGraphs and which can compute a winning Region as well as synthesize a Strategy. In more details,
ISolution<GraphType>¶
The most basic solution type on <GraphType> graphs. It records only the initial winner label for convenience and provides serialization for solution payloads. There is no solved/valid flag in solutions; solvers either return a result or signal errors via logs/exceptions.
See also: include/libggg/solutions/isolution.hpp
RSolution<GraphType>¶
Contains info about winning Regions.
In addition to the inherited methods of ISolution, solutions of this kind implement:
is_won_by_player0(vertex)- Check if vertex is won by player 0is_won_by_player1(vertex)- Check if vertex is won by player 1get_winning_player(vertex)- Get winning player (0, 1, or -1)set_winning_player(vertex, player)- Set winning player for vertex
See also: include/libggg/solutions/rsolution.hpp
SSolution<GraphType>¶
Contains info about a winning Strategy
In addition to the inherited methods of ISolution, solutions of this kind implement:
get_strategy(vertex)- Get strategic choice for vertexhas_strategy(vertex)- Check if vertex has strategy definedset_strategy(vertex, strategy)- Set strategy for vertex
There is actually a second generic parameter specifying the type of strategy contained (mixing, deterministic, positional, finite-memory etc). The default is DeterministicStrategy, representing deterministic and positional strategies (i.e. mappings from vertices to vertices).
See also: include/libggg/solutions/ssolution.hpp
QSolution<GraphType, ValueType>¶
Contains info about Qualitative (vertex) values of type ValueType.
In addition to the inherited methods of ISolution, solutions of this kind implement:
get_value(vertex)- Get numerical value for vertexhas_value(vertex)- Check if vertex has value definedset_value(vertex, value)- Set value for vertex
See also: include/libggg/solutions/qsolution.hpp
Combined Solution Types¶
RSSolution<GraphType>: Regions + Strategies (inherits R and S methods)RQSolution<GraphType, ValueType>: Regions + Quantitative values (inherits R and Q methods)RSQSolution<GraphType, ValueType>: All capabilities (inherits R, S, and Q methods)
See also: include/libggg/solutions/rssolution.hpp, include/libggg/solutions/rsqsolution.hpp
Choose the appropriate solution type based on what your solver provides.