using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; // Overview: // The algorithm works by finding the word with the best-worst possible response from guessing // it. If there are an infeasible amount of words to analyse a sensible amount are // checked starting with the longest and working towards the shortest. This condition // could be changed or even removed if you've got lots of time which would ensure you // ALWAYS find the best word. In several places it is optimised for speed. Disgarding // luck this method will always use the least amount of guesses possible. class ProgComp3 { // Data structure to hold the contents of the dictionary file public static List dictionary = new List(); static void Main(string[] args) { // Fill up the dictionary from the file specified for (TextReader dictionaryReader = new StreamReader(args[0]); dictionaryReader.Peek() >= 0; dictionary.Add(new Word(dictionaryReader.ReadLine()))) ; // Make a first guess Word guess = FindBestWorst(dictionary.Where(w => w.candidate).ToList()); Console.WriteLine(guess.word); // Keep guessing till you get a YES for (string response = Console.ReadLine(); response != "YES"; response = Console.ReadLine()) { UpdateDictionary(guess, int.Parse(response)); guess = FindBestWorst(dictionary.Where(w => w.candidate).ToList()); Console.WriteLine(guess.word); } //// ALTERNATE VERSION OF ABOVE LOOP FOR TESTING PURPOSES //// //for (string response = Player1.GenerateResponse(guess); response != "YES"; response = Player1.GenerateResponse(guess)) //{ // UpdateDictionary(guess, int.Parse(response)); // guess = FindBestWorst(dictionary.Where(w => w.candidate).ToList()); // Console.WriteLine(guess.word); //} } static Word FindBestWorst(List guessSet) { // Sort the dictionary by word length so that once the g.word.Length > cutOff // condition has been broken you can break out of the loop and do no further // comparisons guessSet.Sort((w1, w2) => w2.word.Length.CompareTo(w1.word.Length)); // Get hold of all Word's in the dictionary that are still candidates List answerSet = dictionary.Where(w => w.candidate).ToList(); // If theres only one word in the guessSet it MUST be the answer if (guessSet.Count == 1) return guessSet[0]; // If there's an infeasable amount of words to compare take as many of the // longest as will be processed in roughly under a minute. This condition // can be changed or removed to ensure the best possible word is ALAYS chosen. if (guessSet.Count > 1000) return FindBestWorst(guessSet.GetRange(0, 1000)); // Variables to hold the current best Word and the worst case scenario // number of candidates remaining if this word was guessed. // cutOff is to hold the minimum length of word worth considering as // a potential guess based on the current best Word. This is // based on the observation that a word of x letters can have a best-worst // case of ruling out ((x/(x+1))*100)% of a set of candidates. int bestWorstCase = answerSet.Count, cutOff = 0; Word bestWord = new Word(); // Find out for each of the potential guesses which would rule out // the most words if the worst case response was to come back // from Player 1 foreach (Word g in guessSet) { // The Words are ordered by length so once this condition is broken // you can terminate the loop if (g.word.Length > cutOff) { // Create an array to hold the number of words that have 0,1,2... etc // letters in common with this potential guess int[] wordsWithIndexLettersInCommon = new int[g.word.Length + 1]; // For each of the candidates calculate how many letters they have in // common with this potential guess foreach (Word a in answerSet) { int lettersInCommon = 0; for (int i = 0; i <= 26; i++) lettersInCommon += Math.Min(g.letters[i], a.letters[i]); // If you ever get past the current bestWorstCase just stop and move on if(++wordsWithIndexLettersInCommon[lettersInCommon]>bestWorstCase) break; } // If we've found a new best guess several things need updating if (wordsWithIndexLettersInCommon.Max() < bestWorstCase) { bestWord = g; bestWorstCase = wordsWithIndexLettersInCommon.Max(); cutOff = (int) Math.Ceiling((double)(answerSet.Count / bestWorstCase)); } } else break; } return bestWord; } static void UpdateDictionary(Word guess, int response) { // Remove the just guessed word from answer candidates dictionary.Find(w => w.word == guess.word).candidate = false; // Remove all other words that do not have the correct number of letters in common for(int i=0; i