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,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 |
+} |