using System.Linq; using MagnetismHeroes.Core; using NUnit.Framework; namespace MagnetismHeroes.Tests { public sealed class CompositionSessionTests { private sealed class SpyStore : IProgressStore { public ProgressSave Saved = new ProgressSave { completedStageIds = Enumerable.Range(1, 13).ToArray() }; public int Writes; public bool TryLoad(out ProgressSave save, out string error) { save = Saved; error = null; return true; } public bool TrySave(ProgressSave save, out string error) { Saved = save; Writes++; error = null; return true; } } [Test] public void MoveAndMergeCompleteWithOneWriteAndPreserveEveryIdentity() { var store = new SpyStore { Saved = new ProgressSave { completedStageIds = Enumerable.Range(1, 5).ToArray() } }; var session = new GameSession(new[] { new StageDefinition(6, StageKind.Composition, new[] { 1, 1 }, new[] { 2 }) }, store); session.StartStage(6); var groups = session.Board.Groups; var ids = session.Board.Balls.Select(ball => ball.id).ToArray(); var writes = store.Writes; var result = session.MoveAndMerge(groups[0].id, new Position3(0.4f, 0.2f, 1), groups[1].id); Assert.That(result.Accepted, Is.True); Assert.That(store.Writes, Is.EqualTo(writes + 1)); Assert.That(session.Screen, Is.EqualTo(SessionScreen.FreePlay)); Assert.That(session.Board.Groups.Single().ballIds, Is.EquivalentTo(ids)); Assert.That(store.Saved.puzzleStates, Is.Empty); Assert.That(store.Saved.latestFreePlay, Is.Not.Null); Assert.That(store.Saved.completedStageIds, Is.EqualTo(Enumerable.Range(1, 6).ToArray())); } [Test] public void UnknownMergeTargetDoesNotMoveOrWrite() { var store = new SpyStore(); var session = new GameSession(new[] { new StageDefinition(6, StageKind.Composition, new[] { 1, 1 }, new[] { 2 }) }, store); session.StartStage(6); var original = session.Board.Balls; var writes = store.Writes; Assert.That(session.MoveAndMerge(session.Board.Groups[0].id, new Position3(1, 0, 0), "missing").Accepted, Is.False); Assert.That(session.Board.Balls.Select(ball => ball.position), Is.EqualTo(original.Select(ball => ball.position))); Assert.That(store.Writes, Is.EqualTo(writes)); } [TestCase(0)] [TestCase(3)] public void EmptyAndEntireSplitKeepStateAndSave(int count) { var store = new SpyStore(); var session = new GameSession(new[] { new StageDefinition(14, StageKind.Composition, new[] { 3, 1 }, new[] { 2, 2 }) }, store); session.StartStage(14); var group = session.Board.Groups[0]; var writes = store.Writes; var original = session.Board.Balls; Assert.That(session.SplitAndMove(group.id, group.ballIds.Take(count).ToArray(), "new", new Position3(1, 1, 0)).Accepted, Is.False); Assert.That(store.Writes, Is.EqualTo(writes)); Assert.That(session.Board.Groups.Length, Is.EqualTo(2)); Assert.That(session.Board.Balls.Select(ball => ball.position), Is.EqualTo(original.Select(ball => ball.position))); } [Test] public void DistantSelectionProducesExactlyTwoFriendsAndFinalCompositionIgnoresHistory() { var store = new SpyStore(); var session = new GameSession(new[] { new StageDefinition(14, StageKind.Composition, new[] { 3, 1 }, new[] { 2, 2 }) }, store); session.StartStage(14); var groups = session.Board.Groups; session.MoveAndMerge(groups[0].id, new Position3(0.1f, 0, 0), groups[1].id); var all = session.Board.Groups.Single(); var ids = all.ballIds.ToArray(); var writes = store.Writes; Assert.That(session.SplitAndMove(all.id, new[] { ids[0], ids[3] }, "split-result", new Position3(3, 0, 0)).Accepted, Is.True); Assert.That(store.Writes, Is.EqualTo(writes + 1)); Assert.That(session.Screen, Is.EqualTo(SessionScreen.FreePlay)); Assert.That(session.Board.Groups.Select(group => group.ballIds.Length), Is.EqualTo(new[] { 2, 2 })); Assert.That(session.Board.Groups.SelectMany(group => group.ballIds), Is.EquivalentTo(ids)); } [Test] public void SplitAndMergeKeepCollectedAndCountedIdsThroughPuzzleRestore() { var definition = new StageDefinition(1, StageKind.Counting, new[] { 1, 1, 1 }, new[] { 3 }); var store = new SpyStore(); var session = new GameSession(new[] { definition }, store); session.StartStage(1); var groups = session.Board.Groups; var ids = session.Board.Balls.Select(ball => ball.id).ToArray(); foreach (var group in groups) session.MoveGroup(group.id, new Position3(0, 1, 0)); session.Merge(groups[0].id, groups[1].id); session.Merge(groups[0].id, groups[2].id); session.Count(ids[0]); var writes = store.Writes; Assert.That(session.SplitAndMove(groups[0].id, new[] { ids[0], ids[2] }, "detached", new Position3(2, 0, 0)).Accepted, Is.True); Assert.That(store.Writes, Is.EqualTo(writes + 1)); Assert.That(session.Board.CountedBallIds, Is.EqualTo(new[] { ids[0] })); var restored = new GameSession(new[] { definition }, store); restored.StartStage(1); Assert.That(restored.Board.CollectedBallIds, Is.EquivalentTo(ids)); Assert.That(restored.Board.CountedBallIds, Is.EqualTo(new[] { ids[0] })); Assert.That(restored.Board.Groups.Length, Is.EqualTo(2)); Assert.That(restored.MoveAndMerge("detached", new Position3(-1, 0, 0), groups[0].id).Accepted, Is.True); Assert.That(restored.Board.CountedBallIds, Is.EqualTo(new[] { ids[0] })); Assert.That(restored.Board.BallCount, Is.EqualTo(3)); } [Test] public void InvalidCoordinatesNeverPartiallySplitOrMerge() { var store = new SpyStore(); var session = new GameSession(new[] { new StageDefinition(14, StageKind.Composition, new[] { 3, 1 }, new[] { 2, 2 }) }, store); session.StartStage(14); var groups = session.Board.Groups; var original = session.Board.Balls; var writes = store.Writes; Assert.That(session.MoveAndMerge(groups[0].id, new Position3(float.NaN, 0, 0), groups[1].id).Accepted, Is.False); Assert.That(session.SplitAndMove(groups[0].id, new[] { groups[0].ballIds[0] }, "invalid", new Position3(float.PositiveInfinity, 0, 0)).Accepted, Is.False); Assert.That(store.Writes, Is.EqualTo(writes)); Assert.That(session.Board.Groups.Select(group => group.ballIds.Length), Is.EqualTo(new[] { 3, 1 })); Assert.That(session.Board.Balls.Select(ball => ball.position), Is.EqualTo(original.Select(ball => ball.position))); } } }