public IEnumerable <IPositional> Search(Game game, int maxTile = 2, int blankTolerance = 1)
        {
            if (game is null)
            {
                throw new ArgumentNullException(nameof(game));
            }

            if (maxTile < 0)
            {
                throw new ArgumentException(nameof(maxTile));
            }

            if (blankTolerance < 0)
            {
                throw new ArgumentException(nameof(blankTolerance));
            }

            // Get all the placed tiles to determine all the correct playable tiles
            IReadOnlyList <Tile> placedTiles = game.History;

            // Get all the playable tiles using a HashSet where only distinct tiles
            // are added
            var playableTiles = new HashSet <Tile>();

            foreach (Tile tile in placedTiles)
            {
                // Loop all 4 Orientation enumeration
                Orientations[] orientations = new[]
                {
                    Orientations.Horizontal,
                    Orientations.Vertical,
                    Orientations.Diagonal,
                    Orientations.RvDiagonal
                };

                foreach (Orientations orientation in orientations)
                {
                    // Retrieve line of each orientation within 2-tile range where the
                    // tiles are empty
                    foreach (Tile t in
                             OrientedlLine.FromBoard(
                                 game.Board,
                                 tile.X,
                                 tile.Y,
                                 (Piece)Pieces.None,
                                 orientation,
                                 maxTile: maxTile,
                                 blankTolerance: blankTolerance)
                             .GetSameTiles())
                    {
                        playableTiles.Add(t);
                    }
                }
            }

            return(playableTiles);
        }
Esempio n. 2
0
        /// <summary>
        /// Evaluates a <paramref name="game"/>'s state at
        /// <paramref name="positional"/> for <paramref name="piece"/>.
        /// </summary>
        /// <param name="game">the <see cref="Game"/> to be evaluated.</param>
        /// <param name="positional">the <see cref="IPositional"/> to evaluate.</param>
        /// <param name="piece">the <see cref="Piece"/> to evaluate for.</param>
        /// <returns>
        /// A non-negative number representing the probability of winning the game
        /// at <paramref name="positional"/>. The only negative number
        /// <see cref="double.MinValue"/> is returned when the
        /// <paramref name="game"/> is lost but the winner is not
        /// <paramref name="piece"/>. If the <paramref name="piece"/> wins the
        /// <paramref name="game"/>, returns <see cref="double.MaxValue"/>
        /// </returns>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        public double Evaluate(Game game, IPositional positional, Piece piece)
        {
            if (game is null)
            {
                throw new ArgumentNullException(nameof(game));
            }

            if (positional is null)
            {
                throw new ArgumentNullException(nameof(positional));
            }

            if (piece == Pieces.None)
            {
                throw new ArgumentException($"{nameof(piece)} must not be {Pieces.None}");
            }

            var point = 0.0;

            // If game is over the point should be high enough so that this node is
            // more likely to get noticed
            if (game.IsOver)
            {
                if (game.Manager.PreviousPlayer.Piece == piece)
                {
                    point = double.MaxValue;
                }
                else
                {
                    point = double.MinValue;
                }
            }

            // Otherwise evaluate the point matching lines and other information
            else
            {
                // Retrieve line group of each orientation to fully evaluate a tile
                Orientations[] orientations = new[]
                {
                    Orientations.Horizontal,
                    Orientations.Vertical,
                    Orientations.Diagonal,
                    Orientations.RvDiagonal
                };

                foreach (Orientations orientation in orientations)
                {
                    // Get line within 5-tile range
                    var line =
                        OrientedlLine.FromBoard(
                            game.Board,
                            positional.X,
                            positional.Y,
                            piece,
                            orientation,
                            maxTile: Game.WINPIECES,
                            blankTolerance: 1);

                    // Calculate points
                    var sameTilesCount  = line.SameTileCount;
                    var blockTilesCount = line.BlockTilesCount;

                    // When the line is not blocked
                    if (blockTilesCount == 0)
                    {
                        // If the line chain has more tiles than win pieces, then this tile
                        // is less worth.
                        if (sameTilesCount + 1 >= Game.WINPIECES)
                        {
                            point += sameTilesCount;
                        }

                        // Otherwise
                        else
                        {
                            // Calculate point using Geometric series of 2.0 so that the more
                            // chain it has, the more valuable the line
                            var _point =
                                1.0 * (1.0 - Math.Pow(2.0, sameTilesCount)) / (1.0 - 2.0);

                            if (_point < 0)
                            {
                                throw new Exception();
                            }

                            // Finally the point is added with the power of itself
                            point += Math.Pow(_point, line.IsChained ? 2.0 : 1.5);
                        }
                    }

                    // When the line is partially blocked, only add the point which equals
                    // to the same count
                    else if (blockTilesCount == 1)
                    {
                        point += sameTilesCount;
                    }

                    // Otherwise, add no point.

                    //point += (1.0 * (1.0 - Math.Pow(2.0, line.CountChainTiles())) / (1.0 - 2.0));
                    //point += 0.25 * line.CountBlankTiles();
                    //point -= 1.0 * line.BlockTileCount;
                }
            }

            return(point);
        }