//Check for tiles which disqualify square (1,2), otherwise for a connection which qualifies it (3) static bool CheckSquare(Grid grid, int top, int bottom, int column, ref int contactRow) { if ((!grid.GetSquare(Math.Max(top - 1, 0), column).IsBlank && top > 0)) // 1)Check square above for letter { return false;} for (var row = top; row < BoardSize; row++) // 2)Check for continuous column of letters to bottom (Includes bottom row) { if (grid.GetSquare(row, column).IsBlank) { break; } if (!grid.GetSquare(row, column).IsBlank && row == BoardSize - 1) { return false; } } for (var row = top; row < bottom + 1; row++) // 3)Check letters-by-3 box (squares in column and adjacent columns on either side) { if (grid.GetSquare(row, Math.Max(column - 1, 0)).Letter.ToString(CultureInfo.InvariantCulture) + grid.GetSquare(row, column).Letter + grid.GetSquare(row, Math.Min(BoardSize - 1, column + 1)).Letter != " ") { contactRow = row - top + 1; return true; } } if (bottom != BoardSize - 1 && !grid.GetSquare(bottom + 1, column).IsBlank) // 3)Check square below bottom-most reach { contactRow = bottom - top + 2; return true; } return false; }
public Mould(Grid grid, int letterCount, int row, int column, int boardSize) { _mould = new List<char>(); for (var r = row; r < boardSize; r++) { _mould.Add(grid.GetSquare(r, column).Letter); } var blanks = _mould.Count(i => i == Constants.Blank); if (blanks > letterCount) { for (var r = _mould.Count - 1; r > -1; r--) { if (blanks == letterCount) { break; } if (_mould[r] == Constants.Blank) { _mould.RemoveAt(r); blanks--; continue; } _mould.RemoveAt(r); } } }
public Grid RotateCounterClockwise() { var rotatedGrid = new Grid(_grid.GetLength(0)); for (var r = 0; r < _grid.GetLength(0); r++) { for (var c = 0; c < _grid.GetLength(1); c++) { rotatedGrid.SetSquare(r, c, GetSquare(_grid.GetLength(1) - 1 - c, r)); } } return rotatedGrid; }
public Fill(int boardSize) { BoardFilled = BoardEmpty; var letters = new List<string> { //012345678901234 " ", //0 " ", //1 " ", //2 " ", //3 " ", //4 " ", //5 "choleric ", //6 " a ", //7 M " bottle ", //8 " ", //9 " ", //10 " ", //11 " ", //12 " ", //13 " ", //14 //012345678901234 }; for (var i = 0; i < boardSize; i++) { var chrs = letters[i].ToCharArray().ToList(); for (var j = 0; j < boardSize; j++) { BoardFilled.GetSquare(i, j).Row = i; BoardFilled.GetSquare(i, j).Column = j; BoardFilled.GetSquare(i, j).Letter = chrs[j]; BoardFilled.GetSquare(i, j).Score = Program.LetterValues(BoardFilled.GetSquare(i, j).Letter.ToString(CultureInfo.InvariantCulture).ToCharArray().ToList())[0]; //TODO: !! BoardFilled.GetSquare(i, j).LetterBonus = BoardFilled.GetSquare(i, j).LetterBonus == 0 ? 1 : BoardFilled.GetSquare(i, j).LetterBonus; BoardFilled.GetSquare(i, j).WordBonus = BoardFilled.GetSquare(i, j).WordBonus == 0 ? 1 : BoardFilled.GetSquare(i, j).WordBonus; } } }
static Grid GetGrid(Grid board) { var letterFrequencyTotal = new List<int>(); //Cumulative letter frequency for board. Max # of each tile allowed. for (var row = 0; row < BoardSize; row++) { for (; ; ) { var letterFrequencyRow = new List<int>(letterFrequencyTotal); Console.Write("Enter Row {0} letters: ", row + 1); var rowLetters = (Console.ReadLine().ToLower().ToList()); if (rowLetters.Count < BoardSize) //Fill rest of row with blanks { for (var j = rowLetters.Count; j < BoardSize; j++) { rowLetters.Add(Constants.Blank); } } letterFrequencyRow = LetterFreq(rowLetters, letterFrequencyRow, BoardSize); //Get letter frequencies of current row if (!LettersCheck(rowLetters, letterFrequencyRow, BoardSize)) { continue; } //Check validity of entered characters letterFrequencyTotal = letterFrequencyRow; for (var column = 0; column < BoardSize; column++) //Add row letters to board { board.GetSquare(row, column).Letter = rowLetters[column]; } break; } } return board; }
static void WriteBoard(Grid board) { Console.WriteLine(" 123456789012345"); for (var i = 0; i < BoardSize; i++) { Console.Write("|"); for (var j = 0; j < BoardSize - 1; j++) { Console.Write((board.GetSquare(i, j)).Letter.ToString(CultureInfo.InvariantCulture).ToUpper()); } Console.WriteLine((board.GetSquare(i, BoardSize - 1)).Letter.ToString(CultureInfo.InvariantCulture).ToUpper() + "| {0}", i + 1); } }
static List<Word> WordSolver(Grid grid, List<char> letters, int row, int column, int contactRow, Direction direction) { var mould = new Mould(grid, letters.Count, row, column, BoardSize); //Find 'mould' (line of empty and filled squares down or across) for current square, eg. " C D" var words = new List<Word>(); var lowerIndex = Dictionary.Lengths[Math.Max(contactRow - 2, 0)]; //Check only dictionary words of lengths in applicable range var upperIndex = Dictionary.Lengths[mould.Count - 1] + 1; for (var i = lowerIndex; i < upperIndex; i++) { var dictionaryWord = Dictionary.Dict[i]; if (dictionaryWord.Length > mould.Count) { continue; } var word = dictionaryWord.ToCharArray().ToList(); var blankLetters = new List<char>(); if (!mould.MouldFits(word) || !mould.WordFits(contactRow, letters, word, ref blankLetters) || !PerpendicularWords(grid, word, row, column, direction)) { continue; } //Blank tiles have 0 score. Thus when used for a letter that occurs more than once multiple words must be recored, e.g LOSSE#, LOS#ES and LO#SES var wordBlanks = mould.GenerateBlankVariations(word, blankLetters); foreach (var w in wordBlanks) //Add each word to final list { words.Add(new Word { Row = row, Column = column, Direction = direction, Letters = word, Wrd = string.Concat(word), Scores = LetterValues(w), }); } } for (var w = 0; w < words.Count; w++) //Score each word { words[w] = WordScorer(words[w], grid); if (direction == Direction.Across) //Reset coordinates of across words due to rotation { var temp = words[w].Row; words[w].Row = BoardSize - 1 - words[w].Column; words[w].Column = temp; } } return words; }
static Word WordScorer(Word word, Grid grid) { var totalScore = 0; var totalWordMultiplier = 1; //Total word multiplier for base word score var baseScore = 0; //Basic score of actual word before final word multiplication var letterCount = 0; //Check if all letters are used for 35 bonus for (var r = 0; r < word.Letters.Count; r++) { var letterMultiplier = grid.GetSquare(r + word.Row, word.Column).IsBlank ? grid.GetSquare(r + word.Row, word.Column).LetterBonus : 1; //Determine if square letter muliplier is applicable var wordMultiplier = grid.GetSquare(r + word.Row, word.Column).IsBlank ? grid.GetSquare(r + word.Row, word.Column).WordBonus : 1; //Determine if square word multiplier is applicable totalWordMultiplier *= wordMultiplier; //Add square word multiplier to total word multiplier baseScore += word.Scores[r] * letterMultiplier; //Add current letter score to base word score if (!grid.GetSquare(r + word.Row, word.Column).IsBlank) { continue; } //Existing letter - perpendicular word doesn't score letterCount++; var crossWord = word.Scores[r] * letterMultiplier; //Letter value times letter bonus var count = 0; //Count of perpendicular tiles. Needed due to potential blank tiles with 0 score. for (var c = word.Column - 1; c > -1; c--) //Add letter scores for perp word to left { if (grid.GetSquare(r + word.Row, c).IsBlank) { break; } crossWord += grid.GetSquare(r + word.Row, c).Score; count++; } for (var c = word.Column + 1; c < BoardSize; c++) //Add letter scores for perp word to right { if (grid.GetSquare(r + word.Row, c).IsBlank) { break; } crossWord += grid.GetSquare(r + word.Row, c).Score; count++; } if (count == 0) { continue; } //No perpendicular word totalScore += crossWord * wordMultiplier; //Add each perp word score to total score } totalScore += baseScore * totalWordMultiplier; //Add base word score to total score if (letterCount == 7) { totalScore += 35; } word.WordScore = totalScore; return word; }
static List<Word> SquareRunThrough(Grid board, List<char> letters, Direction direction) { var words = new List<Word>(); var limitsLtRtUpLw = Limits(board); //Limits let code ignore checking large sections of board for (var row = 0; row < limitsLtRtUpLw[3]; row++) { for (var column = limitsLtRtUpLw[0]; column < limitsLtRtUpLw[1] + 1; column++) { var contactRow = BoardSize; if (!CheckSquare(board, row, Math.Min(row + letters.Count - 1, BoardSize - 1), column, ref contactRow)) { continue; } words.AddRange(WordSolver(board, letters, row, column, contactRow, direction)); } } var wordsOrdered = from element in words //Order across words by square (down words ordered incidentally) orderby element.Row select element; return wordsOrdered.ToList(); }
//Check any perpendicular words are legal static bool PerpendicularWords(Grid grid, List<char> word, int row, int column, Direction direction) { for (var r = 0; r < word.Count; r++) //Check perp word at each letter { var perpWord = word[r].ToString(CultureInfo.InvariantCulture); for (var c = column - 1; c > -1; c--) //Construct perp word with letters to left { if (grid.GetSquare(r + row, c).IsBlank) { break; } perpWord = grid.GetSquare(r + row, c).Letter + perpWord; } for (var c = column + 1; c < BoardSize; c++) //Add letters to right { if (grid.GetSquare(r + row, c).IsBlank) { break; } perpWord += grid.GetSquare(r + row, c).Letter.ToString(CultureInfo.InvariantCulture); } if (perpWord.Length == 1) { continue; } //I.E. no adjacent letters var dictionary = direction == Direction.Down ? new List<string>(Dictionary.Dict) : new List<string>(Dictionary.DictRev); //Rotation for across words means perp words are reversed. Use dictionary with reverse words. var lowerIndex = Dictionary.Lengths[perpWord.Length - 2]; //Indices to only check dictionary words of exact length var upperIndex = Dictionary.Lengths[perpWord.Length - 1]; for (var w = lowerIndex; w < upperIndex + 1; w++ ) { if (perpWord == dictionary[w]) { break; } if (w == upperIndex && perpWord != dictionary[w] ) { return false; } } } return true; }
static List<int> Limits(Grid grid) { var lts = new List<int> { BoardSize - 1, 0, BoardSize - 1, 0 }; //Left-Column/Right-Column/Upper-Row/Lower-Row bounds for (var r = 0; r < BoardSize; r++) { for (var c = 0; c < BoardSize; c++) { var chr = Regex.IsMatch(grid.GetSquare(r,c).Letter.ToString(CultureInfo.InvariantCulture), @"[a-z]"); if (chr && c <= lts[0]){lts[0] = Math.Max(c - 1, 0);} if (chr && c >= lts[1]){lts[1] = Math.Min(c + 1, BoardSize - 1);} if (chr && r <= lts[2]){lts[2] = Math.Max(r - 1, 0);} if (chr && r >= lts[3]){lts[3] = Math.Min(r + 1, BoardSize - 1);} } } return lts; }