diff --git a/02/piotrrybinski88/README.md b/02/piotrrybinski88/README.md new file mode 100644 index 000000000..ddd1a4f85 --- /dev/null +++ b/02/piotrrybinski88/README.md @@ -0,0 +1,5 @@ +## Code Challenge 02 - Word Values Part II - a simple game + +Instructions [here](http://pybit.es/codechallenge02.html). + +Previous challenges and About [here](http://pybit.es/pages/challenges.html). diff --git a/02/piotrrybinski88/data.py b/02/piotrrybinski88/data.py new file mode 100644 index 000000000..b711163bb --- /dev/null +++ b/02/piotrrybinski88/data.py @@ -0,0 +1,30 @@ +from collections import namedtuple + +Letter = namedtuple('Letter', 'name amount value') + +def _load_words(): + 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)) + ) +assert len(POUCH) == 98 # no wildcards in this simple game + + +LETTER_SCORES = dict(zip( + [letter.name for letter in distribution], + [int(letter.value) for letter in distribution] + )) + +assert LETTER_SCORES['A'] == 1 +assert LETTER_SCORES['Q'] == 10 +assert sum(LETTER_SCORES.values()) == 87 diff --git a/02/piotrrybinski88/game-nohelp.py b/02/piotrrybinski88/game-nohelp.py new file mode 100644 index 000000000..1531d09e5 --- /dev/null +++ b/02/piotrrybinski88/game-nohelp.py @@ -0,0 +1,27 @@ +#!python3 +# Code Challenge 02 - Word Values Part II - a simple game +# http://pybit.es/codechallenge02.html + +from data import DICTIONARY, LETTER_SCORES, POUCH + +NUM_LETTERS = 7 + + +# 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 main(): + pass + + +if __name__ == "__main__": + main() diff --git a/02/piotrrybinski88/game.py b/02/piotrrybinski88/game.py new file mode 100644 index 000000000..d5dca6de8 --- /dev/null +++ b/02/piotrrybinski88/game.py @@ -0,0 +1,100 @@ +#!python3 +# Code Challenge 02 - Word Values Part II - a simple game +# http://pybit.es/codechallenge02.html + +import itertools +import random +from collections import Counter + + +from data import DICTIONARY, LETTER_SCORES, POUCH + +NUM_LETTERS = 7 + + +def draw_letters(): + """Pick NUM_LETTERS letters randomly. Hint: use stdlib random""" + return random.sample(POUCH, k=NUM_LETTERS) + + +def input_word(draw): + """Ask player for a word and validate against draw. + Use _validation(word, draw) helper.""" + word = input('Enter your word from draw: ') + _validation(word, draw) + return word + + +def _validation(word, draw): + """Validations: 1) only use letters of draw, 2) valid dictionary word""" + # Copy list to save all letters from draw + draw_for_check = draw.copy() + if word.lower() not in DICTIONARY: + raise ValueError('word not in dictionary') + + if len(word) > NUM_LETTERS: + raise ValueError('word can be max 7 letters') + + for letter in word: + draw_for_check.remove(letter) + + +# 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) + + +# 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""" + all_permutations = _get_permutations_draw(draw) + + valid_words = [permutation for permutation in all_permutations if permutation.lower() in DICTIONARY] + return valid_words + + +def _get_permutations_draw(draw): + """Helper for get_possible_dict_words to get all permutations of draw letters. + Hint: use itertools.permutations""" + list_of_permutation = [] + for num in range(1, NUM_LETTERS + 1): + list_of_permutation.append( + [''.join(word) for word in (itertools.permutations(draw, r=num))] + ) + + return list(itertools.chain.from_iterable(list_of_permutation)) + + +# 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 main(): + """Main game interface calling the previously defined methods""" + draw = draw_letters() + print('Letters drawn: {}'.format(', '.join(draw))) + + word = input_word(draw) + word_score = calc_word_value(word) + print('Word chosen: {} (value: {})'.format(word, word_score)) + + possible_words = get_possible_dict_words(draw) + print(possible_words) + max_word = max_word_value(possible_words) + max_word_score = calc_word_value(max_word) + print('Optimal word possible: {} (value: {})'.format( + max_word, max_word_score)) + + game_score = word_score / max_word_score * 100 + print('You scored: {:.1f}'.format(game_score)) + + +if __name__ == "__main__": + main() diff --git a/02/piotrrybinski88/test_game.py b/02/piotrrybinski88/test_game.py new file mode 100644 index 000000000..93888fa1e --- /dev/null +++ b/02/piotrrybinski88/test_game.py @@ -0,0 +1,52 @@ +import itertools +import unittest + +from game import draw_letters, calc_word_value, max_word_value +from game import get_possible_dict_words, _get_permutations_draw +from game import _validation + +NUM_LETTERS = 7 +TEST_WORDS = ('bob', 'julian', 'pybites', 'quit', 'barbeque') + +class TestGame(unittest.TestCase): + + def setUp(self): + self.draw = draw_letters() + + def test_draw_letters(self): + letter_str = ''.join(self.draw) + self.assertRegex(letter_str, r'^[A-Z]{%s}$' % NUM_LETTERS) + + # from ch01 + def test_calc_word_value(self): + self.assertEqual(calc_word_value('bob'), 7) + self.assertEqual(calc_word_value('JuliaN'), 13) + + # from ch01 + def test_max_word_value(self): + self.assertEqual(max_word_value(TEST_WORDS), 'barbeque') + + def test_get_permutations_draw(self): + gen_permutations_n_letters = sum(len(list(itertools.permutations(self.draw, n))) for n in range(1, NUM_LETTERS+1)) + game_permutations = len(list(_get_permutations_draw(self.draw))) + self.assertEqual(gen_permutations_n_letters, game_permutations) + alist = range(1,8) + gen_permutations_any_list = sum(len(list(itertools.permutations(alist, n))) for n in range(1, NUM_LETTERS+1)) + self.assertEqual(gen_permutations_any_list, gen_permutations_n_letters) + + def test_get_possible_dict_words(self): + self.fixed_draw = list('garytev'.upper()) + words = get_possible_dict_words(self.fixed_draw) + self.assertEqual(len(words), 137) + + def test_validation(self): + draw = list('garytev'.upper()) + word = 'GARYTEV' + self.assertRaises(ValueError, _validation, word, draw) + word = 'F' + self.assertRaises(ValueError, _validation, word, draw) + word = 'GARETTA' + self.assertRaises(ValueError, _validation, word, draw) + +if __name__ == "__main__": + unittest.main()