Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C22 Tami Gaertner #37

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 37 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

## Goal

An [anagram](https://en.wikipedia.org/wiki/Anagram) is a word or phrase formed by rearranging the letters of a different word or phrase. In this project you will be creating _Adagrams_, a game in which a player is given a random set of letters and must make an anagram using those letters. Each submitted word will score points.
An [anagram](https://en.wikipedia.org/wiki/Anagram) is a word or phrase formed by rearranging the letters of a different word or phrase. In this project you will be creating _Adagrams_, a game in which a player is given a random set of letters and must make an anagram using those letters. Each submitted word will score points.

While working on _Adagrams_, it may help to think of a physical metaphor for this game, such as other common word games like [_Scrabble_](https://en.wikipedia.org/wiki/Scrabble) or [_Bananagrams_](https://en.wikipedia.org/wiki/Bananagrams). These games all feature a _pool_ of letter _tiles_ that the player _draws_ from.

Expand All @@ -21,7 +21,6 @@ In this version of _Adagrams_, we will only be working with the English alphabet

Follow these directions once, at the beginning of your project:


1. Navigate to your projects folder named `projects`.

If you followed Ada's recommended file system structure from the Intro to Dev Environment lesson in Learn, you can navigate to your projects folder with the following command:
Expand All @@ -30,11 +29,11 @@ If you followed Ada's recommended file system structure from the Intro to Dev En
$ cd ~/Developer/projects
```

2. In Github click on the "Fork" button in github and fork the repository to your Github account. This will make a copy of the project in your github account.
2. In Github click on the "Fork" button in github and fork the repository to your Github account. This will make a copy of the project in your github account.

![Fork Button](images/fork.png)

3. "Clone" (download a copy of this project) into your projects folder. This command makes a new folder called `adagrams-py`, and then puts the project into this new folder. Make sure you are cloning from your copy of the project and not the class version (ada-cX).
3. "Clone" (download a copy of this project) into your projects folder. This command makes a new folder called `adagrams-py`, and then puts the project into this new folder. Make sure you are cloning from your copy of the project and not the class version (ada-cX).

```bash
$ git clone ...
Expand Down Expand Up @@ -73,6 +72,7 @@ $ pip install -r requirements.txt
```

Summary of one-time project setup:

- [ ] Fork the project respository
- [ ] `cd` into your `projects` folder
- [ ] Clone the project onto your machine
Expand All @@ -89,7 +89,7 @@ Summary of one-time project setup:
$ source venv/bin/activate
```

2. Run the game and play test your current wave. Replace the word `wave` below with the wave number (ie, 1, 2, 3 or 4):
2. Run the game and play test your current wave. Replace the word `wave` below with the wave number (ie, 1, 2, 3 or 4):

```bash
# Must be in activated virtual environment
Expand All @@ -98,9 +98,9 @@ $ python main.py wave

3. Use play-testing to guide your development.

4. Use tests to verify your functions after thoroughly play-testing. See instructions for running tests in the section below. *A complete set of unit tests is provided for this project.*
4. Use tests to verify your functions after thoroughly play-testing. See instructions for running tests in the section below. _A complete set of unit tests is provided for this project._

5. Use git to commit your work regularly! Commit between each wave.
5. Use git to commit your work regularly! Commit between each wave.

```bash
# Add your current work
Expand All @@ -123,7 +123,6 @@ $ deactivate
- Check the `tests` folder, and find the test file you want to run
- In that test file, read through each test case


2. Run the tests for your specific wave

```bash
Expand All @@ -147,8 +146,7 @@ $ pytest -s

## Project Write-Up: How to Complete and Submit


The goal of this project is to write code in `game.py` so each of the functions meet the requirements outlined in the Project Directions below.
The goal of this project is to write code in `game.py` so each of the functions meet the requirements outlined in the Project Directions below.

Go through the waves one-by-one and build the features of this game.

Expand Down Expand Up @@ -184,33 +182,32 @@ Your first task is to build a hand of 10 letters for the user. To do so, impleme
#### Distribution of Letters

| Letter : Qty. | Letter : Qty. |
|:------:|:-----:|
| A : 9 | N : 6 |
| B : 2 | O : 8 |
| C : 2 | P : 2 |
| D : 4 | Q : 1 |
| E : 12 | R : 6 |
| F : 2 | S : 4 |
| G : 3 | T : 6 |
| H : 2 | U : 4 |
| I : 9 | V : 2 |
| J : 1 | W : 2 |
| K : 1 | X : 1 |
| L : 4 | Y : 2 |
| M : 2 | Z : 1 |
| :-----------: | :-----------: |
| A : 9 | N : 6 |
| B : 2 | O : 8 |
| C : 2 | P : 2 |
| D : 4 | Q : 1 |
| E : 12 | R : 6 |
| F : 2 | S : 4 |
| G : 3 | T : 6 |
| H : 2 | U : 4 |
| I : 9 | V : 2 |
| J : 1 | W : 2 |
| K : 1 | X : 1 |
| L : 4 | Y : 2 |
| M : 2 | Z : 1 |

**Note:** Making sure that the drawn letters match the rules of the letter pool can be straightforward or very difficult, depending on how you build the data structure for the letter pool. It is worth spending some time to think carefully about this.


### Wave 2: use_available_letters

Next, you need a way to check if an input word (a word a player submits) only uses characters that are contained within a collection (or hand) of drawn letters. Essentially, you need a way to check if the word is an anagram of some or all of the given letters in the hand.

To do so, implement the function called `uses_available_letters` in `game.py`. This function should have the following properties:

- Has two parameters:
- `word`, the first parameter, describes some input word, and is a string
- `letter_bank`, the second parameter, describes an array of drawn letters in a hand. You can expect this to be an array of ten strings, with each string representing a letter
- `word`, the first parameter, describes some input word, and is a string
- `letter_bank`, the second parameter, describes an array of drawn letters in a hand. You can expect this to be an array of ten strings, with each string representing a letter
- Returns either `True` or `False`
- Returns `True` if every letter in the `input` word is available (in the right quantities) in the `letter_bank`
- Returns `False` if not; if there is a letter in `input` that is not present in the `letter_bank` or has too much of compared to the `letter_bank`
Expand All @@ -229,15 +226,15 @@ Implement the function `score_word` in `game.py`. This method should have the fo

#### Score chart

|Letter | Value|
|:----------------------------:|:----:|
|A, E, I, O, U, L, N, R, S, T | 1 |
|D, G | 2 |
|B, C, M, P | 3 |
|F, H, V, W, Y | 4 |
|K | 5 |
|J, X | 8 |
|Q, Z | 10 |
| Letter | Value |
| :--------------------------: | :---: |
| A, E, I, O, U, L, N, R, S, T | 1 |
| D, G | 2 |
| B, C, M, P | 3 |
| F, H, V, W, Y | 4 |
| K | 5 |
| J, X | 8 |
| Q, Z | 10 |

### Wave 4: get_highest_word_score

Expand All @@ -246,10 +243,10 @@ After several hands have been drawn, words have been submitted, checked, scored,
Implement a function called `get_highest_word_score` in `game.py`. This method should have the following properties:

- Has one parameter: `word_list`, which is a list of strings
- Returns a tuple that represents the data of a winning word and it's score. The tuple must contain the following elements:
- Returns a tuple that represents the data of a winning word and it's score. The tuple must contain the following elements:
- index 0 ([0]): a string of a word
- index 1 ([1]): the score of that word
- In the case of tie in scores, use these tie-breaking rules:
- prefer the word with the fewest letters...
- ...unless one word has 10 letters. If the top score is tied between multiple words and one is 10 letters long, choose the one with 10 letters over the one with fewer tiles
- If the there are multiple words that are the same score and the same length, pick the first one in the supplied list
- prefer the word with the fewest letters...
- ...unless one word has 10 letters. If the top score is tied between multiple words and one is 10 letters long, choose the one with 10 letters over the one with fewer tiles
- If the there are multiple words that are the same score and the same length, pick the first one in the supplied list
28 changes: 28 additions & 0 deletions adagrams/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
LETTER_POOL = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love the thought to separate out data from our implementation code. In larger projects, especially when the data is shared by multiple functions or files, this makes a lot of sense.

There is a little bit of a tradeoff happening when only one function uses the data. By placing the data in a larger scope, more code has access to and could potentially change the values in the data structure. If only one function will ever use the data, it can make sense to place the data inside the function. That way the data is only created when the function is called, and only the code that need access to the data has the ability to see or change it.

'A': 9,
'B': 2,
'C': 2,
'D': 4,
'E': 12,
'F': 2,
'G': 3,
'H': 2,
'I': 9,
'J': 1,
'K': 1,
'L': 4,
'M': 2,
'N': 6,
'O': 8,
'P': 2,
'Q': 1,
'R': 6,
'S': 4,
'T': 6,
'U': 4,
'V': 2,
'W': 2,
'X': 1,
'Y': 2,
'Z': 1
}
92 changes: 88 additions & 4 deletions adagrams/game.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,95 @@

from random import randint
from .data import LETTER_POOL
from .score_chart import score_of_letters
# wave 1
def draw_letters():
pass

hand =[]
letter_counts = LETTER_POOL.copy()

while len(hand)< 10:

available_letters =[]

for letter, count in letter_counts.items():
if count > 0:
available_letters.append(letter)
if not available_letters:
break
Comment on lines +18 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we ever reach a situation where if not available_letters will evaluate to True?


index = randint(0, len(available_letters) -1)
choose_letter = available_letters[index]
letter_counts[choose_letter] -= 1
hand.append(choose_letter)

return hand

# wave 2

def uses_available_letters(word, letter_bank):
pass
word = word.lower()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't typically want to reassign parameter variables in case we ever need to access the original value that was passed in. It's okay to create a new variable in this case like lower_word.


for letter in word:

letter_found = False

for bank_letter in letter_bank:
if letter == bank_letter.lower():
letter_found = True
break
Comment on lines +39 to +40
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we break on line 47 right after changing letter_found to True, does line 46 impact the flow of the code?

if not letter_found:
return False
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the early exit as soon as we find something invalid.

word_count = word.count(letter)

bank_count = 0

for bank_letter in letter_bank:

if letter == bank_letter.lower():
bank_count += 1
if word_count > bank_count:
return False
return True

# wave 3
def score_word(word):
pass

if not word:
return 0

total_score = 0

word = word.upper()

for letter in word:

if letter.upper() in score_of_letters:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice check to let us keep scoring if we have something like a hyphenated word!

letter_score = score_of_letters[letter.upper()]
total_score += letter_score
if len(word) >= 7 :
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if len(word) >= 7 :
if len(word) >= 7:

total_score += 8

return total_score

# wave 4
def get_highest_word_score(word_list):
pass

highest_score = (None, 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea to use a tuple from the start so you have your return value ready to go by the end of the loop!


for word in word_list:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great approach to tie break in a single loop over word_list!

new_score = score_word(word)
if new_score > highest_score[1]:
highest_score = (word, new_score)
elif new_score == highest_score[1]:
if len(word) == 10 and len(highest_score[0]) != 10:
highest_score = (word, new_score)
elif len(highest_score[0]) != 10 and len(word) < len(highest_score[0]):
highest_score = (word, new_score)
return highest_score






28 changes: 28 additions & 0 deletions adagrams/score_chart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
score_of_letters = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're treating this data structure like a constant, we should follow constant variable naming conventions and use all capital letters like: SCORE_OF_LETTERS = ...

'A': 1,
'E': 1,
'I': 1,
'O': 1,
'U': 1,
'L': 1,
'N': 1,
'R': 1,
'S': 1,
'T': 1,
'D': 2,
'G': 2,
'B': 3,
'C': 3,
'M': 3,
'P': 3,
'F': 4,
'H': 4,
'V': 4,
'W': 4,
'Y': 4,
'K': 5,
'J': 8,
'X': 8,
'Q': 10,
'Z': 10
}
5 changes: 4 additions & 1 deletion tests/test_wave_01.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ def test_draw_letters_draws_ten():

# Assert
assert len(letters) == 10


# @pytest.mark.skip(reason="not yet")
def test_draw_letters_is_list_of_letter_strings():
# Arrange/Act
letters = draw_letters()
Expand All @@ -49,6 +50,7 @@ def test_draw_letters_is_list_of_letter_strings():
assert type(elem) == str
assert len(elem) == 1

# @pytest.mark.skip
def test_letter_not_selected_too_many_times():

for i in range(1000):
Expand All @@ -66,6 +68,7 @@ def test_letter_not_selected_too_many_times():
for letter in letters:
assert letter_freq[letter] <= LETTER_POOL[letter]

# @pytest.mark.skip
def test_draw_letters_returns_different_hands():
# Arrange/Act
hand1 = draw_letters()
Expand Down