# I don't like this dictionary thing. W6

I’m struggling to keep the shape of the solutions dictionary in my mind. Therefore I need something simpler.

The solution dictionary is built like this:

```
class SolutionDictionary:
def __init__(self, guesses, solutions):
self.dict = self.create_dict(guesses, solutions)
@staticmethod
def create_dict(guesses, solutions):
solutions_dict = {} # guess -> dict (score -> [solutions])
for guess in guesses:
guess_dict = {} # score -> [solutions]
solutions_dict[guess] = guess_dict
for solution in solutions:
score = guess.score(solution)
if not score in guess_dict:
guess_dict[score] = ScoredWords(score)
guess_dict[score].add_word(solution)
return solutions_dict
```

This is a dictionary keyed by guess, pointing to a dictionary keyed by score, such that if `score`

= `guess.score(solution)`

, solutions_dict[guess][score] is an instance of ScoredWords containing that score, and all the solutions that result in that score.

I find that impossible to understand, impossible to describe in words. This is not a good sign. I need a better idea. Let me try new words.

For every possible guess, we want a GuessDescription, consisting of a ScoreDescription, consisting of a score, and a list of all the solutions having that score. Let’s rename ScoredWords to ScoreDescription.

Now we have this:

```
def create_dict(guesses, solutions):
solutions_dict = {} # guess -> dict (score -> [solutions])
for guess in guesses:
guess_dict = {} # score -> [solutions]
solutions_dict[guess] = guess_dict
for solution in solutions:
score = guess.score(solution)
if not score in guess_dict:
guess_dict[score] = ScoreDescription(score)
guess_dict[score].add_word(solution)
return solutions_dict
```

Now let’s make a GuessDescription by intention:

```
@staticmethod
def create_dict(guesses, solutions):
solutions_dict = {} # guess -> dict (score -> [solutions])
for guess in guesses:
guess_desc = GuessDescription()
solutions_dict[guess] = guess_desc
for solution in solutions:
score = guess.score(solution)
guess_desc.add_word(score, solution)
return solutions_dict
```

That demands this:

```
class GuessDescription:
def __init__(self):
self.score_descriptions = {}
def add_word(self, score, solution):
try:
description = self.score_descriptions[score]
except KeyError:
description = ScoreDescription(score)
description.add_word(solution)
```

That’ll break some tests, telling us that our refactoring is not complete. The first error is:

```
def solutions_for(self, guess, score):
try:
> return self.dict[guess][score]
E TypeError: 'GuessDescription' object is not subscriptable
```

Yes. I think we can do this to get to the next flaw:

```
class SolutionDictionary:
def solutions_for(self, guess, score):
guess_description = self.dict[guess]
return guess_description.solutions_for(score)
class GuessDescription:
def solutions_for(self, score):
try:
return self.score_descriptions[score]
except KeyError:
return ScoreDescription(score)
```

Continues to break, now saying:

```
Expected :<score_description.ScoreDescription object at 0x105a8c090>
Actual :<score_description.ScoreDescription object at 0x105a8c310>
```

Looks like we need ScoreDescriptions to understand `__eq__`

. But we have that. What’s up?

Ah. This was wrong:

```
class GuessDescription:
def add_word(self, score, solution):
try:
description = self.score_descriptions[score]
except KeyError:
description = ScoreDescription(score)
self.score_descriptions[score] = description # added
description.add_word(solution)
```

I wasn’t adding the new ScoreDescription to the dictionary.

One more test failing. Create statistics needs revision. We have:

```
def create_statistics(self):
stats = []
for word in self.dict:
word_dict = self.dict[word] # {score -> scoredWords}
number_of_buckets = len(word_dict)
max_words = max(len(bucket) for bucket in word_dict.values())
min_words = min(len(bucket) for bucket in word_dict.values())
avg_words = sum(len(bucket) for bucket in word_dict.values()) / number_of_buckets
stat = Statistic(word, number_of_buckets, max_words, min_words, avg_words)
stats.append(stat)
def my_key(stat: Statistic):
return -stat.number_of_buckets
stats.sort(key=my_key)
return stats
```

We need:

```
def create_statistics(self):
stats = []
for word in self.dict:
guess_description = self.dict[word] # {score -> scoredWords}
number_of_buckets = guess_description.number_of_buckets
max_words = max(len(bucket) for bucket in guess_description.buckets)
min_words = min(len(bucket) for bucket in guess_description.buckets)
avg_words = sum(len(bucket) for bucket in guess_description.buckets) / number_of_buckets
stat = Statistic(word, number_of_buckets, max_words, min_words, avg_words)
stats.append(stat)
def my_key(stat: Statistic):
return -stat.number_of_buckets
stats.sort(key=my_key)
return stats
class GuessDescription:
def __init__(self):
self.score_descriptions = {}
def add_word(self, score, solution):
try:
description = self.score_descriptions[score]
except KeyError:
description = ScoreDescription(score)
self.score_descriptions[score] = description
description.add_word(solution)
@property
def buckets(self):
return self.score_descriptions.values()
@property
def number_of_buckets(self):
return len(self.score_descriptions)
def solutions_for(self, score):
try:
return self.score_descriptions[score]
except KeyError:
return ScoreDescription(score)
```

We added the `buckets`

and `number_of_buckets`

properties. Tests are green. I am fried. Commit: *refactor to provide GuessDescription and ScoreDescription objects.*

## Summary

I think these two objects make things easier to understand. I’ll try to explain them next time. Right now … well, I’ll try:

The SolutionDictionaryis a keyed collection (dictionary) of guess word to instances of GuessDescription.

A GuessDescription is a keyed collection of score to instances of ScoreDescription.

ScoreDescription is an object holding a score, and a WordCollection holding all the solutions such that the guess gets that score from each of those solutions.

It’s still weird, isn’t it?

Try this:

SolutionDictionary is an object that knows about guesses, solutions, and scores. It is created from two WordCollections, a collection of guesses and a collection of possible solutions.

Given a SolutionDictionary `sd`

, `sd.solutions_for(guess_word, score)`

returns all the solutions that would give the guess_word the provided score. That is, it returns all the solutions that might be correct given guess_word and its score from Wordle.

Enough. See you next time!