// Copy constructor // m_WordPositionsList contains the same references as tho copied layout // m_Squares contains new copies of copied layout squares // m_WordsList is a new copy of original list internal WordPositionLayout(WordPositionLayout copy) { m_WordPositionList.AddRange(copy.m_WordPositionList); using var e = copy.m_Squares.GetEnumerator(); while (e.MoveNext()) { m_Squares.Add(e.Current.Key, new Square(e.Current.Value)); } }
/// <summary>Core placement function, adds a list of words to current layout</summary> /// <param name="wordsToAddList">List of words to place</param> /// <param name="withLayoutBackup">If true, current layout is backed up, and restored if placement of all words failed</param> /// <returns>Returns a list of WordPosition for placed words in case of success, or false if placement failed, current layout is preserved in this case</returns> public IEnumerable <WordPosition> PlaceWordsList(IEnumerable <string> wordsToAddList, bool withLayoutBackup) { if (wordsToAddList == null) { throw new ArgumentNullException(nameof(wordsToAddList)); } // Keep a copy of current layout to restore if placement fails at some point WordPositionLayout backupLayout = null; if (withLayoutBackup) { backupLayout = new WordPositionLayout(Layout); } string checkMessage = CheckWordsList(wordsToAddList); if (!string.IsNullOrEmpty(checkMessage)) { throw new BonzaException(checkMessage); } List <string> shuffledList = new List <string>(wordsToAddList).Shuffle(); List <WordPosition> placedWordPositionList = new List <WordPosition>(); while (shuffledList.Count > 0) { List <string> placedWords = new List <string>(); foreach (string word in shuffledList) { WordPosition wordPosition = PlaceWord(word); if (wordPosition != null) { placedWords.Add(word); placedWordPositionList.Add(wordPosition); //Debug.WriteLine($"Placed {placedWordPositionList.Count}/{wordsToAddList.Count}: {wordPosition.Word}"); } } // If at the end of this loop no canonizedWord has been placed, we have a problem... if (placedWords.Count == 0) { if (withLayoutBackup) { Layout = backupLayout; // Restore initial layout } return(null); } // On the other hand, if pass was successful, remove all placed words and go for a new pass foreach (string placedWord in placedWords) { shuffledList.Remove(placedWord); } } return(placedWordPositionList); }
/// <summary>Shuffle words after reinitializing layout.</summary> /// <returns>Returns true if new placement is successful, or false if placement failed (current layout is preserved in this case)</returns> public bool PlaceWordsAgain() { // AddWordsList keeps a backup of Layout and restore it if placement failed... // but since we call it after reinitializing the layout, it won't work for us WordPositionLayout backupLayout = new WordPositionLayout(Layout); var wordsList = Layout.WordPositionList.Select(wordPosition => wordPosition.OriginalWord).ToList(); NewLayout(); if (PlaceWordsList(wordsList, false) == null) { Layout = backupLayout; return(false); } return(true); }
/// <summary>Complete reinitialization of layout, WordPosition and Words</summary> public void NewLayout() { Layout = new WordPositionLayout(); }
private IList <WordPosition> FindWordPossiblePlacements(string originalWord, PlaceWordOptimization optimization) { string canonizedWord = CanonizeWord(originalWord); // If it's the first canonizedWord of the layout, chose random orientation and place it at position (0, 0) if (Layout.WordPositionList.Count == 0) { WordPosition wordPosition = new WordPosition(canonizedWord, originalWord, new PositionOrientation(0, 0, rnd.NextDouble() > 0.5)); return(new List <WordPosition> { wordPosition }); } // Get current layout since we'll prefer placements that minimize layout extension to keep words grouped BoundingRectangle r = Layout.Bounds; int surface = ComputeAdjustedSurface(r.Max.Column - r.Min.Column + 1, r.Max.Row - r.Min.Row + 1); // Find first all positions where the canonizedWord can be added to current layout; List <WordPosition> possibleWordPositions = new List <WordPosition>(); List <WordPosition> possibleWordPositionsBelowThreshold = new List <WordPosition>(); int minSurface = int.MaxValue; foreach (WordPosition wordPosition in Layout.WordPositionList) { foreach (WordPosition placedWordPosition in TryPlace(canonizedWord, originalWord, wordPosition)) { BoundingRectangle newR = WordPositionLayout.ExtendBounds(r, placedWordPosition); if (newR.Equals(r)) { if (optimization == PlaceWordOptimization.Aggressive) { return new List <WordPosition> { placedWordPosition } } ; if (optimization == PlaceWordOptimization.High) { possibleWordPositionsBelowThreshold.Add(placedWordPosition); } } int newSurface = ComputeAdjustedSurface(newR.Max.Column - newR.Min.Column + 1, newR.Max.Row - newR.Min.Row + 1); possibleWordPositions.Add(placedWordPosition); switch (optimization) { case PlaceWordOptimization.Aggressive: case PlaceWordOptimization.High: if (possibleWordPositions.Count > 0 && minSurface > newSurface) { possibleWordPositions.RemoveAt(0); minSurface = newSurface; } if (possibleWordPositions.Count == 0) { possibleWordPositions.Add(placedWordPosition); } break; case PlaceWordOptimization.Standard: if (newSurface < surface * 1.15) { possibleWordPositionsBelowThreshold.Add(placedWordPosition); } possibleWordPositions.Add(placedWordPosition); break; default: possibleWordPositions.Add(placedWordPosition); break; } } } if (possibleWordPositionsBelowThreshold.Count > 0) { return(possibleWordPositionsBelowThreshold); } return(possibleWordPositions); }