Skip to content

Commit 85f4b65

Browse files
committed
initial commit
1 parent 461d01c commit 85f4b65

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+4675
-0
lines changed

bacronym/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# BACRONYM
2+
3+
Write a Python program called `bacronym.py` that takes an string like "FBI" and retrofits some `-n|--number` (default 5) of acronyms by reading a `-w|--wordlist` argument (defualt "/usr/share/dict/words"), skipping over words to `-e|--exclude` (default "a, an, the") and randomly selecting words that start with each of the letters. Be sure to include a `-s|--seed` argument (default `None`) to pass to `random.seed` for the test suite.
4+
5+
````
6+
$ ./bacronym.py
7+
usage: bacronym.py [-h] [-n NUM] [-w STR] [-x STR] [-s INT] STR
8+
bacronym.py: error: the following arguments are required: STR
9+
$ ./bacronym.py -h
10+
usage: bacronym.py [-h] [-n NUM] [-w STR] [-x STR] [-s INT] STR
11+
12+
Explain acronyms
13+
14+
positional arguments:
15+
STR Acronym
16+
17+
optional arguments:
18+
-h, --help show this help message and exit
19+
-n NUM, --num NUM Maximum number of definitions (default: 5)
20+
-w STR, --wordlist STR
21+
Dictionary/word file (default: /usr/share/dict/words)
22+
-x STR, --exclude STR
23+
List of words to exclude (default: a,an,the)
24+
-s INT, --seed INT Random seed (default: None)
25+
$ ./bacronym.py FBI -s 1
26+
FBI =
27+
- Fecundity Brokage Imitant
28+
- Figureless Basketmaking Ismailite
29+
- Frumpery Bonedog Irregardless
30+
- Foxily Blastomyces Inedited
31+
- Fastland Bouncingly Idiospasm
32+
````

bacronym/bacronym.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Purpose: Make guesses about acronyms
4+
Author: Ken Youens-Clark <kyclark@gmail.com>
5+
Date: 2019-05-19
6+
"""
7+
8+
import argparse
9+
import sys
10+
import os
11+
import random
12+
import re
13+
from collections import defaultdict
14+
15+
16+
# --------------------------------------------------
17+
def get_args():
18+
"""get arguments"""
19+
parser = argparse.ArgumentParser(
20+
description='Explain acronyms',
21+
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
22+
23+
parser.add_argument('acronym', help='Acronym', type=str, metavar='STR')
24+
25+
parser.add_argument('-n',
26+
'--num',
27+
help='Maximum number of definitions',
28+
type=int,
29+
metavar='NUM',
30+
default=5)
31+
32+
parser.add_argument('-w',
33+
'--wordlist',
34+
help='Dictionary/word file',
35+
type=str,
36+
metavar='STR',
37+
default='/usr/share/dict/words')
38+
39+
parser.add_argument('-x',
40+
'--exclude',
41+
help='List of words to exclude',
42+
type=str,
43+
metavar='STR',
44+
default='a,an,the')
45+
46+
parser.add_argument('-s',
47+
'--seed',
48+
help='Random seed',
49+
type=int,
50+
metavar='INT',
51+
default=None)
52+
53+
return parser.parse_args()
54+
55+
56+
# --------------------------------------------------
57+
def main():
58+
"""main"""
59+
args = get_args()
60+
acronym = args.acronym
61+
wordlist = args.wordlist
62+
limit = args.num
63+
goodword = r'^[a-z]{2,}$'
64+
badwords = set(re.split(r'\s*,\s*', args.exclude.lower()))
65+
66+
random.seed(args.seed)
67+
68+
if not re.match(goodword, acronym.lower()):
69+
print('"{}" must be >1 in length, only use letters'.format(acronym))
70+
sys.exit(1)
71+
72+
if not os.path.isfile(wordlist):
73+
print('"{}" is not a file.'.format(wordlist))
74+
sys.exit(1)
75+
76+
seen = set()
77+
words_by_letter = defaultdict(list)
78+
for word in open(wordlist).read().lower().split():
79+
clean = re.sub('[^a-z]', '', word)
80+
if not clean: # nothing left?
81+
continue
82+
83+
if re.match(goodword,
84+
clean) and clean not in seen and clean not in badwords:
85+
seen.add(clean)
86+
words_by_letter[clean[0]].append(clean)
87+
88+
len_acronym = len(acronym)
89+
definitions = []
90+
for i in range(0, limit):
91+
definition = []
92+
for letter in acronym.lower():
93+
possible = words_by_letter.get(letter, [])
94+
if len(possible) > 0:
95+
definition.append(
96+
random.choice(possible).title() if possible else '?')
97+
98+
if len(definition) == len_acronym:
99+
definitions.append(' '.join(definition))
100+
101+
if len(definitions) > 0:
102+
print(acronym.upper() + ' =')
103+
for definition in definitions:
104+
print(' - ' + definition)
105+
else:
106+
print('Sorry I could not find any good definitions')
107+
108+
109+
# --------------------------------------------------
110+
if __name__ == '__main__':
111+
main()

blackjack/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.PHONY: doc test
2+
3+
doc:
4+
pandoc README.md -o README.pdf --latex-engine=xelatex
5+
6+
test:
7+
pytest -v test.py

blackjack/README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Python Blackjack Game
2+
3+
Write a Python program called "blackjack.py" that plays an abbreviated game of Blackjack. You will need to `import random` to get random cards from a deck you will construct, and so your program will need to accept a `-s|--seed` that will set `random.seed()` with the value that is passed in so that the test suite will work. The other arguments you will accept are two flags (Boolean values) of `-p|--player_hits` and `-d|--dealer_hits`. As usual, you will also have a `-h|--help` option for usage statement.
4+
5+
To play the game, the user will run the program and will see a display of what cards the dealer has (noted "D") and what cards the player has (noted "P") along with a sum of the values of the cards. In Blackjack, number cards are worth their value, face cards are worth 10, and the Ace will be worth 1 for our game (though in the real game it can alternate between 1 and 11).
6+
7+
To create your deck of cards, you will need to use the Unicode symbols for the suites ( ♥ ♠ ♣ ♦ ) [which won't display in the PDF, so consult the Markdown file].
8+
9+
Combine these with the numbers 2-10 and the letters "A", "J", "Q," and "K" (hint: look at `itertools.product`). Because your game will use randomness, you will need to sort your deck and then use the `random.shuffle` method so that your cards will be in the correct order to pass the tests.
10+
11+
When you make the initial deal, keep in mind how cards are actually dealt -- first one card to each of the players, then one to the dealer, then the players, then the dealer, etc. You might be tempted to use `random.choice` or something like that to select your cards, but you need to keep in mind that you are modeling an actual deck and so selected cards should no longer be present in the deck. If the `-p|--player_htis` flag is present, deal an additional card to the player; likewise with the `-d|--dealer_hits` flag.
12+
13+
After displaying the hands, the code should:
14+
15+
1. Check if the player has more than 21; if so, print 'Player busts! You lose, loser!' and exit(0)
16+
2. Check if the dealer has more than 21; if so, print 'Dealer busts.' and exit(0)
17+
3. Check if the player has exactly 21; if so, print 'Player wins. You probably cheated.' and exit(0)
18+
4. Check if the dealer has exactly 21; if so, print 'Dealer wins!' and exit(0)
19+
5. If the either the dealer or the player has less than 18, you should indicate "X should hit."
20+
21+
NB: Look at the Markdown format to see the actual output as the suites won't display in the PDF version!
22+
23+
````
24+
$ ./blackjack.py
25+
D [11]: ♠J ♥A
26+
P [18]: ♦8 ♠10
27+
Dealer should hit.
28+
$ ./blackjack.py
29+
D [13]: ♥3 ♦J
30+
P [16]: ♥6 ♦10
31+
Dealer should hit.
32+
Player should hit.
33+
$ ./blackjack.py -s 5
34+
D [ 5]: ♣4 ♣A
35+
P [19]: ♦10 ♦9
36+
Dealer should hit.
37+
$ ./blackjack.py -s 3 -p
38+
D [19]: ♥K ♠9
39+
P [22]: ♣3 ♥9 ♣J
40+
Player busts! You lose, loser!
41+
$ ./blackjack.py -s 15 -p
42+
D [19]: ♠10 ♦9
43+
P [21]: ♣10 ♥8 ♠3
44+
Player wins. You probably cheated.
45+
````
46+
47+
# Test Suite
48+
49+
A passing test suite looks like this:
50+
51+
````
52+
$ make test
53+
pytest -v test.py
54+
============================= test session starts ==============================
55+
platform darwin -- Python 3.6.8, pytest-4.2.0, py-1.7.0, pluggy-0.8.1 -- /anaconda3/bin/python
56+
cachedir: .pytest_cache
57+
rootdir: /Users/kyclark/work/python/practical_python_for_data_science/ch09-python-games/exercises/blackjack_b, inifile:
58+
plugins: remotedata-0.3.1, openfiles-0.3.2, doctestplus-0.2.0, arraydiff-0.3
59+
collected 13 items
60+
61+
test.py::test_usage PASSED [ 7%]
62+
test.py::test_play01 PASSED [ 15%]
63+
test.py::test_play02 PASSED [ 23%]
64+
test.py::test_play03 PASSED [ 30%]
65+
test.py::test_play04 PASSED [ 38%]
66+
test.py::test_play05 PASSED [ 46%]
67+
test.py::test_play06 PASSED [ 53%]
68+
test.py::test_play07 PASSED [ 61%]
69+
test.py::test_play08 PASSED [ 69%]
70+
test.py::test_play09 PASSED [ 76%]
71+
test.py::test_play10 PASSED [ 84%]
72+
test.py::test_play11 PASSED [ 92%]
73+
test.py::test_play12 PASSED [100%]
74+
75+
========================== 13 passed in 0.82 seconds ===========================
76+
````

blackjack/README.pdf

22.1 KB
Binary file not shown.
13.6 KB
Binary file not shown.

blackjack/solution.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Author : Ken Youens-Clark <kyclark@gmail.com>
4+
Date : 2019-03-15
5+
Purpose: Rock the Casbah
6+
"""
7+
8+
import argparse
9+
import random
10+
import re
11+
import sys
12+
from itertools import product
13+
14+
15+
# --------------------------------------------------
16+
def get_args():
17+
"""get command-line arguments"""
18+
parser = argparse.ArgumentParser(
19+
description='Argparse Python script',
20+
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
21+
22+
parser.add_argument(
23+
'-s',
24+
'--seed',
25+
help='Random seed',
26+
metavar='int',
27+
type=int,
28+
default=None)
29+
30+
parser.add_argument(
31+
'-d',
32+
'--dealer_hits',
33+
help='Dealer hits',
34+
action='store_true')
35+
36+
parser.add_argument(
37+
'-p',
38+
'--player_hits',
39+
help='Player hits',
40+
action='store_true')
41+
42+
return parser.parse_args()
43+
44+
45+
# --------------------------------------------------
46+
def warn(msg):
47+
"""Print a message to STDERR"""
48+
print(msg, file=sys.stderr)
49+
50+
51+
# --------------------------------------------------
52+
def die(msg='Something bad happened'):
53+
"""warn() and exit with error"""
54+
warn(msg)
55+
sys.exit(1)
56+
57+
# --------------------------------------------------
58+
def bail(msg):
59+
"""print() and exit(0)"""
60+
print(msg)
61+
sys.exit(0)
62+
63+
# --------------------------------------------------
64+
def card_value(card):
65+
"""card to numeric value"""
66+
val = card[1:]
67+
faces = {'A': 1, 'J': 10, 'Q': 10, 'K': 10}
68+
if val.isdigit():
69+
return int(val)
70+
elif val in faces:
71+
return faces[val]
72+
else:
73+
die('Unknown card value for "{}"'.format(card))
74+
75+
76+
# --------------------------------------------------
77+
def main():
78+
"""Make a jazz noise here"""
79+
args = get_args()
80+
81+
random.seed(args.seed)
82+
83+
# seed = args.seed
84+
# if seed is not None:
85+
# random.seed(seed)
86+
87+
suites = list('♥♠♣♦')
88+
values = list(range(2, 11)) + list('AJQK')
89+
cards = sorted(map(lambda t: '{}{}'.format(*t), product(suites, values)))
90+
random.shuffle(cards)
91+
92+
p1, d1, p2, d2 = cards.pop(), cards.pop(), cards.pop(), cards.pop()
93+
player = [p1, p2]
94+
dealer = [d1, d2]
95+
96+
if args.player_hits:
97+
player.append(cards.pop())
98+
99+
if args.dealer_hits:
100+
dealer.append(cards.pop())
101+
102+
player_hand = sum(map(card_value, player))
103+
dealer_hand = sum(map(card_value, dealer))
104+
105+
print('D [{:2}]: {}'.format(dealer_hand, ' '.join(dealer)))
106+
print('P [{:2}]: {}'.format(player_hand, ' '.join(player)))
107+
108+
if player_hand > 21:
109+
bail('Player busts! You lose, loser!')
110+
elif dealer_hand > 21:
111+
bail('Dealer busts.')
112+
elif player_hand == 21:
113+
bail('Player wins. You probably cheated.')
114+
elif dealer_hand == 21:
115+
bail('Dealer wins!')
116+
117+
if dealer_hand < 18:
118+
print('Dealer should hit.')
119+
120+
if player_hand < 18:
121+
print('Player should hit.')
122+
123+
124+
# --------------------------------------------------
125+
if __name__ == '__main__':
126+
main()

0 commit comments

Comments
 (0)