Skip to content

Pcc02 #776

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

Open
wants to merge 3 commits into
base: community
Choose a base branch
from
Open

Pcc02 #776

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
5 changes: 5 additions & 0 deletions 02/piotrrybinski88/README.md
Original file line number Diff line number Diff line change
@@ -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).
30 changes: 30 additions & 0 deletions 02/piotrrybinski88/data.py
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions 02/piotrrybinski88/game-nohelp.py
Original file line number Diff line number Diff line change
@@ -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()
100 changes: 100 additions & 0 deletions 02/piotrrybinski88/game.py
Original file line number Diff line number Diff line change
@@ -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()
52 changes: 52 additions & 0 deletions 02/piotrrybinski88/test_game.py
Original file line number Diff line number Diff line change
@@ -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()