docs: function (words, settings) {
                  var wordList, puzzle, attempts = 0, opts = settings || {};
                  // copy and sort the words by length, inserting words into the puzzle
                  // from longest to shortest works out the best
                  wordList = words.slice(0).sort(function (a, b) {
                      return (a.length < b.length) ? 1 : 0;
                  });
                  // initialize the options
                  var options = {
                      height: opts.height || wordList[0].length,
                      width: opts.width || wordList[0].length,
                      orientations: opts.orientations || allOrientations,
                      fillBlanks: opts.fillBlanks !== undefined ? opts.fillBlanks : true,
                      maxAttempts: opts.maxAttempts || 3,
                      preferOverlap: opts.preferOverlap !== undefined ? opts.preferOverlap : true
                  };
                  // add the words to the puzzle
                  // since puzzles are random, attempt to create a valid one up to
                  // maxAttempts and then increase the puzzle size and try again
                  while (!puzzle) {
                      while (!puzzle && attempts++ < options.maxAttempts) {
                          puzzle = fillPuzzle(wordList, options);
                      }
                      if (!puzzle) {
                          options.height++;
                          options.width++;
                          attempts = 0;
                      }
                  }
                  // fill in empty spaces with random letters
                  if (options.fillBlanks) {
                      this.fillBlanks(puzzle, options);
                  }
                  return puzzle;
              }