/// <inheritdoc/> public int Write(ObservationWriter writer) { m_Board.CheckBoardSizes(m_MaxBoardSize); var currentBoardSize = m_Board.GetCurrentBoardSize(); int offset = 0; var isVisual = m_ObservationType != Match3ObservationType.Vector; // This is equivalent to // for (var r = 0; r < m_MaxBoardSize.Rows; r++) // for (var c = 0; c < m_MaxBoardSize.Columns; c++) // if (r < currentBoardSize.Rows && c < currentBoardSize.Columns) // WriteOneHot // else // WriteZero // but rearranged to avoid the branching. for (var r = 0; r < currentBoardSize.Rows; r++) { for (var c = 0; c < currentBoardSize.Columns; c++) { var val = m_GridValues(r, c); writer.WriteOneHot(offset, r, c, val, m_OneHotSize, isVisual); offset += m_OneHotSize; } for (var c = currentBoardSize.Columns; c < m_MaxBoardSize.Columns; c++) { writer.WriteZero(offset, r, c, m_OneHotSize, isVisual); offset += m_OneHotSize; } } for (var r = currentBoardSize.Rows; r < m_MaxBoardSize.Columns; r++) { for (var c = 0; c < m_MaxBoardSize.Columns; c++) { writer.WriteZero(offset, r, c, m_OneHotSize, isVisual); offset += m_OneHotSize; } } return(offset); }
/// <inheritdoc/> 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++) { // Check that the move is allowed for the current boardSize (e.g. it won't move a piece out of // bounds), and that it's allowed by the game itself. if (currentMove.InRangeForBoard(currentBoardSize) && m_Board.IsMoveValid(currentMove)) { foundValidMove = true; } else { actionMask.SetActionEnabled(branch, i, false); } currentMove.Next(m_MaxBoardSize); } if (!foundValidMove) { // If all the moves are invalid and we mask all the actions out, this will cause an assert // later on in IDiscreteActionMask. Instead, fire a callback to the user if they provided one, // (or log a warning if not) and leave the last action unmasked. This isn't great, but // an invalid move should be easier to handle than an exception.. 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); } } }