ppo-Pyramids-Training
/
com.unity.ml-agents
/Tests
/Editor
/Integrations
/Match3
/AbstractBoardTests.cs
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using NUnit.Framework; | |
using Unity.MLAgents.Integrations.Match3; | |
namespace Unity.MLAgents.Tests.Integrations.Match3 | |
{ | |
internal class StringBoard : AbstractBoard | |
{ | |
internal int MaxRows; | |
internal int MaxColumns; | |
internal int NumCellTypes; | |
internal int NumSpecialTypes; | |
public int CurrentRows; | |
public int CurrentColumns; | |
public override BoardSize GetMaxBoardSize() | |
{ | |
return new BoardSize | |
{ | |
Rows = MaxRows, | |
Columns = MaxColumns, | |
NumCellTypes = NumCellTypes, | |
NumSpecialTypes = NumSpecialTypes | |
}; | |
} | |
public override BoardSize GetCurrentBoardSize() | |
{ | |
return new BoardSize | |
{ | |
Rows = CurrentRows, | |
Columns = CurrentColumns, | |
NumCellTypes = NumCellTypes, | |
NumSpecialTypes = NumSpecialTypes | |
}; | |
} | |
private string[] m_Board; | |
private string[] m_Special; | |
/// <summary> | |
/// Convert a string like "000\n010\n000" to a board representation | |
/// Row 0 is considered the bottom row | |
/// </summary> | |
/// <param name="newBoard"></param> | |
public void SetBoard(string newBoard) | |
{ | |
m_Board = newBoard.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); | |
MaxRows = m_Board.Length; | |
MaxColumns = m_Board[0].Length; | |
CurrentRows = MaxRows; | |
CurrentColumns = MaxColumns; | |
NumCellTypes = 0; | |
for (var r = 0; r < MaxRows; r++) | |
{ | |
for (var c = 0; c < MaxColumns; c++) | |
{ | |
NumCellTypes = Mathf.Max(NumCellTypes, 1 + GetCellType(r, c)); | |
} | |
} | |
} | |
public void SetSpecial(string newSpecial) | |
{ | |
m_Special = newSpecial.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); | |
Debug.Assert(MaxRows == m_Special.Length); | |
Debug.Assert(MaxColumns == m_Special[0].Length); | |
NumSpecialTypes = 0; | |
for (var r = 0; r < MaxRows; r++) | |
{ | |
for (var c = 0; c < MaxColumns; c++) | |
{ | |
NumSpecialTypes = Mathf.Max(NumSpecialTypes, GetSpecialType(r, c)); | |
} | |
} | |
} | |
public override bool MakeMove(Move m) | |
{ | |
return true; | |
} | |
public override bool IsMoveValid(Move m) | |
{ | |
return SimpleIsMoveValid(m); | |
} | |
public override int GetCellType(int row, int col) | |
{ | |
if (row >= CurrentRows || col >= CurrentColumns) | |
{ | |
throw new IndexOutOfRangeException("Tried to get celltype out of bounds"); | |
} | |
var character = m_Board[m_Board.Length - 1 - row][col]; | |
return (character - '0'); | |
} | |
public override int GetSpecialType(int row, int col) | |
{ | |
if (row >= CurrentRows || col >= CurrentColumns) | |
{ | |
throw new IndexOutOfRangeException("Tried to get specialtype out of bounds"); | |
} | |
var character = m_Special[m_Board.Length - 1 - row][col]; | |
return (character - '0'); | |
} | |
} | |
public class AbstractBoardTests | |
{ | |
[ | ]|
public void TestBoardInit() | |
{ | |
var boardString = | |
@"000 | |
000 | |
010"; | |
var gameObj = new GameObject("board"); | |
var board = gameObj.AddComponent<StringBoard>(); | |
board.SetBoard(boardString); | |
var boardSize = board.GetMaxBoardSize(); | |
Assert.AreEqual(3, boardSize.Rows); | |
Assert.AreEqual(3, boardSize.Columns); | |
Assert.AreEqual(2, boardSize.NumCellTypes); | |
for (var r = 0; r < 3; r++) | |
{ | |
for (var c = 0; c < 3; c++) | |
{ | |
var expected = (r == 0 && c == 1) ? 1 : 0; | |
Assert.AreEqual(expected, board.GetCellType(r, c)); | |
} | |
} | |
} | |
internal static List<Move> GetValidMoves4x4(bool fullBoard, BoardSize boardSize) | |
{ | |
var validMoves = new List<Move> | |
{ | |
Move.FromPositionAndDirection(2, 1, Direction.Down, boardSize), // equivalent to (1, 1, Up) | |
Move.FromPositionAndDirection(1, 1, Direction.Down, boardSize), | |
Move.FromPositionAndDirection(1, 1, Direction.Left, boardSize), | |
Move.FromPositionAndDirection(1, 1, Direction.Right, boardSize), | |
Move.FromPositionAndDirection(0, 1, Direction.Left, boardSize), | |
}; | |
if (fullBoard) | |
{ | |
// This would move out of range on the small board | |
// Equivalent to (3, 1, Down) | |
validMoves.Add(Move.FromPositionAndDirection(2, 1, Direction.Up, boardSize)); | |
// These moves require matching with a cell that's off the small board, so they're invalid | |
// (even though the move itself doesn't go out of range). | |
validMoves.Add(Move.FromPositionAndDirection(2, 1, Direction.Left, boardSize)); // Equivalent to (2, 0, Right) | |
validMoves.Add(Move.FromPositionAndDirection(2, 1, Direction.Right, boardSize)); | |
} | |
return validMoves; | |
} | |
[ | ]|
[ | ]|
public void TestCheckValidMoves(bool fullBoard) | |
{ | |
var gameObj = new GameObject("board"); | |
var board = gameObj.AddComponent<StringBoard>(); | |
var boardString = | |
@"0105 | |
1024 | |
0203 | |
2022"; | |
board.SetBoard(boardString); | |
var boardSize = board.GetMaxBoardSize(); | |
if (!fullBoard) | |
{ | |
board.CurrentRows -= 1; | |
} | |
var validMoves = GetValidMoves4x4(fullBoard, boardSize); | |
foreach (var m in validMoves) | |
{ | |
Assert.IsTrue(board.IsMoveValid(m)); | |
} | |
// Run through all moves and make sure those are the only valid ones | |
HashSet<int> validIndices = new HashSet<int>(); | |
foreach (var m in validMoves) | |
{ | |
validIndices.Add(m.MoveIndex); | |
} | |
// Make sure iterating over AllMoves is OK with the smaller board | |
foreach (var move in board.AllMoves()) | |
{ | |
var expected = validIndices.Contains(move.MoveIndex); | |
Assert.AreEqual(expected, board.IsMoveValid(move), $"({move.Row}, {move.Column}, {move.Direction})"); | |
} | |
HashSet<int> validIndicesFromIterator = new HashSet<int>(); | |
foreach (var move in board.ValidMoves()) | |
{ | |
validIndicesFromIterator.Add(move.MoveIndex); | |
} | |
Assert.IsTrue(validIndices.SetEquals(validIndicesFromIterator)); | |
} | |
} | |
} | |