Reinforcement Learning
ml-agents
TensorBoard
ONNX
Pyramids
deep-reinforcement-learning
ML-Agents-Pyramids
Instructions to use AnnaMats/ppo-Pyramids-Training with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- ml-agents
How to use AnnaMats/ppo-Pyramids-Training with ml-agents:
mlagents-load-from-hf --repo-id="AnnaMats/ppo-Pyramids-Training" --local-dir="./download: string[]s"
- Notebooks
- Google Colab
- Kaggle
| using System; | |
| using Unity.MLAgents.Integrations.Match3; | |
| using UnityEngine; | |
| using UnityEngine.Serialization; | |
| namespace Unity.MLAgentsExamples | |
| { | |
| public class Match3Board : AbstractBoard | |
| { | |
| public int MinRows; | |
| [] | |
| public int MaxRows; | |
| public int MinColumns; | |
| [] | |
| public int MaxColumns; | |
| public int NumCellTypes; | |
| public int NumSpecialTypes; | |
| public const int k_EmptyCell = -1; | |
| [] | |
| public int BasicCellPoints = 1; | |
| [] | |
| public int SpecialCell1Points = 2; | |
| [] | |
| public int SpecialCell2Points = 3; | |
| /// <summary> | |
| /// Seed to initialize the <see cref="System.Random"/> object. | |
| /// </summary> | |
| public int RandomSeed; | |
| (int CellType, int SpecialType)[,] m_Cells; | |
| bool[,] m_Matched; | |
| private BoardSize m_CurrentBoardSize; | |
| System.Random m_Random; | |
| void Awake() | |
| { | |
| m_Cells = new (int, int)[MaxColumns, MaxRows]; | |
| m_Matched = new bool[MaxColumns, MaxRows]; | |
| // Start using the max rows and columns, but we'll update the current size at the start of each episode. | |
| m_CurrentBoardSize = new BoardSize | |
| { | |
| Rows = MaxRows, | |
| Columns = MaxColumns, | |
| NumCellTypes = NumCellTypes, | |
| NumSpecialTypes = NumSpecialTypes | |
| }; | |
| } | |
| void Start() | |
| { | |
| m_Random = new System.Random(RandomSeed == -1 ? gameObject.GetInstanceID() : RandomSeed); | |
| InitRandom(); | |
| } | |
| public override BoardSize GetMaxBoardSize() | |
| { | |
| return new BoardSize | |
| { | |
| Rows = MaxRows, | |
| Columns = MaxColumns, | |
| NumCellTypes = NumCellTypes, | |
| NumSpecialTypes = NumSpecialTypes | |
| }; | |
| } | |
| public override BoardSize GetCurrentBoardSize() | |
| { | |
| return m_CurrentBoardSize; | |
| } | |
| /// <summary> | |
| /// Change the board size to a random size between the min and max rows and columns. This is | |
| /// cached so that the size is consistent until it is updated. | |
| /// This is just for an example; you can change your board size however you want. | |
| /// </summary> | |
| public void UpdateCurrentBoardSize() | |
| { | |
| var newRows = m_Random.Next(MinRows, MaxRows + 1); | |
| var newCols = m_Random.Next(MinColumns, MaxColumns + 1); | |
| m_CurrentBoardSize.Rows = newRows; | |
| m_CurrentBoardSize.Columns = newCols; | |
| } | |
| public override bool MakeMove(Move move) | |
| { | |
| if (!IsMoveValid(move)) | |
| { | |
| return false; | |
| } | |
| var originalValue = m_Cells[move.Column, move.Row]; | |
| var (otherRow, otherCol) = move.OtherCell(); | |
| var destinationValue = m_Cells[otherCol, otherRow]; | |
| m_Cells[move.Column, move.Row] = destinationValue; | |
| m_Cells[otherCol, otherRow] = originalValue; | |
| return true; | |
| } | |
| public override int GetCellType(int row, int col) | |
| { | |
| if (row >= m_CurrentBoardSize.Rows || col >= m_CurrentBoardSize.Columns) | |
| { | |
| throw new IndexOutOfRangeException(); | |
| } | |
| return m_Cells[col, row].CellType; | |
| } | |
| public override int GetSpecialType(int row, int col) | |
| { | |
| if (row >= m_CurrentBoardSize.Rows || col >= m_CurrentBoardSize.Columns) | |
| { | |
| throw new IndexOutOfRangeException(); | |
| } | |
| return m_Cells[col, row].SpecialType; | |
| } | |
| public override bool IsMoveValid(Move m) | |
| { | |
| if (m_Cells == null) | |
| { | |
| return false; | |
| } | |
| return SimpleIsMoveValid(m); | |
| } | |
| public bool MarkMatchedCells(int[,] cells = null) | |
| { | |
| ClearMarked(); | |
| bool madeMatch = false; | |
| for (var i = 0; i < m_CurrentBoardSize.Rows; i++) | |
| { | |
| for (var j = 0; j < m_CurrentBoardSize.Columns; j++) | |
| { | |
| // Check vertically | |
| var matchedRows = 0; | |
| for (var iOffset = i; iOffset < m_CurrentBoardSize.Rows; iOffset++) | |
| { | |
| if (m_Cells[j, i].CellType != m_Cells[j, iOffset].CellType) | |
| { | |
| break; | |
| } | |
| matchedRows++; | |
| } | |
| if (matchedRows >= 3) | |
| { | |
| madeMatch = true; | |
| for (var k = 0; k < matchedRows; k++) | |
| { | |
| m_Matched[j, i + k] = true; | |
| } | |
| } | |
| // Check vertically | |
| var matchedCols = 0; | |
| for (var jOffset = j; jOffset < m_CurrentBoardSize.Columns; jOffset++) | |
| { | |
| if (m_Cells[j, i].CellType != m_Cells[jOffset, i].CellType) | |
| { | |
| break; | |
| } | |
| matchedCols++; | |
| } | |
| if (matchedCols >= 3) | |
| { | |
| madeMatch = true; | |
| for (var k = 0; k < matchedCols; k++) | |
| { | |
| m_Matched[j + k, i] = true; | |
| } | |
| } | |
| } | |
| } | |
| return madeMatch; | |
| } | |
| /// <summary> | |
| /// Sets cells that are matched to the empty cell, and returns the score earned. | |
| /// </summary> | |
| /// <returns></returns> | |
| public int ClearMatchedCells() | |
| { | |
| var pointsByType = new[] { BasicCellPoints, SpecialCell1Points, SpecialCell2Points }; | |
| int pointsEarned = 0; | |
| for (var i = 0; i < m_CurrentBoardSize.Rows; i++) | |
| { | |
| for (var j = 0; j < m_CurrentBoardSize.Columns; j++) | |
| { | |
| if (m_Matched[j, i]) | |
| { | |
| var speciaType = GetSpecialType(i, j); | |
| pointsEarned += pointsByType[speciaType]; | |
| m_Cells[j, i] = (k_EmptyCell, 0); | |
| } | |
| } | |
| } | |
| ClearMarked(); // TODO clear here or at start of matching? | |
| return pointsEarned; | |
| } | |
| public bool DropCells() | |
| { | |
| var madeChanges = false; | |
| // Gravity is applied in the negative row direction | |
| for (var j = 0; j < m_CurrentBoardSize.Columns; j++) | |
| { | |
| var writeIndex = 0; | |
| for (var readIndex = 0; readIndex < m_CurrentBoardSize.Rows; readIndex++) | |
| { | |
| m_Cells[j, writeIndex] = m_Cells[j, readIndex]; | |
| if (m_Cells[j, readIndex].CellType != k_EmptyCell) | |
| { | |
| writeIndex++; | |
| } | |
| } | |
| // Fill in empties at the end | |
| for (; writeIndex < m_CurrentBoardSize.Rows; writeIndex++) | |
| { | |
| madeChanges = true; | |
| m_Cells[j, writeIndex] = (k_EmptyCell, 0); | |
| } | |
| } | |
| return madeChanges; | |
| } | |
| public bool FillFromAbove() | |
| { | |
| bool madeChanges = false; | |
| for (var i = 0; i < m_CurrentBoardSize.Rows; i++) | |
| { | |
| for (var j = 0; j < m_CurrentBoardSize.Columns; j++) | |
| { | |
| if (m_Cells[j, i].CellType == k_EmptyCell) | |
| { | |
| madeChanges = true; | |
| m_Cells[j, i] = (GetRandomCellType(), GetRandomSpecialType()); | |
| } | |
| } | |
| } | |
| return madeChanges; | |
| } | |
| public (int, int)[,] Cells | |
| { | |
| get { return m_Cells; } | |
| } | |
| public bool[,] Matched | |
| { | |
| get { return m_Matched; } | |
| } | |
| // Initialize the board to random values. | |
| public void InitRandom() | |
| { | |
| for (var i = 0; i < MaxRows; i++) | |
| { | |
| for (var j = 0; j < MaxColumns; j++) | |
| { | |
| m_Cells[j, i] = (GetRandomCellType(), GetRandomSpecialType()); | |
| } | |
| } | |
| } | |
| public void InitSettled() | |
| { | |
| InitRandom(); | |
| while (true) | |
| { | |
| var anyMatched = MarkMatchedCells(); | |
| if (!anyMatched) | |
| { | |
| return; | |
| } | |
| ClearMatchedCells(); | |
| DropCells(); | |
| FillFromAbove(); | |
| } | |
| } | |
| void ClearMarked() | |
| { | |
| for (var i = 0; i < MaxRows; i++) | |
| { | |
| for (var j = 0; j < MaxColumns; j++) | |
| { | |
| m_Matched[j, i] = false; | |
| } | |
| } | |
| } | |
| int GetRandomCellType() | |
| { | |
| return m_Random.Next(0, NumCellTypes); | |
| } | |
| int GetRandomSpecialType() | |
| { | |
| // 1 in N chance to get a type-2 special | |
| // 2 in N chance to get a type-1 special | |
| // otherwise 0 (boring) | |
| var N = 10; | |
| var val = m_Random.Next(0, N); | |
| if (val == 0) | |
| { | |
| return 2; | |
| } | |
| if (val <= 2) | |
| { | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| } | |
| } | |