newPuzzle method
Generates a new word find (word search) puzzle.
Example:
final List<String> wl = ['hello', 'world', 'foo', 'bar', 'baz', 'dart'];
final WSSettings ws = WSSettings();
final WordSearchSafety WordSearchSafety = WordSearchSafety();
final WSNewPuzzle newPuzzle = WordSearchSafety.newPuzzle(wl, ws);
print(newPuzzle.puzzle);
Implementation
WSNewPuzzle newPuzzle(
/// The words to be placed in the puzzle
List<String> words,
/// The puzzle setting object
WSSettings settings,
) {
// New instance of the output data
WSNewPuzzle output = WSNewPuzzle();
if (words.isEmpty) {
output.errors!.add('Zero words provided');
return output;
}
List<List<String>>? puzzle;
int attempts = 0;
int gridGrowths = 0;
// copy and sort the words by length, inserting words into the puzzle
// from longest to shortest works out the best
final List<String> wordList = []
..addAll(words)
..sort((String a, String b) {
return b.length - a.length;
});
// max word length
//final maxWordLength = wordList.first.length;
// create new options instance of the settings
final WSSettings options = WSSettings(
width: settings.width,
height: settings.height,
orientations: settings.orientations,
fillBlanks: settings.fillBlanks ?? true,
maxAttempts: settings.maxAttempts,
maxGridGrowth: settings.maxGridGrowth,
preferOverlap: settings.preferOverlap,
allowExtraBlanks: settings.allowExtraBlanks,
);
while (puzzle == null) {
while (puzzle == null && attempts++ < options.maxAttempts) {
_wordsNotPlaced = [];
puzzle = _fillPuzzle(wordList, options);
}
if (puzzle == null) {
// Increase the size of the grid
gridGrowths += 1;
// No more grid growths allowed
if (gridGrowths > options.maxGridGrowth) {
output.errors!.add(
'No valid ${options.width}x${options.height} grid found and not allowed to grow more',
);
return output;
}
print('Trying a bigger grid after ${attempts - 1}');
options.height += 1;
options.width += 1;
attempts = 0;
}
}
// fill in empty spaces with random letters
if (options.fillBlanks != null) {
List<String> lettersToAdd = [];
int fillingBlanksCount = 0;
int extraLettersCount = 0;
double gridFillPercent = 0;
Function extraLetterGenerator;
// Custom fill blanks function
if (options.fillBlanks is Function) {
extraLetterGenerator = options.fillBlanks;
} else if (options.fillBlanks is String) {
// Use the simple array pop mechanism for the input string
lettersToAdd.addAll(options.fillBlanks.toLowerCase().split(''));
extraLetterGenerator = () {
if (lettersToAdd.isNotEmpty) {
return lettersToAdd.removeLast();
}
fillingBlanksCount += 1;
return '';
};
} else {
// Use the default random letters
extraLetterGenerator = () {
return WSLetters[Random().nextInt(WSLetters.length)];
};
}
// Fill all the blanks in the puzzle
extraLettersCount = _fillBlanks(puzzle, extraLetterGenerator);
// Warn the user that some letters were not used
if (lettersToAdd.isNotEmpty) {
output.warnings!
.add('Some extra letters provided were not used: ${lettersToAdd}');
}
// Extra letters not filled in the grid if allow blanks is false
if (fillingBlanksCount > 0 && !options.allowExtraBlanks) {
output.errors!.add(
'${fillingBlanksCount} extra letters were missing to fill the grid');
return output;
}
gridFillPercent =
100 * (1 - extraLettersCount / (options.width * options.height));
print('Blanks filled with ${extraLettersCount} random letters');
print('Final grid is filled at ${gridFillPercent.toStringAsFixed(0)}%');
}
output.puzzle = puzzle;
output.wordsNotPlaced = _wordsNotPlaced;
return output;
}