Esempio n. 1
0
        private static IList <int> CompleteConsecutiveness(
            Board board,
            IEnumerable <Consecutiveness> incompleteMoves)
        {
            var cells           = board.Cells;
            var boardDimensions = board.Dimensions;

            var moves = new List <int>();

            void collect(CellCoordinates coordinates)
            {
                if (CellCoordinates.IsInside(coordinates, boardDimensions))
                {
                    var cellAt = CellCoordinates.ToCellAt(coordinates, boardDimensions);
                    if (cells[cellAt] == CellOwner.None)
                    {
                        moves.Add(cellAt);
                    }
                }
            }

            var prioritized = incompleteMoves
                              .OrderByDescending(consecutiveness => consecutiveness.CellsAt.Count);

            foreach (var consecutiveness in prioritized)
            {
                // single moves cannot be completed in one turn
                if (consecutiveness.CellsAt.Count < 2)
                {
                    continue;
                }

                var first    = CellCoordinates.FromCellAt(consecutiveness.CellsAt.First(), boardDimensions);
                var second   = CellCoordinates.FromCellAt(consecutiveness.CellsAt.Skip(1).First(), boardDimensions);
                var last     = CellCoordinates.FromCellAt(consecutiveness.CellsAt.Last(), boardDimensions);
                var stepSpan = second - first;
                collect(first - stepSpan);
                collect(last + stepSpan);
            }

            return(moves);
        }
Esempio n. 2
0
        private static IList <int> FindWinningMoves(
            Board board,
            IEnumerable <Consecutiveness> incompleteMoves,
            Predicate <CellOwner> cellOwnerPredicate)
        {
            var dimensions = board.Dimensions;
            var cells      = board.Cells;
            var filteredIncompleteMoves = incompleteMoves.Where(
                (consecutiveness) => cellOwnerPredicate(cells[consecutiveness.CellsAt.First()]));

            // When there are moves, which could complete a winning consecutiveness on the board,
            // we should preferably take it.
            var completingMoves = CompleteConsecutiveness(board, filteredIncompleteMoves);

            if (completingMoves.Count > 0)
            {
                return(Cells.TakeAnyCell(completingMoves));
            }

            // Gaps between taken cells could also complete a winning consecutiveness
            var gaps           = new List <Gap>();
            var smallestSpread = int.MaxValue;

            for (int i = 0; i < cells.Count; i++)
            {
                // Skip cells which are not set (we find the gaps otherwise)
                // and skip cells which are not of interest for now
                var leftCellOwner = cells[i];
                if (leftCellOwner == CellOwner.None || !cellOwnerPredicate(leftCellOwner))
                {
                    continue;
                }

                // Bubble upwards and find a complementing cell of the same cellOwner
                var leftCoordinates = CellCoordinates.FromCellAt(i, dimensions);
                for (int j = i + 1; j < cells.Count; j++)
                {
                    var rightCellOwner = cells[j];
                    if (rightCellOwner != leftCellOwner)
                    {
                        continue;
                    }

                    // When the half distance between them is an integer (or distance from the middle to both
                    // sides is equal), we have found a potential gap.
                    var distance     = j - i;
                    var halfDistance = distance / 2;
                    if (halfDistance + halfDistance != distance)
                    {
                        continue;
                    }

                    // The middle cell as gap must be claimable (only concerns boards bigger than 3x3)
                    var middleCellAt    = i + halfDistance;
                    var middleCellOwner = cells[middleCellAt];
                    if (middleCellOwner != CellOwner.None)
                    {
                        continue;
                    }

                    // Now we need to determine the spread between left cell and middle cell in terms of
                    // board coordinates. This is actually only a priorization help for boards bigger than 3x3.
                    // On 3x3 we will always have a spread of 1.
                    var middleCoordinates = CellCoordinates.FromCellAt(middleCellAt, dimensions);
                    var spread            = (middleCoordinates - leftCoordinates).Spread();
                    smallestSpread = Math.Min(smallestSpread, spread);

                    gaps.Add(new Gap(middleCellAt, spread));
                }
            }

            // Gaps with smaller spread will be filled first
            var gapsWithSmallestSpread = gaps.Where((gap) => gap.Spread == smallestSpread)
                                         .Select((gap) => gap.CellAt);

            return(Cells.TakeAnyCell(gapsWithSmallestSpread.ToList()));
        }