From 9e69bfde962137a94387b69d96f3aac3b233189d Mon Sep 17 00:00:00 2001 From: Lee Richardson Date: Wed, 24 Jan 2018 21:04:42 +0000 Subject: [PATCH] Partially implements AdjacentFeaturesMatchRule for top-bottom placements. --- Sharpasonne.Models/Tile.cs | 37 ++++++++ .../Rules/AdjacentFeaturesMatchRuleTest.cs | 90 +++++++++++++++++++ Sharpasonne/IRule.cs | 2 +- .../Rules/AdjacentFeaturesMatchRule.cs | 52 +++++++++++ .../Rules/MatchingAdjecentFeatureRule.cs | 10 --- 5 files changed, 180 insertions(+), 11 deletions(-) create mode 100644 Sharpasonne.Tests/Rules/AdjacentFeaturesMatchRuleTest.cs create mode 100644 Sharpasonne/Rules/AdjacentFeaturesMatchRule.cs delete mode 100644 Sharpasonne/Rules/MatchingAdjecentFeatureRule.cs diff --git a/Sharpasonne.Models/Tile.cs b/Sharpasonne.Models/Tile.cs index 6e0d197..7a7b9b8 100644 --- a/Sharpasonne.Models/Tile.cs +++ b/Sharpasonne.Models/Tile.cs @@ -1,5 +1,7 @@ +using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using JetBrains.Annotations; namespace Sharpasonne.Models @@ -12,5 +14,40 @@ internal Tile([NotNull] IEnumerable features) { this.Features = features.ToImmutableList(); } + + public IFeature[] GetEdge(Orientation direction) + { + var segments = this.GetSegments(direction); + var features = segments.Select(segment => + this.Features.First(feature => + feature.Connections.Contains(segment) + ) + ); + + return features.ToArray(); + } + + protected Segment[] GetSegments(Orientation direction) + { + switch (direction) + { + case Orientation.Top: + return new[] + { + Segment.TopLeft, + Segment.Top, + Segment.TopRight, + }; + case Orientation.Bottom: + return new[] + { + Segment.BottomLeft, + Segment.Bottom, + Segment.BottomRight, + }; + default: + throw new NotImplementedException(); + } + } } } diff --git a/Sharpasonne.Tests/Rules/AdjacentFeaturesMatchRuleTest.cs b/Sharpasonne.Tests/Rules/AdjacentFeaturesMatchRuleTest.cs new file mode 100644 index 0000000..f8a94d5 --- /dev/null +++ b/Sharpasonne.Tests/Rules/AdjacentFeaturesMatchRuleTest.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using System.Linq; +using Sharpasonne.GameActions; +using Sharpasonne.Models; +using Sharpasonne.Models.Features; +using Sharpasonne.Rules; +using Xunit; +using System.Collections.Immutable; +using Optional.Unsafe; + +namespace Sharpasonne.Tests.Rules +{ + public class AdjacentFeaturesMatchRuleTest : UnitTest + { + [Fact] + public void Given_Empty_Then_True() + { + AssertTrue(new Engine(), MakePlaceTile(0, 0)); + } + + [Fact] + public void Given_OneNeighbour_When_FeaturesMatch_Then_True() + { + var aboveTile = new TileBuilder() + .CreateTile(new [] + { + new Field(ImmutableHashSet.Create( + Segment.BottomLeft, + Segment.Bottom, + Segment.BottomRight + )), + }) + .ValueOrFailure(); + + var belowTile = new TileBuilder() + .CreateTile(new [] + { + new Field(ImmutableHashSet.Create( + Segment.TopLeft, + Segment.Top, + Segment.TopRight + )), + }) + .ValueOrFailure(); + + var aboveAction = MakePlaceTile(0, 1, aboveTile); + var belowAction = MakePlaceTile(0, 0, belowTile); + var board = MakeBoard(aboveAction); + var engine = MockEngine(board); + + AssertTrue(engine, belowAction); + } + + [Fact] + public void Given_OneNeighbour_When_FeaturesDoNotMatch_Then_False() + { + var aboveTile = new TileBuilder() + .CreateTile(new IFeature[] + { + new City( + ImmutableHashSet.Create(Segment.BottomLeft), + hasShield: true + ), + new Field(ImmutableHashSet.Create( + Segment.Bottom, + Segment.BottomRight + )), + }) + .ValueOrFailure(); + + var belowTile = new TileBuilder() + .CreateTile(new[] + { + new Field(ImmutableHashSet.Create( + Segment.TopLeft, + Segment.Top, + Segment.TopRight + )), + }) + .ValueOrFailure(); + + var aboveAction = MakePlaceTile(0, 1, aboveTile); + var belowAction = MakePlaceTile(0, 0, belowTile); + var board = MakeBoard(aboveAction); + var engine = MockEngine(board); + + AssertFalse(engine, belowAction); + } + } +} \ No newline at end of file diff --git a/Sharpasonne/IRule.cs b/Sharpasonne/IRule.cs index d86ec55..b3f33dc 100644 --- a/Sharpasonne/IRule.cs +++ b/Sharpasonne/IRule.cs @@ -2,7 +2,7 @@ namespace Sharpasonne { - public interface IRule where T2: IGameAction + public interface IRule where T2 : IGameAction { bool Verify(IEngine engine, T1 gameAction) where T1 : T2 ; diff --git a/Sharpasonne/Rules/AdjacentFeaturesMatchRule.cs b/Sharpasonne/Rules/AdjacentFeaturesMatchRule.cs new file mode 100644 index 0000000..4a970b4 --- /dev/null +++ b/Sharpasonne/Rules/AdjacentFeaturesMatchRule.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections; +using System.Linq; +using Sharpasonne.GameActions; +using Sharpasonne.Models; +using System.Collections.Generic; + +namespace Sharpasonne.Rules +{ + public class AdjacentFeaturesMatchRule : IRule + { + public bool Verify(IEngine engine, T1 action) + where T1 : PlaceTileGameAction + { + var adjacent = engine.Board.GetAdjecentTiles(action.Point); + var allMatch = adjacent + .Where(o => o.Value.HasValue) + .Select(o => ( + o.Key, + o.Value.ValueOr(null as Placement) + )) + .All(o => + { + (var direction, var placement) = o; + + switch (direction) + { + case Orientation.Top: + return this.EdgesMatch( + placement.Tile.GetEdge(Orientation.Bottom), + action.Placement.Tile.GetEdge(Orientation.Top) + ); + default: + throw new NotImplementedException(); + } + }); + + return allMatch; + } + + protected bool EdgesMatch( + IFeature[] from, + IFeature[] to + ) + { + var fromTypes = from.Select(feature => feature.GetType()); + var toTypes = to.Select(feature => feature.GetType()); + + return fromTypes.SequenceEqual(toTypes); + } + } +} diff --git a/Sharpasonne/Rules/MatchingAdjecentFeatureRule.cs b/Sharpasonne/Rules/MatchingAdjecentFeatureRule.cs deleted file mode 100644 index 13fd145..0000000 --- a/Sharpasonne/Rules/MatchingAdjecentFeatureRule.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Sharpasonne.GameActions; - -namespace Sharpasonne.Rules -{ - abstract class MatchingAdjecentFeatureRule : IRule - { - public abstract bool Verify(IEngine engine, T1 gameAction) where T1 : PlaceTileGameAction; - } -} -