예제 #1
0
        /// <summary>
        /// Builds left part of word before anchor
        /// </summary>
        /// <param name="partialWord">Word preceding anchor. Might already be on the board or placed from previous LeftPart calls</param>
        /// <param name="limit">Limit of tiles we can place before anchor</param>
        /// <param name="anchor">Anchor from which to build left and right part</param>
        /// <param name="boardArray">Board array to work with (untransposed or transposed)</param>
        /// <param name="validCrossChecks">A list of crosschecks which say what tiles can be played in each board tile</param>
        /// <param name="validMovesList">A list of valid moves which is continuously updated</param>
        /// <param name="boardIsHorizontal">Board is untransposed or transposed</param>
        /// <param name="boardBeforeMove">Board before any moves were generated</param>
        public void LeftPart(string partialWord, int limit, int[] anchor, BoardTile[,] boardArray, Dictionary <BoardTile, List <CharTile> > validCrossChecks,
                             HashSet <GeneratedMove> validMovesList, bool boardIsHorizontal, BoardTile[,] boardBeforeMove)
        {
            //Tries to build all right parts for given left part
            ExtendRight(partialWord, anchor, boardArray, validCrossChecks, validMovesList, boardIsHorizontal, boardBeforeMove);
            if (limit > 0)
            {
                //Gets a list of letters that can follow the current left part and can be placed to the left of the anchor
                HashSet <string> labelsOfDawgEdges = new HashSet <string>(new DawgEdgeEqualityComparer());
                var wordsWithCommonPreffix         = Dawg.MatchPrefix(partialWord);
                foreach (var word in wordsWithCommonPreffix)
                {
                    labelsOfDawgEdges.Add(word.Key.Substring(partialWord.Length));
                }
                foreach (var label in labelsOfDawgEdges)
                {
                    if (label == "")
                    {
                        continue;
                    }
                    if (OriginalRackOfPlayer.CheckIfWordIsPlayable(partialWord + label[0]) &&
                        ((!validCrossChecks.Any(c => c.Key == boardArray[anchor[0], anchor[1] - 1]) ||
                          validCrossChecks.Any(c => c.Key == boardArray[anchor[0], anchor[1] - 1] &&
                                               c.Value.Any(x => x.Letter == label[0])))))
                    {
                        CharTile tileToWorkWith = null;
                        if (RackOfCurrentPlayer.CheckIfTileIsInRack(label[0], false))
                        {
                            tileToWorkWith = Dictionary.CharTiles.Where(c => c.Letter == label[0] && c.Score != 0).FirstOrDefault();
                        }
                        else
                        {
                            tileToWorkWith = Dictionary.CharTiles.Where(c => c.Letter == '*').FirstOrDefault();
                        }

                        //Rebuilds left part with new tiles
                        for (int i = 0; i < partialWord.Length; i++)
                        {
                            boardArray[anchor[0], anchor[1] - 1 - i].CharTile = null;
                        }
                        for (int i = 0; i < partialWord.Length; i++)
                        {
                            var tileToAdd = OriginalRackOfPlayer.GetTile(partialWord[i]);
                            boardArray[anchor[0], anchor[1] - partialWord.Length - 1 + i].CharTile = tileToAdd;
                        }
                        RackOfCurrentPlayer.SubstractFromRack(tileToWorkWith);
                        boardArray[anchor[0], anchor[1] - 1].CharTile = tileToWorkWith;
                        bool validPrefix = true;

                        //Checks if the newly formed left part's components are valid for the board
                        for (int i = 0; i < partialWord.Length; i++)
                        {
                            var boardTile = boardArray[anchor[0], anchor[1] - 1 - partialWord.Length + i];
                            if (!(!validCrossChecks.Any(c => c.Key == boardTile) ||
                                  validCrossChecks.Any(c => c.Key == boardTile &&
                                                       c.Value.Any(x => x.Letter == partialWord[i]))))
                            {
                                validPrefix = false;
                                break;
                            }
                        }

                        //If they are valid, call LeftPart again, which will call ExtendRight for all possible right moves and eventually try a new left part
                        if (validPrefix)
                        {
                            LeftPart(partialWord + label[0], limit - 1, anchor, boardArray, validCrossChecks, validMovesList, boardIsHorizontal, boardBeforeMove);
                        }
                        RackOfCurrentPlayer.AddToRack(tileToWorkWith);
                        boardArray[anchor[0], anchor[1] - 1].CharTile = null;

                        //Resets the left part once done
                        for (int i = 0; i < partialWord.Length; i++)
                        {
                            boardArray[anchor[0], anchor[1] - partialWord.Length - 1 + i].CharTile = null;
                        }
                    }
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Builds all possible right parts for given anchor
        /// </summary>
        /// <param name="partialWord">Word preceding right part</param>
        /// <param name="anchor">Tile from which to build up right part</param>
        /// <param name="boardArray">Board to work with</param>
        /// <param name="validCrossChecks">List of valid cross checks for all empty tiles</param>
        /// <param name="validMovesList">List of valid moves that gets updated</param>
        /// <param name="boardIsHorizontal">Board is untransposed or transposed</param>
        /// <param name="boardBeforeMove">Board before any moves were generated</param>
        /// <param name="tileExtendedWith">Tile that was used to extend the right part</param>
        public void ExtendRight(string partialWord, int[] anchor, BoardTile[,] boardArray, Dictionary <BoardTile,
                                                                                                       List <CharTile> > validCrossChecks, HashSet <GeneratedMove> validMovesList, bool boardIsHorizontal, BoardTile[,] boardBeforeMove,
                                CharTile tileExtendedWith = null)
        {
            //If no tile is present..
            if (boardArray[anchor[0], anchor[1]].CharTile == null)
            {
                //If word up until current tile is valid..
                if (Helper.CheckWordValidity(Dawg, partialWord))
                {
                    //If the move (word, row and column indexes) has not been added already..
                    if (!Moves.Any(m => m.Word.Equals(partialWord)))
                    {
                        //Adds generated move to list of valid moves
                        Dictionary <BoardTile, CharTile> tilesUsed = new Dictionary <BoardTile, CharTile>();

                        //Adds tiles that were used in move
                        for (int i = 0; i < partialWord.Length; i++)
                        {
                            var letter    = partialWord[i];
                            var boardTile = boardArray[anchor[0], anchor[1] - partialWord.Length + i];

                            if (boardTile.CharTile != null)
                            {
                                tilesUsed.Add(boardTile, boardTile.CharTile);
                            }
                            else
                            {
                                tilesUsed.Add(boardTile, tileExtendedWith);
                            }
                        }
                        validMovesList.Add(new GeneratedMove(boardIsHorizontal, anchor[1] - partialWord.Length, anchor[1] - 1, anchor, tilesUsed, boardBeforeMove, RackOfCurrentPlayer));
                    }
                }

                //GEts all letters that can follow current right part word
                HashSet <string> labelsOfDawgEdges = new HashSet <string>(new DawgEdgeEqualityComparer());
                var wordsWithCommonPreffix         = Dawg.MatchPrefix(partialWord);
                foreach (var word in wordsWithCommonPreffix)
                {
                    labelsOfDawgEdges.Add(word.Key.Substring(partialWord.Length));
                }
                foreach (var label in labelsOfDawgEdges)
                {
                    //If the valid letter is in our rack and can be played on the board tile, places it and extends right again
                    //with the newly filled board tile used as anchor if board limit is not reached
                    if (label == "")
                    {
                        continue;
                    }
                    if (RackOfCurrentPlayer.CheckIfTileIsInRack(label[0], true) &&
                        (!validCrossChecks.Any(c => c.Key == boardArray[anchor[0], anchor[1]]) ||
                         validCrossChecks.Any(c => c.Key == boardArray[anchor[0], anchor[1]] && c.Value.Any(x => x.Letter == label[0]))))
                    {
                        CharTile tileToWorkWith = null;
                        if (RackOfCurrentPlayer.CheckIfTileIsInRack(label[0], false))
                        {
                            tileToWorkWith = Dictionary.CharTiles.Where(c => c.Letter == label[0] && c.Score != 0).FirstOrDefault();
                        }
                        else
                        {
                            tileToWorkWith = Dictionary.CharTiles.Where(c => c.Letter == '*').FirstOrDefault();
                        }
                        RackOfCurrentPlayer.SubstractFromRack(tileToWorkWith);
                        boardArray[anchor[0], anchor[1]].CharTile = Dictionary.CharTiles.Where(c => c.Letter == label[0] && c.Score == tileToWorkWith.Score).FirstOrDefault();
                        if (anchor[1] < boardArray.GetLength(1) - 1)
                        {
                            ExtendRight(partialWord + label[0], new int[] { anchor[0], anchor[1] + 1 }, boardArray, validCrossChecks, validMovesList, boardIsHorizontal, boardBeforeMove, tileToWorkWith);
                        }

                        //Otherwise places the tile on the last empty square of the board, checks if its valid and doesn't attempt to extend anymore
                        else
                        {
                            var finalWord = partialWord + boardArray[anchor[0], anchor[1]].CharTile.Letter;
                            if (Helper.CheckWordValidity(Dawg, finalWord))
                            {
                                if (!Moves.Any(m => m.Word.Equals(finalWord)))
                                {
                                    Dictionary <BoardTile, CharTile> tilesUsed = new Dictionary <BoardTile, CharTile>();
                                    for (int i = 0; i < finalWord.Length; i++)
                                    {
                                        var letter    = finalWord[i];
                                        var boardTile = boardArray[anchor[0], anchor[1] - finalWord.Length + 1 + i];
                                        if (boardTile.CharTile != null)
                                        {
                                            tilesUsed.Add(boardTile, boardTile.CharTile);
                                        }
                                        else
                                        {
                                            tilesUsed.Add(boardTile, tileToWorkWith);
                                        }
                                    }
                                    validMovesList.Add(new GeneratedMove(boardIsHorizontal, anchor[1] - finalWord.Length + 1, anchor[1], anchor, tilesUsed, boardBeforeMove, RackOfCurrentPlayer));
                                }
                            }
                        }
                        RackOfCurrentPlayer.AddToRack(tileToWorkWith);
                        boardArray[anchor[0], anchor[1]].CharTile = null;
                    }
                }
            }
            //Otherwise if the current tile is already taken and not empty, used letter from board tile to build to the right again
            else
            {
                var tile = boardArray[anchor[0], anchor[1]].CharTile;
                HashSet <string> labelsOfDawgEdges = new HashSet <string>(new DawgEdgeEqualityComparer());
                var wordsWithCommonPreffix         = Dawg.MatchPrefix(partialWord + tile.Letter);
                foreach (var word in wordsWithCommonPreffix)
                {
                    labelsOfDawgEdges.Add(word.Key.Substring((partialWord + tile.Letter).Length));
                }

                //Extends right if any letters can follow the current right part
                if (labelsOfDawgEdges.Any() && anchor[1] < boardArray.GetLength(1) - 1)
                {
                    ExtendRight(partialWord + tile.Letter, new int[] { anchor[0], anchor[1] + 1 }, boardArray, validCrossChecks, validMovesList, boardIsHorizontal, boardBeforeMove, tile);
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Builds and returns possible moves for the current board
        /// The same algorithm is used for a untransposed(horizontal) and transposed (vertical) board.
        /// This limits the problem to one dimension - with the given rack, anchors and cross checks,
        /// build all possible words on this row.
        /// </summary>
        /// <param name="boardIsHorizontal"></param>
        /// <param name="giveOneResult"></param>
        /// <returns></returns>
        public HashSet <GeneratedMove> GetValidMoves(bool boardIsHorizontal, bool giveOneResult = false)
        {
            var retriesPerAnchor = 2;
            var boardArray       = boardIsHorizontal ? BoardArray : TransposedBoardArray;

            //Recreates board before any moves are generated
            var boardBeforeMove = new BoardTile[boardArray.GetLength(0), boardArray.GetLength(1)];

            for (int i = 0; i < boardArray.GetLength(0); i++)
            {
                for (int j = 0; j < boardArray.GetLength(1); j++)
                {
                    boardBeforeMove[i, j] = new BoardTile
                    {
                        BoardLocationX  = boardArray[i, j].BoardLocationX,
                        BoardLocationY  = boardArray[i, j].BoardLocationY,
                        BoardTileType   = boardArray[i, j].BoardTileType,
                        CharTile        = boardArray[i, j].CharTile,
                        Board           = boardArray[i, j].Board,
                        BoardID         = boardArray[i, j].BoardID,
                        BoardTileTypeID = boardArray[i, j].BoardTileTypeID,
                        CharTileID      = boardArray[i, j].CharTileID,
                        ID       = boardArray[i, j].ID,
                        IsFilled = boardArray[i, j].IsFilled
                    };
                }
            }
            var listOfValidAnchorCoordinates       = boardIsHorizontal ? ListOfValidAnchorCoordinatesOnUntransposedBoard : ListOfValidAnchorCoordinatesOnTransposedBoard;
            var validCrossChecks                   = boardIsHorizontal ? ValidUntransposedCrossChecks : ValidTransposedCrossChecks;
            HashSet <GeneratedMove> validMovesList = new HashSet <GeneratedMove>(new GeneratedMoveEqualityComparer());

            //Shuffles anchors to add unpredictability to move generation
            listOfValidAnchorCoordinates.Shuffle();
            var originalTimeLimit       = TimeLimit;
            var retriesForCurrentAnchor = retriesPerAnchor;

            //Starts stopwatch. Move generation stops if time limit has been reached after a recursive call has returned to its original state.
            Stopwatch.Start();
            //listOfValidAnchorCoordinates = new List<int[]>();
            //listOfValidAnchorCoordinates.Add(new int[] { 12, 7 });
            //listOfValidAnchorCoordinates.Add(new int[] { 12, 8 });
            //listOfValidAnchorCoordinates.Add(new int[] { 12, 9 });

            //Tries to attach words to all anchors on the board
            for (int i = 0; i < listOfValidAnchorCoordinates.Count; i++)
            {
                if (retriesForCurrentAnchor == 0)
                {
                    retriesForCurrentAnchor = retriesPerAnchor;
                    continue;
                }
                var anchor = listOfValidAnchorCoordinates[i];

                //Checks if any letter can be played in tile. If not, target next anchor
                var crossCheckEntry = validCrossChecks.Where(c => c.Key == boardArray[anchor[0], anchor[1]]).FirstOrDefault().Value;
                if (crossCheckEntry != null && crossCheckEntry.Count == 0)
                {
                    continue;
                }
                var limit            = 0;
                var columnIndex      = anchor[1];
                var wordBeforeAnchor = "";

                //Builds up word before anchor if it exists, otherwise builds a limit to tiles we can place before anchor
                while (columnIndex > 0)
                {
                    if (boardArray[anchor[0], columnIndex - 1].CharTile == null && !listOfValidAnchorCoordinates.Any(c => c[0] == anchor[0] && c[1] == columnIndex - 1))
                    {
                        limit++;
                        columnIndex--;
                    }
                    else if (boardArray[anchor[0], columnIndex - 1].CharTile != null)
                    {
                        while (columnIndex > 0 && boardArray[anchor[0], columnIndex - 1].CharTile != null)
                        {
                            wordBeforeAnchor = wordBeforeAnchor.Insert(0, boardArray[anchor[0], columnIndex - 1].CharTile.Letter.ToString());
                            columnIndex--;
                        }
                        break;
                    }
                    else if (boardArray[anchor[0], columnIndex - 1].CharTile == null)
                    {
                        break;
                    }
                }

                //If no letter can be placed before anchor, try to build right part after anchor
                if (limit == 0)
                {
                    ExtendRight(wordBeforeAnchor, anchor, boardArray, validCrossChecks, validMovesList, boardIsHorizontal, boardBeforeMove);
                }

                //Otherwise places a valid tile from rack
                else if (wordBeforeAnchor.Length == 0)
                {
                    foreach (var entry in Dictionary.CharTiles.Where(d => d.Score != 0))
                    {
                        if (RackOfCurrentPlayer.CheckIfTileIsInRack(entry.Letter, true) &&
                            (!validCrossChecks.Any(c => c.Key == boardArray[anchor[0], anchor[1]]) ||
                             validCrossChecks.Any(c => c.Key == boardArray[anchor[0], anchor[1]] &&
                                                  c.Value.Any(x => x.Letter == entry.Letter))) &&
                            anchor[1] < boardArray.GetLength(1) - 1)
                        {
                            var tile = RackOfCurrentPlayer.SubstractFromRack(entry.Letter);
                            boardArray[anchor[0], anchor[1]].CharTile = tile;

                            //Once a tile is placed to the left of anchor, LeftPart is called which firstly tries to build all possible right parts for that anchor
                            //Once the extension to the right is complete, goes back to try and build a new left part
                            LeftPart(tile.Letter.ToString(), limit, new int[] { anchor[0], anchor[1] + 1 }, boardArray, validCrossChecks, validMovesList, boardIsHorizontal, boardBeforeMove);
                            RackOfCurrentPlayer.AddToRack(tile);
                            boardArray[anchor[0], anchor[1]].CharTile = null;
                            if (Helper.CheckIfTimeLimitIsReached(Stopwatch, TimeLimit) && validMovesList.Count > 0)
                            {
                                Stopwatch.Reset();
                                return(validMovesList);
                            }
                        }
                    }
                }
                if (giveOneResult)
                {
                    return(validMovesList);
                }
                //If timer is reached and no moves are found for anchor, increase time limit and try again for that anchor
                //If there are still no moves after two retries, move to different anchor
                if (Helper.CheckIfTimeLimitIsReached(Stopwatch, TimeLimit))
                {
                    if (validMovesList.Count == 0)
                    {
                        if (retriesForCurrentAnchor != 0)
                        {
                            TimeLimit += 1;
                            retriesForCurrentAnchor--;
                            i--;
                            Stopwatch.Restart();
                        }
                    }
                    else
                    {
                        TimeLimit = originalTimeLimit;
                        Stopwatch.Reset();
                        return(validMovesList);
                    }
                }
            }
            return(validMovesList);
        }