Implement a new version of an existing module
Implement a new version of an existing module
Anyone is free at any time to implement a module
which already exists. Of course, there is no
point in having multiple versions of straightforward
modules like Board or Group, but less straightforward
modules such as Shape or Scoring might benefit from
having multiple implementations. Some reasons for
implementing a module that already exists include:
- Sophistication - An existing module may take a simple
approach to accomplish something without a lot of development
effort. By using a more sophisticated approach, a stronger
module can be written than the existing one.
- Experimentation - By trying different approaches,
this provides a yardstick for evaluating the results
of existing modules.
- Performance - One implementation might try to
achieve the best possible results while being
cpu and memory intensive. It would be useful to
have another implementation that gives good results
while using less resources.
- Bug Fixing - It may be possible to run multiple
implementations of a module against test data, and
use the differences in output to find bugs.
Here is a list of the things that a developer would
need to do in order to implement an existing module.
This example assumes that a new implementation of the
Shape module is being created called nnshape.
- Make sure that you have read and understood the
requirements and high-level design documents. It is
hard to overstate the importance of this. It would be
silly to spend six months implementing a module and then
find out that it needs to be redesigned, where a few hours
up front could have saved that wasted effort. You
should be able to answer the following questions before proceeding:
- What data am I saving after each move?
- Do I know which data should be declared transient,
and how to reinitialize it if it is null?
- How do I clone my data?
- Read the Coding Standards
- Make an nnshape directory in the org/moyoman/module/shape directory.
- Create an NNShape class which extends the Module class,
and implements the Shape interface.
- Override the generateMove() method. This is the method
that identifies good and bad moves.
- Override the makeMove() method. This is the method that
indicates that a move was made. Update internal data
structures accordingly.
- Override the getDebugTypes() method.
- Override the getDebugInformation() method. This should
return one Debug object for each type returned by the
getDebugTypes() method. At least one Debug object must be generated.
- Override the getRequiredModulesList() method. This method
indicates which other modules this module uses the output from.
- Override the clone() method. See an advanced Java book for
more details on this. This is important, because the module will be
cloned whenever another module uses its results.
- Run the Module admin tool to add NNShape to the list of Shape modules,
and make sure that it is rated first. You can also use the Mode admin tool
to create a user defined mode where NNShape is the first Shape module.
Read the documentation
The Moyoman software will become very large it is to achieve its goal of
being a strong Go playing program. In order to allow for many developers
to create modules independently, there must be strict rules on how a
module interacts with the rest of the system. Make sure that you understand
this before you start development.
Read the coding standards
This document explains some of the
specific things to avoid in developing a module.
Create an nnshape directory in the org/moyoman/module/shape directory
This is where your classes go. Of course, only one class can extend the Module class,
but you may wish to organize your code as multiple classes. The extra classes which
do not implement the Shape interface should not have any public methods. An exception
is where a module has multiple interfaces. For example, the Groups module type has
Groups and SingleGroup interfaces, so one class will extend Groups and the
other SingleGroup. Still, any other classes in the Groups implementation will
not have public methods.
Create an NNShape class.
The NNShape class will extend the Module class, and implement the Shape interface.
Before implementing this class, you will need to answer the following questions:
- Is the module stateless or stateful?
- What debug objects are going to be created, and when are they valid?
Is the module stateless or stateful?
The NNShape module might remember which moves cause good and bad shape and do an
incremental update, or it might recompute the data from scratch on every move. The
developer should have a clear understanding of which approach he is going to take
before starting.
What debug objects are going to be created?
The module must return at least one debug object. The developer needs to determine what
debug objects he is going to return, and whether they are valid after the other player
has moved last, or only when this player has moved last. For example, if an incremental
update is done on each move, then the debug object showing good and bad shapes might
always be accurate. On the other hand, if the analysis is done from scratch, then the
debug object would only show good or bad shapes when the program made the last move.
Override the generateMove() method
This method is what does the analysis for the given board position when it is the
computer players turn to move. All appropriate analysis should be done, and any
information that the debug objects need as well.
Override the makeMove() method
If incremental analysis is being done, then each time that this method is called,
then the appropriate data structures must be updated by the last move made. If
analysis is done from scratch each time that generateMove() is called, then this
method may not have much to do, except for clearing out the debug objects if necessary.
Override the getDebugTypes() method
This method returns the types of debug objects that it creates.
Override the getDebugInformation() method.
Return the Debug objects that your module creates. Note that if the computer play has
not moved last, then some of these debug objects may not be valid. They can be returned
without any information set, or just the valid ones can be returned.
Override the getRequiredModuleList() method
This allows the framework to know what modules will be needed by your module. The same
modules are passed to the generateMove() and makeMove() methods.
Implement the clone() method
Whenever a module is passed to generateMove() or makeMove() it was cloned from the
original module. Therefore, the clone() method will be used frequently. It is important
to make sure that you understand how cloning works, because this is a common source of
errors, and it is not always obvious during testing that this is the cause of any bugs
that occur.
Run the module admin tool
In order to provide flexibility and to reduce dependencies between modules as much
as possible, the configuration files determine which module implementations are
actually run. You need to configure the software so that your NNShape module is
actually being run. It would be an easy to make mistake to think that your module
was running when in fact it was another shape module. You can easily check this by
looking at the logs/info file.