using System; using System.Collections.Generic; using UnityEngine; using NUnit.Framework; using Unity.MLAgents.Extensions.Match3; namespace Unity.MLAgents.Extensions.Tests.Match3 { internal class StringBoard : AbstractBoard { private string[] m_Board; private string[] m_Special; /// /// Convert a string like "000\n010\n000" to a board representation /// Row 0 is considered the bottom row /// /// public void SetBoard(string newBoard) { m_Board = newBoard.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); Rows = m_Board.Length; Columns = m_Board[0].Length; NumCellTypes = 0; for (var r = 0; r < Rows; r++) { for (var c = 0; c < Columns; c++) { NumCellTypes = Mathf.Max(NumCellTypes, 1 + GetCellType(r, c)); } } } public void SetSpecial(string newSpecial) { m_Special = newSpecial.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); Debug.Assert(Rows == m_Special.Length); Debug.Assert(Columns == m_Special[0].Length); NumSpecialTypes = 0; for (var r = 0; r < Rows; r++) { for (var c = 0; c < Columns; 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) { var character = m_Board[m_Board.Length - 1 - row][col]; return (character - '0'); } public override int GetSpecialType(int row, int col) { var character = m_Special[m_Board.Length - 1 - row][col]; return (character - '0'); } } public class AbstractBoardTests { [Test] public void TestBoardInit() { var boardString = @"000 000 010"; var gameObj = new GameObject("board"); var board = gameObj.AddComponent(); board.SetBoard(boardString); Assert.AreEqual(3, board.Rows); Assert.AreEqual(3, board.Columns); Assert.AreEqual(2, board.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)); } } } [Test] public void TestCheckValidMoves() { var gameObj = new GameObject("board"); var board = gameObj.AddComponent(); var boardString = @"0105 1024 0203 2022"; board.SetBoard(boardString); var validMoves = new[] { Move.FromPositionAndDirection(2, 1, Direction.Up, board.Rows, board.Columns), // equivalent to (3, 1, Down) Move.FromPositionAndDirection(2, 1, Direction.Left, board.Rows, board.Columns), // equivalent to (2, 0, Right) Move.FromPositionAndDirection(2, 1, Direction.Down, board.Rows, board.Columns), // equivalent to (1, 1, Up) Move.FromPositionAndDirection(2, 1, Direction.Right, board.Rows, board.Columns), Move.FromPositionAndDirection(1, 1, Direction.Down, board.Rows, board.Columns), Move.FromPositionAndDirection(1, 1, Direction.Left, board.Rows, board.Columns), Move.FromPositionAndDirection(1, 1, Direction.Right, board.Rows, board.Columns), Move.FromPositionAndDirection(0, 1, Direction.Left, board.Rows, board.Columns), }; foreach (var m in validMoves) { Assert.IsTrue(board.IsMoveValid(m)); } // Run through all moves and make sure those are the only valid ones HashSet validIndices = new HashSet(); foreach (var m in validMoves) { validIndices.Add(m.MoveIndex); } 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 validIndicesFromIterator = new HashSet(); foreach (var move in board.ValidMoves()) { validIndicesFromIterator.Add(move.MoveIndex); } Assert.IsTrue(validIndices.SetEquals(validIndicesFromIterator)); } } }