/// <summary> /// Initializes the Fence that splits 4 adjacent squares that from a square into 2 pairs. /// </summary> /// <param name="link1"></param> /// <param name="link2"></param> public Fence(Link link1, Link link2) { // Check if they are null if (link1.Squares.Any(s1 => link2.Squares.All(s2 => !s1.IsAdjacentTo(s2)))) { throw new InvalidOperationException("Squares should be adjacent"); } _splittedLinks[0] = link1; _splittedLinks[1] = link2; }
/// <summary> /// /// </summary> /// <param name="link1"></param> /// <param name="link2"></param> /// <returns></returns> public FenceSetResult GetFenceSetResult(Link link1, Link link2) { // validate if squares are adjacent and no fence available for jump links if ((link1 is JumpLink) || (link2 is JumpLink) || !link1.Squares.All(s1 => link2.Squares.Any(s2 => s1.IsAdjacentTo(s2)))) { return FenceSetResult.Invalid; } var allFencesLinks = _fences.SelectMany(f => f.SplittedLinks); if (allFencesLinks.Any(l => l == link1 || l == link2)) { return FenceSetResult.OverlappedWithOtherFence; } // validate that fence is not crossing another fence (situation when all 4 squares match) if (_fences.Any(f => FencesCross(f, link1, link2))) { return FenceSetResult.CrossedAnotherFence; } // validate that the path to goal exists. For that move shortest path logic to the board. if (!Players.All(p => CanReachDestination(p))) { return FenceSetResult.BlockedPlayerGoal; } return FenceSetResult.Succesfull; }
private void InitializeField(int size) { _squares = new Square[size * size]; var links = new List<Link>(2 * size * (size - 1)); for (int y = 0; y < _size; y++) { for (int x = 0; x < _size; x++) { int position = GetIndexFromPosition(x, y); _squares[position] = new Square(x, y); if (x > 0) { // Add horizontal link int neibourPosition = GetIndexFromPosition((x - 1), y); Link link = new Link(_squares[position], _squares[neibourPosition]); links.Add(link); _squares[position].AddLink(link); _squares[neibourPosition].AddLink(link); } if (y > 0) { // Add vertical link int neibourPosition = GetIndexFromPosition(x, y - 1); Link link = new Link(_squares[position], _squares[neibourPosition]); links.Add(link); _squares[position].AddLink(link); _squares[neibourPosition].AddLink(link); } } } _links = links; }
private Link GetStraitJumpLink(Square square, Link link) { Square target = link.LinkedSquare(square); // Check strait jump first int jumpX = target.X + (target.X - square.X); int jumpY = target.Y + (target.Y - square.Y); Square straitJump = target.Neighbours.FirstOrDefault(s => s.X == jumpX && s.Y == jumpY); if (straitJump == null) { return null; } MoveResult fromTarget = GetMoveResult(target, straitJump); switch (fromTarget) { case MoveResult.Succesfull: return new JumpLink(square, target, straitJump); case MoveResult.BlockedByFence: case MoveResult.BlockedByPlayer: return null; default: throw new InvalidOperationException("Unexpected move result."); } }
// Assumes the linked square is ocupied by player private IEnumerable<Link> GetJumpLinks(Square square, Link link) { // Jump links are used in the case one player stands in front of the other. // Jump links aren't presisted as they become stale quickly // Jumps can be made over a single player. // In case strait jump not possible because of the fences diagonal jumps can be made if aren't blocked by fences. Square target = link.LinkedSquare(square); Link straitJump = GetStraitJumpLink(square, link); if (straitJump != null) { yield return straitJump; yield break; } // handle diagonal jumps foreach (var l in GetDiagonalJumpLinks(square, link)) { yield return l; } }
private IEnumerable<Link> GetDiagonalJumpLinks(Square square, Link link) { Square target = link.LinkedSquare(square); int deltaX = target.X - square.X; int deltaY = target.Y - square.Y; int[] jumpDirection = { -1, 1 }; foreach (int direction in jumpDirection) { int jumpX = deltaX == 0 ? target.X + direction : target.X; int jumpY = deltaY == 0 ? target.Y + direction : target.Y; Square diagonalJump = target.Neighbours.FirstOrDefault(s => s.X == jumpX && s.Y == jumpY); if (diagonalJump == null) { continue; } MoveResult fromTarget = GetMoveResult(target, diagonalJump); switch (fromTarget) { case MoveResult.Succesfull: yield return new JumpLink(square, target, diagonalJump); break; case MoveResult.BlockedByFence: case MoveResult.BlockedByPlayer: break; default: throw new InvalidOperationException("Unexpected move result."); } } }
private bool FencesCross(Fence f, Link link1, Link link2) { HashSet<Square> squares = new HashSet<Square>(f.SplittedLinks.SelectMany(link => link.Squares).Concat(link1.Squares).Concat(link2.Squares)); return squares.Count == 4; }
/// <summary> /// Sets fence to separate 2 pairs of squares. /// </summary> /// <param name="firstSplit">Fist pair of split squares.</param> /// <param name="secondSplit">Second pair of split squares.</param> protected void SetFence(Link first, Link second) { if (_fences.Count == _maxFences) { throw new InvalidOperationException("All fences already used."); } // Check for NULL //TODO: Make sure that the fence is not splitting existing fence into 2 pieces // for this check if the square is not crossed already by existing fences _fences.Add(new Fence(first, second)); }
/// <summary> /// Moves player in by the link. /// </summary> /// <param name="player"></param> /// <param name="link"></param> protected void MovePlayer(Player player, Link link) { Square currentPosition = player.Position; // Validate current -> desired is not bloced by fence and isn't occupied if (GetMoveResult(currentPosition, link) != MoveResult.Succesfull) { throw new InvalidOperationException("Can not jump over the fence or step on the other player."); } Square newPosition = link.LinkedSquare(currentPosition); ((MovablePlayer)player).MoveTo(newPosition); }
/// <summary> /// /// </summary> /// <param name="from"></param> /// <param name="link"></param> /// <returns></returns> public MoveResult GetMoveResult(Square from, Link link) { JumpLink jumpLink = link as JumpLink; if (jumpLink != null) { if (GetMoveResult(from, jumpLink.Via) != MoveResult.BlockedByPlayer) { return MoveResult.Invalid; } return GetMoveResult(jumpLink.Via, jumpLink.LinkedSquare(from)); } // Blocked by fence has higher priority than blocked by player so it goes first. if (Fences.Any(f => f.SplittedLinks.Contains(link))) { return MoveResult.BlockedByFence; } if (IsSquareOccupied(link.LinkedSquare(from))) { return MoveResult.BlockedByPlayer; } return MoveResult.Succesfull; }
/// <summary> /// Adds a link to other adjacent node. /// </summary> /// <param name="link">The link.</param> internal void AddLink(Link link) { // handle null if (!link.Squares.Contains(this)) { throw new InvalidOperationException("Link doesn't connect to current square."); } _links.Add(link); }
/// <summary> /// /// </summary> /// <param name="link"></param> public void MoveCurrentPlayer(Link link) { // check link is not null if (!GameFinished) { if (!link.Squares.Contains(CurrentPlayer.Position)) { throw new InvalidOperationException("Invalid link provided."); } Square target = link.LinkedSquare(CurrentPlayer.Position); _board.MovePlayer(CurrentPlayer, link); GameFinished = CheckWinningConditions(); if (!GameFinished) { _currentPlayer = NextPlayer(); } } }
/// <summary> /// Sets fence to separate 2 pairs of squares. /// </summary> /// <param name="firstSplit">Fist split link.</param> /// <param name="secondSplit">Second split link.</param> internal new void SetFence(Link firstLink, Link secondLink) { base.SetFence(firstLink, secondLink); }
/// <summary> /// Moves player by the link. /// </summary> internal new void MovePlayer(Player player, Link link) { base.MovePlayer(player, link); }
private void ValidateFence(Link link1, Link link2) { switch (_board.GetFenceSetResult(link1, link2)) { case FenceSetResult.Succesfull: break; case FenceSetResult.OverlappedWithOtherFence: throw new InvalidOperationException("Can not set a fence where another is set already."); case FenceSetResult.CrossedAnotherFence: throw new InvalidOperationException("Fences couldn't cross each other."); case FenceSetResult.BlockedPlayerGoal: throw new InvalidOperationException("Players should always be able to reach their destination."); default: throw new InvalidOperationException("Invalid links provided"); } }