| | using Unity.MLAgents.Actuators; |
| | using Debug = UnityEngine.Debug; |
| |
|
| |
|
| | namespace Unity.MLAgents.Integrations.Match3 |
| | { |
| | |
| | |
| | |
| | |
| | public class Match3Actuator : IActuator, IBuiltInActuator |
| | { |
| | AbstractBoard m_Board; |
| | System.Random m_Random; |
| | ActionSpec m_ActionSpec; |
| | bool m_ForceHeuristic; |
| | BoardSize m_MaxBoardSize; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | public Match3Actuator(AbstractBoard board, |
| | bool forceHeuristic, |
| | int seed, |
| | string name) |
| | { |
| | m_Board = board; |
| | m_MaxBoardSize = m_Board.GetMaxBoardSize(); |
| | Name = name; |
| |
|
| | m_ForceHeuristic = forceHeuristic; |
| |
|
| | var numMoves = Move.NumPotentialMoves(m_MaxBoardSize); |
| | m_ActionSpec = ActionSpec.MakeDiscrete(numMoves); |
| | m_Random = new System.Random(seed); |
| | } |
| |
|
| | |
| | public ActionSpec ActionSpec => m_ActionSpec; |
| |
|
| | |
| | public void OnActionReceived(ActionBuffers actions) |
| | { |
| | m_Board.CheckBoardSizes(m_MaxBoardSize); |
| | if (m_ForceHeuristic) |
| | { |
| | Heuristic(actions); |
| | } |
| | var moveIndex = actions.DiscreteActions[0]; |
| |
|
| | Move move = Move.FromMoveIndex(moveIndex, m_MaxBoardSize); |
| | m_Board.MakeMove(move); |
| | } |
| |
|
| | |
| | public void WriteDiscreteActionMask(IDiscreteActionMask actionMask) |
| | { |
| | var currentBoardSize = m_Board.GetCurrentBoardSize(); |
| | m_Board.CheckBoardSizes(m_MaxBoardSize); |
| | const int branch = 0; |
| | bool foundValidMove = false; |
| | using (TimerStack.Instance.Scoped("WriteDiscreteActionMask")) |
| | { |
| | var numMoves = m_Board.NumMoves(); |
| |
|
| | var currentMove = Move.FromMoveIndex(0, m_MaxBoardSize); |
| | for (var i = 0; i < numMoves; i++) |
| | { |
| | |
| | |
| | if (currentMove.InRangeForBoard(currentBoardSize) && m_Board.IsMoveValid(currentMove)) |
| | { |
| | foundValidMove = true; |
| | } |
| | else |
| | { |
| | actionMask.SetActionEnabled(branch, i, false); |
| | } |
| | currentMove.Next(m_MaxBoardSize); |
| | } |
| |
|
| | if (!foundValidMove) |
| | { |
| | |
| | |
| | |
| | |
| | if (m_Board.OnNoValidMovesAction != null) |
| | { |
| | m_Board.OnNoValidMovesAction(); |
| | } |
| | else |
| | { |
| | Debug.LogWarning( |
| | "No valid moves are available. The last action will be left unmasked, so " + |
| | "an invalid move will be passed to AbstractBoard.MakeMove()." |
| | ); |
| | } |
| | actionMask.SetActionEnabled(branch, numMoves - 1, true); |
| | } |
| | } |
| | } |
| |
|
| | |
| | public string Name { get; } |
| |
|
| | |
| | public void ResetData() |
| | { |
| | } |
| |
|
| | |
| | public BuiltInActuatorType GetBuiltInActuatorType() |
| | { |
| | return BuiltInActuatorType.Match3Actuator; |
| | } |
| |
|
| | |
| | public void Heuristic(in ActionBuffers actionsOut) |
| | { |
| | var discreteActions = actionsOut.DiscreteActions; |
| | discreteActions[0] = GreedyMove(); |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | internal int GreedyMove() |
| | { |
| | var bestMoveIndex = 0; |
| | var bestMovePoints = -1; |
| | var numMovesAtCurrentScore = 0; |
| |
|
| | foreach (var move in m_Board.ValidMoves()) |
| | { |
| | var movePoints = EvalMovePoints(move); |
| | if (movePoints < bestMovePoints) |
| | { |
| | |
| | continue; |
| | } |
| |
|
| | if (movePoints > bestMovePoints) |
| | { |
| | |
| | bestMovePoints = movePoints; |
| | bestMoveIndex = move.MoveIndex; |
| | numMovesAtCurrentScore = 1; |
| | } |
| | else |
| | { |
| | |
| | |
| | numMovesAtCurrentScore++; |
| | var randVal = m_Random.Next(0, numMovesAtCurrentScore); |
| | if (randVal == 0) |
| | { |
| | |
| | bestMoveIndex = move.MoveIndex; |
| | } |
| | } |
| | } |
| |
|
| | return bestMoveIndex; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | protected virtual int EvalMovePoints(Move move) |
| | { |
| | return 1; |
| | } |
| | } |
| | } |
| |
|