forked from pybites/challenges
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request pybites#657 from ronaldokun/PCC02
Pcc02
- Loading branch information
Showing
5 changed files
with
170 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../data.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../dictionary.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import unittest | ||
|
||
from data import DICTIONARY, LETTER_SCORES | ||
from wordvalue import load_words, calc_word_value, max_word_value | ||
|
||
TEST_WORDS = ('bob', 'julian', 'pybites', 'quit', 'barbeque') | ||
|
||
class TestWordValue(unittest.TestCase): | ||
|
||
def test_load_words(self): | ||
words = load_words() | ||
self.assertEqual(len(words), 235886) | ||
self.assertEqual(words[0], 'A') | ||
self.assertEqual(words[-1], 'Zyzzogeton') | ||
self.assertNotIn(' ', ''.join(words)) | ||
|
||
def test_calc_word_value(self): | ||
self.assertEqual(calc_word_value('bob'), 7) | ||
self.assertEqual(calc_word_value('JuliaN'), 13) | ||
self.assertEqual(calc_word_value('PyBites'), 14) | ||
self.assertEqual(calc_word_value('benzalphenylhydrazone'), 56) | ||
|
||
def test_max_word_value(self): | ||
self.assertEqual(max_word_value(TEST_WORDS), 'barbeque') | ||
self.assertEqual(max_word_value(), 'benzalphenylhydrazone') | ||
|
||
if __name__ == "__main__": | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,58 @@ | ||
from collections import namedtuple | ||
|
||
Letter = namedtuple('Letter', 'name amount value') | ||
Letter = namedtuple("Letter", "name amount value") | ||
|
||
|
||
def _load_words(): | ||
with open('dictionary.txt') as f: | ||
with open("dictionary.txt") as f: | ||
return set([word.strip().lower() for word in f.read().split()]) | ||
|
||
|
||
DICTIONARY = _load_words() | ||
assert len(DICTIONARY) == 234371 | ||
|
||
|
||
# generated with https://github.com/pybites/blog_code/blob/master/BeautifulSoup/scrabble_distribution.py | ||
distribution = [Letter(name='A', amount='9', value='1'), Letter(name='B', amount='2', value='3'), Letter(name='C', amount='2', value='3'), Letter(name='D', amount='4', value='2'), Letter(name='E', amount='12', value='1'), Letter(name='F', amount='2', value='4'), Letter(name='G', amount='3', value='2'), Letter(name='H', amount='2', value='4'), Letter(name='I', amount='9', value='1'), Letter(name='J', amount='1', value='8'), Letter(name='K', amount='1', value='5'), Letter(name='L', amount='4', value='1'), Letter(name='M', amount='2', value='3'), Letter(name='N', amount='6', value='1'), Letter(name='O', amount='8', value='1'), Letter(name='P', amount='2', value='3'), Letter(name='Q', amount='1', value='10'), Letter(name='R', amount='6', value='1'), Letter(name='S', amount='4', value='1'), Letter(name='T', amount='6', value='1'), Letter(name='U', amount='4', value='1'), Letter(name='V', amount='2', value='4'), Letter(name='W', amount='2', value='4'), Letter(name='X', amount='1', value='8'), Letter(name='Y', amount='2', value='4'), Letter(name='Z', amount='1', value='10')] | ||
|
||
POUCH = list(''.join( | ||
list(letter.name * int(letter.amount) | ||
for letter in distribution)) | ||
) | ||
distribution = [ | ||
Letter(name="A", amount="9", value="1"), | ||
Letter(name="B", amount="2", value="3"), | ||
Letter(name="C", amount="2", value="3"), | ||
Letter(name="D", amount="4", value="2"), | ||
Letter(name="E", amount="12", value="1"), | ||
Letter(name="F", amount="2", value="4"), | ||
Letter(name="G", amount="3", value="2"), | ||
Letter(name="H", amount="2", value="4"), | ||
Letter(name="I", amount="9", value="1"), | ||
Letter(name="J", amount="1", value="8"), | ||
Letter(name="K", amount="1", value="5"), | ||
Letter(name="L", amount="4", value="1"), | ||
Letter(name="M", amount="2", value="3"), | ||
Letter(name="N", amount="6", value="1"), | ||
Letter(name="O", amount="8", value="1"), | ||
Letter(name="P", amount="2", value="3"), | ||
Letter(name="Q", amount="1", value="10"), | ||
Letter(name="R", amount="6", value="1"), | ||
Letter(name="S", amount="4", value="1"), | ||
Letter(name="T", amount="6", value="1"), | ||
Letter(name="U", amount="4", value="1"), | ||
Letter(name="V", amount="2", value="4"), | ||
Letter(name="W", amount="2", value="4"), | ||
Letter(name="X", amount="1", value="8"), | ||
Letter(name="Y", amount="2", value="4"), | ||
Letter(name="Z", amount="1", value="10"), | ||
] | ||
|
||
POUCH = list("".join(list(letter.name * int(letter.amount) for letter in distribution))) | ||
assert len(POUCH) == 98 # no wildcards in this simple game | ||
|
||
|
||
LETTER_SCORES = dict(zip( | ||
LETTER_SCORES = dict( | ||
zip( | ||
[letter.name for letter in distribution], | ||
[int(letter.value) for letter in distribution] | ||
)) | ||
[int(letter.value) for letter in distribution], | ||
) | ||
) | ||
|
||
assert LETTER_SCORES['A'] == 1 | ||
assert LETTER_SCORES['Q'] == 10 | ||
assert LETTER_SCORES["A"] == 1 | ||
assert LETTER_SCORES["Q"] == 10 | ||
assert sum(LETTER_SCORES.values()) == 87 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
#!python3 | ||
# Code Challenge 02 - Word Values Part II - a simple game | ||
# http://pybit.es/codechallenge02.html | ||
""" | ||
Author: Ronaldo S.A.Batista | ||
Email: [email protected] | ||
Github: @ronaldokun | ||
""" | ||
from itertools import permutations | ||
from random import choices | ||
|
||
from data import DICTIONARY, LETTER_SCORES, POUCH | ||
|
||
NUM_LETTERS = 7 | ||
|
||
def draw_letters(): | ||
"""Pick NUM_LETTERS letters randomly. Hint: use stdlib random""" | ||
return choices(POUCH, k=NUM_LETTERS) | ||
# re-use from challenge 01 | ||
def calc_word_value(word): | ||
"""Calc a given word value based on Scrabble LETTER_SCORES mapping""" | ||
return sum(LETTER_SCORES.get(char.upper(), 0) for char in word) | ||
|
||
|
||
# re-use from challenge 01 | ||
def max_word_value(words): | ||
"""Calc the max value of a collection of words""" | ||
return max(words, key=calc_word_value) | ||
|
||
|
||
def _validation(word: str, draw: list) -> bool: | ||
"""Check if word can be formed from letters in draw | ||
:param word: word to be checked | ||
:param draw: set of letters to form a word | ||
:return: True if word can be formed from draw, False otherwise | ||
""" | ||
copy = draw[:] | ||
for c in word: | ||
try: | ||
copy.remove(c) | ||
except ValueError as e: | ||
raise ValueError(f"The word {word} cannot be formed from the {draw}") from e | ||
if not in_dict(word): | ||
raise ValueError(f"The word {word} is not a valid one") | ||
|
||
return True | ||
|
||
|
||
def in_dict(word: str) -> bool: | ||
""" Check if word in in DICTIONARY | ||
:param word: string to check | ||
:return: True if word is found, False otherwise | ||
""" | ||
return "".join(word).lower() in DICTIONARY | ||
|
||
def input_word(draw): | ||
"""Ask player for a word and validate against draw. | ||
Use _validation(word, draw) helper.""" | ||
word = input("Insert a valid word: ").upper() | ||
if _validation(word, draw): | ||
return word | ||
|
||
# Below 2 functions pass through the same 'draw' argument (smell?). | ||
# Maybe you want to abstract this into a class? | ||
# get_possible_dict_words and _get_permutations_draw would be instance methods. | ||
# 'draw' would be set in the class constructor (__init__). | ||
def get_possible_dict_words(draw): | ||
"""Get all possible words from draw which are valid dictionary words. | ||
Use the _get_permutations_draw helper and DICTIONARY constant""" | ||
return list(filter(in_dict, _get_permutations_draw(draw))) | ||
|
||
|
||
def _get_permutations_draw(draw): | ||
"""Helper for get_possible_dict_words to get all permutations of draw letters. | ||
Hint: use itertools.permutations""" | ||
return (w for k in range(1, NUM_LETTERS+1) for w in permutations(draw, k)) | ||
|
||
|
||
def main(): | ||
|
||
draw = draw_letters() | ||
print(f"Letters drawn: {draw}") | ||
|
||
word = input_word(draw) | ||
|
||
word_score = calc_word_value(word) | ||
print(f"Word Chosen: {word} (Value: {word_score})") | ||
|
||
possible_words = get_possible_dict_words(draw) | ||
|
||
max_word = max_word_value(possible_words) | ||
val_max_word = calc_word_value(max_word) | ||
|
||
print(f"The Optimal Word for this Draw is {''.join(max_word)} (Value: {val_max_word})") | ||
print(f"Your Score (player_score / optimal_score) : {word_score / val_max_word * 100: .2f}") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |