public static (int, int) TranslatePosition(int row, int column, OrdinalDirection ordinalDirection) { return(ordinalDirection switch { OrdinalDirection.North => (row - 1, column), OrdinalDirection.NorthEast => (row - 1, column + 1), OrdinalDirection.East => (row, column + 1), OrdinalDirection.SouthEast => (row + 1, column + 1), OrdinalDirection.South => (row + 1, column), OrdinalDirection.SouthWest => (row + 1, column - 1), OrdinalDirection.West => (row, column - 1), OrdinalDirection.NorthWest => (row - 1, column - 1), _ => throw new ArgumentException("Invalid OrdinalDirection enum value"), });
/// <summary> /// Try to generate a random wordsearch from the given words. If this runs into a situation where there is no valid cells /// to put a word, then it will restart. It will do this for the number of times given in the <see cref="attemptLimit"/> before it gives up. /// </summary> /// <param name="words">Words to put in the wordsearch</param> /// <param name="wordsearch">The produced wordsearch</param> /// <param name="attemptLimit">The number of attempts allowed before giving up</param> /// <returns>Whether the wordsearch was completed successfully within the number of attempts allowed</returns> public static bool TryGenerateRandom(IEnumerable <string> words, out Wordsearch wordsearch, int attemptLimit = 1000) { wordsearch = new Wordsearch { // Process the strings into words Words = words.Select(i => i.Trim()) .Where(i => i.Length != 0) .Select(i => new Word() { Text = i }) .ToList() }; // Confirm that maximum word length is not exceeded if (wordsearch.Words.Where(i => i.Text.RemoveWhitespace().Length > 20).Any()) { return(false); } Random random = new Random(); // Limit the attempt number. With many words (expecially long ones) it may struggle to fit them int attempt = 0; bool complete = false; while (attempt < attemptLimit && !complete) // max attempts will take around 1 second { attempt++; // Generate an empty matrix CustomMatrix matrix = new CustomMatrix(20, 20); // if the end of this loop is reached without encountering the break // then the wordsearch generation is complete complete = true; foreach (Word word in wordsearch.Words) { var validCells = new List <RowAndCol>(); // Get a random ordinal direction OrdinalDirection direction = (OrdinalDirection)random.Next(0, 8); string wordWithoutWhitespace = word.Text.RemoveWhitespace(); int wlen = wordWithoutWhitespace.Length; // Initialise the cells for consideration as the whole matrix int rowstart = 0; int rowend = matrix.Rows; int colstart = 0; int colend = matrix.Columns; // Remove cells around the edge from consideration based on the length and direction of the word switch (direction) { case OrdinalDirection.North: rowstart = rowstart + wlen - 1; break; case OrdinalDirection.NorthEast: rowstart = rowstart + wlen - 1; colend = colend - wlen + 1; break; case OrdinalDirection.East: colend = colend - wlen + 1; break; case OrdinalDirection.SouthEast: rowend = rowend - wlen + 1; colend = colend - wlen + 1; break; case OrdinalDirection.South: rowend = rowend - wlen + 1; break; case OrdinalDirection.SouthWest: rowend = rowend - wlen + 1; colstart = colstart + wlen - 1; break; case OrdinalDirection.West: colstart = colstart + wlen - 1; break; case OrdinalDirection.NorthWest: rowstart = rowstart + wlen - 1; colstart = colstart + wlen - 1; break; } // using the adjusted strart and end row and column, check if that each cell is valid for (int i = rowstart; i < rowend; i++) { for (int j = colstart; j < colend; j++) { int row = i; int col = j; bool occupied = false; // using the words direction, for each letter in the word, check if the cell it would be in is availiable // the cell is also valid if they are the same letter for (int c = 0; c < wlen; c++) { if (matrix.PositionIsOccupied(row, col) && wordWithoutWhitespace[c] != matrix.GetValueAt(row, col)) { occupied = true; break; } ; (row, col) = CustomMatrix.TranslatePosition(row, col, direction); } if (!occupied) { validCells.Add(new RowAndCol(i, j)); } } } // if there is no valid position found for the word // break from the loop with an incomplete marker // which will begin a new attempt if (validCells.Count < 1) { complete = false; break; } #region idea prob never use // pick a random segment to avoid bias towards parallel close together words /* * int randomSegmentRowStart = random.Next(0, 15); * int randomSegmentColStart = random.Next(0, 15); * * List<int[]> segmentValidCells = validCells.Where * (i => * i[0] >= randomSegmentRowStart && * i[0] < randomSegmentRowStart + 5 && * i[0] >= randomSegmentColStart && * i[0] < randomSegmentColStart + 5 * ).ToList(); * * if (segmentValidCells.Count > 0) * { * validCells = segmentValidCells; * } */ #endregion // Choose a random cell from all of the valid options RowAndCol cell = validCells[random.Next(0, validCells.Count)]; word.Direction = direction; word.StartRow = cell.Row; word.StartColumn = cell.Col; // Input the word into the matrix at the chosen position int rw = word.StartRow; int cl = word.StartColumn; foreach (char chr in word.Text.RemoveWhitespace()) { matrix.SetValueAt(rw, cl, chr); (rw, cl) = CustomMatrix.TranslatePosition(rw, cl, word.Direction); } } } // if complete == false here that means the attempt limit was reached without success if (!complete) { return(false); } return(true); }