Revert "Removing byte order mark in file using for f in `ls`; do awk '{if(NR==1)sub(/^\xef\xbb\xbf/,"");print}' $f > $f; done"
Dev Ghai

Dev Ghai commited on 2013-09-23 06:37:32
Showing 12 changed files, with 978 additions and 0 deletions.


This reverts commit 9bf2983895bb10fa5d7f651511133f9018c7e980.
It deleted the complete files!
... ...
@@ -0,0 +1,166 @@
1
+//#define DEVELOPING
2
+
3
+using System;
4
+using System.Collections.Generic;
5
+using System.IO;
6
+using System.Diagnostics;
7
+
8
+namespace Boggle
9
+{
10
+    public class Boggle
11
+    {
12
+        static void PrintWords(HashSet<WordOnBoard> words)
13
+        {
14
+            int score = 0;
15
+            foreach (WordOnBoard word in words)
16
+            {
17
+                Console.Write("{0} [{1}], ", word.Word, word.Score);
18
+                score += word.Score;
19
+            }
20
+            Console.WriteLine("\n\nTotal score for this board: {0}", score);
21
+        }
22
+        static void Main(string[] args)
23
+        {
24
+            Stopwatch st = new Stopwatch();
25
+            BoggleList bl = new BoggleList();
26
+            Console.WriteLine("Current directory: {0}", Directory.GetCurrentDirectory());
27
+#if !DEVELOPING
28
+            string input;
29
+            bool isError = false;
30
+#endif
31
+            int boardSideLength = 3;
32
+            int minWordLength = 4;
33
+
34
+            char wordListInput = 'Z';
35
+            BoggleLists listToUseForLookup;
36
+            
37
+#if !DEVELOPING
38
+            do
39
+            {
40
+                isError = false;
41
+                Console.WriteLine("Please enter the word list that you wish to use [Z for Zingarelli, T for TWS or standard scrabble list]: ");
42
+                input = Console.ReadLine();
43
+                if (!char.TryParse(input, out wordListInput))
44
+                {
45
+                    Console.WriteLine("Invalid Input. Couldn't parse character");
46
+                    isError = true;
47
+                }
48
+                wordListInput = char.ToUpper(wordListInput);
49
+                if (wordListInput != 'T' && wordListInput != 'Z')
50
+                {
51
+                    Console.WriteLine("Please enter only T or Z.");
52
+                    isError = true;
53
+                }
54
+            } while (isError);
55
+#endif
56
+            switch (wordListInput)
57
+            {
58
+                case 'T':
59
+                    listToUseForLookup = BoggleLists.TWS;
60
+                    break;
61
+
62
+                case 'Z':
63
+                    listToUseForLookup = BoggleLists.ZINGARELLI;
64
+                    break;
65
+                default:
66
+                    throw new Exception("Unexpected!!");
67
+            }
68
+
69
+#if !DEVELOPING
70
+            do
71
+            {
72
+                isError = false;
73
+                Console.WriteLine("Please enter number of tiles that make up one side of the board: ");
74
+                input = Console.ReadLine();
75
+                if (!int.TryParse(input, out boardSideLength))
76
+                {
77
+                    Console.WriteLine("Invalid Input.");
78
+                    isError = true;
79
+                }
80
+            } while (isError);
81
+
82
+            do
83
+            {
84
+                isError = false;
85
+                Console.WriteLine("Please enter number of ALPHABETS that should be there in shortest word: ");
86
+                input = Console.ReadLine();
87
+                if (!int.TryParse(input, out minWordLength))
88
+                {
89
+                    Console.WriteLine("Invalid Input.");
90
+                    isError = true;
91
+                }
92
+            } while (isError);
93
+#endif
94
+            st.Start();
95
+            bl.LoadList(listToUseForLookup, boardSideLength, minWordLength, Directory.GetCurrentDirectory());
96
+            st.Stop();
97
+            Console.WriteLine("Loaded list in {0} ms.", st.ElapsedMilliseconds);
98
+
99
+            string boardString = "qwertyuio";
100
+#if !DEVELOPING
101
+            do
102
+            {
103
+                isError = false;
104
+                Console.WriteLine("Please enter the characters on board in row first fashion ({0} expected characters): ", boardSideLength*boardSideLength);
105
+                boardString = Console.ReadLine();
106
+                if (boardString.Length != boardSideLength*boardSideLength)
107
+                {
108
+                    Console.WriteLine("Invalid Input. Q will be translated to QU internally. Please enter only Q if you want to enter the QU tile.");
109
+                    isError = true;
110
+                }
111
+            } while (isError);
112
+#endif
113
+            boardString = boardString.ToUpper();
114
+
115
+            BoggleBoard board = new BoggleBoard(boardSideLength, boardString);
116
+            board.Print();
117
+            BoggleSolver solution = new BoggleSolver(board, bl, minWordLength);
118
+            st.Start();
119
+            //HashSet<WordOnBoard> wordsUnThreaded = solution.GetWordsOnBoard(false);
120
+            //st.Stop();
121
+            //PrintWords(wordsUnThreaded);
122
+
123
+            //Console.WriteLine("Got solution in {0} ms. Number of words (unthreaded): {1}", st.ElapsedMilliseconds, wordsUnThreaded.Count);
124
+            //Console.WriteLine("Press any key to run in threaded mode.");
125
+            //Console.ReadKey();
126
+            st.Start();
127
+            HashSet<WordOnBoard> wordsThreaded = solution.GetWordsOnBoard(true);
128
+            //wordsThreaded.ExceptWith(wordsUnThreaded);
129
+            st.Stop();
130
+            st.Reset();
131
+            PrintWords(wordsThreaded);
132
+            Console.WriteLine("Got solution in {0} ms. Number of words (threaded): {1}.", st.ElapsedMilliseconds, wordsThreaded.Count);
133
+
134
+            
135
+
136
+
137
+            //if (wordsUnThreaded.Count > wordsThreaded.Count)
138
+            //{
139
+            //    wordsUnThreaded.ExceptWith(wordsThreaded);
140
+            //    Console.WriteLine("Words in unthreaded collection that are not in threaded collection:");
141
+            //    PrintWords(wordsUnThreaded);
142
+            //}
143
+            //if (wordsThreaded.Count > wordsUnThreaded.Count)
144
+            //{
145
+            //    wordsThreaded.ExceptWith(wordsUnThreaded);
146
+            //    Console.WriteLine("Words in threaded collection that are not in unthreaded collection:");
147
+            //    PrintWords(wordsThreaded);
148
+            //}
149
+
150
+            //wordsUnThreaded.RemoveWhere(WordsWithLowestScore);
151
+            //Console.WriteLine("***Removed words with score 1.***");
152
+            //PrintWords(wordsUnThreaded);
153
+            Console.ReadKey();
154
+        }
155
+
156
+        private static bool WordsWithLowestScore(WordOnBoard word)
157
+        {
158
+            if (word.Score == Constants.MIN_SCORE)
159
+            {
160
+                return true;
161
+            }
162
+
163
+            return false;
164
+        }
165
+    }
166
+}
... ...
@@ -0,0 +1,71 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+  <PropertyGroup>
4
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5
+    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
6
+    <ProductVersion>8.0.30703</ProductVersion>
7
+    <SchemaVersion>2.0</SchemaVersion>
8
+    <ProjectGuid>{AB752D5B-37CC-4029-8621-FA970CDD5E2A}</ProjectGuid>
9
+    <OutputType>Exe</OutputType>
10
+    <AppDesignerFolder>Properties</AppDesignerFolder>
11
+    <RootNamespace>Boggle</RootNamespace>
12
+    <AssemblyName>Boggle</AssemblyName>
13
+    <FileAlignment>512</FileAlignment>
14
+  </PropertyGroup>
15
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
16
+    <PlatformTarget>x86</PlatformTarget>
17
+    <DebugSymbols>true</DebugSymbols>
18
+    <DebugType>full</DebugType>
19
+    <Optimize>false</Optimize>
20
+    <OutputPath>bin\Debug\</OutputPath>
21
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
22
+    <ErrorReport>prompt</ErrorReport>
23
+    <WarningLevel>4</WarningLevel>
24
+  </PropertyGroup>
25
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
26
+    <PlatformTarget>x86</PlatformTarget>
27
+    <DebugType>pdbonly</DebugType>
28
+    <Optimize>true</Optimize>
29
+    <OutputPath>bin\Release\</OutputPath>
30
+    <DefineConstants>TRACE</DefineConstants>
31
+    <ErrorReport>prompt</ErrorReport>
32
+    <WarningLevel>4</WarningLevel>
33
+  </PropertyGroup>
34
+  <ItemGroup>
35
+    <Reference Include="System" />
36
+    <Reference Include="System.Core" />
37
+    <Reference Include="System.Xml.Linq" />
38
+    <Reference Include="System.Data.DataSetExtensions" />
39
+    <Reference Include="Microsoft.CSharp" />
40
+    <Reference Include="System.Data" />
41
+    <Reference Include="System.Xml" />
42
+  </ItemGroup>
43
+  <ItemGroup>
44
+    <Compile Include="Boggle.cs" />
45
+    <Compile Include="BoggleBoard.cs" />
46
+    <Compile Include="BoggleList.cs" />
47
+    <Compile Include="BoggleSolver.cs" />
48
+    <Compile Include="Constants.cs" />
49
+    <Compile Include="ListWord.cs" />
50
+    <Compile Include="Properties\AssemblyInfo.cs" />
51
+    <Compile Include="Tile.cs" />
52
+    <Compile Include="WordComparer.cs" />
53
+    <Compile Include="WordOnBoard.cs" />
54
+  </ItemGroup>
55
+  <ItemGroup>
56
+    <Content Include="wordlists\TWL06.txt">
57
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
58
+    </Content>
59
+    <Content Include="wordlists\zingarelli2005.txt">
60
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
61
+    </Content>
62
+  </ItemGroup>
63
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
64
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
65
+       Other similar extension points exist, see Microsoft.Common.targets.
66
+  <Target Name="BeforeBuild">
67
+  </Target>
68
+  <Target Name="AfterBuild">
69
+  </Target>
70
+  -->
71
+</Project>
0 72
\ No newline at end of file
... ...
@@ -0,0 +1,6 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+  <PropertyGroup>
4
+    <ProjectView>ProjectFiles</ProjectView>
5
+  </PropertyGroup>
6
+</Project>
0 7
\ No newline at end of file
... ...
@@ -0,0 +1,20 @@
1
+
2
+Microsoft Visual Studio Solution File, Format Version 11.00
3
+# Visual Studio 2010
4
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Boggle", "Boggle.csproj", "{AB752D5B-37CC-4029-8621-FA970CDD5E2A}"
5
+EndProject
6
+Global
7
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
8
+		Debug|x86 = Debug|x86
9
+		Release|x86 = Release|x86
10
+	EndGlobalSection
11
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
12
+		{AB752D5B-37CC-4029-8621-FA970CDD5E2A}.Debug|x86.ActiveCfg = Debug|x86
13
+		{AB752D5B-37CC-4029-8621-FA970CDD5E2A}.Debug|x86.Build.0 = Debug|x86
14
+		{AB752D5B-37CC-4029-8621-FA970CDD5E2A}.Release|x86.ActiveCfg = Release|x86
15
+		{AB752D5B-37CC-4029-8621-FA970CDD5E2A}.Release|x86.Build.0 = Release|x86
16
+	EndGlobalSection
17
+	GlobalSection(MonoDevelopProperties) = preSolution
18
+		StartupItem = Boggle.csproj
19
+	EndGlobalSection
20
+EndGlobal
... ...
@@ -0,0 +1,128 @@
1
+using System;
2
+using System.Text;
3
+
4
+namespace Boggle
5
+{
6
+    public class BoggleBoard
7
+    {
8
+        private readonly Tile[,] _Tiles;
9
+        /// <summary>
10
+        /// A 2D array containing all the tles in the board.
11
+        /// This is a read only propert
12
+        /// </summary>
13
+        public Tile[,] Tiles
14
+        {
15
+            get
16
+            {
17
+                return _Tiles;
18
+            }
19
+        }
20
+
21
+        private readonly int _SideLength;
22
+        /// <summary>
23
+        /// Number of tiles in one side of the square board.
24
+        /// This is a read only property.
25
+        /// </summary>
26
+        public int SideLength
27
+        {
28
+            get
29
+            {
30
+                return _SideLength;
31
+            }
32
+        }
33
+
34
+        /// <summary>
35
+        /// BoggleBoard constructor.
36
+        /// </summary>
37
+        /// <param name="sideLength">Number of TILES that make up the side of the board.</param>
38
+        /// <param name="letters">String containing alphabets in row first order.</param>
39
+        public BoggleBoard(int sideLength, string letters)
40
+        {
41
+            if (sideLength < Constants.MIN_SIDE_LENGTH)
42
+            {
43
+                throw new ArgumentException(string.Format("Length of side of board has to be greater than or equal to {0}", Constants.MIN_SIDE_LENGTH));
44
+            }
45
+
46
+            int numTiles = sideLength * sideLength;
47
+
48
+            if (letters.Length != numTiles)
49
+            {
50
+                throw new ArgumentException(string.Format("Board's initialization string contains {0} characters. {1} characters were expected. 'Q' tranlates to 'QU' internally and only 'Q' needs to be mentioned.", letters.Length, numTiles));
51
+            }
52
+            _SideLength = sideLength;
53
+            _Tiles = new Tile[_SideLength, _SideLength];
54
+
55
+            //row-first addition = manner in which humans read.
56
+            string alphabet;
57
+            for (int i = 0; i < sideLength; i++)
58
+            {
59
+                for (int j = 0; j < sideLength; j++)
60
+                {
61
+                    alphabet = letters.Substring(i * sideLength + j, 1);
62
+                    //the special condition
63
+                    if (alphabet == "Q")
64
+                        alphabet += "U";
65
+                    _Tiles[j, i] = new Tile(j, i, alphabet);
66
+                }
67
+            }
68
+        }
69
+
70
+        /// <summary>
71
+        /// Copy constructor.
72
+        /// </summary>
73
+        /// <param name="orignalBoard">Board to copy.</param>
74
+        public BoggleBoard(BoggleBoard orignalBoard)
75
+        {
76
+            _SideLength = orignalBoard._SideLength;
77
+            _Tiles = new Tile[_SideLength, _SideLength];
78
+            for (int i = 0; i < _SideLength; i++)
79
+            {
80
+                for (int j = 0; j < _SideLength; j++)
81
+                {
82
+                    _Tiles[i, j] = new Tile(i, j, orignalBoard.Tiles[i, j].Alphabet);
83
+                }
84
+            }
85
+        }
86
+
87
+        /// <summary>
88
+        /// Indexer to allow more natural access to tiles in the board.
89
+        /// </summary>
90
+        /// <param name="x">X co-ordinate of the tile in the board.</param>
91
+        /// <param name="y">Y co-ordinate of the tile in the board.</param>
92
+        /// <returns>Tile at x, y co-ordinate.</returns>
93
+        public Tile this[int x, int y]
94
+        {
95
+            get
96
+            {
97
+                return _Tiles[x, y];
98
+            }
99
+        }
100
+
101
+        /// <summary>
102
+        /// Prints out the board on console.
103
+        /// </summary>
104
+        public void Print()
105
+        {
106
+            int horizontalLength = _SideLength * 4 + 1;
107
+            StringBuilder line = new StringBuilder(horizontalLength);
108
+            string horizontalSeparator = string.Empty;
109
+            for (int i = 0; i < horizontalLength; i++)
110
+            {
111
+                horizontalSeparator += "\u2550";
112
+            }
113
+
114
+            for (int i = 0; i < _SideLength; i++)
115
+            {
116
+                Console.WriteLine(horizontalSeparator);
117
+                line.Clear();
118
+                line.Append("\u2551");
119
+                for (int j = 0; j < _SideLength; j++)
120
+                {
121
+                    line.AppendFormat(" {0} \u2551", _Tiles[j, i].Alphabet);
122
+                }
123
+                Console.WriteLine(line);
124
+            }
125
+            Console.WriteLine(horizontalSeparator);
126
+        }
127
+    }
128
+}
... ...
@@ -0,0 +1,226 @@
1
+using System;
2
+using System.Collections.Generic;
3
+using System.IO;
4
+
5
+namespace Boggle
6
+{
7
+    /// <summary>
8
+    /// Supported lists.
9
+    /// </summary>
10
+    public enum BoggleLists
11
+    {
12
+        /// <summary>
13
+        /// Scrabble dictionary for USA, Canada and Thailand
14
+        /// </summary>
15
+        TWS,
16
+
17
+        /// <summary>
18
+        /// Scrabble dictionary used world wide except in USA, Canada and Thailand.
19
+        /// </summary>
20
+        //SOWPODS,
21
+
22
+        /// <summary>
23
+        /// A huge dictionary found on official Internet Scrabble Club's website (http://www.isc.ro/en/commands/lists.html)
24
+        /// </summary>
25
+        ZINGARELLI
26
+    }
27
+
28
+    public class BoggleList
29
+    {
30
+        /// <summary>
31
+        /// Contains a hybrid tree that has been optimized for recursive search.
32
+        /// </summary>
33
+        private Dictionary<string, ListWord> _WordList = new Dictionary<string, ListWord>(27); //top level will store only 3 letter words or substrings. Declaring proper size makes insertion operation O(1)
34
+        public Dictionary<string, ListWord> Wordlist
35
+        {
36
+            get
37
+            {
38
+                return _WordList;
39
+            }
40
+        }
41
+
42
+        /// <summary>
43
+        /// Contains a list of dictionaries that have been loaded, indexed by enum that contains dictionaries we support.
44
+        /// </summary>
45
+        private Dictionary<BoggleLists, Dictionary<string, ListWord>> _LoadedDictionaries;
46
+
47
+        /// <summary>
48
+        /// Determines if we have an absolute path
49
+        /// </summary>
50
+        /// <param name="filePath">File that contains the list SORTED list of words. Path can be relative or absolute.</param>
51
+        /// <returns>The absolute file path that we can use to access it.</returns>
52
+        private string GetAbsoluteFilePath(string filePath)
53
+        {
54
+            //check if the absolute file path exists.            
55
+            if (!File.Exists(filePath))
56
+            {
57
+                //try checking if this is a relative file path and prepend current directory's path.
58
+                filePath = string.Format("{0}\\{1}", Directory.GetCurrentDirectory(), filePath);
59
+                if (!File.Exists(filePath))
60
+                {
61
+                    filePath = string.Empty;
62
+                }
63
+            }
64
+
65
+            return filePath;
66
+        }
67
+
68
+        /// <summary>
69
+        /// Curates the list for use by this solver. Basically culls out words that do not meet the criteria.
70
+        /// Criteria: Min_word_length < word_length < Max_word_length. Max_word_length = sideLength^2 + 1.
71
+        /// </summary>
72
+        /// <param name="filePath"> Path to file that contains the SORTED list of words. 
73
+        ///                         It can be relative to current execution directory or absolute.</param>
74
+        /// <param name="boardSideLength">Length of the side of board in number of tiles.</param>
75
+        /// <param name="minWordLength">Number of alphabets that can be there in the shortest word on the board.</param>
76
+        private void CurateList(string filePath, int boardSideLength, int minWordLength)
77
+        {
78
+            //filePath = GetAbsoluteFilePath(filePath);
79
+            //true tells stream reader to auto detect encoding of the file.
80
+            StreamReader rawList = new StreamReader(filePath, true);
81
+            //Create a new file. Do not append anything.
82
+            string curatedFilePath = string.Format("{0}.{1}.{2}", filePath, Constants.CURATED_FILE_SUFFIX, minWordLength.ToString());
83
+            StreamWriter curatedList = new StreamWriter(curatedFilePath, false);
84
+
85
+            string word;
86
+            //write out the words whose number of characters are between the shortest and largest provided length.
87
+            while ((word = rawList.ReadLine()) != null)
88
+            {
89
+                if (word.Length >= minWordLength && word.Length <= boardSideLength * boardSideLength + 1)
90
+                {
91
+                    curatedList.WriteLine(word);
92
+                }
93
+            }
94
+
95
+            curatedList.Flush();
96
+            curatedList.Close();
97
+            rawList.Close();
98
+        }
99
+
100
+        public BoggleList()
101
+        {
102
+            //Initialize the dictionary containing the lists.
103
+            _LoadedDictionaries = new Dictionary<BoggleLists, Dictionary<string, ListWord>>(Enum.GetNames(typeof(BoggleLists)).Length);
104
+        }
105
+
106
+        /// <summary>
107
+        /// Loads the list depending on the selected dictionary.
108
+        /// </summary>
109
+        /// <param name="dict">Type of dictionary to load.</param>
110
+        /// <param name="boardSideLength"></param>
111
+        /// <param name="minWordLength"></param>
112
+        /// <returns>A hybird tree containing the words with each level containing one alphabet (or QU)</returns>
113
+        public Dictionary<string, ListWord> LoadList(BoggleLists dict, int boardSideLength, int minWordLength, string listRootPath)
114
+        {
115
+            if (_LoadedDictionaries.ContainsKey(dict))
116
+                return _LoadedDictionaries[dict];
117
+
118
+            string filePath = string.Empty;
119
+
120
+            if (NeedsCuration(dict, minWordLength, listRootPath, ref filePath))
121
+                CurateList(filePath, boardSideLength, minWordLength);
122
+
123
+            _WordList = new Dictionary<string, ListWord>(27);
124
+
125
+            //true tells stream reader to auto detect encoding of the file.
126
+            StreamReader rawList = new StreamReader(filePath, true);
127
+            string word, substring;
128
+            ListWord prevListWord;
129
+            int startIndex;
130
+
131
+            while ((word = rawList.ReadLine()) != null)
132
+            {
133
+                //First deal with the head node and then deal with following letters.
134
+                startIndex = 0;
135
+                word = word.ToUpper();
136
+                substring = word.Substring(0, 1);
137
+                //if the string starts with Q and there is a 'U' following it, then put QU on tile and increment starting index accordingly.
138
+                if (substring == "Q" && word[1] == 'U')
139
+                {
140
+                    substring += "U";
141
+                    startIndex++;
142
+                }
143
+
144
+                //If there is no space allocated for this letter at root level, allocate some.
145
+                if (!_WordList.ContainsKey(substring))
146
+                {
147
+                    _WordList.Add(substring, new ListWord());
148
+                }
149
+                prevListWord = _WordList[substring];
150
+
151
+                //fit the word in the tree structure
152
+                for (int i = startIndex + 1; i < word.Length; i++)
153
+                {
154
+                    //We are dealing with one letter at each level
155
+                    substring = word.Substring(i, 1);
156
+                    //But if the letter is Q and there is a U following it, then treat this as one letter.
157
+                    if (substring == "Q" && i + 1 < word.Length && word[i + 1] == 'U')
158
+                    {
159
+                        substring += "U";
160
+                        //also increment the index as we have consumed the next letter.
161
+                        i++;
162
+                    }
163
+                    //Has this combination of letters appeared before?
164
+                    if (prevListWord.Next == null)
165
+                    {
166
+                        //if not, then make some space to store this combination
167
+                        prevListWord.Next = new Dictionary<string, ListWord>(27);
168
+                        //and store it.
169
+                        prevListWord.Next.Add(substring, new ListWord());
170
+                    }
171
+                    //if it has,
172
+                    else if (!prevListWord.Next.ContainsKey(substring))
173
+                    {
174
+                        //then just store it.
175
+                        prevListWord.Next.Add(substring, new ListWord());
176
+                    }
177
+
178
+                    //Update the dictionary to point to current substring's last character/entry in the tree.
179
+                    prevListWord = prevListWord.Next[substring];
180
+                }
181
+                //set the value of the leaf node to be true to point that we have completed the word.
182
+                prevListWord.IsWord = true;
183
+            }
184
+
185
+            //Store it in the collection 
186
+            _LoadedDictionaries.Add(dict, _WordList);
187
+            return _WordList;
188
+        }
189
+
190
+        /// <summary>
191
+        /// Checks to see if the proper file exists that contains the appropriate words.
192
+        /// </summary>
193
+        /// <param name="list">Type of list to check for.</param>
194
+        /// <param name="minWordLength">Minimum number of LETTERS the word should contain. </param>
195
+        /// <param name="filePath">Reference to path of file. If the list will need curation, it will store path to source list. 
196
+        /// If list is there, it will store the path to the curated list.</param>
197
+        /// <returns>Whether there is a need to run the curation procedure or not.</returns>
198
+        private bool NeedsCuration(BoggleLists list, int minWordLength, string wordlistContainer, ref string filePath)
199
+        {
200
+            bool needsCuration = false;
201
+            switch (list)
202
+            {
203
+                case BoggleLists.TWS:
204
+                    filePath = GetAbsoluteFilePath(string.Format("{0}/{1}.{2}.{3}", filePath, Constants.TWS_FILE_PATH, Constants.CURATED_FILE_SUFFIX, minWordLength.ToString()));
205
+                    if (filePath == string.Empty)
206
+                    {
207
+                        filePath = string.Format("{0}/{1}", wordlistContainer, Constants.TWS_FILE_PATH);
208
+                        needsCuration = true;
209
+                    }
210
+                    break;
211
+                case BoggleLists.ZINGARELLI:
212
+                    filePath = GetAbsoluteFilePath(string.Format("{0}/{1}.{2}.{3}", filePath, Constants.ZINGARELLI_FILE_PATH, Constants.CURATED_FILE_SUFFIX, minWordLength.ToString()));
213
+                    if (filePath == string.Empty)
214
+                    {
215
+                        filePath = string.Format("{0}/{1}", wordlistContainer, Constants.ZINGARELLI_FILE_PATH);
216
+                        needsCuration = true;
217
+                    }
218
+                    break;
219
+                default:
220
+                    throw new ArgumentException(string.Format("Support for {0} wordlist has not been coded yet :(", list));
221
+            }
222
+
223
+            return needsCuration;
224
+        }
225
+    }
226
+}
... ...
@@ -0,0 +1,206 @@
1
+using System;
2
+using System.Collections.Generic;
3
+using System.Text;
4
+using System.Threading;
5
+
6
+namespace Boggle
7
+{
8
+    public class BoggleSolver
9
+    {
10
+        BoggleList _WordList;
11
+        WordComparer _wordEqualityComparer;
12
+        BoggleBoard _Board;
13
+        int _MinWordLength;
14
+        HashSet<WordOnBoard> _wordsOnBoard;
15
+
16
+        /// <summary>
17
+        /// Constructor.
18
+        /// </summary>
19
+        /// <param name="board">Board on which we need to run the solution,</param>
20
+        /// <param name="words">List of valid words.</param>
21
+        /// <param name="minWordLength">Length of smallest word in number of tiles.</param>
22
+        public BoggleSolver(BoggleBoard board, BoggleList words, int minWordLength)
23
+        {
24
+            _Board = board;
25
+            _WordList = words;
26
+            _MinWordLength = minWordLength;
27
+            _wordEqualityComparer = new WordComparer();
28
+            _wordsOnBoard = new HashSet<WordOnBoard>(_wordEqualityComparer);
29
+        }
30
+
31
+        /// <summary>
32
+        /// Get the set of words existing on the board.
33
+        /// </summary>
34
+        /// <returns>Set of words on the board. We are using a Hashset because it automatically takes care of duplicate words.</returns>
35
+        public HashSet<WordOnBoard> GetWordsOnBoard(bool useThreads = false)
36
+        {
37
+            if (!useThreads)
38
+                return GetWordsOnBoardNoThreads();
39
+
40
+            return GetWordsOnBoardThreaded();
41
+        }
42
+
43
+        private ManualResetEvent[] doneEvents;
44
+
45
+        /// <summary>
46
+        /// Entry point for getting all possible words on board in threaded manner.
47
+        /// </summary>
48
+        /// <returns>Hashset of all words that can appear on the board.</returns>
49
+        private HashSet<WordOnBoard> GetWordsOnBoardThreaded()
50
+        {
51
+            doneEvents = new ManualResetEvent[_Board.SideLength * _Board.SideLength];
52
+            for (int i = 0; i < _Board.SideLength; i++)
53
+            {
54
+                for (int j = 0; j < _Board.SideLength; j++)
55
+                {
56
+                    doneEvents[i * _Board.SideLength + j] = new ManualResetEvent(false);
57
+
58
+                    ThreadPool.QueueUserWorkItem(CollectWordsFromPivotThreaded, _Board[i, j]);
59
+                }
60
+            }
61
+            WaitHandle.WaitAll(doneEvents);
62
+            return _wordsOnBoard;
63
+        }
64
+
65
+        private void CollectWordsFromPivotThreaded(Object obj)
66
+        {
67
+            Tile passedTile = (Tile)obj;
68
+            //Copy the board
69
+            BoggleBoard boardForThisThread = new BoggleBoard(this._Board);
70
+            //Initialize a list that will hold the tiles that will make up the word. Its max size will be square of side.
71
+            List<Tile> newWordList = new List<Tile>(boardForThisThread.SideLength * boardForThisThread.SideLength);
72
+            //Figure out the tile in the new board
73
+            Tile t = boardForThisThread[passedTile.X, passedTile.Y];
74
+            //Initialize this list with the tile that we will start finding words from.
75
+            newWordList.Add(t);
76
+            //Collect all the possible words from this tile. End result should be in the Hashset.
77
+            CollectWordsFromPivot(boardForThisThread, _WordList.Wordlist[t.Alphabet].Next, newWordList);
78
+            doneEvents[t.X * _Board.SideLength + t.Y].Set();
79
+        }
80
+
81
+        private HashSet<WordOnBoard> GetWordsOnBoardNoThreads()
82
+        {
83
+            for (int i = 0; i < _Board.SideLength; i++)
84
+            {
85
+                for (int j = 0; j < _Board.SideLength; j++)
86
+                {
87
+                    //Collect all the tiles that make up the word.
88
+                    List<Tile> newWordList = new List<Tile>(_Board.SideLength * _Board.SideLength);
89
+                    //Initialize this list with the tile that we will start finding words from.
90
+                    newWordList.Add(_Board[i, j]);
91
+                    //Collect all the possible words from this tile. End result should be in the Hashset.
92
+                    CollectWordsFromPivot(this._Board, _WordList.Wordlist[_Board[i, j].Alphabet].Next, newWordList);
93
+                    //Set everything visited to false so that next traversal has the proper state.
94
+                    ResetBoard();
95
+                }
96
+            }
97
+
98
+            return _wordsOnBoard;
99
+        }
100
+
101
+        /// <summary>
102
+        /// Sets IsVisited of every tile in the board to false.
103
+        /// </summary>
104
+        private void ResetBoard()
105
+        {
106
+            for (int i = 0; i < _Board.SideLength; i++)
107
+            {
108
+                for (int j = 0; j < _Board.SideLength; j++)
109
+                {
110
+                    _Board[i, j].IsVisited = false;
111
+                }
112
+            }
113
+
114
+        }
115
+
116
+        /// <summary>
117
+        /// Extracts the word from tiles and stores it in a synchronized fashion.
118
+        /// </summary>
119
+        /// <param name="wordTiles">Tiles making up the word.</param>
120
+        private void StoreWord(List<Tile> wordTiles)
121
+        {
122
+            StringBuilder builder = new StringBuilder(wordTiles[0].Alphabet);
123
+            for (int i = 1; i < wordTiles.Count; i++)
124
+            {
125
+                builder.Append(wordTiles[i].Alphabet);
126
+            }
127
+
128
+            string word = builder.ToString();
129
+            int score = Constants.MIN_SCORE;
130
+            if (word.Length <= _MinWordLength + 1)
131
+                score = Constants.MIN_SCORE;
132
+            else if (word.Length == _MinWordLength + 2)
133
+                score = Constants.MIN_SCORE + 1;
134
+            else if (word.Length == _MinWordLength + 3)
135
+                score = Constants.MIN_SCORE + 2;
136
+            else if (word.Length == _MinWordLength + 4)
137
+                score = Constants.MIN_SCORE + 4;
138
+            else 
139
+                score = Constants.MIN_SCORE + 10;
140
+
141
+            lock (_wordsOnBoard)
142
+            {
143
+                _wordsOnBoard.Add(new WordOnBoard(word, score));
144
+            }
145
+        }
146
+
147
+        /// <summary>
148
+        /// Recursive function to get all the possible words from a tile.
149
+        /// </summary>
150
+        /// <param name="t">Tile that needs to be analyzed for words.</param>
151
+        /// <param name="validNextSubstrings">A dictionary containing all letters that can come after the one contained in the tile.</param>
152
+        /// <param name="currentSubstring">List of tiles that make up all or part of the word.</param>
153
+        private void CollectWordsFromPivot(BoggleBoard board, Dictionary<string, ListWord> validNextSubstrings, List<Tile> currentSubstring)
154
+        {
155
+            Tile t = currentSubstring[currentSubstring.Count - 1];
156
+            t.IsVisited = true;
157
+
158
+            string validWord = string.Empty;
159
+
160
+            int adjPosX = -1, adjPosY = -1;
161
+            for (int x = -1; x <= 1; x++)
162
+            {
163
+                adjPosX = t.X + x;
164
+                if (adjPosX < 0 || adjPosX >= board.SideLength) //we don't want to hit anything outside the boundries of the board.
165
+                    continue;
166
+
167
+                for (int y = -1; y <= 1; y++)
168
+                {
169
+                    adjPosY = t.Y + y;
170
+                    if (adjPosY < 0 || adjPosY >= board.SideLength || (adjPosX == t.X && adjPosY == t.Y))    //in third condition, it will  point to itself and we dont want that
171
+                        continue;
172
+
173
+                    Tile adjacentTile = board[adjPosX, adjPosY];
174
+                    //no need to parse the tile if it has been already visited or there is no word possible with current string
175
+                    if (adjacentTile.IsVisited || !validNextSubstrings.ContainsKey(adjacentTile.Alphabet))
176
+                        continue;
177
+
178
+                    //either we now have a word or have a part of the word
179
+                    currentSubstring.Add(adjacentTile);
180
+
181
+                    //if we have the complete word that has minimum word length we require, add it to the hashet of words
182
+                    if (validNextSubstrings[adjacentTile.Alphabet].IsWord && currentSubstring.Count >= _MinWordLength)
183
+                        StoreWord(currentSubstring);
184
+
185
+                    //now we want to see if there is any good reason to proceed with passing control to the adjacent tile.
186
+                    Dictionary<string, ListWord> validNextSubstringsAdjTile = validNextSubstrings[adjacentTile.Alphabet].Next;
187
+
188
+                    //there will no more words to make after this... no need to recurse.
189
+                    if (validNextSubstringsAdjTile == null)
190
+                    {
191
+                        //remove the alphabet just added from the end of the string as it will not be hitting the appropriate statement later.
192
+                        currentSubstring.RemoveAt(currentSubstring.Count - 1);
193
+                        continue;
194
+                    }
195
+
196
+                    //collect words from the new pivot
197
+                    CollectWordsFromPivot(board, validNextSubstringsAdjTile, currentSubstring);
198
+
199
+                    //once used, remove the alphabet from the string and mark the tile as not visited. This is because this tile maybe involved in paths from other tiles.
200
+                    currentSubstring.RemoveAt(currentSubstring.Count - 1);
201
+                    adjacentTile.IsVisited = false;
202
+                }
203
+            }
204
+        }
205
+    }
206
+}
... ...
@@ -0,0 +1,13 @@
1
+namespace Boggle
2
+{
3
+    static class Constants
4
+    {
5
+        public const string CURATED_FILE_SUFFIX = "curated";
6
+        public const int MIN_SIDE_LENGTH = 3;
7
+        public const int MIN_WORD_LENGTH = 3;
8
+        public const int MIN_SCORE = 1;
9
+        public const string TWS_FILE_PATH = @"wordlists/TWL06.txt";
10
+        public const string ZINGARELLI_FILE_PATH = @"wordlists/zingarelli2005.txt";
11
+
12
+    }
13
+}
... ...
@@ -0,0 +1,29 @@
1
+using System.Collections.Generic;
2
+
3
+namespace Boggle
4
+{
5
+    public class ListWord
6
+    {
7
+        public bool IsWord;
8
+        public Dictionary<string, ListWord> Next;
9
+
10
+        public ListWord()
11
+        {
12
+            this.IsWord = false;
13
+            this.Next = null;
14
+        }
15
+
16
+        public ListWord(bool isWord)
17
+        {
18
+            this.IsWord = isWord;
19
+            this.Next = null;
20
+        }
21
+
22
+        public ListWord(bool isWord, Dictionary<string, ListWord> next)
23
+        {
24
+            this.IsWord = isWord;
25
+            this.Next = next;
26
+        }
27
+
28
+    }
29
+}
... ...
@@ -0,0 +1,70 @@
1
+namespace Boggle
2
+{
3
+    public class Tile
4
+    {
5
+        //declaring readonly as these values will never be modified once set and compiler has ways to optimize for this.
6
+        private readonly int _X;
7
+        /// <summary>
8
+        /// X co-ordinate of the tile in the board array.
9
+        /// This is a read only property.
10
+        /// </summary>
11
+        public int X
12
+        {
13
+            get
14
+            {
15
+                return _X;
16
+            }
17
+        }
18
+
19
+        /// <summary>
20
+        /// Y co-ordinate of the tile in the board array.
21
+        /// This is a read only property.
22
+        /// </summar
23
+        private readonly int _Y;
24
+        public int Y
25
+        {
26
+            get
27
+            {
28
+                return _Y;
29
+            }
30
+        }
31
+
32
+        /// <summary>
33
+        /// The alphabet contained in the tile. It can be any valid letter or 'QU'.
34
+        /// This is a read only property.
35
+        /// </summary>
36
+        private readonly string _Alphabet;
37
+        public string Alphabet
38
+        {
39
+            get
40
+            {
41
+                return _Alphabet;
42
+            }
43
+        }
44
+
45
+        /// <summary>
46
+        /// Tells whether this tile has been visited or not during recursion.
47
+        /// </summary>
48
+        public bool IsVisited { get; set; }
49
+
50
+        public Tile(int x, int y, string alphabet)
51
+        {
52
+            _X = x;
53
+            _Y = y;
54
+            _Alphabet = alphabet;
55
+            IsVisited = false;
56
+        }
57
+
58
+        /// <summary>
59
+        /// Copy constructor to use when using threads.
60
+        /// </summary>
61
+        /// <param name="t"></param>
62
+        public Tile(Tile t)
63
+        {
64
+            _X = t._X;
65
+            _Y = t._Y;
66
+            _Alphabet = t._Alphabet;
67
+            IsVisited = t.IsVisited;
68
+        }
69
+    }
70
+}
... ...
@@ -0,0 +1,28 @@
1
+using System.Collections.Generic;
2
+
3
+namespace Boggle
4
+{
5
+    /// <summary>
6
+    /// Equality comparer used for HashSet. No longer used as I switched to Dictionaries.
7
+    /// </summary>
8
+    public class WordComparer : EqualityComparer<WordOnBoard>
9
+    {
10
+        public override bool Equals(WordOnBoard x, WordOnBoard y)
11
+        {
12
+            return string.Compare(x.Word, y.Word, true) == 0;
13
+        }
14
+
15
+        public override int GetHashCode(WordOnBoard obj)
16
+        {
17
+            int hash = 0;
18
+            for (int i = 0; i < obj.Word.Length; i++)
19
+            {
20
+                hash += (int)obj.Word[i] * 10 ^ (obj.Word.Length - 1 - i);
21
+
22
+                //cat would result in 99 * 10^2 + 97 * 10^1 + 116 * 10^0 = 10986
23
+            }
24
+
25
+            return hash;
26
+        }
27
+    }
28
+}
... ...
@@ -0,0 +1,15 @@
1
+
2
+namespace Boggle
3
+{
4
+    public struct WordOnBoard
5
+    {
6
+        public string Word;
7
+        public int Score;
8
+
9
+        public WordOnBoard(string word, int score)
10
+        {
11
+            this.Word = word;
12
+            this.Score = score;
13
+        }
14
+    }
15
+}
0 16