/* * 1. Choosing which pattern to fill (i.e. which variable to solve for). * 2. Picking a suitable word (i.e. which value to select). * 3. Choosing where to backtrack to when we reach an impasse. */ public IEnumerable <ICrossBoard> Generate() { var history = new List <int>(); var historyTrans = new List <List <CrossTransformation> >(); var matchingWords = new List <string>(); var usedWords = new HashSet <string>(); CrossPattern patt = _board.GetMostConstrainedPattern(_dict); while (true) { DoCommands(); if (patt != null) { matchingWords.Clear(); _dict.GetMatch(patt.Pattern, matchingWords); var succTrans = new List <CrossTransformation>(); foreach (string t in matchingWords) { if (usedWords.Contains(t)) { continue; } var trans = patt.TryFill(t, t.AsSpan(), _dict); if (trans != null) { succTrans.Add(trans); trans.Pattern = patt; } } if (succTrans.Count > 0) { succTrans.Sort(new CrossTransformationComparer()); var trans = succTrans[0]; usedWords.Add(trans.Word); trans.Transform(patt); historyTrans.Add(succTrans); history.Add(0); patt = _board.GetMostConstrainedPattern(_dict); } else { patt = BackTrack(history, historyTrans, usedWords); if (patt == null) { yield break; } } } else { yield return(_board.Clone()); patt = BackTrack(history, historyTrans, usedWords); if (patt == null) { yield break; } } } }
public IEnumerable <ICrossBoard> GetAllPossiblePlacements(ICrossDictionary dictionary) { var puzzle = NormalizePuzzle(_puzzle).AsMemory(); var board = _board.Clone(); board.Preprocess(dictionary); var patterns = new List <CrossPattern>(); for (int i = 0; i < board.GetPatternCount(); i++) { patterns.Add(board.GetCrossPattern(i)); } patterns.Sort((a, b) => - 1 * a.Length.CompareTo(b.Length)); if (patterns.Count == 0) { yield break; } var restPuzzleLength = puzzle.Length; var stack = new List <int>(); var appliedTransformations = new List <CrossTransformation>(); int idx = 0; while (true) { continueOuterLoop: for (; idx < patterns.Count; idx++) { var pattern = patterns[idx]; if (restPuzzleLength < pattern.Length) { continue; } if (restPuzzleLength - pattern.Length == 1) { continue; } var trans = pattern.TryFillPuzzle(puzzle.Slice(puzzle.Length - restPuzzleLength, pattern.Length).Span, dictionary); if (trans != null) { trans.Transform(pattern); if (restPuzzleLength == pattern.Length) { var cloned = board.Clone(); trans.Undo(pattern); yield return(cloned); continue; } stack.Add(idx + 1); trans.Pattern = pattern; appliedTransformations.Add(trans); restPuzzleLength -= pattern.Length; idx = 0; goto continueOuterLoop; } } if (stack.Count == 0) { break; } idx = stack.Back(); stack.Pop(); var appTr = appliedTransformations.Back(); appliedTransformations.Pop(); appTr.Undo(appTr.Pattern); restPuzzleLength += appTr.Pattern.Length; } }
public IEnumerable <ICrossBoard> GetAllPossiblePlacements(ICrossDictionary dictionary) { var puzzle = NormalizePuzzle(_puzzle); // used to be .AsSpan() but throwing exception: Instance of type 'ReadOnlySpan<char>' cannot be used inside a nested function, query expression, iterator block or async method var board = _board.Clone(); board.Preprocess(dictionary); var patterns = new List <CrossPattern>(); for (int i = 0; i < board.GetPatternCount(); i++) { patterns.Add(board.GetCrossPattern(i)); } // sort by word length patterns.Sort((a, b) => - 1 * a.Length.CompareTo(b.Length)); if (patterns.Count == 0) { yield break; } var restPuzzleLength = puzzle.Length; var stack = new List <int>(); var appliedTransformations = new List <CrossTransformation>(); int idx = 0; while (true) { continueOuterLoop: for (; idx < patterns.Count; idx++) { var pattern = patterns[idx]; if (restPuzzleLength < pattern.Length) { continue; } if (restPuzzleLength - pattern.Length == 1) { break; // PIN: this was a continue statement - which seems like a bug } var trans = pattern.TryFillPuzzle(puzzle.AsSpan(puzzle.Length - restPuzzleLength, pattern.Length), dictionary); if (trans != null) { trans.Transform(pattern); if (restPuzzleLength == pattern.Length) { // ensure only one pattern is marked as a puzzle patterns.All(c => { c.IsPuzzle = false; return(true); }); // set the current pattern as puzzle pattern.IsPuzzle = true; var cloned = (ICrossBoard)board.Clone(); // clone before we revert the puzzle pattern trans.Undo(pattern); yield return(cloned); continue; } stack.Add(idx + 1); trans.Pattern = pattern; appliedTransformations.Add(trans); restPuzzleLength -= pattern.Length; idx = 0; goto continueOuterLoop; } } if (stack.Count == 0) { break; } idx = stack.Back(); stack.Pop(); var appTr = appliedTransformations.Back(); appliedTransformations.Pop(); appTr.Undo(appTr.Pattern); restPuzzleLength += appTr.Pattern.Length; } yield break; }
/* * 1. Choosing which pattern to fill (i.e. which variable to solve for). * 2. Picking a suitable word (i.e. which value to select). * 3. Choosing where to backtrack to when we reach an impasse. */ public IEnumerable <ICrossBoard> Generate() { var history = new List <int>(); var historyTrans = new List <List <CrossTransformation> >(); var matchingWords = new List <string>(); var usedWords = new HashSet <string>(); CrossPattern pattern = _board.GetMostConstrainedPattern(_dict); Random rnd = new Random(); while (true) { DoCommands(); if (pattern != null) { matchingWords.Clear(); _dict.GetMatch(pattern.Pattern, matchingWords); var succTrans = new List <CrossTransformation>(); foreach (string t in matchingWords) { if (usedWords.Count > 0 && usedWords.Contains(t)) { continue; } // checking if there exist words in the dictionary matching each of the adjacent patterns var trans = pattern.TryFill(t, t.AsSpan(), _dict); if (trans != null) { succTrans.Add(trans); trans.Pattern = pattern; } } if (succTrans.Count > 0) { succTrans.Sort(new CrossTransformationComparer()); // using the successfull transform with most ?! // always use the first index (i.e. the one with the most possible adjacent hits) var trans = succTrans[0]; history.Add(0); // don't always use the "best" match to randomize the crossword better // var lowestIndexToUse = 0; // var highestIndexToUse = succTrans.Count > 10 ? 10 : succTrans.Count; // int index = rnd.Next(lowestIndexToUse, highestIndexToUse); // var trans = succTrans[index]; // history.Add(index); usedWords.Add(trans.Word); trans.Transform(pattern); historyTrans.Add(succTrans); pattern = _board.GetMostConstrainedPattern(_dict); } else { pattern = BackTrack(history, historyTrans, usedWords); if (pattern == null) { yield break; } } } else { yield return(_board.Clone()); pattern = BackTrack(history, historyTrans, usedWords); if (pattern == null) { yield break; } } } }