/// <summary> /// Construct a Move from its move index and the board size. /// This is useful for iterating through all the Moves on a board, or constructing /// the Move corresponding to an Agent decision. /// </summary> /// <param name="moveIndex">Must be between 0 and NumPotentialMoves(maxRows, maxCols).</param> /// <param name="maxBoardSize"></param> /// <returns></returns> /// <exception cref="ArgumentOutOfRangeException"></exception> public static Move FromMoveIndex(int moveIndex, BoardSize maxBoardSize) { var maxRows = maxBoardSize.Rows; var maxCols = maxBoardSize.Columns; if (moveIndex < 0 || moveIndex >= NumPotentialMoves(maxBoardSize)) { throw new ArgumentOutOfRangeException("Invalid move index."); } Direction dir; int row, col; if (moveIndex < (maxCols - 1) * maxRows) { dir = Direction.Right; col = moveIndex % (maxCols - 1); row = moveIndex / (maxCols - 1); } else { dir = Direction.Up; var offset = moveIndex - (maxCols - 1) * maxRows; col = offset % maxCols; row = offset / maxCols; } return(new Move { MoveIndex = moveIndex, Direction = dir, Row = row, Column = col }); }
/// <summary> /// Increment the Move to the next MoveIndex, and update the Row, Column, and Direction accordingly. /// </summary> /// <param name="maxBoardSize"></param> public void Next(BoardSize maxBoardSize) { var maxRows = maxBoardSize.Rows; var maxCols = maxBoardSize.Columns; var switchoverIndex = (maxCols - 1) * maxRows; MoveIndex++; if (MoveIndex < switchoverIndex) { Column++; if (Column == maxCols - 1) { Row++; Column = 0; } } else if (MoveIndex == switchoverIndex) { // switch from moving right to moving up Row = 0; Column = 0; Direction = Direction.Up; } else { Column++; if (Column == maxCols) { Row++; Column = 0; } } }
/// <summary> /// Check if the move is valid for the given board size. /// This will be passed the return value from AbstractBoard.GetCurrentBoardSize(). /// </summary> /// <param name="boardSize"></param> /// <returns></returns> public bool InRangeForBoard(BoardSize boardSize) { var(otherRow, otherCol) = OtherCell(); // Get the maximum row and column this move would affect. var maxMoveRow = Mathf.Max(Row, otherRow); var maxMoveCol = Mathf.Max(Column, otherCol); return(maxMoveRow < boardSize.Rows && maxMoveCol < boardSize.Columns); }
/// <summary> /// Construct a Move from the row, column, and direction. /// </summary> /// <param name="row"></param> /// <param name="col"></param> /// <param name="dir"></param> /// <param name="maxBoardSize"></param> /// <returns></returns> public static Move FromPositionAndDirection(int row, int col, Direction dir, BoardSize maxBoardSize) { // Check for out-of-bounds if (row < 0 || row >= maxBoardSize.Rows) { throw new IndexOutOfRangeException($"row was {row}, but must be between 0 and {maxBoardSize.Rows - 1}."); } if (col < 0 || col >= maxBoardSize.Columns) { throw new IndexOutOfRangeException($"col was {col}, but must be between 0 and {maxBoardSize.Columns - 1}."); } // Check moves that would go out of bounds e.g. col == 0 and dir == Left if ( row == 0 && dir == Direction.Down || row == maxBoardSize.Rows - 1 && dir == Direction.Up || col == 0 && dir == Direction.Left || col == maxBoardSize.Columns - 1 && dir == Direction.Right ) { throw new IndexOutOfRangeException($"Cannot move cell at row={row} col={col} in Direction={dir}"); } // Normalize - only consider Right and Up if (dir == Direction.Left) { dir = Direction.Right; col = col - 1; } else if (dir == Direction.Down) { dir = Direction.Up; row = row - 1; } int moveIndex; if (dir == Direction.Right) { moveIndex = col + row * (maxBoardSize.Columns - 1); } else { var offset = (maxBoardSize.Columns - 1) * maxBoardSize.Rows; moveIndex = offset + col + row * maxBoardSize.Columns; } return(new Move { Row = row, Column = col, Direction = dir, MoveIndex = moveIndex, }); }
internal void CheckBoardSizes(BoardSize originalMaxBoardSize) { var currentBoardSize = GetCurrentBoardSize(); if (!(currentBoardSize <= originalMaxBoardSize)) { Debug.LogWarning( "Current BoardSize is larger than maximum board size was on initialization. This may cause unexpected results.\n" + $"Original GetMaxBoardSize() result: {originalMaxBoardSize}\n" + $"GetCurrentBoardSize() result: {currentBoardSize}" ); } }
/// <summary> /// Create a sensor for the GridValueProvider with the specified observation type. /// </summary> /// <remarks> /// Use Match3Sensor.CellTypeSensor() or Match3Sensor.SpecialTypeSensor() instead of calling /// the constructor directly. /// </remarks> /// <param name="board">The abstract board.</param> /// <param name="gvp">The GridValueProvider, should be either board.GetCellType or board.GetSpecialType.</param> /// <param name="oneHotSize">The number of possible values that the GridValueProvider can return.</param> /// <param name="obsType">Whether to produce vector or visual observations</param> /// <param name="name">Name of the sensor.</param> public Match3Sensor(AbstractBoard board, GridValueProvider gvp, int oneHotSize, Match3ObservationType obsType, string name) { var maxBoardSize = board.GetMaxBoardSize(); m_Name = name; m_MaxBoardSize = maxBoardSize; m_GridValues = gvp; m_OneHotSize = oneHotSize; m_Board = board; m_ObservationType = obsType; m_ObservationSpec = obsType == Match3ObservationType.Vector ? ObservationSpec.Vector(maxBoardSize.Rows * maxBoardSize.Columns * oneHotSize) : ObservationSpec.Visual(maxBoardSize.Rows, maxBoardSize.Columns, oneHotSize); }
/// <summary> /// Create a Match3Actuator. /// </summary> /// <param name="board"></param> /// <param name="forceHeuristic">Whether the inference action should be ignored and the Agent's Heuristic /// should be called. This should only be used for generating comparison stats of the Heuristic.</param> /// <param name="seed">The seed used to initialize <see cref="System.Random"/>.</param> /// <param name="agent"></param> /// <param name="name"></param> 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); }
/// <summary> /// Return the number of potential moves for a board of the given size. /// This is equivalent to the number of internal edges in the board. /// </summary> /// <param name="maxBoardSize"></param> /// <returns></returns> public static int NumPotentialMoves(BoardSize maxBoardSize) { return(maxBoardSize.Rows * (maxBoardSize.Columns - 1) + (maxBoardSize.Rows - 1) * (maxBoardSize.Columns)); }