Match-3 with ML-Agents
Getting started
The C# code for Match-3 exists inside of the Unity package (com.unity.ml-agents
).
The good first step would be to take a look at how we have implemented the C# code in the example Match-3 scene (located
under /Project/Assets/ML-Agents/Examples/match3). Once you have some familiarity, then the next step would be to
implement the C# code for Match-3 from the extensions package.
Additionally, see below for additional technical specifications on the C# code for Match-3. Please note the Match-3 game isn't human playable as implemented and can be only played via training.
Technical specifications for Match-3 with ML-Agents
AbstractBoard class
The AbstractBoard
is the bridge between ML-Agents and your game. It allows ML-Agents to
* ask your game what the current and maximum sizes (rows, columns, and potential piece types) of the board are
* ask your game what the "color" of a cell is
* ask whether the cell is a "special" piece type or not
* ask your game whether a move is allowed
* request that your game make a move
These are handled by implementing the abstract methods of AbstractBoard
.
public abstract BoardSize GetMaxBoardSize()
Returns the largest BoardSize
that the game can use. This is used to determine the sizes of observations and sensors,
so don't make it larger than necessary.
public virtual BoardSize GetCurrentBoardSize()
Returns the current size of the board. Each field on this BoardSize must be less than or equal to the corresponding
field returned by GetMaxBoardSize()
. This method is optional; if your always use the same size board, you don't
need to override it.
If the current board size is smaller than the maximum board size, GetCellType()
and GetSpecialType()
will not be
called for cells outside the current board size, and IsValidMove
won't be called for moves that would go outside of
the current board size.
public abstract int GetCellType(int row, int col)
Returns the "color" of piece at the given row and column. This should be between 0 and BoardSize.NumCellTypes-1 (inclusive). The actual order of the values doesn't matter.
public abstract int GetSpecialType(int row, int col)
Returns the special type of the piece at the given row and column. This should be between 0 and BoardSize.NumSpecialTypes (inclusive). The actual order of the values doesn't matter.
public abstract bool IsMoveValid(Move m)
Check whether the particular Move
is valid for the game.
The actual results will depend on the rules of the game, but we provide the SimpleIsMoveValid()
method
that handles basic match3 rules with no special or immovable pieces.
public abstract bool MakeMove(Move m)
Instruct the game to make the given move. Returns true if the move was made. Note that during training, a move that was marked as invalid may occasionally still be requested. If this happens, it is safe to do nothing and request another move.
Move
struct
The Move struct encapsulates a swap of two adjacent cells. You can get the number of potential moves
for a board of a given size with. Move.NumPotentialMoves(maxBoardSize)
. There are two helper
functions to create a new Move
:
* public static Move FromMoveIndex(int moveIndex, BoardSize maxBoardSize)
can be used to
iterate over all potential moves for the board by looping from 0 to Move.NumPotentialMoves()
* public static Move FromPositionAndDirection(int row, int col, Direction dir, BoardSize maxBoardSize)
creates
a Move
from a row, column, and direction (and board size).
BoardSize
struct
Describes the "size" of the board, including the number of potential piece types that the board can have. This is returned by the AbstractBoard.GetMaxBoardSize() and GetCurrentBoardSize() methods.
Match3Sensor
and Match3SensorComponent
classes
The Match3Sensor
generates observations about the state using the AbstractBoard
interface. You can
choose whether to use vector or "visual" observations; in theory, visual observations should perform
better because they are 2-dimensional like the board, but we need to experiment more on this.
A Match3SensorComponent
generates Match3Sensor
s (the exact number of sensors depends on your configuration)
at runtime, and should be added to the same GameObject as your Agent
implementation. You do not need to write any
additional code to use them.
Match3Actuator
and Match3ActuatorComponent
classes
The Match3Actuator
converts actions from training or inference into a Move
that is sent toAbstractBoard.MakeMove()
It also checks AbstractBoard.IsMoveValid
for each potential move and uses this to set the action mask for Agent.
A Match3ActuatorComponent
generates a Match3Actuator
at runtime, and should be added to the same GameObject
as your Agent
implementation. You do not need to write any additional code to use them.
Setting up Match-3 simulation
- Implement the
AbstractBoard
methods to integrate with your game. - Give the
Agent
rewards when it does what you want it to (match multiple pieces in a row, clears pieces of a certain type, etc). - Add the
Agent
,AbstractBoard
implementation,Match3SensorComponent
, andMatch3ActuatorComponent
to the sameGameObject
. - Call
Agent.RequestDecision()
when you're ready for theAgent
to make a move on the nextAcademy
step. During the nextAcademy
step, theMakeMove()
method on the board will be called.
Implementation Details
Action Space
The indexing for actions is the same as described in Human Like Playtesting with Deep Learning (for example, Figure 2b). The horizontal moves are enumerated first, then the vertical ones.