By now we've seen how subclasses of cgsuite.Game can be used to
specify the combinatorics of a game and its positions. This is all that's
needed to write makeshift programs that calculate canonical forms on their own
(such as the main function that we saw in the previous section).
Often, though, it's desirable to bring a game into the CGSuite interface, to
take advantage of features like tables, scripts, and the Explorer. The
interface cgsuite.plugin.Plugin serves as a "bridge" for
communicating between the user interface and the Game class.
As we'll see, a single Plugin can handle an unlimited number of
games.
Plugin InterfaceWe'll write a class, SamplePlugin, that implements the Plugin
interface. Note that every implementation of Plugin must have
a no-arg public constructor:
import cgsuite.plugin.*;
public class SamplePlugin implements Plugin
{
public SamplePlugin()
{
}
// ...
}
Plugin has just two methods, getPluginInfo and initialize.
getPluginInfo is a straightforward method that collects basic information about
a plug-in. It should return quickly and load as few other classes as
possible. The sample plug-in's implementation is:
public PluginInfo getPluginInfo()
{
PluginInfo info = new PluginInfo();
info.name = "Sample Plug-in";
info.version = new int[] { 1, 0 };
info.description = "An example plug-in that includes Partizan Nim and Fission.";
return info;
}
Note how the version is specified as an int array: It will be displayed in Combinatorial Game Suite as 1.0. PluginInfo contains several other fields that you can fill in to describe your plug-in to the user.
initialize MethodThe meat of the plug-in is the initialize method. When
Combinatorial Game Suite loads our plug-in, it will construct a new instance of
SamplePlugin and call its initialize method. The
method has a single parameter of type PluginContext, which the
plug-in uses to call back to the user interface and register appropriate types
and methods. We'll start off by registering the PartizanNimPosition
class:
public void initialize(PluginContext context)
{
context.registerType(PartizanNimPosition.class, "PartizanNimPosition");
// ...
}
The second argument is the internal type name of a partizan nim position; it's the name cgsuite will use, for example, to display an error message involving such a position.
Finally, we need to declare a CGSuite method that lets the user construct
partizan nim positions from within the interface. This is done by calling declareMethod:
context.declareMethod(
"PartizanNim",
new Class[] { int[].class, int[].class, Integer.class },
new MethodInvoker() {
public Object invoke(String name, Object[] args, java.util.Map optionalArgumentMap) {
return new PartizanNimPosition((int[]) args[0], (int[]) args[1], ((Integer) args[2]).intValue());
}});
The first argument to declareMethod specifies the name of the method; we use "PartizanNim" instead of "PartizanNimPosition" to keep things simple for the user,
and to avoid conflicts with our type name. The second argument specifies the number of parameters and the type of each parameter.
We use two int[] parameters, one for each subtraction set, and a
third parameter specifying the heap size. Note that the third argument has
type Integer rather than int: This is because method
parameters must be Object types, not primitive types.
The third argument defines the action to take when the user calls the method.
The "action" is an instance of MethodInvoker. When the user
types in, say, PartizanNim([1,3,5],[2,4],9), the CGSuite interface
type-checks each argument against the parameter types specified by declareMethod,
condenses the arguments into a single Object array, and calls the invoke method of the associated MethodInvoker. Notice that it's safe to assume that args has the
proper length and that each argument has the correct type - if the user supplies
invalid method arguments, then CGSuite will catch this before calling out to the MethodInvoker.
That's it! We now have a working plug-in. Instructions for loading the plug-in into cgsuite can be found in a previous section, Building the Plug-in. Be sure to check out the next section to learn about some more sophisticated techniques specifically for games played on a grid.
Continue on to Fission