├── .gitattributes ├── Chapter_1 ├── ETAOIN_practice.py ├── myconfig.pylintrc ├── pig_Latin_practice.py ├── pseudonyms.py ├── pseudonyms_main.py └── pseudonyms_main_fixed.py ├── Chapter_10 ├── empire_practice.py ├── galaxy_practice.py ├── galaxy_simuator.py ├── probability_of_detection.py └── rounded_detection_practice.py ├── Chapter_11 ├── all_closed.png ├── birthday_paradox_practice.py ├── goat_a.png ├── goat_b.png ├── goat_c.png ├── money_a.png ├── money_b.png ├── money_c.png ├── monty_hall_gui.py ├── monty_hall_mcs.py ├── reveal_a.png ├── reveal_b.png └── reveal_c.png ├── Chapter_12 ├── 10-yr_TBond_returns_1926-2013_pct.txt ├── 3_mo_TBill_rate_1926-2013_pct.txt ├── S-B-C_blend_1926-2013_pct.txt ├── S-B_blend_1926-2013_pct.txt ├── SP500_returns_1926-2013_pct.txt ├── annual_infl_rate_1926-2013_pct.txt ├── nest_egg_mcs.py └── nest_egg_mcs_1st_5yrs.py ├── Chapter_13 ├── practice_45.py ├── tvashtar.py └── tvashtar_plume.gif ├── Chapter_14 ├── mars.png ├── mars_orbiter.py ├── mars_water.png ├── satellite.png ├── satellite_crash_40x33.png └── thrust_audio.ogg ├── Chapter_15 ├── Figure 15-10.pdf ├── Figure 15-9.pdf ├── crop_n_scale_images.py ├── enhance_image.py ├── moon_cropped │ ├── moon_1.png │ ├── moon_2.png │ ├── moon_3.png │ ├── moon_4.png │ └── moon_5.png ├── stack_images.py └── video_frames │ ├── MVI_6450 001.jpg │ ├── MVI_6450 002.jpg │ ├── MVI_6450 003.jpg │ ├── MVI_6450 004.jpg │ ├── MVI_6450 005.jpg │ ├── MVI_6450 006.jpg │ ├── MVI_6450 007.jpg │ ├── MVI_6450 008.jpg │ ├── MVI_6450 009.jpg │ ├── MVI_6450 01.jpg │ ├── MVI_6450 010.jpg │ ├── MVI_6450 011.jpg │ ├── MVI_6450 012.jpg │ ├── MVI_6450 013.jpg │ ├── MVI_6450 014.jpg │ ├── MVI_6450 015.jpg │ ├── MVI_6450 016.jpg │ ├── MVI_6450 017.jpg │ ├── MVI_6450 018.jpg │ ├── MVI_6450 019.jpg │ ├── MVI_6450 02.jpg │ ├── MVI_6450 020.jpg │ ├── MVI_6450 021.jpg │ ├── MVI_6450 022.jpg │ ├── MVI_6450 023.jpg │ ├── MVI_6450 024.jpg │ ├── MVI_6450 025.jpg │ ├── MVI_6450 026.jpg │ ├── MVI_6450 027.jpg │ ├── MVI_6450 028.jpg │ ├── MVI_6450 029.jpg │ ├── MVI_6450 03.jpg │ ├── MVI_6450 030.jpg │ ├── MVI_6450 031.jpg │ ├── MVI_6450 032.jpg │ ├── MVI_6450 033.jpg │ ├── MVI_6450 034.jpg │ ├── MVI_6450 035.jpg │ ├── MVI_6450 036.jpg │ ├── MVI_6450 037.jpg │ ├── MVI_6450 038.jpg │ ├── MVI_6450 039.jpg │ ├── MVI_6450 04.jpg │ ├── MVI_6450 040.jpg │ ├── MVI_6450 041.jpg │ ├── MVI_6450 042.jpg │ ├── MVI_6450 043.jpg │ ├── MVI_6450 044.jpg │ ├── MVI_6450 045.jpg │ ├── MVI_6450 046.jpg │ ├── MVI_6450 047.jpg │ ├── MVI_6450 048.jpg │ ├── MVI_6450 049.jpg │ ├── MVI_6450 05.jpg │ ├── MVI_6450 050.jpg │ ├── MVI_6450 051.jpg │ ├── MVI_6450 052.jpg │ ├── MVI_6450 053.jpg │ ├── MVI_6450 054.jpg │ ├── MVI_6450 055.jpg │ ├── MVI_6450 056.jpg │ ├── MVI_6450 057.jpg │ ├── MVI_6450 058.jpg │ ├── MVI_6450 059.jpg │ ├── MVI_6450 06.jpg │ ├── MVI_6450 060.jpg │ ├── MVI_6450 061.jpg │ ├── MVI_6450 062.jpg │ ├── MVI_6450 063.jpg │ ├── MVI_6450 064.jpg │ ├── MVI_6450 065.jpg │ ├── MVI_6450 066.jpg │ ├── MVI_6450 067.jpg │ ├── MVI_6450 068.jpg │ ├── MVI_6450 069.jpg │ ├── MVI_6450 07.jpg │ ├── MVI_6450 070.jpg │ ├── MVI_6450 071.jpg │ ├── MVI_6450 072.jpg │ ├── MVI_6450 073.jpg │ ├── MVI_6450 074.jpg │ ├── MVI_6450 075.jpg │ ├── MVI_6450 076.jpg │ ├── MVI_6450 077.jpg │ ├── MVI_6450 078.jpg │ ├── MVI_6450 079.jpg │ ├── MVI_6450 08.jpg │ ├── MVI_6450 080.jpg │ ├── MVI_6450 081.jpg │ ├── MVI_6450 082.jpg │ ├── MVI_6450 083.jpg │ ├── MVI_6450 084.jpg │ ├── MVI_6450 085.jpg │ ├── MVI_6450 086.jpg │ ├── MVI_6450 087.jpg │ ├── MVI_6450 088.jpg │ ├── MVI_6450 089.jpg │ ├── MVI_6450 09.jpg │ ├── MVI_6450 090.jpg │ ├── MVI_6450 091.jpg │ ├── MVI_6450 092.jpg │ ├── MVI_6450 093.jpg │ ├── MVI_6450 094.jpg │ ├── MVI_6450 095.jpg │ ├── MVI_6450 096.jpg │ ├── MVI_6450 097.jpg │ ├── MVI_6450 098.jpg │ ├── MVI_6450 099.jpg │ ├── MVI_6450 10.jpg │ ├── MVI_6450 100.jpg │ ├── MVI_6450 101.jpg │ ├── MVI_6450 102.jpg │ ├── MVI_6450 103.jpg │ ├── MVI_6450 104.jpg │ ├── MVI_6450 105.jpg │ ├── MVI_6450 106.jpg │ ├── MVI_6450 107.jpg │ ├── MVI_6450 108.jpg │ ├── MVI_6450 109.jpg │ ├── MVI_6450 11.jpg │ ├── MVI_6450 110.jpg │ ├── MVI_6450 111.jpg │ ├── MVI_6450 112.jpg │ ├── MVI_6450 113.jpg │ ├── MVI_6450 114.jpg │ ├── MVI_6450 115.jpg │ ├── MVI_6450 116.jpg │ ├── MVI_6450 117.jpg │ ├── MVI_6450 118.jpg │ ├── MVI_6450 119.jpg │ ├── MVI_6450 12.jpg │ ├── MVI_6450 120.jpg │ ├── MVI_6450 121.jpg │ ├── MVI_6450 122.jpg │ ├── MVI_6450 123.jpg │ ├── MVI_6450 124.jpg │ ├── MVI_6450 125.jpg │ ├── MVI_6450 126.jpg │ ├── MVI_6450 127.jpg │ ├── MVI_6450 128.jpg │ ├── MVI_6450 129.jpg │ ├── MVI_6450 13.jpg │ ├── MVI_6450 130.jpg │ ├── MVI_6450 131.jpg │ ├── MVI_6450 132.jpg │ ├── MVI_6450 133.jpg │ ├── MVI_6450 134.jpg │ ├── MVI_6450 135.jpg │ ├── MVI_6450 136.jpg │ ├── MVI_6450 137.jpg │ ├── MVI_6450 138.jpg │ ├── MVI_6450 139.jpg │ ├── MVI_6450 14.jpg │ ├── MVI_6450 140.jpg │ ├── MVI_6450 141.jpg │ ├── MVI_6450 142.jpg │ ├── MVI_6450 143.jpg │ ├── MVI_6450 144.jpg │ ├── MVI_6450 145.jpg │ ├── MVI_6450 146.jpg │ ├── MVI_6450 147.jpg │ ├── MVI_6450 148.jpg │ ├── MVI_6450 149.jpg │ ├── MVI_6450 15.jpg │ ├── MVI_6450 150.jpg │ ├── MVI_6450 151.jpg │ ├── MVI_6450 152.jpg │ ├── MVI_6450 153.jpg │ ├── MVI_6450 154.jpg │ ├── MVI_6450 155.jpg │ ├── MVI_6450 156.jpg │ ├── MVI_6450 157.jpg │ ├── MVI_6450 158.jpg │ ├── MVI_6450 159.jpg │ ├── MVI_6450 16.jpg │ ├── MVI_6450 160.jpg │ ├── MVI_6450 161.jpg │ ├── MVI_6450 162.jpg │ ├── MVI_6450 163.jpg │ ├── MVI_6450 164.jpg │ ├── MVI_6450 165.jpg │ ├── MVI_6450 166.jpg │ ├── MVI_6450 167.jpg │ ├── MVI_6450 168.jpg │ ├── MVI_6450 169.jpg │ ├── MVI_6450 17.jpg │ ├── MVI_6450 170.jpg │ ├── MVI_6450 171.jpg │ ├── MVI_6450 172.jpg │ ├── MVI_6450 173.jpg │ ├── MVI_6450 174.jpg │ ├── MVI_6450 175.jpg │ ├── MVI_6450 176.jpg │ ├── MVI_6450 177.jpg │ ├── MVI_6450 178.jpg │ ├── MVI_6450 179.jpg │ ├── MVI_6450 18.jpg │ ├── MVI_6450 180.jpg │ ├── MVI_6450 181.jpg │ ├── MVI_6450 182.jpg │ ├── MVI_6450 183.jpg │ ├── MVI_6450 184.jpg │ ├── MVI_6450 185.jpg │ ├── MVI_6450 186.jpg │ ├── MVI_6450 187.jpg │ ├── MVI_6450 188.jpg │ ├── MVI_6450 189.jpg │ ├── MVI_6450 19.jpg │ ├── MVI_6450 190.jpg │ ├── MVI_6450 191.jpg │ ├── MVI_6450 192.jpg │ ├── MVI_6450 193.jpg │ ├── MVI_6450 194.jpg │ ├── MVI_6450 195.jpg │ ├── MVI_6450 196.jpg │ ├── MVI_6450 197.jpg │ ├── MVI_6450 198.jpg │ ├── MVI_6450 199.jpg │ ├── MVI_6450 20.jpg │ ├── MVI_6450 200.jpg │ ├── MVI_6450 21.jpg │ ├── MVI_6450 22.jpg │ ├── MVI_6450 23.jpg │ ├── MVI_6450 24.jpg │ ├── MVI_6450 25.jpg │ ├── MVI_6450 26.jpg │ ├── MVI_6450 27.jpg │ ├── MVI_6450 28.jpg │ ├── MVI_6450 29.jpg │ ├── MVI_6450 30.jpg │ ├── MVI_6450 31.jpg │ ├── MVI_6450 32.jpg │ ├── MVI_6450 33.jpg │ ├── MVI_6450 34.jpg │ ├── MVI_6450 35.jpg │ ├── MVI_6450 36.jpg │ ├── MVI_6450 37.jpg │ ├── MVI_6450 38.jpg │ ├── MVI_6450 39.jpg │ ├── MVI_6450 40.jpg │ ├── MVI_6450 41.jpg │ ├── MVI_6450 42.jpg │ ├── MVI_6450 43.jpg │ ├── MVI_6450 44.jpg │ ├── MVI_6450 45.jpg │ ├── MVI_6450 46.jpg │ ├── MVI_6450 47.jpg │ ├── MVI_6450 48.jpg │ ├── MVI_6450 49.jpg │ ├── MVI_6450 50.jpg │ ├── a (10).jpg │ ├── a (5).jpg │ ├── a (6).jpg │ ├── a (7).jpg │ ├── a (8).jpg │ └── a (9).jpg ├── Chapter_16 ├── Clinton_votes_Illinois.txt ├── Illinois_votes.txt ├── Johnson_votes_Illinois.txt ├── Stein_votes_Illinois.txt ├── Trump_votes_Illinois.txt ├── beat_benford_practice.py ├── benford.py └── fake_Illinois_counts.txt ├── Chapter_2 ├── cprofile_test.py ├── dictionary_cleanup_practice.py ├── load_dictionary.py ├── palindromes.py ├── palingrams.py ├── palingrams_optimized.py └── palingrams_timed.py ├── Chapter_3 ├── anagrams.py ├── count_digrams_practice.py ├── least-likely_trigrams.txt ├── load_dictionary.py ├── phrase_anagrams.py └── voldemort_british.py ├── Chapter_4 ├── cipher_a.txt ├── cipher_b.txt ├── identify_cipher_type_practice.py ├── key_dictionary_practice.py ├── perms.py ├── permutations_practice.py ├── rail_fence_cipher_decrypt.py ├── rail_fence_cipher_encrypt.py ├── route_cipher_decrypt.py ├── route_cipher_decrypt_prototype.py └── route_cipher_hacker.py ├── Chapter_5 ├── colchester_message.txt ├── colchester_practice.py ├── list_cipher.py ├── load_dictionary.py ├── null_cipher_finder.py ├── save_Mary_practice.py ├── supporters.txt └── trevanion.txt ├── Chapter_6 ├── ciphertext_message_letterhead.docx ├── elementary_ink.py ├── elementary_ink_practice.py ├── example_template_prep.docx ├── fakeMessage.docx ├── realMessage.docx ├── realMessageChallenge.docx ├── realMessage_Vig.docx └── template.docx ├── Chapter_7 ├── brute_force_cracker.py ├── safe_cracker.py └── super_rats.py ├── Chapter_8 ├── count_syllables.py ├── missing_words.json ├── missing_words_finder.py ├── test_count_syllables_w_dict.py ├── test_count_syllables_w_full_corpus.py └── train.txt ├── Chapter_9 ├── count_syllables.py ├── markov_haiku.py ├── missing_words.json └── train.txt └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Chapter_1/ETAOIN_practice.py: -------------------------------------------------------------------------------- 1 | """Map letters from string into dictionary & print bar chart of frequency.""" 2 | import sys 3 | import pprint 4 | from collections import defaultdict 5 | 6 | # Note: text should be a short phrase for bars to fit in IDLE window 7 | text = 'Like the castle in its corner in a medieval game, I foresee terrible \ 8 | trouble and I stay here just the same.' 9 | 10 | ALPHABET = 'abcdefghijklmnopqrstuvwxyz' 11 | 12 | # defaultdict module lets you build dictionary keys on the fly! 13 | mapped = defaultdict(list) 14 | for character in text: 15 | character = character.lower() 16 | if character in ALPHABET: 17 | mapped[character].append(character) 18 | 19 | # pprint lets you print stacked output 20 | print("\nYou may need to stretch console window if text wrapping occurs.\n") 21 | print("text = ", end='') 22 | print("{}\n".format(text), file=sys.stderr) 23 | pprint.pprint(mapped, width=110) 24 | -------------------------------------------------------------------------------- /Chapter_1/pig_Latin_practice.py: -------------------------------------------------------------------------------- 1 | """Turn a word into its Pig Latin equivalent.""" 2 | import sys 3 | 4 | VOWELS = 'aeiouy' 5 | 6 | while True: 7 | word = input("Type a word and get its pig Latin translation: ") 8 | 9 | if word[0] in VOWELS: 10 | pig_Latin = word + 'way' 11 | else: 12 | pig_Latin = word[1:] + word[0] + 'ay' 13 | print() 14 | print("{}".format(pig_Latin), file=sys.stderr) 15 | 16 | try_again = input("\n\nTry again? (Press Enter else n to stop)\n ") 17 | if try_again.lower() == "n": 18 | sys.exit() 19 | -------------------------------------------------------------------------------- /Chapter_1/pseudonyms.py: -------------------------------------------------------------------------------- 1 | 2 | import sys, random 3 | 4 | print("Welcome to the Psych 'Sidekick Name Picker.'\n") 5 | print("A name just like Sean would pick for Gus:\n\n") 6 | 7 | first = ('Baby Oil', 'Bad News', 'Big Burps', "Bill 'Beenie-Weenie'", 8 | "Bob 'Stinkbug'", 'Bowel Noises', 'Boxelder', "Bud 'Lite'", 9 | 'Butterbean', 'Buttermilk', 'Buttocks', 'Chad', 'Chesterfield', 10 | 'Chewy', 'Chigger', 'Cinnabuns', 'Cleet', 'Cornbread', 'Crab Meat', 11 | 'Crapps', 'Dark Skies', 'Dennis Clawhammer', 'Dicman', 'Elphonso', 12 | 'Fancypants', 'Figgs', 'Foncy', 'Gootsy', 'Greasy Jim', 'Huckleberry', 13 | 'Huggy', 'Ignatious', 'Jimbo', "Joe 'Pottin Soil'", 'Johnny', 14 | 'Lemongrass', 'Lil Debil', 'Longbranch', '"Lunch Money"', 'Mergatroid', 15 | '"Mr Peabody"', 'Oil-Can', 'Oinks', 'Old Scratch', 'Ovaltine', 16 | 'Pennywhistle', 'Pitchfork Ben', 'Potato Bug', 'Pushmeet', 17 | 'Rock Candy', 'Schlomo', 'Scratchensniff', 'Scut', 18 | "Sid 'The Squirts'", 'Skidmark', 'Slaps', 'Snakes', 'Snoobs', 19 | 'Snorki', 'Soupcan Sam', 'Spitzitout', 'Squids', 'Stinky', 20 | 'Storyboard', 'Sweet Tea', 'TeeTee', 'Wheezy Joe', 21 | "Winston 'Jazz Hands'", 'Worms') 22 | 23 | last = ('Appleyard', 'Bigmeat', 'Bloominshine', 'Boogerbottom', 24 | 'Breedslovetrout', 'Butterbaugh', 'Clovenhoof', 'Clutterbuck', 25 | 'Cocktoasten', 'Endicott', 'Fewhairs', 'Gooberdapple', 'Goodensmith', 26 | 'Goodpasture', 'Guster', 'Henderson', 'Hooperbag', 'Hoosenater', 27 | 'Hootkins', 'Jefferson', 'Jenkins', 'Jingley-Schmidt', 'Johnson', 28 | 'Kingfish', 'Listenbee', "M'Bembo", 'McFadden', 'Moonshine', 'Nettles', 29 | 'Noseworthy', 'Olivetti', 'Outerbridge', 'Overpeck', 'Overturf', 30 | 'Oxhandler', 'Pealike', 'Pennywhistle', 'Peterson', 'Pieplow', 31 | 'Pinkerton', 'Porkins', 'Putney', 'Quakenbush', 'Rainwater', 32 | 'Rosenthal', 'Rubbins', 'Sackrider', 'Snuggleshine', 'Splern', 33 | 'Stevens', 'Stroganoff', 'Sugar-Gold', 'Swackhamer', 'Tippins', 34 | 'Turnipseed', 'Vinaigrette', 'Walkingstick', 'Wallbanger', 'Weewax', 35 | 'Weiners', 'Whipkey', 'Wigglesworth', 'Wimplesnatch', 'Winterkorn', 36 | 'Woolysocks') 37 | 38 | while True: 39 | 40 | firstName = random.choice(first) 41 | 42 | lastName = random.choice(last) 43 | 44 | print("\n\n") 45 | print(firstName, lastName, file = sys.stderr) 46 | print("\n\n") 47 | 48 | try_again = input("\n\nTry again? (Press Enter else n to quit)\n ") 49 | if try_again.lower() == "n": 50 | break 51 | 52 | input("\nPress Enter to exit.") 53 | -------------------------------------------------------------------------------- /Chapter_1/pseudonyms_main.py: -------------------------------------------------------------------------------- 1 | 2 | import sys, random 3 | 4 | def main(): 5 | 6 | print("Welcome to the Psych 'Sidekick Name Picker.'\n") 7 | print("A name just like Sean would pick for Gus:\n\n") 8 | 9 | first = ('Baby Oil', 'Bad News', 'Big Burps', "Bill 'Beenie-Weenie'", 10 | "Bob 'Stinkbug'", 'Bowel Noises', 'Boxelder', "Bud 'Lite'", 11 | 'Butterbean', 'Buttermilk', 'Buttocks', 'Chad', 'Chesterfield', 12 | 'Chewy', 'Chigger', 'Cinnabuns', 'Cleet', 'Cornbread', 'Crab Meat', 13 | 'Crapps', 'Dark Skies', 'Dennis Clawhammer', 'Dicman', 'Elphonso', 14 | 'Fancypants', 'Figgs', 'Foncy', 'Gootsy', 'Greasy Jim', 'Huckleberry', 15 | 'Huggy', 'Ignatious', 'Jimbo', "Joe 'Pottin Soil'", 'Johnny', 16 | 'Lemongrass', 'Lil Debil', 'Longbranch', '"Lunch Money"', 'Mergatroid', 17 | '"Mr Peabody"', 'Oil-Can', 'Oinks', 'Old Scratch', 'Ovaltine', 18 | 'Pennywhistle', 'Pitchfork Ben', 'Potato Bug', 'Pushmeet', 19 | 'Rock Candy', 'Schlomo', 'Scratchensniff', 'Scut', 20 | "Sid 'The Squirts'", 'Skidmark', 'Slaps', 'Snakes', 'Snoobs', 21 | 'Snorki', 'Soupcan Sam', 'Spitzitout', 'Squids', 'Stinky', 22 | 'Storyboard', 'Sweet Tea', 'TeeTee', 'Wheezy Joe', 23 | "Winston 'Jazz Hands'", 'Worms') 24 | 25 | last = ('Appleyard', 'Bigmeat', 'Bloominshine', 'Boogerbottom', 26 | 'Breedslovetrout', 'Butterbaugh', 'Clovenhoof', 'Clutterbuck', 27 | 'Cocktoasten', 'Endicott', 'Fewhairs', 'Gooberdapple', 'Goodensmith', 28 | 'Goodpasture', 'Guster', 'Henderson', 'Hooperbag', 'Hoosenater', 29 | 'Hootkins', 'Jefferson', 'Jenkins', 'Jingley-Schmidt', 'Johnson', 30 | 'Kingfish', 'Listenbee', "M'Bembo", 'McFadden', 'Moonshine', 'Nettles', 31 | 'Noseworthy', 'Olivetti', 'Outerbridge', 'Overpeck', 'Overturf', 32 | 'Oxhandler', 'Pealike', 'Pennywhistle', 'Peterson', 'Pieplow', 33 | 'Pinkerton', 'Porkins', 'Putney', 'Quakenbush', 'Rainwater', 34 | 'Rosenthal', 'Rubbins', 'Sackrider', 'Snuggleshine', 'Splern', 35 | 'Stevens', 'Stroganoff', 'Sugar-Gold', 'Swackhamer', 'Tippins', 36 | 'Turnipseed', 'Vinaigrette', 'Walkingstick', 'Wallbanger', 'Weewax', 37 | 'Weiners', 'Whipkey', 'Wigglesworth', 'Wimplesnatch', 'Winterkorn', 38 | 'Woolysocks') 39 | 40 | while True: 41 | 42 | firstName = random.choice(first) 43 | 44 | lastName = random.choice(last) 45 | 46 | print("\n\n") 47 | print ("{} {}".format(firstName, lastName), file=sys.stderr) 48 | print("\n\n") 49 | 50 | try_again = input("\n\nTry again? (Press Enter else n to quit)\n ") 51 | if try_again.lower() == "n": 52 | break 53 | 54 | input("\nPress Enter to exit.") 55 | 56 | if __name__ == "__main__": 57 | main() 58 | -------------------------------------------------------------------------------- /Chapter_1/pseudonyms_main_fixed.py: -------------------------------------------------------------------------------- 1 | """Generate funny names by randomly combining names from 2 separate lists.""" 2 | import sys 3 | import random 4 | 5 | def main(): 6 | """Choose names at random from 2 tuples of names and print to screen.""" 7 | print("Welcome to the Psych 'Sidekick Name Picker.'\n") 8 | print("A name just like Sean would pick for Gus:\n\n") 9 | 10 | first = ('Baby Oil', 'Bad News', 'Big Burps', "Bill 'Beenie-Weenie'", 11 | "Bob 'Stinkbug'", 'Bowel Noises', 'Boxelder', "Bud 'Lite'", 12 | 'Butterbean', 'Buttermilk', 'Buttocks', 'Chad', 'Chesterfield', 13 | 'Chewy', 'Chigger', 'Cinnabuns', 'Cleet', 'Cornbread', 14 | 'Crab Meat', 'Crapps', 'Dark Skies', 'Dennis Clawhammer', 15 | 'Dicman', 'Elphonso', 'Fancypants', 'Figgs', 'Foncy', 'Gootsy', 16 | 'Greasy Jim', 'Huckleberry', 'Huggy', 'Ignatious', 'Jimbo', 17 | "Joe 'Pottin Soil'", 'Johnny', 'Lemongrass', 'Lil Debil', 18 | 'Longbranch', '"Lunch Money"', 'Mergatroid', '"Mr Peabody"', 19 | 'Oil-Can', 'Oinks', 'Old Scratch', 'Ovaltine', 'Pennywhistle', 20 | 'Pitchfork Ben', 'Potato Bug', 'Pushmeet', 'Rock Candy', 21 | 'Schlomo', 'Scratchensniff', 'Scut', "Sid 'The Squirts'", 22 | 'Skidmark', 'Slaps', 'Snakes', 'Snoobs', 'Snorki', 'Soupcan Sam', 23 | 'Spitzitout', 'Squids', 'Stinky', 'Storyboard', 'Sweet Tea', 24 | 'TeeTee', 'Wheezy Joe', "Winston 'Jazz Hands'", 'Worms') 25 | 26 | last = ('Appleyard', 'Bigmeat', 'Bloominshine', 'Boogerbottom', 27 | 'Breedslovetrout', 'Butterbaugh', 'Clovenhoof', 'Clutterbuck', 28 | 'Cocktoasten', 'Endicott', 'Fewhairs', 'Gooberdapple', 29 | 'Goodensmith', 'Goodpasture', 'Guster', 'Henderson', 'Hooperbag', 30 | 'Hoosenater', 'Hootkins', 'Jefferson', 'Jenkins', 31 | 'Jingley-Schmidt', 'Johnson', 'Kingfish', 'Listenbee', "M'Bembo", 32 | 'McFadden', 'Moonshine', 'Nettles', 'Noseworthy', 'Olivetti', 33 | 'Outerbridge', 'Overpeck', 'Overturf', 'Oxhandler', 'Pealike', 34 | 'Pennywhistle', 'Peterson', 'Pieplow', 'Pinkerton', 'Porkins', 35 | 'Putney', 'Quakenbush', 'Rainwater', 'Rosenthal', 'Rubbins', 36 | 'Sackrider', 'Snuggleshine', 'Splern', 'Stevens', 'Stroganoff', 37 | 'Sugar-Gold', 'Swackhamer', 'Tippins', 'Turnipseed', 'Vinaigrette', 38 | 'Walkingstick', 'Wallbanger', 'Weewax', 'Weiners', 'Whipkey', 39 | 'Wigglesworth', 'Wimplesnatch', 'Winterkorn', 'Woolysocks') 40 | 41 | while True: 42 | first_name = random.choice(first) 43 | last_name = random.choice(last) 44 | 45 | print("\n\n") 46 | print("{} {}".format(first_name, last_name), file=sys.stderr) 47 | print("\n\n") 48 | 49 | try_again = input("\n\nTry again? (Press Enter else n to quit)\n ") 50 | 51 | if try_again.lower() == "n": 52 | break 53 | 54 | input("\nPress Enter to exit.") 55 | 56 | if __name__ == "__main__": 57 | main() 58 | -------------------------------------------------------------------------------- /Chapter_10/galaxy_practice.py: -------------------------------------------------------------------------------- 1 | """Use spiral formula to build galaxy display.""" 2 | import math 3 | from random import randint 4 | import tkinter 5 | 6 | root = tkinter.Tk() 7 | root.title("Galaxy BR549") 8 | c = tkinter.Canvas(root, width=1000, height=800, bg='black') 9 | c.grid() 10 | c.configure(scrollregion=(-500, -400, 500, 400)) 11 | oval_size = 0 12 | 13 | # build spiral arms 14 | num_spiral_stars = 500 15 | angle = 3.5 16 | core_diameter = 120 17 | spiral_stars = [] 18 | for i in range(num_spiral_stars): 19 | theta = i * angle 20 | r = math.sqrt(i) / math.sqrt(num_spiral_stars) 21 | spiral_stars.append((r * math.cos(theta), r * math.sin(theta))) 22 | for x, y in spiral_stars: 23 | x = x * 350 + randint(-5, 3) 24 | y = y * 350 + randint(-5, 3) 25 | oval_size = randint(1, 3) 26 | c.create_oval(x-oval_size, y-oval_size, x+oval_size, y+oval_size, 27 | fill='white', outline='') 28 | 29 | # build wisps 30 | wisps = [] 31 | for i in range(2000): 32 | theta = i * angle 33 | # divide by num_spiral_stars for better dust lanes 34 | r = math.sqrt(i) / math.sqrt(num_spiral_stars) 35 | spiral_stars.append((r * math.cos(theta), r * math.sin(theta))) 36 | for x, y in spiral_stars: 37 | x = x * 330 + randint(-15, 10) 38 | y = y * 330 + randint(-15, 10) 39 | h = math.sqrt(x**2 + y**2) 40 | if h < 350: 41 | wisps.append((x, y)) 42 | c.create_oval(x-1, y-1, x+1, y+1, fill='white', outline='') 43 | 44 | # build galactic core 45 | core = [] 46 | for i in range(900): 47 | x = randint(-core_diameter, core_diameter) 48 | y = randint(-core_diameter, core_diameter) 49 | h = math.sqrt(x**2 + y**2) 50 | if h < core_diameter - 70: 51 | core.append((x, y)) 52 | oval_size = randint(2, 4) 53 | c.create_oval(x-oval_size, y-oval_size, x+oval_size, y+oval_size, 54 | fill='white', outline='') 55 | elif h < core_diameter: 56 | core.append((x, y)) 57 | oval_size = randint(0, 2) 58 | c.create_oval(x-oval_size, y-oval_size, x+oval_size, y+oval_size, 59 | fill='white', outline='') 60 | 61 | root.mainloop() 62 | -------------------------------------------------------------------------------- /Chapter_10/probability_of_detection.py: -------------------------------------------------------------------------------- 1 | """Calculate probability of detecting alien radio bubbles in galaxy.""" 2 | from random import randint 3 | from collections import Counter 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | 7 | NUM_EQUIV_VOLUMES = 1000 # number of locations in which to place civilizations 8 | MAX_CIVS = 5000 # maximum number of advanced civilizations 9 | TRIALS = 1000 # number of times to model a given number of civilizations 10 | CIV_STEP_SIZE = 100 # civilizations count step size 11 | 12 | x = [] # x values for polynomial fit 13 | y = [] # y values for polynomial fit 14 | 15 | for num_civs in range(2, MAX_CIVS + 2, CIV_STEP_SIZE): 16 | civs_per_vol = num_civs / NUM_EQUIV_VOLUMES 17 | num_single_civs = 0 18 | for trial in range(TRIALS): 19 | locations = [] # equivalent volumes containing a civilization 20 | while len(locations) < num_civs: 21 | location = randint(1, NUM_EQUIV_VOLUMES) 22 | locations.append(location) 23 | overlap_count = Counter(locations) 24 | overlap_rollup = Counter(overlap_count.values()) 25 | num_single_civs += overlap_rollup[1] 26 | 27 | prob = 1 - (num_single_civs / (num_civs * TRIALS)) 28 | 29 | # print ratio of civs-per-volume vs. probability of 2+ civs per location 30 | print("{:.4f} {:.4f}".format(civs_per_vol, prob)) 31 | x.append(civs_per_vol) 32 | y.append(prob) 33 | 34 | coefficients = np.polyfit(x, y, 4) # 4th order polynomial fit 35 | p = np.poly1d(coefficients) 36 | print("\n{}".format(p)) 37 | xp = np.linspace(0, 5) 38 | _ = plt.plot(x, y, '.', xp, p(xp), '-') 39 | plt.ylim(-0.5, 1.5) 40 | plt.show() 41 | -------------------------------------------------------------------------------- /Chapter_10/rounded_detection_practice.py: -------------------------------------------------------------------------------- 1 | """Calculate probability of detecting 32 LY-diameter radio bubble given 15.6 M 2 | randomly-distributed civilizations in the galaxy.""" 3 | import math 4 | from random import uniform, random 5 | from collections import Counter 6 | 7 | # length units in light-years 8 | DISC_RADIUS = 50000 9 | DISC_HEIGHT = 1000 10 | NUM_CIVS = 15600000 11 | DETECTION_RADIUS = 16 12 | 13 | 14 | def random_polar_coordinates_xyz(): 15 | """Generate uniform random xyz point within a 3D disc.""" 16 | r = random() 17 | theta = uniform(0, 2 * math.pi) 18 | x = round(math.sqrt(r) * math.cos(theta) * DISC_RADIUS, 3) 19 | y = round(math.sqrt(r) * math.sin(theta) * DISC_RADIUS, 3) 20 | z = round(uniform(0, DISC_HEIGHT), 3) 21 | return x, y, z 22 | 23 | 24 | def rounded(n, base): 25 | """Round a number to the nearest number designated by base parameter.""" 26 | return int(round(n/base) * base) 27 | 28 | 29 | def distribute_civs(): 30 | """Distribute xyz locations in galactic disc model and return list.""" 31 | civ_locs = [] 32 | while len(civ_locs) < NUM_CIVS: 33 | loc = random_polar_coordinates_xyz() 34 | civ_locs.append(loc) 35 | return civ_locs 36 | 37 | 38 | def round_civ_locs(civ_locs): 39 | """Round xyz locations and return list of rounded locations.""" 40 | # convert radius to cubic dimensions: 41 | detect_distance = round((4 / 3 * math.pi * DETECTION_RADIUS**3)**(1/3)) 42 | print("\ndetection radius = {} LY".format(DETECTION_RADIUS)) 43 | print("cubic detection distance = {} LY".format(detect_distance)) 44 | 45 | # round civilization xyz to detection distance 46 | civ_locs_rounded = [] 47 | 48 | for x, y, z in civ_locs: 49 | i = rounded(x, detect_distance) 50 | j = rounded(y, detect_distance) 51 | k = rounded(z, detect_distance) 52 | civ_locs_rounded.append((i, j, k)) 53 | 54 | return civ_locs_rounded 55 | 56 | 57 | def calc_prob_of_detection(civ_locs_rounded): 58 | """Count locations and calculate probability of duplicate values.""" 59 | overlap_count = Counter(civ_locs_rounded) 60 | overlap_rollup = Counter(overlap_count.values()) 61 | num_single_civs = overlap_rollup[1] 62 | prob = 1 - (num_single_civs / NUM_CIVS) 63 | 64 | return overlap_rollup, prob 65 | 66 | 67 | def main(): 68 | """Call functions and print results.""" 69 | civ_locs = distribute_civs() 70 | civ_locs_rounded = round_civ_locs(civ_locs) 71 | overlap_rollup, detection_prob = calc_prob_of_detection(civ_locs_rounded) 72 | print("length pre-rounded civ_locs = {}".format(len(civ_locs))) 73 | print("length of rounded civ_locs_rounded = {}" 74 | .format(len(civ_locs_rounded))) 75 | print("overlap_rollup = {}\n".format(overlap_rollup)) 76 | print("probability of detection = {0:.3f}".format(detection_prob)) 77 | 78 | # QC step to check rounding 79 | print("\nFirst 3 locations pre- and post-rounding:\n") 80 | for i in range(3): 81 | print("pre-round: {}".format(civ_locs[i])) 82 | print("post-round: {} \n".format(civ_locs_rounded[i])) 83 | 84 | 85 | if __name__ == '__main__': 86 | main() 87 | -------------------------------------------------------------------------------- /Chapter_11/all_closed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/all_closed.png -------------------------------------------------------------------------------- /Chapter_11/birthday_paradox_practice.py: -------------------------------------------------------------------------------- 1 | """Calculate probability of a shared birthday per x number of people.""" 2 | import random 3 | 4 | max_people = 50 5 | num_runs = 2000 6 | 7 | print("\nProbability of at least 2 people having the same birthday:\n") 8 | 9 | for people in range(2, max_people + 1): 10 | found_shared = 0 11 | for run in range(num_runs): 12 | bdays = [] 13 | for i in range(0, people): 14 | bday = random.randrange(0, 364) # ignore leap years 15 | bdays.append(bday) 16 | set_of_bdays = set(bdays) 17 | if len(set_of_bdays) < len(bdays): 18 | found_shared += 1 19 | prob = found_shared/num_runs 20 | print("Number people = {} Prob = {:.4f}".format(people, prob)) 21 | 22 | print(""" 23 | According to the Birthday Paradox, if there are 23 people in a room, 24 | there's a 50% chance that two of them will share the same birthday. 25 | """) 26 | -------------------------------------------------------------------------------- /Chapter_11/goat_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/goat_a.png -------------------------------------------------------------------------------- /Chapter_11/goat_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/goat_b.png -------------------------------------------------------------------------------- /Chapter_11/goat_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/goat_c.png -------------------------------------------------------------------------------- /Chapter_11/money_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/money_a.png -------------------------------------------------------------------------------- /Chapter_11/money_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/money_b.png -------------------------------------------------------------------------------- /Chapter_11/money_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/money_c.png -------------------------------------------------------------------------------- /Chapter_11/monty_hall_mcs.py: -------------------------------------------------------------------------------- 1 | """Use Monte Carlo simulation to find correct response to Monty Hall Problem.""" 2 | import random 3 | 4 | def user_prompt(prompt, default=None): 5 | """Allow use of default values in input.""" 6 | prompt = '{} [{}]: '.format(prompt, default) 7 | response = input(prompt) 8 | if not response and default: 9 | return default 10 | else: 11 | return response 12 | 13 | # input number of times to run simulation 14 | num_runs = int(user_prompt("Input number of runs", "20000")) 15 | 16 | # declare counters for ways to win 17 | first_choice_wins = 0 18 | pick_change_wins = 0 19 | doors = ['a', 'b', 'c'] 20 | 21 | # run Monte Carlo 22 | for i in range(num_runs): 23 | winner = random.choice(doors) 24 | pick = random.choice(doors) 25 | 26 | if pick == winner: 27 | first_choice_wins += 1 28 | else: 29 | pick_change_wins += 1 30 | 31 | print("Wins with original pick = {}.".format(first_choice_wins)) 32 | print("Wins with changed pick = {}.".format(pick_change_wins)) 33 | print("Probability of winning with initial guess: {:.2f}" 34 | .format(first_choice_wins / num_runs)) 35 | print("Probability of winning by switching: {:.2f}" 36 | .format(pick_change_wins / num_runs)) 37 | 38 | input("\nPress Enter key to exit.") 39 | -------------------------------------------------------------------------------- /Chapter_11/reveal_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/reveal_a.png -------------------------------------------------------------------------------- /Chapter_11/reveal_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/reveal_b.png -------------------------------------------------------------------------------- /Chapter_11/reveal_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_11/reveal_c.png -------------------------------------------------------------------------------- /Chapter_12/10-yr_TBond_returns_1926-2013_pct.txt: -------------------------------------------------------------------------------- 1 | 3.6800 2 | 3.4000 3 | 0.8400 4 | 4.2000 5 | 4.5400 6 | -2.5600 7 | 8.7900 8 | 1.8600 9 | 7.9600 10 | 4.4700 11 | 5.0200 12 | 1.3800 13 | 4.2100 14 | 4.4100 15 | 5.4000 16 | -2.0200 17 | 2.2900 18 | 2.4900 19 | 2.5800 20 | 3.8000 21 | 3.1300 22 | 0.9200 23 | 1.9500 24 | 4.6600 25 | 0.4300 26 | -0.3000 27 | 2.2700 28 | 4.1400 29 | 3.2900 30 | -1.3400 31 | -2.2600 32 | 6.8000 33 | -2.1000 34 | -2.6500 35 | 11.6400 36 | 2.0600 37 | 5.6900 38 | 1.6800 39 | 3.7300 40 | 0.7200 41 | 2.9100 42 | -1.5800 43 | 3.2700 44 | -5.0100 45 | 16.7500 46 | 9.7900 47 | 2.8200 48 | 3.6600 49 | 1.9900 50 | 3.6100 51 | 15.9800 52 | 1.2900 53 | -0.7800 54 | 0.6700 55 | -2.9900 56 | 8.2000 57 | 32.8100 58 | 3.2000 59 | 13.7300 60 | 25.7100 61 | 24.2800 62 | -4.9600 63 | 8.2200 64 | 17.6900 65 | 6.2400 66 | 15.0000 67 | 9.3600 68 | 14.2100 69 | -8.0400 70 | 23.4800 71 | 1.4300 72 | 9.9400 73 | 14.9200 74 | -8.2500 75 | 16.6600 76 | 5.5700 77 | 15.1200 78 | 0.3800 79 | 4.4900 80 | 2.8700 81 | 1.9600 82 | 10.2100 83 | 20.1000 84 | -11.1200 85 | 8.4600 86 | 16.0400 87 | 2.9700 88 | -9.1000 89 | -------------------------------------------------------------------------------- /Chapter_12/3_mo_TBill_rate_1926-2013_pct.txt: -------------------------------------------------------------------------------- 1 | 3.0000 2 | 3.0000 3 | 3.0800 4 | 3.1600 5 | 4.5500 6 | 2.3100 7 | 1.0700 8 | 0.9600 9 | 0.3200 10 | 0.1800 11 | 0.1700 12 | 0.3000 13 | 0.0800 14 | 0.0400 15 | 0.0300 16 | 0.0800 17 | 0.3400 18 | 0.3800 19 | 0.3800 20 | 0.3800 21 | 0.3800 22 | 0.5700 23 | 1.0200 24 | 1.1000 25 | 1.1700 26 | 1.4800 27 | 1.6700 28 | 1.8900 29 | 0.9600 30 | 1.6600 31 | 2.5600 32 | 3.2300 33 | 1.7800 34 | 3.2600 35 | 3.0500 36 | 2.2700 37 | 2.7800 38 | 3.1100 39 | 3.5100 40 | 3.9000 41 | 4.8400 42 | 4.3300 43 | 5.2600 44 | 6.5600 45 | 6.6900 46 | 4.5400 47 | 3.9500 48 | 6.7300 49 | 7.7800 50 | 5.9900 51 | 4.9700 52 | 5.1300 53 | 6.9300 54 | 9.9400 55 | 11.2200 56 | 14.3000 57 | 11.0100 58 | 8.4500 59 | 9.6100 60 | 7.4900 61 | 6.0400 62 | 5.7200 63 | 6.4500 64 | 8.1100 65 | 7.5500 66 | 5.6100 67 | 3.4100 68 | 2.9800 69 | 3.9900 70 | 5.5200 71 | 5.0200 72 | 5.0500 73 | 4.7300 74 | 4.5100 75 | 5.7600 76 | 3.6700 77 | 1.6600 78 | 1.0300 79 | 1.2300 80 | 3.0100 81 | 4.6800 82 | 4.6400 83 | 1.5900 84 | 0.1400 85 | 0.1300 86 | 0.0300 87 | 0.0500 88 | 0.0700 89 | -------------------------------------------------------------------------------- /Chapter_12/S-B-C_blend_1926-2013_pct.txt: -------------------------------------------------------------------------------- 1 | 6.7698 2 | 17.0131 3 | 18.2525 4 | -0.9032 5 | -7.3245 6 | -18.5840 7 | 1.0451 8 | 21.0189 9 | 3.5366 10 | 20.9492 11 | 15.3044 12 | -13.4147 13 | 13.8261 14 | 1.7700 15 | -1.5661 16 | -6.1106 17 | 8.8485 18 | 11.3075 19 | 8.9403 20 | 16.2664 21 | -1.7687 22 | 2.5970 23 | 3.3588 24 | 9.7613 25 | 12.6542 26 | 9.4694 27 | 8.5624 28 | 1.7757 29 | 22.7663 30 | 12.5349 31 | 2.1018 32 | -0.4599 33 | 16.6160 34 | 3.8236 35 | 6.2596 36 | 11.9121 37 | -0.4016 38 | 10.1958 39 | 8.7822 40 | 5.7097 41 | -2.0494 42 | 9.1642 43 | 6.4869 44 | -5.1455 45 | 10.4685 46 | 11.0375 47 | 9.3071 48 | -3.2202 49 | -8.5877 50 | 17.2021 51 | 18.0194 52 | -1.6339 53 | 2.9067 54 | 8.7368 55 | 12.3211 56 | 3.6490 57 | 25.6736 58 | 11.3799 59 | 10.2845 60 | 26.0981 61 | 20.1418 62 | 0.4171 63 | 11.3699 64 | 22.2461 65 | 2.6492 66 | 20.1549 67 | 8.0185 68 | 11.3898 69 | -3.0906 70 | 27.1701 71 | 10.2894 72 | 18.7165 73 | 19.2682 74 | 4.6801 75 | 5.2933 76 | -1.5879 77 | -1.0604 78 | 11.6353 79 | 6.6651 80 | 3.6698 81 | 7.6930 82 | 7.7629 83 | -4.4119 84 | 4.8281 85 | 10.1714 86 | 8.8623 87 | 7.8462 88 | 8.3150 89 | -------------------------------------------------------------------------------- /Chapter_12/S-B_blend_1926-2013_pct.txt: -------------------------------------------------------------------------------- 1 | 7.4701 2 | 19.8909 3 | 21.5111 4 | -1.8048 5 | -9.6224 6 | -21.7632 7 | 0.6058 8 | 25.0176 9 | 3.4729 10 | 24.8192 11 | 18.0429 12 | -15.9557 13 | 16.2799 14 | 1.7675 15 | -2.2441 16 | -6.8419 17 | 10.3590 18 | 13.3186 19 | 10.4880 20 | 19.2114 21 | -2.4182 22 | 3.0600 23 | 3.8482 24 | 11.1103 25 | 14.8048 26 | 11.1743 27 | 9.8748 28 | 1.6580 29 | 26.7561 30 | 14.9818 31 | 2.5283 32 | -1.5066 33 | 20.0298 34 | 4.5634 35 | 6.0389 36 | 14.0193 37 | -1.3600 38 | 11.8503 39 | 9.8749 40 | 6.4210 41 | -3.2955 42 | 10.8011 43 | 6.9302 44 | -6.4257 45 | 10.1539 46 | 11.8384 47 | 10.5766 48 | -5.0028 49 | -11.1490 50 | 19.6495 51 | 19.5292 52 | -2.5510 53 | 2.8360 54 | 9.2544 55 | 13.7618 56 | 2.0198 57 | 26.2507 58 | 12.3955 59 | 9.9053 60 | 27.9617 61 | 21.1402 62 | 0.3888 63 | 12.1504 64 | 24.1302 65 | 1.7104 66 | 22.2084 67 | 8.3621 68 | 11.9926 69 | -3.3346 70 | 29.9453 71 | 11.8518 72 | 21.2738 73 | 21.4532 74 | 6.2066 75 | 3.8766 76 | -3.0505 77 | -3.2095 78 | 14.1616 79 | 7.5442 80 | 3.8255 81 | 8.6668 82 | 7.8140 83 | -7.6213 84 | 7.1719 85 | 11.5250 86 | 9.0692 87 | 9.2834 88 | 11.2320 -------------------------------------------------------------------------------- /Chapter_12/SP500_returns_1926-2013_pct.txt: -------------------------------------------------------------------------------- 1 | 11.6 2 | 37.5 3 | 43.8 4 | -8.6 5 | -25.1 6 | -43.8 7 | -8.6 8 | 50.0 9 | -1.2 10 | 46.7 11 | 31.9 12 | -35.3 13 | 29.3 14 | -1.1 15 | -10.7 16 | -12.8 17 | 19.2 18 | 25.1 19 | 19.0 20 | 35.8 21 | -8.4 22 | 5.2 23 | 5.7 24 | 18.3 25 | 30.8 26 | 23.7 27 | 18.2 28 | -1.2 29 | 52.6 30 | 32.6 31 | 7.4 32 | -10.4 33 | 43.7 34 | 12.0 35 | 0.3 36 | 26.6 37 | -8.8 38 | 22.6 39 | 16.4 40 | 12.4 41 | -10.0 42 | 23.8 43 | 10.8 44 | -8.2 45 | 3.6 46 | 14.2 47 | 18.8 48 | -14.3 49 | -25.9 50 | 37.0 51 | 23.8 52 | -7.0 53 | 6.5 54 | 18.5 55 | 31.7 56 | -4.7 57 | 20.4 58 | 22.3 59 | 6.1 60 | 31.2 61 | 18.5 62 | 5.8 63 | 16.5 64 | 31.5 65 | -3.1 66 | 30.2 67 | 7.5 68 | 10.0 69 | 1.3 70 | 37.2 71 | 22.7 72 | 33.1 73 | 28.3 74 | 20.9 75 | -9.0 76 | -11.8 77 | -22.0 78 | 28.4 79 | 10.7 80 | 4.8 81 | 15.6 82 | 5.5 83 | -36.6 84 | 25.9 85 | 14.8 86 | 2.1 87 | 15.9 88 | 32.1 -------------------------------------------------------------------------------- /Chapter_12/annual_infl_rate_1926-2013_pct.txt: -------------------------------------------------------------------------------- 1 | 0.94 2 | -1.92 3 | -1.15 4 | 0 5 | -2.66 6 | -8.94 7 | -10.3 8 | -5.09 9 | 3.51 10 | 2.56 11 | 1.04 12 | 3.73 13 | -2.01 14 | -1.3 15 | 0.73 16 | 5.11 17 | 10.97 18 | 6 19 | 1.64 20 | 2.27 21 | 8.43 22 | 14.65 23 | 7.74 24 | -0.95 25 | 1.09 26 | 7.88 27 | 2.29 28 | 0.82 29 | 0.32 30 | -0.28 31 | 1.52 32 | 3.34 33 | 2.73 34 | 1.01 35 | 1.46 36 | 1.07 37 | 1.2 38 | 1.24 39 | 1.28 40 | 1.59 41 | 3.01 42 | 2.78 43 | 4.27 44 | 5.46 45 | 5.84 46 | 4.3 47 | 3.27 48 | 6.16 49 | 11.03 50 | 9.2 51 | 5.75 52 | 6.5 53 | 7.62 54 | 11.22 55 | 13.58 56 | 10.35 57 | 6.16 58 | 3.22 59 | 4.3 60 | 3.55 61 | 1.91 62 | 3.66 63 | 4.08 64 | 4.83 65 | 5.39 66 | 4.25 67 | 3.03 68 | 2.96 69 | 2.61 70 | 2.81 71 | 2.93 72 | 2.34 73 | 1.55 74 | 2.19 75 | 3.38 76 | 2.83 77 | 1.59 78 | 2.27 79 | 2.68 80 | 3.39 81 | 3.24 82 | 2.85 83 | 3.85 84 | -0.34 85 | 1.64 86 | 3.16 87 | 2.07 88 | 1.47 -------------------------------------------------------------------------------- /Chapter_13/practice_45.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import math 3 | import random 4 | import pygame as pg 5 | 6 | pg.init() # initialize pygame 7 | 8 | # define color table 9 | BLACK = (0, 0, 0) 10 | WHITE = (255, 255, 255) 11 | LT_GRAY = (180, 180, 180) 12 | GRAY = (120, 120, 120) 13 | DK_GRAY = (80, 80, 80) 14 | 15 | class Particle(pg.sprite.Sprite): 16 | """Builds ejecta particles for volcano simulation.""" 17 | 18 | gases_colors = {'SO2': LT_GRAY, 'CO2': GRAY, 'H2S': DK_GRAY, 'H2O': WHITE} 19 | 20 | VENT_LOCATION_XY = (320, 300) 21 | IO_SURFACE_Y = 308 22 | GRAVITY = 0.5 # pixels-per-frame 23 | VELOCITY_SO2 = 8 # pixels-per-frame 24 | 25 | # scalars (SO2 atomic weight/particle atomic weight) used for velocity 26 | vel_scalar = {'SO2': 1, 'CO2': 1.45, 'H2S': 1.9, 'H2O': 3.6} 27 | 28 | def __init__(self, screen, background): 29 | super().__init__() 30 | self.screen = screen 31 | self.background = background 32 | self.image = pg.Surface((4, 4)) 33 | self.rect = self.image.get_rect() 34 | self.gas = 'SO2' 35 | self.color = '' 36 | self.vel = Particle.VELOCITY_SO2 * Particle.vel_scalar[self.gas] 37 | self.x, self.y = Particle.VENT_LOCATION_XY 38 | self.vector() 39 | 40 | def vector(self): 41 | """Calculate particle vector at launch.""" 42 | angles = [65, 55, 45, 35, 25] # 90 is vertical 43 | orient = random.choice(angles) 44 | if orient == 45: 45 | self.color = WHITE 46 | else: 47 | self.color = GRAY 48 | radians = math.radians(orient) 49 | self.dx = self.vel * math.cos(radians) 50 | self.dy = -self.vel * math.sin(radians) # negative as y increases down 51 | 52 | def update(self): 53 | """Apply gravity, draw path, and handle boundary conditions.""" 54 | self.dy += Particle.GRAVITY 55 | pg.draw.line(self.background, self.color, (self.x, self.y), 56 | (self.x + self.dx, self.y + self.dy)) 57 | self.x += self.dx 58 | self.y += self.dy 59 | if self.x < 0 or self.x > self.screen.get_width(): 60 | self.kill() 61 | if self.y < 0 or self.y > Particle.IO_SURFACE_Y: 62 | self.kill() 63 | 64 | def main(): 65 | """Set-up and run game screen and loop.""" 66 | screen = pg.display.set_mode((639, 360)) 67 | pg.display.set_caption("Io Volcano Simulator") 68 | background = pg.image.load("tvashtar_plume.gif") 69 | 70 | # Set-up color-coded legend 71 | legend_font = pg.font.SysFont('None', 26) 72 | text = legend_font.render('White = 45 degrees', True, WHITE, BLACK) 73 | 74 | 75 | particles = pg.sprite.Group() 76 | 77 | clock = pg.time.Clock() 78 | 79 | while True: 80 | clock.tick(25) 81 | particles.add(Particle(screen, background)) 82 | for event in pg.event.get(): 83 | if event.type == pg.QUIT: 84 | pg.quit() 85 | sys.exit() 86 | 87 | screen.blit(background, (0, 0)) 88 | screen.blit(text, (320, 170)) 89 | 90 | particles.update() 91 | particles.draw(screen) 92 | 93 | pg.display.flip() 94 | 95 | if __name__ == "__main__": 96 | main() 97 | -------------------------------------------------------------------------------- /Chapter_13/tvashtar.py: -------------------------------------------------------------------------------- 1 | """Use pygame to simulate Io Pele-type volcano with different particle tyes.""" 2 | import sys 3 | import math 4 | import random 5 | import pygame as pg 6 | 7 | pg.init() # initialize pygame 8 | 9 | # define color table 10 | BLACK = (0, 0, 0) 11 | WHITE = (255, 255, 255) 12 | LT_GRAY = (180, 180, 180) 13 | GRAY = (120, 120, 120) 14 | DK_GRAY = (80, 80, 80) 15 | 16 | class Particle(pg.sprite.Sprite): 17 | """Builds ejecta particles for volcano simulation.""" 18 | 19 | gases_colors = {'SO2': LT_GRAY, 'CO2': GRAY, 'H2S': DK_GRAY, 'H2O': WHITE} 20 | 21 | VENT_LOCATION_XY = (320, 300) # mouth of volcano 22 | IO_SURFACE_Y = 308 # y location of Io surface 23 | GRAVITY = 0.5 # pixels-per-frame 24 | VELOCITY_SO2 = 8 # pixels-per-frame 25 | 26 | # scalars (SO2 atomic weight/particle atomic weight) used for velocity 27 | vel_scalar = {'SO2': 1, 'CO2': 1.45, 'H2S': 1.9, 'H2O': 3.6} 28 | 29 | def __init__(self, screen, background): 30 | super().__init__() 31 | self.screen = screen 32 | self.background = background 33 | self.image = pg.Surface((4, 4)) 34 | self.rect = self.image.get_rect() 35 | self.gas = random.choice(list(Particle.gases_colors.keys())) 36 | self.color = Particle.gases_colors[self.gas] 37 | self.vel = Particle.VELOCITY_SO2 * Particle.vel_scalar[self.gas] 38 | self.x, self.y = Particle.VENT_LOCATION_XY 39 | self.vector() 40 | 41 | def vector(self): 42 | """Calculate particle vector at launch.""" 43 | orient = random.uniform(60, 120) # 90 is vertical 44 | radians = math.radians(orient) 45 | self.dx = self.vel * math.cos(radians) 46 | self.dy = -self.vel * math.sin(radians) # negative as y increases down 47 | 48 | def update(self): 49 | """Apply gravity, draw path, and handle boundary conditions.""" 50 | self.dy += Particle.GRAVITY 51 | pg.draw.line(self.background, self.color, (self.x, self.y), 52 | (self.x + self.dx, self.y + self.dy)) 53 | self.x += self.dx 54 | self.y += self.dy 55 | if self.x < 0 or self.x > self.screen.get_width(): 56 | self.kill() 57 | if self.y < 0 or self.y > Particle.IO_SURFACE_Y: 58 | self.kill() 59 | 60 | def main(): 61 | """Set-up and run game screen and loop.""" 62 | screen = pg.display.set_mode((639, 360)) 63 | pg.display.set_caption('Io Volcano Simulator') 64 | background = pg.image.load('tvashtar_plume.gif') 65 | 66 | # Set-up color-coded legend 67 | legend_font = pg.font.SysFont('None', 24) 68 | water_label = legend_font.render('--- H2O', True, WHITE, BLACK) 69 | co2_label = legend_font.render('--- CO2', True, GRAY, BLACK) 70 | so2_label = legend_font.render('--- SO2/S2', True, LT_GRAY, BLACK) 71 | h2s_label = legend_font.render('--- H2S', True, DK_GRAY, BLACK) 72 | 73 | particles = pg.sprite.Group() 74 | 75 | clock = pg.time.Clock() 76 | 77 | while True: 78 | clock.tick(25) 79 | particles.add(Particle(screen, background)) 80 | for event in pg.event.get(): 81 | if event.type == pg.QUIT: 82 | pg.quit() 83 | sys.exit() 84 | 85 | screen.blit(background, (0, 0)) 86 | screen.blit(water_label, (40, 20)) 87 | screen.blit(h2s_label, (40, 40)) 88 | screen.blit(co2_label, (40, 60)) 89 | screen.blit(so2_label, (40, 80)) 90 | 91 | particles.update() 92 | particles.draw(screen) 93 | 94 | pg.display.flip() 95 | 96 | if __name__ == "__main__": 97 | main() 98 | -------------------------------------------------------------------------------- /Chapter_13/tvashtar_plume.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_13/tvashtar_plume.gif -------------------------------------------------------------------------------- /Chapter_14/mars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_14/mars.png -------------------------------------------------------------------------------- /Chapter_14/mars_water.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_14/mars_water.png -------------------------------------------------------------------------------- /Chapter_14/satellite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_14/satellite.png -------------------------------------------------------------------------------- /Chapter_14/satellite_crash_40x33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_14/satellite_crash_40x33.png -------------------------------------------------------------------------------- /Chapter_14/thrust_audio.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_14/thrust_audio.ogg -------------------------------------------------------------------------------- /Chapter_15/Figure 15-10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/Figure 15-10.pdf -------------------------------------------------------------------------------- /Chapter_15/Figure 15-9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/Figure 15-9.pdf -------------------------------------------------------------------------------- /Chapter_15/crop_n_scale_images.py: -------------------------------------------------------------------------------- 1 | """crop images of a planet to a tightly-fitting box around the planet.""" 2 | import os 3 | import sys 4 | import shutil 5 | from PIL import Image, ImageOps 6 | 7 | def main(): 8 | """Get starting folder, copy folder, run crop function, clean folder.""" 9 | # get name of folder in cwd with original video frames 10 | frames_folder = 'video_frames' 11 | 12 | # prepare files & folders 13 | del_folders('cropped') 14 | shutil.copytree(frames_folder, 'cropped') 15 | 16 | # run cropping function 17 | print("start cropping and scaling...") 18 | os.chdir('cropped') 19 | crop_images() 20 | clean_folder(prefix_to_save='cropped') # delete uncropped originals 21 | 22 | print("Done! \n") 23 | 24 | def del_folders(name): 25 | """If a folder with a named prefix exists in directory delete it.""" 26 | contents = os.listdir() 27 | for item in contents: 28 | if os.path.isdir(item) and item.startswith(name): 29 | shutil.rmtree(item) 30 | 31 | def clean_folder(prefix_to_save): 32 | """Delete all files in folder except those with a named prefix.""" 33 | files = os.listdir() 34 | for file in files: 35 | if not file.startswith(prefix_to_save): 36 | os.remove(file) 37 | 38 | def crop_images(): 39 | """Automatically crop and scale images of a planet to box around planet.""" 40 | files = os.listdir() 41 | for file_num, file in enumerate(files, start=1): 42 | with Image.open(file) as img: 43 | gray = img.convert('L') 44 | bw = gray.point(lambda x: 0 if x < 90 else 255) 45 | box = bw.getbbox() 46 | padded_box = (box[0]-20, box[1]-20, box[2]+20, box[3]+20) 47 | cropped = img.crop(padded_box) 48 | scaled = ImageOps.fit(cropped, (860, 860), 49 | Image.LANCZOS, 0, (0.5, 0.5)) 50 | file_name = 'cropped_{}.jpg'.format(file_num) 51 | scaled.save(file_name, "JPEG") 52 | 53 | 54 | if __name__ == '__main__': 55 | main() 56 | -------------------------------------------------------------------------------- /Chapter_15/enhance_image.py: -------------------------------------------------------------------------------- 1 | """Run Pillow image enhancement filters on an image & save results.""" 2 | from PIL import Image, ImageFilter, ImageEnhance 3 | 4 | def main(): 5 | """Get an image and enhance, show and save it.""" 6 | in_file = 'jupiter_stacked.tif' 7 | img = Image.open(in_file) 8 | img_enh = enhance_image(img) 9 | img_enh.show() 10 | img_enh.save('enhanced.tif', 'TIFF') 11 | 12 | def enhance_image(image): 13 | """Improve an image using Pillow filters & transforms.""" 14 | enhancer = ImageEnhance.Brightness(image) 15 | img_enh = enhancer.enhance(0.75) # 0.75 looks good 16 | 17 | enhancer = ImageEnhance.Contrast(img_enh) 18 | img_enh = enhancer.enhance(1.6) 19 | 20 | enhancer = ImageEnhance.Color(img_enh) 21 | img_enh = enhancer.enhance(1.7) 22 | 23 | img_enh = img_enh.rotate(angle=133, expand=True) 24 | 25 | img_enh = img_enh.filter(ImageFilter.SHARPEN) 26 | 27 | return img_enh 28 | 29 | if __name__ == '__main__': 30 | main() 31 | -------------------------------------------------------------------------------- /Chapter_15/moon_cropped/moon_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/moon_cropped/moon_1.png -------------------------------------------------------------------------------- /Chapter_15/moon_cropped/moon_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/moon_cropped/moon_2.png -------------------------------------------------------------------------------- /Chapter_15/moon_cropped/moon_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/moon_cropped/moon_3.png -------------------------------------------------------------------------------- /Chapter_15/moon_cropped/moon_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/moon_cropped/moon_4.png -------------------------------------------------------------------------------- /Chapter_15/moon_cropped/moon_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/moon_cropped/moon_5.png -------------------------------------------------------------------------------- /Chapter_15/stack_images.py: -------------------------------------------------------------------------------- 1 | """Average pixels in a series of images to produce a single stacked image.""" 2 | import os 3 | from PIL import Image 4 | 5 | print("\nstart stacking images...") 6 | # list images in directory 7 | os.chdir('cropped') 8 | images = os.listdir() 9 | 10 | # loop through images and extract RGB channels as separate lists 11 | red_data = [] 12 | green_data = [] 13 | blue_data = [] 14 | for image in images: 15 | with Image.open(image) as img: 16 | if image == images[0]: # get size of 1st cropped image 17 | img_size = img.size # width-height tuple to use later 18 | red_data.append(list(img.getdata(0))) 19 | green_data.append(list(img.getdata(1))) 20 | blue_data.append(list(img.getdata(2))) 21 | 22 | ave_red = [round(sum(x) / len(red_data)) for x in zip(*red_data)] 23 | ave_blue = [round(sum(x) / len(blue_data)) for x in zip(*blue_data)] 24 | ave_green = [round(sum(x) / len(green_data)) for x in zip(*green_data)] 25 | 26 | merged_data = [(x) for x in zip(ave_red, ave_green, ave_blue)] 27 | stacked = Image.new('RGB', (img_size)) 28 | stacked.putdata(merged_data) 29 | stacked.show() 30 | 31 | os.chdir('..') 32 | stacked.save('jupiter_stacked.tif', 'TIFF') 33 | -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 001.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 002.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 003.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 004.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 005.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 006.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 006.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 007.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 007.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 008.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 008.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 009.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 009.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 01.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 010.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 010.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 011.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 011.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 012.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 012.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 013.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 013.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 014.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 014.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 015.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 015.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 016.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 016.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 017.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 017.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 018.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 018.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 019.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 019.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 02.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 020.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 020.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 021.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 021.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 022.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 022.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 023.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 023.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 024.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 025.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 025.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 026.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 026.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 027.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 027.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 028.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 028.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 029.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 029.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 03.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 030.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 030.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 031.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 031.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 032.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 032.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 033.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 033.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 034.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 034.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 035.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 035.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 036.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 036.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 037.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 037.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 038.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 038.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 039.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 039.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 04.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 040.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 040.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 041.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 041.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 042.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 042.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 043.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 043.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 044.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 044.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 045.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 045.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 046.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 046.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 047.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 047.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 048.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 049.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 049.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 05.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 050.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 050.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 051.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 051.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 052.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 052.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 053.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 053.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 054.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 054.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 055.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 055.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 056.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 056.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 057.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 057.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 058.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 058.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 059.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 059.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 06.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 060.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 060.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 061.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 061.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 062.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 062.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 063.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 063.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 064.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 064.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 065.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 065.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 066.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 066.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 067.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 067.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 068.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 068.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 069.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 069.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 07.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 070.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 070.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 071.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 071.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 072.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 072.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 073.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 073.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 074.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 074.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 075.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 075.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 076.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 076.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 077.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 077.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 078.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 078.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 079.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 079.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 08.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 080.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 080.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 081.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 081.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 082.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 082.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 083.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 083.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 084.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 084.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 085.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 085.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 086.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 086.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 087.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 087.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 088.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 088.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 089.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 089.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 09.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 090.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 090.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 091.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 091.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 092.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 092.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 093.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 093.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 094.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 094.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 095.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 095.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 096.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 096.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 097.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 097.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 098.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 098.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 099.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 099.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 10.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 100.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 101.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 101.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 102.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 102.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 103.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 103.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 104.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 104.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 105.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 105.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 106.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 106.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 107.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 107.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 108.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 108.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 109.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 109.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 11.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 110.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 110.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 111.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 111.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 112.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 113.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 113.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 114.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 114.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 115.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 115.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 116.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 116.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 117.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 117.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 118.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 118.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 119.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 119.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 12.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 120.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 120.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 121.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 121.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 122.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 122.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 123.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 123.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 124.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 124.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 125.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 125.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 126.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 126.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 127.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 127.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 128.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 128.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 129.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 129.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 13.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 130.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 130.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 131.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 131.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 132.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 132.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 133.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 133.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 134.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 134.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 135.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 135.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 136.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 136.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 137.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 137.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 138.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 138.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 139.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 139.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 14.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 140.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 140.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 141.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 141.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 142.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 142.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 143.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 143.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 144.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 144.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 145.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 145.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 146.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 146.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 147.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 147.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 148.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 148.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 149.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 149.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 15.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 150.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 150.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 151.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 151.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 152.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 152.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 153.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 153.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 154.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 154.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 155.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 155.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 156.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 156.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 157.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 157.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 158.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 158.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 159.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 159.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 16.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 160.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 160.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 161.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 161.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 162.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 162.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 163.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 163.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 164.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 164.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 165.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 165.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 166.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 166.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 167.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 167.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 168.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 168.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 169.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 169.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 17.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 170.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 170.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 171.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 171.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 172.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 172.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 173.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 173.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 174.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 174.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 175.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 175.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 176.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 176.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 177.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 177.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 178.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 178.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 179.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 179.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 18.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 180.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 180.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 181.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 181.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 182.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 182.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 183.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 183.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 184.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 184.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 185.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 185.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 186.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 186.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 187.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 187.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 188.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 188.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 189.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 189.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 19.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 190.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 190.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 191.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 191.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 192.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 192.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 193.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 193.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 194.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 194.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 195.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 195.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 196.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 196.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 197.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 197.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 198.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 198.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 199.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 199.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 20.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 200.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 200.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 21.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 22.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 23.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 24.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 25.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 26.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 27.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 28.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 29.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 30.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 31.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 32.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 33.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 34.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 35.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 36.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 37.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 38.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 39.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 40.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 41.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 41.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 42.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 42.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 43.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 43.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 44.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 44.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 45.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 45.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 46.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 46.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 47.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 47.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 48.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 48.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 49.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 49.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/MVI_6450 50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/MVI_6450 50.jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/a (10).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/a (10).jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/a (5).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/a (5).jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/a (6).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/a (6).jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/a (7).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/a (7).jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/a (8).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/a (8).jpg -------------------------------------------------------------------------------- /Chapter_15/video_frames/a (9).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_15/video_frames/a (9).jpg -------------------------------------------------------------------------------- /Chapter_16/Clinton_votes_Illinois.txt: -------------------------------------------------------------------------------- 1 | 7676 2 | 1262 3 | 2068 4 | 8986 5 | 476 6 | 6029 7 | 739 8 | 2447 9 | 1621 10 | 50137 11 | 3992 12 | 1877 13 | 1020 14 | 3945 15 | 7309 16 | 1611946 17 | 1992 18 | 1031 19 | 20466 20 | 1910 21 | 1949 22 | 228622 23 | 1793 24 | 434 25 | 3083 26 | 1819 27 | 1414 28 | 4727 29 | 6133 30 | 657 31 | 1205 32 | 8065 33 | 802 34 | 2139 35 | 420 36 | 1155 37 | 8871 38 | 2504 39 | 11634 40 | 924 41 | 4425 42 | 2679 43 | 4462 44 | 1142 45 | 103665 46 | 18971 47 | 24884 48 | 10083 49 | 171095 50 | 19543 51 | 1290 52 | 5528 53 | 4023 54 | 3313 55 | 18343 56 | 6689 57 | 50587 58 | 4369 59 | 1789 60 | 2014 61 | 1558 62 | 5288 63 | 60803 64 | 36196 65 | 1817 66 | 3071 67 | 5535 68 | 3504 69 | 4696 70 | 1481 71 | 8050 72 | 38060 73 | 2462 74 | 2645 75 | 1413 76 | 375 77 | 962 78 | 1147 79 | 3439 80 | 1584 81 | 32298 82 | 2572 83 | 40907 84 | 1075 85 | 535 86 | 2288 87 | 60756 88 | 751 89 | 7768 90 | 20685 91 | 2402 92 | 10039 93 | 1151 94 | 2987 95 | 1448 96 | 1048 97 | 1412 98 | 11035 99 | 151927 100 | 8581 101 | 55713 102 | 5092 103 | -------------------------------------------------------------------------------- /Chapter_16/Illinois_votes.txt: -------------------------------------------------------------------------------- 1 | 10 2 | 12 3 | 14 4 | 18 5 | 18 6 | 19 7 | 20 8 | 20 9 | 22 10 | 24 11 | 26 12 | 26 13 | 33 14 | 34 15 | 35 16 | 39 17 | 40 18 | 41 19 | 41 20 | 42 21 | 42 22 | 44 23 | 46 24 | 47 25 | 48 26 | 48 27 | 49 28 | 49 29 | 51 30 | 54 31 | 55 32 | 55 33 | 55 34 | 57 35 | 57 36 | 61 37 | 62 38 | 64 39 | 67 40 | 68 41 | 68 42 | 72 43 | 73 44 | 74 45 | 75 46 | 77 47 | 77 48 | 78 49 | 81 50 | 82 51 | 83 52 | 87 53 | 90 54 | 91 55 | 92 56 | 94 57 | 98 58 | 100 59 | 113 60 | 114 61 | 116 62 | 125 63 | 125 64 | 126 65 | 128 66 | 130 67 | 135 68 | 136 69 | 149 70 | 149 71 | 150 72 | 154 73 | 158 74 | 158 75 | 164 76 | 164 77 | 172 78 | 175 79 | 181 80 | 182 81 | 183 82 | 189 83 | 191 84 | 194 85 | 194 86 | 197 87 | 200 88 | 200 89 | 205 90 | 209 91 | 210 92 | 214 93 | 219 94 | 226 95 | 235 96 | 238 97 | 238 98 | 239 99 | 247 100 | 249 101 | 250 102 | 257 103 | 266 104 | 275 105 | 277 106 | 278 107 | 283 108 | 287 109 | 289 110 | 296 111 | 311 112 | 321 113 | 323 114 | 323 115 | 324 116 | 326 117 | 328 118 | 330 119 | 347 120 | 348 121 | 358 122 | 359 123 | 362 124 | 372 125 | 375 126 | 395 127 | 396 128 | 410 129 | 420 130 | 431 131 | 434 132 | 447 133 | 459 134 | 475 135 | 476 136 | 490 137 | 500 138 | 535 139 | 537 140 | 541 141 | 548 142 | 554 143 | 558 144 | 573 145 | 587 146 | 599 147 | 635 148 | 644 149 | 649 150 | 649 151 | 657 152 | 662 153 | 670 154 | 733 155 | 739 156 | 742 157 | 742 158 | 751 159 | 751 160 | 777 161 | 780 162 | 802 163 | 813 164 | 830 165 | 842 166 | 896 167 | 924 168 | 934 169 | 936 170 | 942 171 | 962 172 | 997 173 | 1020 174 | 1025 175 | 1031 176 | 1048 177 | 1068 178 | 1075 179 | 1081 180 | 1085 181 | 1097 182 | 1109 183 | 1140 184 | 1142 185 | 1147 186 | 1151 187 | 1155 188 | 1159 189 | 1168 190 | 1205 191 | 1208 192 | 1243 193 | 1262 194 | 1285 195 | 1290 196 | 1412 197 | 1413 198 | 1414 199 | 1443 200 | 1448 201 | 1473 202 | 1481 203 | 1496 204 | 1558 205 | 1584 206 | 1621 207 | 1653 208 | 1675 209 | 1678 210 | 1703 211 | 1721 212 | 1767 213 | 1778 214 | 1789 215 | 1793 216 | 1796 217 | 1797 218 | 1817 219 | 1819 220 | 1838 221 | 1851 222 | 1877 223 | 1910 224 | 1942 225 | 1949 226 | 1966 227 | 1992 228 | 1992 229 | 2014 230 | 2068 231 | 2139 232 | 2155 233 | 2155 234 | 2288 235 | 2298 236 | 2402 237 | 2447 238 | 2462 239 | 2482 240 | 2504 241 | 2524 242 | 2572 243 | 2645 244 | 2675 245 | 2679 246 | 2778 247 | 2896 248 | 2987 249 | 3015 250 | 3071 251 | 3083 252 | 3206 253 | 3216 254 | 3313 255 | 3358 256 | 3439 257 | 3504 258 | 3725 259 | 3785 260 | 3821 261 | 3885 262 | 3945 263 | 3975 264 | 3992 265 | 4023 266 | 4047 267 | 4058 268 | 4145 269 | 4181 270 | 4206 271 | 4231 272 | 4275 273 | 4369 274 | 4425 275 | 4434 276 | 4455 277 | 4462 278 | 4480 279 | 4512 280 | 4521 281 | 4547 282 | 4649 283 | 4696 284 | 4727 285 | 4807 286 | 4846 287 | 4855 288 | 4888 289 | 5021 290 | 5064 291 | 5077 292 | 5092 293 | 5288 294 | 5472 295 | 5528 296 | 5535 297 | 5571 298 | 5622 299 | 5634 300 | 5640 301 | 5645 302 | 5698 303 | 5739 304 | 5754 305 | 5790 306 | 6029 307 | 6121 308 | 6133 309 | 6277 310 | 6367 311 | 6430 312 | 6689 313 | 6795 314 | 6855 315 | 6967 316 | 7180 317 | 7309 318 | 7372 319 | 7676 320 | 7748 321 | 7768 322 | 8050 323 | 8065 324 | 8181 325 | 8229 326 | 8276 327 | 8492 328 | 8581 329 | 8612 330 | 8630 331 | 8871 332 | 8986 333 | 9076 334 | 9281 335 | 9292 336 | 9750 337 | 10023 338 | 10039 339 | 10083 340 | 10208 341 | 10543 342 | 10737 343 | 10843 344 | 11035 345 | 11083 346 | 11634 347 | 11695 348 | 11822 349 | 11859 350 | 12282 351 | 12412 352 | 12615 353 | 12629 354 | 13003 355 | 13116 356 | 13207 357 | 13312 358 | 13454 359 | 13635 360 | 13985 361 | 14322 362 | 14352 363 | 18343 364 | 18971 365 | 19087 366 | 19091 367 | 19543 368 | 20357 369 | 20466 370 | 20685 371 | 21570 372 | 22790 373 | 24884 374 | 24961 375 | 25129 376 | 26689 377 | 26866 378 | 26998 379 | 32298 380 | 32484 381 | 33368 382 | 35633 383 | 36196 384 | 37237 385 | 38060 386 | 38707 387 | 40907 388 | 49944 389 | 50137 390 | 50587 391 | 53857 392 | 55624 393 | 55713 394 | 58678 395 | 60756 396 | 60803 397 | 70490 398 | 71612 399 | 82734 400 | 103665 401 | 109767 402 | 132720 403 | 151927 404 | 166415 405 | 171095 406 | 228622 407 | 453287 408 | 1611946 409 | -------------------------------------------------------------------------------- /Chapter_16/Johnson_votes_Illinois.txt: -------------------------------------------------------------------------------- 1 | 1168 2 | 48 3 | 447 4 | 1068 5 | 77 6 | 842 7 | 72 8 | 395 9 | 172 10 | 4547 11 | 662 12 | 277 13 | 209 14 | 813 15 | 1025 16 | 58678 17 | 324 18 | 238 19 | 2482 20 | 431 21 | 362 22 | 20357 23 | 328 24 | 91 25 | 644 26 | 283 27 | 323 28 | 541 29 | 780 30 | 73 31 | 164 32 | 1140 33 | 114 34 | 348 35 | 46 36 | 158 37 | 1243 38 | 554 39 | 1081 40 | 181 41 | 573 42 | 410 43 | 500 44 | 182 45 | 9292 46 | 1992 47 | 2675 48 | 1109 49 | 13312 50 | 2298 51 | 194 52 | 896 53 | 777 54 | 537 55 | 1851 56 | 934 57 | 5472 58 | 548 59 | 266 60 | 321 61 | 194 62 | 635 63 | 7180 64 | 4855 65 | 311 66 | 475 67 | 742 68 | 599 69 | 670 70 | 238 71 | 1159 72 | 3725 73 | 347 74 | 558 75 | 257 76 | 62 77 | 55 78 | 136 79 | 490 80 | 235 81 | 3015 82 | 358 83 | 4512 84 | 154 85 | 68 86 | 372 87 | 4181 88 | 149 89 | 936 90 | 3358 91 | 219 92 | 1097 93 | 175 94 | 359 95 | 289 96 | 191 97 | 205 98 | 1285 99 | 11822 100 | 997 101 | 5064 102 | 942 103 | -------------------------------------------------------------------------------- /Chapter_16/Stein_votes_Illinois.txt: -------------------------------------------------------------------------------- 1 | 249 2 | 14 3 | 67 4 | 330 5 | 10 6 | 197 7 | 26 8 | 98 9 | 51 10 | 1797 11 | 149 12 | 49 13 | 48 14 | 125 15 | 287 16 | 32484 17 | 68 18 | 47 19 | 751 20 | 61 21 | 90 22 | 6367 23 | 49 24 | 12 25 | 100 26 | 57 27 | 87 28 | 183 29 | 239 30 | 20 31 | 39 32 | 323 33 | 24 34 | 64 35 | 18 36 | 19 37 | 200 38 | 150 39 | 649 40 | 26 41 | 158 42 | 94 43 | 116 44 | 57 45 | 2896 46 | 587 47 | 742 48 | 278 49 | 3821 50 | 649 51 | 41 52 | 226 53 | 125 54 | 130 55 | 459 56 | 247 57 | 1838 58 | 164 59 | 78 60 | 54 61 | 42 62 | 210 63 | 2155 64 | 1208 65 | 74 66 | 82 67 | 200 68 | 128 69 | 189 70 | 44 71 | 275 72 | 1085 73 | 113 74 | 83 75 | 40 76 | 18 77 | 20 78 | 35 79 | 135 80 | 81 81 | 733 82 | 77 83 | 1443 84 | 41 85 | 22 86 | 55 87 | 1473 88 | 33 89 | 250 90 | 830 91 | 126 92 | 296 93 | 42 94 | 92 95 | 75 96 | 55 97 | 34 98 | 326 99 | 3885 100 | 396 101 | 1703 102 | 214 103 | -------------------------------------------------------------------------------- /Chapter_16/Trump_votes_Illinois.txt: -------------------------------------------------------------------------------- 1 | 22790 2 | 1496 3 | 4888 4 | 12282 5 | 1796 6 | 9281 7 | 1721 8 | 4434 9 | 3216 10 | 33368 11 | 10543 12 | 5622 13 | 5021 14 | 12412 15 | 13003 16 | 453287 17 | 6277 18 | 4206 19 | 19091 20 | 5077 21 | 5698 22 | 166415 23 | 5645 24 | 2778 25 | 13635 26 | 7372 27 | 4480 28 | 13116 29 | 8492 30 | 1942 31 | 4145 32 | 13454 33 | 3206 34 | 6430 35 | 1653 36 | 2155 37 | 13985 38 | 9750 39 | 10843 40 | 3975 41 | 11695 42 | 7748 43 | 6121 44 | 4649 45 | 82734 46 | 25129 47 | 24961 48 | 10737 49 | 109767 50 | 26689 51 | 4521 52 | 8612 53 | 10208 54 | 8181 55 | 26866 56 | 14322 57 | 70490 58 | 11859 59 | 3785 60 | 4058 61 | 4846 62 | 6795 63 | 71612 64 | 37237 65 | 4231 66 | 4807 67 | 12629 68 | 8630 69 | 9076 70 | 4455 71 | 14352 72 | 35633 73 | 6855 74 | 5634 75 | 5754 76 | 1678 77 | 1675 78 | 1767 79 | 10023 80 | 5739 81 | 26998 82 | 8276 83 | 49944 84 | 2524 85 | 1966 86 | 8229 87 | 53857 88 | 1778 89 | 11083 90 | 38707 91 | 5790 92 | 19087 93 | 4047 94 | 4275 95 | 5571 96 | 6967 97 | 5640 98 | 12615 99 | 132720 100 | 21570 101 | 55624 102 | 13207 103 | -------------------------------------------------------------------------------- /Chapter_16/beat_benford_practice.py: -------------------------------------------------------------------------------- 1 | """Manipulate vote counts so that final results conform to Benford's Law.""" 2 | 3 | # example below is for Trump vs Clinton, Illinois, 2016 Presidental Election 4 | 5 | def load_data(filename): 6 | """Open a text file of numbers & turn contents into a list of integers.""" 7 | with open(filename) as f: 8 | lines = f.read().strip().split('\n') 9 | return [int(i) for i in lines] # turn strings to integers 10 | 11 | def steal_votes(opponent_votes, candidate_votes, scalar): 12 | """Use scalar to reduce one vote count & increase another, return as lists. 13 | 14 | Arguments: 15 | opponent_votes – votes to steal from 16 | candidate_votes - votes to increase by stolen amount 17 | scalar - fractional percentage, < 1, used to reduce votes 18 | 19 | Returns: 20 | list of changed opponent votes 21 | list of changed candidate votes 22 | 23 | """ 24 | new_opponent_votes = [] 25 | new_candidate_votes = [] 26 | for opp_vote, can_vote in zip(opponent_votes, candidate_votes): 27 | new_opp_vote = round(opp_vote * scalar) 28 | new_opponent_votes.append(new_opp_vote) 29 | stolen_votes = opp_vote - new_opp_vote 30 | new_can_vote = can_vote + stolen_votes 31 | new_candidate_votes.append(new_can_vote) 32 | return new_opponent_votes, new_candidate_votes 33 | 34 | 35 | def main(): 36 | """Run the program. 37 | 38 | Load data, set target winning vote count, call functions, display 39 | results as table, write new combined vote total as text file to 40 | use as input for Benford's Law analysis. 41 | 42 | """ 43 | # load vote data 44 | c_votes = load_data('Clinton_votes_Illinois.txt') 45 | j_votes = load_data('Johnson_votes_Illinois.txt') 46 | s_votes = load_data('Stein_votes_Illinois.txt') 47 | t_votes = load_data('Trump_votes_Illinois.txt') 48 | 49 | total_votes = sum(c_votes + j_votes + s_votes + t_votes) 50 | 51 | 52 | # assume Trump amasses a plurality of the vote with 49% 53 | t_target = round(total_votes * 0.49) 54 | print("\nTrump winning target = {:,} votes".format(t_target)) 55 | 56 | # calculate extra votes needed for Trump victory 57 | extra_votes_needed = abs(t_target - sum(t_votes)) 58 | print("extra votes needed = {:,}".format(extra_votes_needed)) 59 | 60 | # calculate scalar needed to generate extra votes 61 | scalar = 1 - (extra_votes_needed / sum(c_votes + j_votes + s_votes)) 62 | print("scalar = {:.3}".format(scalar)) 63 | print() 64 | 65 | # flip vote counts based on scalar & build new combined list of votes 66 | fake_counts = [] 67 | new_c_votes, new_t_votes = steal_votes(c_votes, t_votes, scalar) 68 | fake_counts.extend(new_c_votes) 69 | new_j_votes, new_t_votes = steal_votes(j_votes, new_t_votes, scalar) 70 | fake_counts.extend(new_j_votes) 71 | new_s_votes, new_t_votes = steal_votes(s_votes, new_t_votes, scalar) 72 | fake_counts.extend(new_s_votes) 73 | fake_counts.extend(new_t_votes) # add last as has been changing up til now 74 | 75 | # compare old and new vote counts & totals in tabular form 76 | # switch-out "Trump" and "Clinton" as necessary 77 | for i in range(0, len(t_votes)): 78 | print("old Trump: {} \t new Trump: {} \t old Clinton: {} \t " \ 79 | "new Clinton: {}". 80 | format(t_votes[i], new_t_votes[i], c_votes[i], new_c_votes[i])) 81 | print("-" * 95) 82 | print("TOTALS:") 83 | print("old Trump: {:,} \t new Trump: {:,} \t old Clinton: {:,} " \ 84 | "new Clinton: {:,}".format(sum(t_votes), sum(new_t_votes), 85 | sum(c_votes), sum(new_c_votes))) 86 | 87 | # write-out a text file to use as input to benford.py program 88 | # this program will check conformance of faked votes to Benford's Law 89 | with open('fake_Illinois_counts.txt', 'w') as f: 90 | for count in fake_counts: 91 | f.write("{}\n".format(count)) 92 | 93 | 94 | if __name__ == '__main__': 95 | main() 96 | -------------------------------------------------------------------------------- /Chapter_16/benford.py: -------------------------------------------------------------------------------- 1 | """Check conformance of numerical data to Benford's Law.""" 2 | import sys 3 | import math 4 | from collections import defaultdict 5 | import matplotlib.pyplot as plt 6 | 7 | # Benford's Law percentages for leading digits 1-9 8 | BENFORD = [30.1, 17.6, 12.5, 9.7, 7.9, 6.7, 5.8, 5.1, 4.6] 9 | 10 | def load_data(filename): 11 | """Open a text file & return a list of strings.""" 12 | with open(filename) as f: 13 | return f.read().strip().split('\n') 14 | 15 | def count_first_digits(data_list): 16 | """Count 1st digits in list of numbers; return counts & frequency.""" 17 | first_digits = defaultdict(int) # default value of int is 0 18 | for sample in data_list: 19 | if sample == '': 20 | continue 21 | try: 22 | int(sample) 23 | except ValueError as e: 24 | print(e, file=sys.stderr) 25 | print("Samples must be integers. Exiting.", file=sys.stderr) 26 | sys.exit(1) 27 | first_digits[sample[0]] += 1 28 | 29 | # check for missing digits 30 | keys = [str(digit) for digit in range(1, 10)] 31 | for key in keys: 32 | if key not in first_digits: 33 | first_digits[key] = 0 34 | 35 | data_count = [v for (k, v) in sorted(first_digits.items())] 36 | total_count = sum(data_count) 37 | data_pct = [(i / total_count) * 100 for i in data_count] 38 | return data_count, data_pct, total_count 39 | 40 | def get_expected_counts(total_count): 41 | """Return list of expected Benford's Law counts for total sample count.""" 42 | return [round(p * total_count / 100) for p in BENFORD] 43 | 44 | def chi_square_test(data_count, expected_counts): 45 | """Return boolean on chi-square test (8 degrees of freedom & P-val=0.05).""" 46 | chi_square_stat = 0 # chi square test statistic 47 | for data, expected in zip(data_count, expected_counts): 48 | chi_square = math.pow(data - expected, 2) 49 | chi_square_stat += chi_square / expected 50 | print("\nChi-squared Test Statistic = {:.3f}".format(chi_square_stat)) 51 | print("Critical value at a P-value of 0.05 is 15.51.") 52 | return chi_square_stat < 15.51 53 | 54 | def bar_chart(data_pct): 55 | """Make bar chart of observed vs expected 1st digit frequency in percent.""" 56 | fig, ax = plt.subplots() 57 | 58 | index = [i + 1 for i in range(len(data_pct))] # 1st digits for x-axis 59 | 60 | # text for labels, title and ticks 61 | fig.canvas.set_window_title('Percentage First Digits') 62 | ax.set_title('Data vs. Benford Values', fontsize=15) 63 | ax.set_ylabel('Frequency (%)', fontsize=16) 64 | ax.set_xticks(index) 65 | ax.set_xticklabels(index, fontsize=14) 66 | 67 | # build bars 68 | rects = ax.bar(index, data_pct, width=0.95, color='black', label='Data') 69 | 70 | # attach a text label above each bar displaying its height 71 | for rect in rects: 72 | height = rect.get_height() 73 | ax.text(rect.get_x() + rect.get_width()/2, height, 74 | '{:0.1f}'.format(height), ha='center', va='bottom', 75 | fontsize=13) 76 | 77 | # plot Benford values as red dots 78 | ax.scatter(index, BENFORD, s=150, c='red', zorder=2, label='Benford') 79 | 80 | # Hide the right and top spines & add legend 81 | ax.spines['right'].set_visible(False) 82 | ax.spines['top'].set_visible(False) 83 | ax.legend(prop={'size':15}, frameon=False) 84 | 85 | plt.show() 86 | 87 | def main(): 88 | """Call functions and print stats.""" 89 | # load data 90 | while True: 91 | filename = input("\nName of file with COUNT data: ") 92 | try: 93 | data_list = load_data(filename) 94 | except IOError as e: 95 | print("{}. Try again.".format(e), file=sys.stderr) 96 | else: 97 | break 98 | data_count, data_pct, total_count = count_first_digits(data_list) 99 | expected_counts = get_expected_counts(total_count) 100 | print("\nobserved counts = {}".format(data_count)) 101 | print("expected counts = {}".format(expected_counts), "\n") 102 | 103 | print("First Digit Probabilities:") 104 | for i in range(1, 10): 105 | print("{}: observed: {:.3f} expected: {:.3f}". 106 | format(i, data_pct[i - 1] / 100, BENFORD[i - 1] / 100)) 107 | 108 | if chi_square_test(data_count, expected_counts): 109 | print("Observed distribution matches expected distribution.") 110 | else: 111 | print("Observed distribution does not match expected.", file=sys.stderr) 112 | 113 | bar_chart(data_pct) 114 | 115 | if __name__ == '__main__': 116 | main() 117 | -------------------------------------------------------------------------------- /Chapter_16/fake_Illinois_counts.txt: -------------------------------------------------------------------------------- 1 | 6402 2 | 1053 3 | 1725 4 | 7495 5 | 397 6 | 5029 7 | 616 8 | 2041 9 | 1352 10 | 41818 11 | 3330 12 | 1566 13 | 851 14 | 3290 15 | 6096 16 | 1344496 17 | 1661 18 | 860 19 | 17070 20 | 1593 21 | 1626 22 | 190690 23 | 1496 24 | 362 25 | 2571 26 | 1517 27 | 1179 28 | 3943 29 | 5115 30 | 548 31 | 1005 32 | 6727 33 | 669 34 | 1784 35 | 350 36 | 963 37 | 7399 38 | 2089 39 | 9704 40 | 771 41 | 3691 42 | 2235 43 | 3722 44 | 953 45 | 86465 46 | 15823 47 | 20755 48 | 8410 49 | 142707 50 | 16300 51 | 1076 52 | 4611 53 | 3356 54 | 2763 55 | 15300 56 | 5579 57 | 42194 58 | 3644 59 | 1492 60 | 1680 61 | 1300 62 | 4411 63 | 50715 64 | 30190 65 | 1516 66 | 2561 67 | 4617 68 | 2923 69 | 3917 70 | 1235 71 | 6714 72 | 31745 73 | 2054 74 | 2206 75 | 1179 76 | 313 77 | 802 78 | 957 79 | 2868 80 | 1321 81 | 26939 82 | 2145 83 | 34120 84 | 897 85 | 446 86 | 1908 87 | 50676 88 | 626 89 | 6479 90 | 17253 91 | 2003 92 | 8373 93 | 960 94 | 2491 95 | 1208 96 | 874 97 | 1178 98 | 9204 99 | 126720 100 | 7157 101 | 46469 102 | 4247 103 | 974 104 | 40 105 | 373 106 | 891 107 | 64 108 | 702 109 | 60 110 | 329 111 | 143 112 | 3793 113 | 552 114 | 231 115 | 174 116 | 678 117 | 855 118 | 48942 119 | 270 120 | 199 121 | 2070 122 | 359 123 | 302 124 | 16979 125 | 274 126 | 76 127 | 537 128 | 236 129 | 269 130 | 451 131 | 651 132 | 61 133 | 137 134 | 951 135 | 95 136 | 290 137 | 38 138 | 132 139 | 1037 140 | 462 141 | 902 142 | 151 143 | 478 144 | 342 145 | 417 146 | 152 147 | 7750 148 | 1661 149 | 2231 150 | 925 151 | 11103 152 | 1917 153 | 162 154 | 747 155 | 648 156 | 448 157 | 1544 158 | 779 159 | 4564 160 | 457 161 | 222 162 | 268 163 | 162 164 | 530 165 | 5989 166 | 4049 167 | 259 168 | 396 169 | 619 170 | 500 171 | 559 172 | 199 173 | 967 174 | 3107 175 | 289 176 | 465 177 | 214 178 | 52 179 | 46 180 | 113 181 | 409 182 | 196 183 | 2515 184 | 299 185 | 3763 186 | 128 187 | 57 188 | 310 189 | 3487 190 | 124 191 | 781 192 | 2801 193 | 183 194 | 915 195 | 146 196 | 299 197 | 241 198 | 159 199 | 171 200 | 1072 201 | 9861 202 | 832 203 | 4224 204 | 786 205 | 208 206 | 12 207 | 56 208 | 275 209 | 8 210 | 164 211 | 22 212 | 82 213 | 43 214 | 1499 215 | 124 216 | 41 217 | 40 218 | 104 219 | 239 220 | 27094 221 | 57 222 | 39 223 | 626 224 | 51 225 | 75 226 | 5311 227 | 41 228 | 10 229 | 83 230 | 48 231 | 73 232 | 153 233 | 199 234 | 17 235 | 33 236 | 269 237 | 20 238 | 53 239 | 15 240 | 16 241 | 167 242 | 125 243 | 541 244 | 22 245 | 132 246 | 78 247 | 97 248 | 48 249 | 2416 250 | 490 251 | 619 252 | 232 253 | 3187 254 | 541 255 | 34 256 | 189 257 | 104 258 | 108 259 | 383 260 | 206 261 | 1533 262 | 137 263 | 65 264 | 45 265 | 35 266 | 175 267 | 1797 268 | 1008 269 | 62 270 | 68 271 | 167 272 | 107 273 | 158 274 | 37 275 | 229 276 | 905 277 | 94 278 | 69 279 | 33 280 | 15 281 | 17 282 | 29 283 | 113 284 | 68 285 | 611 286 | 64 287 | 1204 288 | 34 289 | 18 290 | 46 291 | 1229 292 | 28 293 | 209 294 | 692 295 | 105 296 | 247 297 | 35 298 | 77 299 | 63 300 | 46 301 | 28 302 | 272 303 | 3240 304 | 330 305 | 1420 306 | 178 307 | 24299 308 | 1715 309 | 5316 310 | 14005 311 | 1890 312 | 10454 313 | 1860 314 | 4922 315 | 3522 316 | 42739 317 | 11340 318 | 5987 319 | 5233 320 | 13223 321 | 14434 322 | 735863 323 | 6673 324 | 4424 325 | 23024 326 | 5476 327 | 6096 328 | 208781 329 | 6004 330 | 2867 331 | 14271 332 | 7730 333 | 4783 334 | 14020 335 | 9679 336 | 2066 337 | 4378 338 | 15035 339 | 3362 340 | 6854 341 | 1734 342 | 2376 343 | 15696 344 | 10282 345 | 13060 346 | 4162 347 | 12550 348 | 8276 349 | 6963 350 | 4877 351 | 101956 352 | 28705 353 | 29657 354 | 12640 355 | 140998 356 | 30421 357 | 4774 358 | 9715 359 | 11025 360 | 8842 361 | 30292 362 | 15628 363 | 80096 364 | 12702 365 | 4139 366 | 4454 367 | 5143 368 | 7812 369 | 83249 370 | 44249 371 | 4596 372 | 5410 373 | 13703 374 | 9331 375 | 9997 376 | 4747 377 | 15926 378 | 42746 379 | 7340 380 | 6180 381 | 6038 382 | 1753 383 | 1847 384 | 1986 385 | 10697 386 | 6054 387 | 32979 388 | 8775 389 | 57719 390 | 2735 391 | 2070 392 | 8680 393 | 64875 394 | 1933 395 | 12568 396 | 42834 397 | 6246 398 | 20984 399 | 4274 400 | 4846 401 | 5871 402 | 7182 403 | 5914 404 | 14713 405 | 160533 406 | 23225 407 | 65991 408 | 14244 409 | -------------------------------------------------------------------------------- /Chapter_2/cprofile_test.py: -------------------------------------------------------------------------------- 1 | 2 | import cProfile 3 | import palingrams 4 | cProfile.run('palingrams.find_palingrams()') 5 | -------------------------------------------------------------------------------- /Chapter_2/dictionary_cleanup_practice.py: -------------------------------------------------------------------------------- 1 | """Remove single-letter words from list if not 'a' or 'i'.""" 2 | word_list = ['a', 'nurses', 'i', 'stack', 'b', 'c', 'cat'] 3 | word_list_clean = [] 4 | 5 | permissible = ('a', 'i') 6 | 7 | # remove single-letter words if not "a" or "I" 8 | for word in word_list: 9 | if len(word) > 1: 10 | word_list_clean.append(word) 11 | elif len(word) == 1 and word in permissible: 12 | word_list_clean.append(word) 13 | else: 14 | continue 15 | 16 | print("{}".format(word_list_clean)) 17 | -------------------------------------------------------------------------------- /Chapter_2/load_dictionary.py: -------------------------------------------------------------------------------- 1 | """Load a text file as a list. 2 | 3 | Arguments: 4 | -text file name 5 | 6 | Exceptions: 7 | -IOError if filename not found. 8 | 9 | Returns: 10 | -A list of all words in text file in lower case. 11 | 12 | Requires-import sys 13 | 14 | """ 15 | import sys 16 | 17 | def load(file): 18 | """Open a text file & turn contents into a list of lowercase strings.""" 19 | try: 20 | with open(file) as in_file: 21 | loaded_txt = in_file.read().strip().split('\n') 22 | loaded_txt = [x.lower() for x in loaded_txt] 23 | return loaded_txt 24 | except IOError as e: 25 | print("{}\nError opening {}. Terminating program.".format(e, file), 26 | file=sys.stderr) 27 | sys.exit(1) 28 | -------------------------------------------------------------------------------- /Chapter_2/palindromes.py: -------------------------------------------------------------------------------- 1 | """Find palindromes (letter palingrams) in a dictionary file.""" 2 | 3 | import load_dictionary 4 | 5 | word_list = load_dictionary.load('2of4brif.txt') 6 | 7 | pali_list = [] 8 | 9 | for word in word_list: 10 | if len(word) > 1 and word == word[::-1]: 11 | pali_list.append(word) 12 | 13 | print("\nNumber of palindromes found = {}\n".format(len(pali_list))) 14 | 15 | # print in list format with no quotes or commas: 16 | print(*pali_list, sep='\n') 17 | -------------------------------------------------------------------------------- /Chapter_2/palingrams.py: -------------------------------------------------------------------------------- 1 | """Find all word-pair palingrams in a dictionary file.""" 2 | import load_dictionary 3 | 4 | word_list = load_dictionary.load('2of4brif.txt') 5 | 6 | # find word-pair palingrams 7 | def find_palingrams(): 8 | """Find dictionary palingrams.""" 9 | pali_list = [] 10 | for word in word_list: 11 | end = len(word) 12 | rev_word = word[::-1] 13 | if end > 1: 14 | for i in range(end): 15 | if word[i:] == rev_word[:end-i]and rev_word[end-i:]in word_list: 16 | pali_list.append((word, rev_word[end-i:])) 17 | if word[:i] == rev_word[end-i:]and rev_word[:end-i]in word_list: 18 | pali_list.append((rev_word[:end-i], word)) 19 | return pali_list 20 | 21 | palingrams = find_palingrams() 22 | 23 | #sort palingrams on first word 24 | palingrams_sorted = sorted(palingrams) 25 | 26 | #display list of palingrams 27 | print("\nNumber of palingrams = {}\n".format(len(palingrams_sorted))) 28 | for first, second in palingrams_sorted: 29 | print("{} {}".format(first, second)) 30 | -------------------------------------------------------------------------------- /Chapter_2/palingrams_optimized.py: -------------------------------------------------------------------------------- 1 | """Find all word-pair palingrams in a dictionary file.""" 2 | import load_dictionary 3 | 4 | word_list = load_dictionary.load('2of4brif.txt') 5 | 6 | # find word-pair palingrams 7 | def find_palingrams(): 8 | """Find dictionary palingrams.""" 9 | pali_list = [] 10 | words = set(word_list) 11 | for word in words: 12 | end = len(word) 13 | rev_word = word[::-1] 14 | if end > 1: 15 | for i in range(end): 16 | if word[i:] == rev_word[:end-i]and rev_word[end-i:]in words: 17 | pali_list.append((word, rev_word[end-i:])) 18 | if word[:i] == rev_word[end-i:]and rev_word[:end-i]in words: 19 | pali_list.append((rev_word[:end-i], word)) 20 | return pali_list 21 | 22 | palingrams = find_palingrams() 23 | 24 | #sort palingrams on first word 25 | palingrams_sorted = sorted(palingrams) 26 | 27 | #display list of palingrams 28 | print("\nNumber of palingrams = {}\n".format(len(palingrams_sorted))) 29 | for first, second in palingrams_sorted: 30 | print("{} {}".format(first, second)) 31 | -------------------------------------------------------------------------------- /Chapter_2/palingrams_timed.py: -------------------------------------------------------------------------------- 1 | """palingrams program with timer added.""" 2 | import load_dictionary 3 | import time 4 | start_time = time.time() 5 | 6 | word_list = load_dictionary.load('2of4brif.txt') 7 | 8 | # find word-pair palingrams 9 | def find_palingrams(): 10 | """Find dictionary palingrams.""" 11 | pali_list = [] 12 | for word in word_list: 13 | end = len(word) 14 | rev_word = word[::-1] 15 | if end > 1: 16 | for i in range(end): 17 | if word[i:] == rev_word[:end-i]and rev_word[end-i:]in word_list: 18 | pali_list.append((word, rev_word[end-i:])) 19 | if word[:i] == rev_word[end-i:]and rev_word[:end-i]in word_list: 20 | pali_list.append((rev_word[:end-i], word)) 21 | return pali_list 22 | 23 | palingrams = find_palingrams() 24 | 25 | #sort palingrams on first word 26 | palingrams_sorted = sorted(palingrams) 27 | 28 | #display list of palingrams 29 | print("Number of palingrams = {}\n\n".format(len(palingrams_sorted))) 30 | for first, second in palingrams_sorted: 31 | print("{} {}".format(first, second)) 32 | 33 | end_time = time.time() 34 | print("Runtime for this program was {} seconds.".format(end_time - start_time)) 35 | -------------------------------------------------------------------------------- /Chapter_3/anagrams.py: -------------------------------------------------------------------------------- 1 | """Find all the anagrams in a dictionary file for a given word. 2 | 3 | Requires access to an external English word dictionary file 4 | and file-loading module "load_dictionary.py" 5 | 6 | """ 7 | 8 | import load_dictionary 9 | 10 | word_list = load_dictionary.load('2of4brif.txt') 11 | 12 | anagram_list = [] 13 | 14 | # input a SINGLE word or SINGLE name below to find its anagram(s): 15 | name = 'Foster' 16 | print("Input name = {}".format(name)) 17 | name = name.lower() 18 | print("Using name = {}".format(name)) 19 | 20 | # sort name and find anagrams 21 | name_sorted = sorted(name) 22 | for word in word_list: 23 | word = word.lower() 24 | if word != name: 25 | if sorted(word) == name_sorted: 26 | anagram_list.append(word) 27 | 28 | #print out list of anagrams 29 | print() 30 | if len(anagram_list) == 0: 31 | print("You need a larger dictionary or a new name!") 32 | else: 33 | print("Anagrams =", *anagram_list, sep='\n') 34 | -------------------------------------------------------------------------------- /Chapter_3/count_digrams_practice.py: -------------------------------------------------------------------------------- 1 | """Generate letter pairs in Voldemort & find their frequency in a dictionary. 2 | 3 | Requires load_dictionary.py module to load an English dictionary file. 4 | 5 | """ 6 | import re 7 | from collections import defaultdict 8 | from itertools import permutations 9 | import load_dictionary 10 | 11 | word_list = load_dictionary.load('2of4brif.txt', 'r') 12 | 13 | name = 'Voldemort' #(tmvoordle) 14 | name = name.lower() 15 | 16 | # generate unique letter pairs from name 17 | digrams = set() 18 | perms = {''.join(i) for i in permutations(name)} 19 | for perm in perms: 20 | for i in range(0, len(perm) - 1): 21 | digrams.add(perm[i] + perm[i + 1]) 22 | print(*sorted(digrams), sep='\n') 23 | print("\nNumber of digrams = {}\n".format(len(digrams))) 24 | 25 | # use regular expressions to find repeating digrams in a word 26 | mapped = defaultdict(int) 27 | for word in word_list: 28 | word = word.lower() 29 | for digram in digrams: 30 | for m in re.finditer(digram, word): 31 | mapped[digram] += 1 32 | 33 | print("digram frequency count:") 34 | count = 0 35 | for k in sorted(mapped): 36 | print("{} {}".format(k, mapped[k])) 37 | -------------------------------------------------------------------------------- /Chapter_3/least-likely_trigrams.txt: -------------------------------------------------------------------------------- 1 | orv 2 | rdv 3 | vro 4 | ldv 5 | mld 6 | mvr 7 | dol 8 | tld 9 | dtm 10 | dtl 11 | vlt 12 | elm 13 | vod 14 | eol 15 | ltd 16 | elv 17 | etl 18 | mrt 19 | trd 20 | mlt 21 | vtr 22 | olm 23 | emr 24 | vrd 25 | dmr 26 | vmo 27 | lmv 28 | vml 29 | drt 30 | dvt 31 | odm 32 | mlr 33 | doo 34 | mvt 35 | tmd 36 | oet 37 | rvo 38 | tvo 39 | tve 40 | vte 41 | moe 42 | rvm 43 | tlv 44 | lod 45 | lrv 46 | ovt 47 | oel 48 | mdt 49 | ltm 50 | eml 51 | ovm 52 | mtd 53 | otd 54 | ovl 55 | lvo 56 | etm 57 | otv 58 | omv 59 | tvl 60 | emt 61 | vdm 62 | mvo 63 | rdm 64 | vrm 65 | etv 66 | tdl 67 | mtv 68 | mtr 69 | lrm 70 | dmt 71 | vto 72 | odr 73 | ltv 74 | lro 75 | eov 76 | vlo 77 | vtl 78 | vmr 79 | tml 80 | vld 81 | mvd 82 | dml 83 | etd 84 | odl 85 | vlr 86 | mdo 87 | mdr 88 | lrd 89 | trv 90 | drm 91 | mol 92 | odv 93 | drl 94 | mdv 95 | oev 96 | mte 97 | vom 98 | lmt 99 | vmd 100 | lom 101 | emd 102 | tov 103 | oer 104 | edv 105 | mve 106 | rml 107 | dlv 108 | dtv 109 | voo 110 | mre 111 | ldm 112 | moo 113 | rtm 114 | vtd 115 | dvm 116 | lvd 117 | mrv 118 | rtl 119 | voe 120 | lvt 121 | ovd 122 | dvr 123 | eod 124 | olr 125 | lmr 126 | ooe 127 | vle 128 | ovo 129 | elr 130 | eoo 131 | vde 132 | lvm 133 | trl 134 | dlm 135 | mtl 136 | vdl 137 | evd 138 | mlo 139 | eom 140 | omd 141 | mde 142 | vtm 143 | oem 144 | rtd 145 | tvr 146 | dlt 147 | drv 148 | tdv 149 | lvr 150 | vre 151 | emv 152 | mrl 153 | dte 154 | dov 155 | rtv 156 | rmd 157 | rmv 158 | mro 159 | rvd 160 | vrt 161 | dve 162 | vdo 163 | evt 164 | tvm 165 | rdl 166 | tlr 167 | tmr 168 | oeo 169 | tvd 170 | rlv 171 | evr 172 | tdm 173 | dvo 174 | tdr 175 | oov 176 | rvl 177 | loe 178 | tmv 179 | mvl 180 | vdr 181 | vme 182 | mdl 183 | rlt 184 | ltr 185 | vlm 186 | mle 187 | evm 188 | lmd 189 | dlr 190 | vor 191 | odt 192 | trm 193 | lrt 194 | vrl 195 | omr 196 | ovr 197 | oml 198 | mrd 199 | olt 200 | otm 201 | vdt 202 | rlm 203 | vmt 204 | mev 205 | tlm 206 | rmt 207 | dvl 208 | oed 209 | roe 210 | rvt 211 | dmv 212 | mlv 213 | evl 214 | otl 215 | lme 216 | -------------------------------------------------------------------------------- /Chapter_3/load_dictionary.py: -------------------------------------------------------------------------------- 1 | """Load a text file as a list. 2 | 3 | Arguments: 4 | -text file name 5 | 6 | Exceptions: 7 | -IOError if filename not found. 8 | 9 | Returns: 10 | -A list of all words in text file in lower case. 11 | 12 | Requires-import sys 13 | 14 | """ 15 | import sys 16 | 17 | def load(file): 18 | """Open a text file & turn contents into a list of lowercase strings.""" 19 | try: 20 | with open(file) as in_file: 21 | loaded_txt = in_file.read().strip().split('\n') 22 | loaded_txt = [x.lower() for x in loaded_txt] 23 | return loaded_txt 24 | except IOError as e: 25 | print("{}\nError opening {}. Terminating program.".format(e, file), 26 | file=sys.stderr) 27 | sys.exit(1) 28 | -------------------------------------------------------------------------------- /Chapter_3/phrase_anagrams.py: -------------------------------------------------------------------------------- 1 | """User enters name and interactively derives anagram phrase from name. 2 | 3 | Requires access to an external English word dictionary file 4 | and file-loading module "load_dictionary.py" 5 | 6 | """ 7 | import sys 8 | from collections import Counter 9 | import load_dictionary 10 | 11 | dict_file = load_dictionary.load('2of4brif.txt') 12 | # ensure "a" & "I" are included 13 | dict_file.append('a') 14 | dict_file.append('i') 15 | dict_file = sorted(dict_file) 16 | 17 | ini_name = input("Enter a name: ") 18 | 19 | #______________________________________________________________________________ 20 | 21 | def find_anagrams(name, word_list): 22 | """Read name & dictionary file & display all anagrams IN name.""" 23 | name_letter_map = Counter(name) 24 | anagrams = [] 25 | for word in word_list: 26 | test = '' 27 | word_letter_map = Counter(word.lower()) 28 | for letter in word: 29 | if word_letter_map[letter] <= name_letter_map[letter]: 30 | test += letter 31 | if Counter(test) == word_letter_map: 32 | anagrams.append(word) 33 | print(*anagrams, sep='\n') 34 | print() 35 | print("Remaining letters = {}".format(name)) 36 | print("Number of remaining letters = {}".format(len(name))) 37 | print("Number of remaining (real word)anagrams = {}".format(len(anagrams))) 38 | 39 | def process_choice(name): 40 | """Check user choice for validity, return choice & left-over letters.""" 41 | while True: 42 | choice = input('\nMake a choice else Enter to start over or # to end: ') 43 | if choice == '': 44 | main() 45 | elif choice == '#': 46 | sys.exit() 47 | else: 48 | candidate = ''.join(choice.lower().split()) 49 | left_over_list = list(name) 50 | for letter in candidate: 51 | if letter in left_over_list: 52 | left_over_list.remove(letter) 53 | if len(name) - len(left_over_list) == len(candidate): 54 | break 55 | else: 56 | print("Won't work! Make another choice!", file=sys.stderr) 57 | name = ''.join(left_over_list) # makes display more readable 58 | return choice, name 59 | 60 | def main(): 61 | """Help user build anagram phrase from their name.""" 62 | # remove spaces & convert to lower case 63 | name = ''.join(ini_name.lower().split()) 64 | # remove hyphens in hyphenated names 65 | name = name.replace('-', '') 66 | 67 | limit = len(name) 68 | phrase = '' 69 | running = True 70 | 71 | while running: 72 | temp_phrase = phrase.replace(' ', '') 73 | if len(temp_phrase) < limit: 74 | print("Length of anagram phrase = {}".format(len(temp_phrase))) 75 | 76 | find_anagrams(name, dict_file) 77 | print("Current anagram phrase =", end=" ") 78 | print(phrase, file=sys.stderr) 79 | 80 | choice, name = process_choice(name) 81 | phrase += choice + ' ' 82 | 83 | elif len(temp_phrase) == limit: 84 | print("\n***** FINISHED!!! *****\n") 85 | print("Anagram of name =", end=" ") 86 | print(phrase, file=sys.stderr) 87 | print() 88 | try_again = input('\n\nTry again? (Press Enter else "n" to quit)\n ') 89 | if try_again.lower() == "n": 90 | running = False 91 | sys.exit() 92 | else: 93 | main() 94 | #______________________________________________________________________________ 95 | 96 | if __name__ == '__main__': 97 | main() 98 | -------------------------------------------------------------------------------- /Chapter_3/voldemort_british.py: -------------------------------------------------------------------------------- 1 | """Filter string of letters to aid finding 'Voldemort' anagram. 2 | 3 | Requires access to an external English word dictionary file 4 | and file-loading module "load_dictionary.py" 5 | 6 | """ 7 | import sys 8 | from itertools import permutations 9 | from collections import Counter 10 | import load_dictionary 11 | 12 | def main(): 13 | """Load files, run filters, allow user to view anagrams by 1st letter.""" 14 | name = 'tmvoordle' 15 | name = name.lower() 16 | 17 | word_list_ini = load_dictionary.load('2of4brif.txt') 18 | 19 | trigrams_filtered = load_dictionary.load('least-likely_trigrams.txt') 20 | 21 | word_list = prep_words(name, word_list_ini) 22 | filtered_cv_map = cv_map_words(word_list) 23 | filter_1 = cv_map_filter(name, filtered_cv_map) 24 | filter_2 = trigram_filter(filter_1, trigrams_filtered) 25 | filter_3 = letter_pair_filter(filter_2) 26 | view_by_letter(name, filter_3) 27 | 28 | def prep_words(name, word_list_ini): 29 | """Prep word list for finding anagrams.""" 30 | print("length initial word_list = {}".format(len(word_list_ini))) 31 | len_name = len(name) 32 | word_list = [word.lower() for word in word_list_ini 33 | if len(word) == len_name] 34 | print("length of new word_list = {}".format(len(word_list))) 35 | return word_list 36 | 37 | def cv_map_words(word_list): 38 | """Map letters in words to consonants & vowels.""" 39 | vowels = 'aeiouy' 40 | cv_mapped_words = [] 41 | for word in word_list: 42 | temp = '' 43 | for letter in word: 44 | if letter in vowels: 45 | temp += 'v' 46 | else: 47 | temp += 'c' 48 | cv_mapped_words.append(temp) 49 | # determine number of UNIQUE c-v patterns 50 | total = len(set(cv_mapped_words)) 51 | # target fraction to eliminate: 52 | target = 0.05 53 | # get number of items in target fraction 54 | n = int(total * target) 55 | count_pruned = Counter(cv_mapped_words).most_common(total - n) 56 | filtered_cv_map = set() 57 | for pattern, count in count_pruned: 58 | filtered_cv_map.add(pattern) 59 | print("length filtered_cv_map = {}".format(len(filtered_cv_map))) 60 | return filtered_cv_map 61 | 62 | def cv_map_filter(name, filtered_cv_map): 63 | """Remove permutations of words based on unlikely cons-vowel combos.""" 64 | perms = {''.join(i) for i in permutations(name)} 65 | print("length of initial permutations set = {}".format(len(perms))) 66 | vowels = 'aeiouy' 67 | filter_1 = set() 68 | for candidate in perms: 69 | temp = '' 70 | for letter in candidate: 71 | if letter in vowels: 72 | temp += 'v' 73 | else: 74 | temp += 'c' 75 | if temp in filtered_cv_map: 76 | filter_1.add(candidate) 77 | print("# choices after filter_1 = {}".format(len(filter_1))) 78 | return filter_1 79 | 80 | def trigram_filter(filter_1, trigrams_filtered): 81 | """Remove unlikely trigrams from permutations.""" 82 | filtered = set() 83 | for candidate in filter_1: 84 | for triplet in trigrams_filtered: 85 | triplet = triplet.lower() 86 | if triplet in candidate: 87 | filtered.add(candidate) 88 | filter_2 = filter_1 - filtered 89 | print("# of choices after filter_2 = {}".format(len(filter_2))) 90 | return filter_2 91 | 92 | def letter_pair_filter(filter_2): 93 | """Remove unlikely letter-pairs from permutations.""" 94 | filtered = set() 95 | rejects = ['dt', 'lr', 'md', 'ml', 'mr', 'mt', 'mv', 96 | 'td', 'tv', 'vd', 'vl', 'vm', 'vr', 'vt'] 97 | first_pair_rejects = ['ld', 'lm', 'lt', 'lv', 'rd', 98 | 'rl', 'rm', 'rt', 'rv', 'tl', 'tm'] 99 | for candidate in filter_2: 100 | for r in rejects: 101 | if r in candidate: 102 | filtered.add(candidate) 103 | for fp in first_pair_rejects: 104 | if candidate.startswith(fp): 105 | filtered.add(candidate) 106 | filter_3 = filter_2 - filtered 107 | print("# of choices after filter_3 = {}".format(len(filter_3))) 108 | if 'voldemort' in filter_3: 109 | print("Voldemort found!", file=sys.stderr) 110 | return filter_3 111 | 112 | def view_by_letter(name, filter_3): 113 | """Filter to anagrams starting with input letter.""" 114 | print("Remaining letters = {}".format(name)) 115 | first = input("select a starting letter or press Enter to see all: ") 116 | subset = [] 117 | for candidate in filter_3: 118 | if candidate.startswith(first): 119 | subset.append(candidate) 120 | print(*sorted(subset), sep='\n') 121 | print("Number of choices starting with {} = {}".format(first, len(subset))) 122 | try_again = input("Try again? (Press Enter else any other key to Exit):") 123 | if try_again.lower() == '': 124 | view_by_letter(name, filter_3) 125 | else: 126 | sys.exit() 127 | 128 | if __name__ == '__main__': 129 | main() 130 | -------------------------------------------------------------------------------- /Chapter_4/cipher_a.txt: -------------------------------------------------------------------------------- 1 | FCNEROTBHTHNNWOCDBADEHPIAMETUWEGATLEGHAIATOEADAAGRROETIFWHODAONALFRNCTWRERSTAMLTTHTAOHSDIIREEOIWNNACT 2 | OSNBMVNDTLREEEAVPOORAEDLENNEWEETNRETDRSSIRRDAETFHRCYOHAUSLAIAFTEDEHAKIBEAMERAACEOTTEHHVLUAOOTEHYLASDL 3 | AEAASOESHNRFOTOMFEBPEHPATSMAOODNSUHRTHITTNNEIENIDEOOTECEAWEEGCWSWETONICINETNEEENATETAAMETROTDIEGEHHET 4 | LTHTIIIOETNPAHDSNGNCTCEOSTAHWGDREIDWREECCDREOWADCWWINOGMHSRINFTTIEFTVATETRHIEKHWUEVSOYNTTOORIDETRNEUT 5 | THEDKRDTOCFIEEALSFTHHIRVTESNVDITNNRHAETRMHVETOYEFELLPHTRURSYAREOFOSIAACINRDCTPSNANRDLENDRIATHRNNYOOVD 6 | DELNWMATLLHRVEDETFFANSPFOOGHIHAIGVSGRIDETOOBAESADACTEENATRTANNDHUDHORIAOOEDETOITORRBAAETEOWHDIOHITODE 7 | EENDWTHGREFNACIHRBECTGTEIFSFHODWEEDITAOCYTSLUDIAEGEETDHOEINTAUGAVWHEAAENHPTOOPELEFHTREEEGFRUONCNNTOVL 8 | TDAORITLAEENAGIEVRIETAONNNESIDODEEGBEDAWETIAITISATLOSHAEVATOHEAEFNPRWUTULRENETACCWNLHOHVLGEOGHANATBUR 9 | RDTTRLTTLEETYBCVRHEHTRENHBIDTUIWHHOHETAODESEUEDAORAMNOTRENDEIAEOHURHGHTMREOTRHSTHEATDNTHTNOLEBOENTRTE 10 | LHPRESNRREHSAVAOASGRTOEEINEIYETTOTHLRAQORANAITNTHTRASCDOCCNUATRAFOTECOCPOHEALIAREEVIETNNTILTIGRTELHTA 11 | SWNDENOREOLIUEEIAASGEVSTFORPTORHLLLEOMRWHUAEGAYEIULGEECHONSOIEFTHHRBVDRRSHETTESAGRHOSOETNSVNASWTAEFEE 12 | VNWELOHEALHIVHIIDDLAIFDDGNOPEELTOHOIOE -------------------------------------------------------------------------------- /Chapter_4/cipher_b.txt: -------------------------------------------------------------------------------- 1 | QWHTGNBCMNPRDRGMAASLEDITQCFEQIGJSCFMZBWUSGQWEVVZAEPVUQZAEQAGBENYMJPOEVZVPQBNRTDRFWYYTJRTHJNYLQGRTPLBRFHZ 2 | GSMCTCABDQGKCYGSIGCZWZPVNTSNEPIGGRPDFIYPCHJPIEGSYTLORFWYNRZRCHNVGQYYOCGPAGKBRJSMGJSCGSIGPOEVZVBTOYLYIGKC 3 | YFZKBPQPVGMQCBOFZLRFWNNEMQEOYYZVTGBOHCMJGOCRXMGQBLTCMNVPLGETRHWPYOWSVVLGHIEYSSNGMPQAPGZLRFWNNEMNRCCGTWAQ 4 | TEULBSKSWQLANHWYNWZRUHTARXYCQPSZZGJCDRHPBJSCRRIIGHSRTZYKJPFEPNVHSNEVNVWZAXQTJHWVGMVVWDNWBBISEUPZSKHEVYON 5 | PRAEZXRTHSNEERUVZHWLQQHSVDJHVWYNWIEISCFPVFGKPPLVAQHOROQPCHPJPKNPBZGNWAUSNELBRYSNNYVBVVLYWWJVVTFRZBWBOGSM 6 | OTOGRXMANWGVYONPRORLLJJCDGCCTIZPQSMEGVLIPKBPGPPCIGGRTGQIECPZIPWHTDZBCXBYSCGZIQFCCQPBECQEGSMJQFWQHQYNZTGE 7 | TRPCERYWENCYTCMZGAMRCEUCHHRDILJSCRMCGKHNNYVRXSCSZZTGHHULBGJSJQTLUGFPVEQFHCCHDBUGZTITVTTOEUPZGQPPQPLVEOER 8 | OPRTSEBEPRWBQVYQFJSOJZZXYVTPSBUGMHUZNBWUSGSMEGVLIPBUWGQNCABPCMYJIQXOYPPLVVWDELBUGFQBCCFVCMRSMEGRPQTKNVSO 9 | GZBUGUCRLBGCGVEPUNKBTARJRHCCRFAGJOESCWZVVPFPPBPCCROLRCRHREIXGWYPCMNUSOQPDBVWZAEWGJOEPLCFGTZEHPVEVEUPGTCJ 10 | PGSMYCGESFTYOSLFFZRQTORGWGKCYGSIGYSSRCMUKUSYJZRUCWIPBUCHEUPARFSLQDPNNZYBEPNXSOVPLVPJLVYBUCHEUTAACHTBYCAF 11 | SCTZLFJOWYSIIGOYRHJVTHSBQNEGSOBXIAFHSNEOBXSCAXMAVCQGSMCGCAYPJLVVPCPWCNSQBCBUGDPBATRUVLYWVBVDPETAUHFZZEPR 12 | GOCGS -------------------------------------------------------------------------------- /Chapter_4/identify_cipher_type_practice.py: -------------------------------------------------------------------------------- 1 | """Load ciphertext & use fraction of ETAOIN present to classify cipher type.""" 2 | import sys 3 | from collections import Counter 4 | 5 | # set arbitrary cutoff fraction of 6-most common letters in English 6 | # ciphertext with target fraction or greater = transposition cipher 7 | CUTOFF = 0.5 8 | 9 | # load ciphertext 10 | def load(filename): 11 | """Open text file and return list.""" 12 | with open(filename) as f: 13 | return f.read().strip() 14 | 15 | try: 16 | ciphertext = load('cipher_a.txt') 17 | except IOError as e: 18 | print("{}. Terminating program.".format(e), 19 | file=sys.stderr) 20 | sys.exit(1) 21 | 22 | # count 6 most-common letters in ciphertext 23 | six_most_frequent = Counter(ciphertext.lower()).most_common(6) 24 | print("\nSix most-frequently-used letters in English = ETAOIN") 25 | print('\nSix most frequent letters in ciphertext =') 26 | print(*six_most_frequent, sep='\n') 27 | 28 | # convert list of tuples to set of letters for comparison 29 | cipher_top_6 = {i[0] for i in six_most_frequent} 30 | 31 | TARGET = 'etaoin' 32 | count = 0 33 | for letter in TARGET: 34 | if letter in cipher_top_6: 35 | count += 1 36 | 37 | if count/len(TARGET) >= CUTOFF: 38 | print("\nThis ciphertext most-likely produced by a TRANSPOSITION cipher") 39 | else: 40 | print("This ciphertext most-likely produced by a SUBSTITUTION cipher") 41 | -------------------------------------------------------------------------------- /Chapter_4/key_dictionary_practice.py: -------------------------------------------------------------------------------- 1 | """Input cipher key string, get user input on route direction as dict value.""" 2 | col_order = """1 3 4 2""" 3 | key = dict() 4 | cols = [int(i) for i in col_order.split()] 5 | for col in cols: 6 | while True: 7 | key[col] = input("Direction to read Column {} (u = up, d = down): " 8 | .format(col).lower()) 9 | if key[col] == 'u' or key[col] == 'd': 10 | break 11 | else: 12 | print("Input should be 'u' or 'd'") 13 | 14 | print("{}, {}".format(col, key[col])) 15 | -------------------------------------------------------------------------------- /Chapter_4/perms.py: -------------------------------------------------------------------------------- 1 | """For a total number of columns, find all unique column arrangements. 2 | 3 | Builds a list of lists containing all possible unique arrangements of 4 | individual column numbers including negative values for route direction 5 | 6 | Input: 7 | -total number of columns 8 | 9 | Returns: 10 | -list of lists of unique column orders including negative values for 11 | route cipher encryption direction 12 | 13 | """ 14 | from itertools import permutations, product 15 | 16 | # build list of lists of column number combinations 17 | # itertools product computes the cartesian product of input iterables 18 | def perms(num_cols): 19 | """Take number of columns integer & generate pos & neg permutations.""" 20 | results = [] 21 | columns = [x for x in range(1, num_cols+1)] 22 | for perm in permutations(columns): 23 | for signs in product([-1, 1], repeat=len(columns)): 24 | results.append([i*sign for i, sign in zip(perm, signs)]) 25 | return results 26 | -------------------------------------------------------------------------------- /Chapter_4/permutations_practice.py: -------------------------------------------------------------------------------- 1 | """For a total number of columns, find all unique column arrangements. 2 | 3 | Builds a list of lists containing all possible unique arrangements of 4 | individual column numbers including negative values for route direction 5 | (read up column vs. down). 6 | 7 | Input: 8 | -total number of columns 9 | 10 | Returns: 11 | -list of lists of unique column orders including negative values for 12 | route cipher encryption direction 13 | 14 | """ 15 | import math 16 | from itertools import permutations, product 17 | 18 | #------BEGIN INPUT----------------------------------------------------------- 19 | 20 | # Input total number of columns: 21 | num_cols = 4 22 | 23 | #------DO NOT EDIT BELOW THIS LINE-------------------------------------------- 24 | 25 | 26 | 27 | 28 | # generate listing of individual column numbers 29 | columns = [x for x in range(1, num_cols+1)] 30 | print("columns = {}".format(columns)) 31 | 32 | # build list of lists of column number combinations 33 | # itertools product computes the cartesian product of input iterables 34 | def perms(columns): 35 | """Take number of columns integer & generate pos & neg permutations.""" 36 | results = [] 37 | for perm in permutations(columns): 38 | for signs in product([-1, 1], repeat=len(columns)): 39 | results.append([i*sign for i, sign in zip(perm, signs)]) 40 | return results 41 | 42 | col_combos = perms(columns) 43 | print(*col_combos, sep="\n") # comment-out for num_cols > 4! 44 | print("Factorial of num_cols without negatives = {}" 45 | .format(math.factorial(num_cols))) 46 | print("Number of column combinations = {}".format(len(col_combos))) 47 | -------------------------------------------------------------------------------- /Chapter_4/rail_fence_cipher_decrypt.py: -------------------------------------------------------------------------------- 1 | r"""Decrypt a Civil War 'rail fence' type cipher. 2 | 3 | This is for a "2-rail" fence cipher for short messages 4 | 5 | Example plaintext: 'Buy more Maine potatoes' 6 | 7 | Rail fence style: B Y O E A N P T T E 8 | U M R M I E O A O S 9 | 10 | Read zig-zag: \/\/\/\/\/\/\/\/\/\/ 11 | 12 | Ciphertext: BYOEA NPTTE UMRMI EOSOS 13 | 14 | """ 15 | import math 16 | import itertools 17 | 18 | #------------------------------------------------------------------------------ 19 | # USER INPUT: 20 | 21 | # the string to be decrypted (paste between quotes): 22 | ciphertext = """LTSRS OETEI EADET NETEH DOTER EEUCO SVRHR VRNRS UDRHS AEFHT ES 23 | 24 | """ 25 | 26 | # END OF USER INPUT - DO NOT EDIT BELOW THIS LINE! 27 | #------------------------------------------------------------------------------ 28 | 29 | 30 | 31 | 32 | def main(): 33 | """Run program to decrypt 2-rail rail fence cipher.""" 34 | message = prep_ciphertext(ciphertext) 35 | row1, row2 = split_rails(message) 36 | decrypt(row1, row2) 37 | 38 | def prep_ciphertext(ciphertext): 39 | """Remove whitespace.""" 40 | message = "".join(ciphertext.split()) 41 | print("\nciphertext = {}".format(ciphertext)) 42 | return message 43 | 44 | def split_rails(message): 45 | """Split message in two, always rounding UP for 1st row.""" 46 | row_1_len = math.ceil(len(message)/2) 47 | row1 = (message[:row_1_len]).lower() 48 | row2 = (message[row_1_len:]).lower() 49 | return row1, row2 50 | 51 | def decrypt(row1, row2): 52 | """Build list with every other letter in 2 strings & print.""" 53 | plaintext = [] 54 | for r1, r2 in itertools.zip_longest(row1, row2): 55 | plaintext.append(r1) 56 | plaintext.append(r2) 57 | if None in plaintext: 58 | plaintext.pop() 59 | print("rail 1 = {}".format(row1)) 60 | print("rail 2 = {}".format(row2)) 61 | print("\nplaintext = {}".format(''.join(plaintext))) 62 | 63 | if __name__ == '__main__': 64 | main() 65 | -------------------------------------------------------------------------------- /Chapter_4/rail_fence_cipher_encrypt.py: -------------------------------------------------------------------------------- 1 | r"""Encrypt a Civil War 'rail fence' type cipher. 2 | 3 | This is for a "2-rail" fence cipher for short messages 4 | 5 | Example text to encrypt: 'Buy more Maine potatoes' 6 | 7 | Rail fence style: B Y O E A N P T T E 8 | U M R M I E O A O S 9 | 10 | Read zig-zag: \/\/\/\/\/\/\/\/\/\/ 11 | 12 | Encrypted: BYOEA NPTTE UMRMI EOSOS 13 | 14 | """ 15 | #------------------------------------------------------------------------------ 16 | # USER INPUT: 17 | 18 | # the string to be encrypted (paste between quotes): 19 | plaintext = """Let us cross over the river and rest under the shade of the trees 20 | """ 21 | 22 | # END OF USER INPUT - DO NOT EDIT BELOW THIS LINE! 23 | #------------------------------------------------------------------------------ 24 | 25 | def main(): 26 | """Run program to encrypt message using 2-rail rail fence cipher.""" 27 | message = prep_plaintext(plaintext) 28 | rails = build_rails(message) 29 | encrypt(rails) 30 | 31 | def prep_plaintext(plaintext): 32 | """Remove spaces & leading/trailing whitespace.""" 33 | message = "".join(plaintext.split()) 34 | message = message.upper() # convention for ciphertext is uppercase 35 | print("\nplaintext = {}".format(plaintext)) 36 | return message 37 | 38 | def build_rails(message): 39 | """Build strings with every other letter in a message.""" 40 | evens = message[::2] 41 | odds = message[1::2] 42 | rails = evens + odds 43 | return rails 44 | 45 | def encrypt(rails): 46 | """Split letters in ciphertext into chunks of 5 & join to make string.""" 47 | ciphertext = ' '.join([rails[i:i+5] for i in range(0, len(rails), 48 | 5)]) 49 | print("ciphertext = {}".format(ciphertext)) 50 | 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /Chapter_4/route_cipher_decrypt.py: -------------------------------------------------------------------------------- 1 | """Decrypt a path through a Union Route Cipher. 2 | 3 | Designed for whole-word transposition ciphers with variable rows & columns. 4 | Assumes encryption began at either top or bottom of a column. 5 | Key indicates the order to read columns and the direction to traverse. 6 | Negative column numbers mean start at bottom and read up. 7 | Positive column numbers means start at top & read down. 8 | 9 | Example below is for 4x4 matrix with key -1 2 -3 4. 10 | Note "0" is not allowed. 11 | Arrows show encryption route; for negative key values read UP. 12 | 13 | 1 2 3 4 14 | ___ ___ ___ ___ 15 | | ^ | | | ^ | | | MESSAGE IS WRITTEN 16 | |_|_|_v_|_|_|_v_| 17 | | ^ | | | ^ | | | ACROSS EACH ROW 18 | |_|_|_v_|_|_|_v_| 19 | | ^ | | | ^ | | | IN THIS MANNER 20 | |_|_|_v_|_|_|_v_| 21 | | ^ | | | ^ | | | LAST ROW IS FILLED WITH DUMMY WORDS 22 | |_|_|_v_|_|_|_v_| 23 | START END 24 | 25 | Required inputs - a text message, # of columns, # of rows, key string 26 | 27 | Prints translated plaintext 28 | """ 29 | import sys 30 | 31 | #============================================================================== 32 | # USER INPUT: 33 | 34 | # the string to be decrypted (type or paste between triple-quotes): 35 | ciphertext = """16 12 8 4 0 1 5 9 13 17 18 14 10 6 2 3 7 11 15 19 36 | """ 37 | 38 | # number of columns in the transposition matrix: 39 | COLS = 4 40 | 41 | # number of rows in the transposition matrix: 42 | ROWS = 5 43 | 44 | # key with spaces between numbers; negative to read UP column (ex = -1 2 -3 4): 45 | key = """ -1 2 -3 4 """ 46 | 47 | # END OF USER INPUT - DO NOT EDIT BELOW THIS LINE! 48 | #============================================================================== 49 | 50 | 51 | 52 | 53 | 54 | def main(): 55 | """Run program and print decrypted plaintext.""" 56 | print("\nCiphertext = {}".format(ciphertext)) 57 | print("Trying {} columns".format(COLS)) 58 | print("Trying {} rows".format(ROWS)) 59 | print("Trying key = {}".format(key)) 60 | 61 | # split elements into words, not letters 62 | cipherlist = list(ciphertext.split()) 63 | 64 | validate_col_row(cipherlist) 65 | key_int = key_to_int(key) 66 | translation_matrix = build_matrix(key_int, cipherlist) 67 | plaintext = decrypt(translation_matrix) 68 | 69 | print("Plaintext = {}".format(plaintext)) 70 | print() 71 | 72 | def validate_col_row(cipherlist): 73 | """Check that input columns & rows are valid vs. message length.""" 74 | factors = [] 75 | len_cipher = len(cipherlist) 76 | for i in range(2, len_cipher): # range excludes 1-column ciphers 77 | if len_cipher % i == 0: 78 | factors.append(i) 79 | print("\nLength of cipher = {}".format(len_cipher)) 80 | print("Acceptable column/row values include: {}".format(factors)) 81 | print() 82 | if ROWS * COLS != len_cipher: 83 | print("\nError - Input columns & rows not factors of length " 84 | "of cipher. Terminating program.", file=sys.stderr) 85 | sys.exit(1) 86 | 87 | def key_to_int(key): 88 | """Turn key into list of integers & check validity.""" 89 | key_int = [int(i) for i in key.split()] 90 | key_int_lo = min(key_int) 91 | key_int_hi = max(key_int) 92 | if len(key_int) != COLS or key_int_lo < -COLS or key_int_hi > COLS \ 93 | or 0 in key_int: 94 | print("\nError - Problem with key. Terminating.", file=sys.stderr) 95 | sys.exit(1) 96 | else: 97 | return key_int 98 | 99 | def build_matrix(key_int, cipherlist): 100 | """Turn every n-items in a list into a new item in a list of lists.""" 101 | translation_matrix = [None] * COLS 102 | start = 0 103 | stop = ROWS 104 | for k in key_int: 105 | if k < 0: # read bottom-to-top of column 106 | col_items = cipherlist[start:stop] 107 | elif k > 0: # read top-to-bottom of columnn 108 | col_items = list((reversed(cipherlist[start:stop]))) 109 | translation_matrix[abs(k) - 1] = col_items 110 | start += ROWS 111 | stop += ROWS 112 | return translation_matrix 113 | 114 | def decrypt(translation_matrix): 115 | """Loop through nested lists popping off last item to a string.""" 116 | plaintext = '' 117 | for i in range(ROWS): 118 | for matrix_col in translation_matrix: 119 | word = str(matrix_col.pop()) 120 | plaintext += word + ' ' 121 | return plaintext 122 | 123 | if __name__ == '__main__': 124 | main() 125 | -------------------------------------------------------------------------------- /Chapter_4/route_cipher_decrypt_prototype.py: -------------------------------------------------------------------------------- 1 | """Decrypt a route cipher by inputing matrix & key.""" 2 | ciphertext = "16 12 8 4 0 1 5 9 13 17 18 14 10 6 2 3 7 11 15 19" 3 | 4 | # split elements into words, not letters 5 | cipherlist = list(ciphertext.split()) 6 | 7 | # initialize variables 8 | COLS = 4 9 | ROWS = 5 10 | key = '-1 2 -3 4' # neg number means read UP column, vs. DOWN 11 | translation_matrix = [None] * COLS 12 | plaintext = '' 13 | start = 0 14 | stop = ROWS 15 | 16 | 17 | # turn key_int into list of integers: 18 | key_int = [int(i) for i in key.split()] 19 | 20 | # turn columns into items in list of lists: 21 | for k in key_int: 22 | if k < 0: # reading bottom-to-top of column 23 | col_items = cipherlist[start:stop] 24 | elif k > 0: # reading top-to-bottom of columnn 25 | col_items = list((reversed(cipherlist[start:stop]))) 26 | translation_matrix[abs(k) - 1] = col_items 27 | start += ROWS 28 | stop += ROWS 29 | 30 | print("\nciphertext = {}".format(ciphertext)) 31 | print("\ntranslation matrix =", *translation_matrix, sep="\n") 32 | print("\nkey length= {}".format(len(key_int))) 33 | 34 | # loop through nested lists popping off last item to new list: 35 | for i in range(ROWS): 36 | for col_items in translation_matrix: 37 | word = str(col_items.pop()) 38 | plaintext += word + ' ' 39 | 40 | print("\nplaintext = {}".format(plaintext)) 41 | -------------------------------------------------------------------------------- /Chapter_4/route_cipher_hacker.py: -------------------------------------------------------------------------------- 1 | """Brute-force hack a Union Route Cipher. 2 | 3 | Designed for whole-word transposition ciphers with variable rows & columns. 4 | Assumes encryption began at either top or bottom of a column. 5 | Possible keys auto-generated based on number of columns & rows input. 6 | Key indicates the order to read columns and the direction to traverse. 7 | Negative column numbers mean start at bottom and read up. 8 | Positive column numbers means start at top & read down. 9 | 10 | Example below is for 4x4 matrix with key -1 2 -3 4. 11 | Note "0" is not allowed. 12 | Arrows show encryption route; for negative key values read UP. 13 | 14 | 1 2 3 4 15 | ___ ___ ___ ___ 16 | | ^ | | | ^ | | | MESSAGE IS WRITTEN 17 | |_|_|_v_|_|_|_v_| 18 | | ^ | | | ^ | | | ACROSS EACH ROW 19 | |_|_|_v_|_|_|_v_| 20 | | ^ | | | ^ | | | IN THIS MANNER 21 | |_|_|_v_|_|_|_v_| 22 | | ^ | | | ^ | | | LAST ROW IS FILLED WITH DUMMY WORDS 23 | |_|_|_v_|_|_|_v_| 24 | START END 25 | 26 | Required inputs - a text message, # of columns, # of rows, key string 27 | Requires custom-made "perms" module to generate keys 28 | Prints off key used and translated plaintext 29 | """ 30 | import sys 31 | import perms 32 | 33 | #============================================================================== 34 | # USER INPUT: 35 | 36 | # the string to be decrypted (type or paste between triple-quotes): 37 | ciphertext = """REST TRANSPORT YOU GODWIN VILLAGE ROANOKE WITH ARE YOUR IS JUST 38 | SUPPLIES FREE SNOW HEADING TO GONE TO SOUTH FILLER 39 | """ 40 | 41 | # the number of columns believed to be in the transposition matrix: 42 | COLS = 4 43 | 44 | # the number of rows believed to be in the transposition matrix: 45 | ROWS = 5 46 | 47 | # END OF USER INPUT - DO NOT EDIT BELOW THIS LINE! 48 | #============================================================================== 49 | 50 | 51 | 52 | 53 | 54 | 55 | def main(): 56 | """Turn ciphertext into list, call validation & decryption functions.""" 57 | cipherlist = list(ciphertext.split()) 58 | validate_col_row(cipherlist) 59 | decrypt(cipherlist) 60 | 61 | def validate_col_row(cipherlist): 62 | """Check that input columns & rows are valid vs. message length.""" 63 | factors = [] 64 | len_cipher = len(cipherlist) 65 | for i in range(2, len_cipher): # range excludes 1-column ciphers 66 | if len_cipher % i == 0: 67 | factors.append(i) 68 | print("\nLength of cipher = {}".format(len_cipher)) 69 | print("Acceptable column/row values include: {}".format(factors)) 70 | print() 71 | if ROWS * COLS != len_cipher: 72 | print("\nError - Input columns & rows not factors of length " 73 | "of cipher. Terminating program.", file=sys.stderr) 74 | sys.exit(1) 75 | 76 | def decrypt(cipherlist): 77 | """Turn columns into items in list of lists & decrypt ciphertext.""" 78 | col_combos = perms.perms(COLS) 79 | for key in col_combos: 80 | translation_matrix = [None] * COLS 81 | plaintext = '' 82 | start = 0 83 | stop = ROWS 84 | for k in key: 85 | if k < 0: # reading bottom-to-top of column 86 | col_items = cipherlist[start:stop] 87 | elif k > 0: # reading top-to-bottom of columnn 88 | col_items = list((reversed(cipherlist[start:stop]))) 89 | translation_matrix[abs(k) - 1] = col_items 90 | start += ROWS 91 | stop += ROWS 92 | # loop through nested lists popping off last item to a new list: 93 | for i in range(ROWS): 94 | for matrix_col in translation_matrix: 95 | word = str(matrix_col.pop()) 96 | plaintext += word + ' ' 97 | print("\nusing key = {}".format(key)) 98 | print("translated = {}".format(plaintext)) 99 | print("\nnumber of keys = {}".format(len(col_combos))) 100 | 101 | if __name__ == '__main__': 102 | main() 103 | -------------------------------------------------------------------------------- /Chapter_5/colchester_message.txt: -------------------------------------------------------------------------------- 1 | Sir John: Odd and too hard, your lot. Still, we will band together and, like you, persevere. 2 | Who else could love their enemies, stand firm when all others fail, hate and despair? 3 | While we all can, let us feel hope. -R.T. -------------------------------------------------------------------------------- /Chapter_5/colchester_practice.py: -------------------------------------------------------------------------------- 1 | """Solve a null cipher based on every nth-letter in every nth-word.""" 2 | import sys 3 | 4 | def load_text(file): 5 | """Load a text file as a string.""" 6 | with open(file) as f: 7 | return f.read().strip() 8 | 9 | # load & process message: 10 | filename = input("\nEnter full filename for message to translate: ") 11 | try: 12 | loaded_message = load_text(filename) 13 | except IOError as e: 14 | print("{}. Terminating program.".format(e), file=sys.stderr) 15 | sys.exit(1) 16 | 17 | # check loaded message & # of lines 18 | print("\nORIGINAL MESSAGE = {}\n".format(loaded_message)) 19 | 20 | # convert message to list and get length 21 | message = loaded_message.split() 22 | end = len(message) 23 | 24 | # get user input on interval to check 25 | increment = int(input("Input max word & letter position to \ 26 | check (e.g., every 1 of 1, 2 of 2, etc.): ")) 27 | print() 28 | 29 | # find letters at designated intervals 30 | for i in range(1, increment + 1): 31 | print("\nUsing increment letter {} of word {}".format(i, i)) 32 | print() 33 | count = i - 1 34 | location = i - 1 35 | for index, word in enumerate(message): 36 | if index == count: 37 | if location < len(word): 38 | print("letter = {}".format(word[location])) 39 | count += i 40 | else: 41 | print("Interval doesn't work", file=sys.stderr) 42 | -------------------------------------------------------------------------------- /Chapter_5/list_cipher.py: -------------------------------------------------------------------------------- 1 | """Hide a null cipher within a vocabulary list.""" 2 | from random import randint 3 | import string 4 | import load_dictionary 5 | 6 | # write a short message and use no punctuation or numbers! 7 | input_message = "Panel at east end of chapel slides" 8 | 9 | message = '' 10 | for char in input_message: 11 | if char in string.ascii_letters: 12 | message += char 13 | print(message, "\n") 14 | message = "".join(message.split()) 15 | 16 | # open dictionary file 17 | word_list = load_dictionary.load('2of4brif.txt') 18 | 19 | # build vocabulary word list with hidden message 20 | vocab_list = [] 21 | for letter in message: 22 | size = randint(6, 10) 23 | for word in word_list: 24 | if len(word) == size and word[2].lower() == letter.lower()\ 25 | and word not in vocab_list: 26 | vocab_list.append(word) 27 | break 28 | 29 | if len(vocab_list) < len(message): 30 | print("Word List is too small. Try larger dictionary or shorter message!") 31 | else: 32 | print("Vocabulary words for Unit 1: \n", *vocab_list, sep="\n") 33 | 34 | -------------------------------------------------------------------------------- /Chapter_5/load_dictionary.py: -------------------------------------------------------------------------------- 1 | """Load a dictionary file as a list. 2 | 3 | Arguments: 4 | -dictionary file name 5 | 6 | Exceptions: 7 | -IOError 8 | 9 | -Requires import sys 10 | 11 | """ 12 | 13 | import sys 14 | 15 | def load(filename): 16 | """Open dict text file, check for errors, & make word list.""" 17 | try: 18 | with open(filename) as my_file: 19 | my_list = my_file.read().strip().split('\n') 20 | my_list = [x.lower() for x in my_list] 21 | return my_list 22 | except IOError as e: 23 | print("Error opening {}.\n Terminating program {}.".format(filename, e), 24 | file=sys.stderr) 25 | sys.exit(1) 26 | -------------------------------------------------------------------------------- /Chapter_5/null_cipher_finder.py: -------------------------------------------------------------------------------- 1 | """Decode a null cipher based on number of letters after punctuation marks.""" 2 | import sys 3 | import string 4 | 5 | def load_text(file): 6 | """Load a text file as a string.""" 7 | with open(file) as f: 8 | return f.read().strip() 9 | 10 | def solve_null_cipher(message, lookahead): 11 | """Solve a null cipher based on number letters after punctuation mark. 12 | 13 | message = null cipher text as string stripped of whitespace 14 | lookahead = endpoint of range of letters after punctuation mark to examine 15 | """ 16 | for i in range(1, lookahead + 1): 17 | plaintext = '' 18 | count = 0 19 | found_first = False 20 | for char in message: 21 | if char in string.punctuation: 22 | count = 0 23 | found_first = True 24 | elif found_first is True: 25 | count += 1 26 | if count == i: 27 | plaintext += char 28 | print("Using offset of {} after punctuation = {}".format(i, plaintext)) 29 | print() 30 | 31 | def main(): 32 | """Load text, solve null cipher.""" 33 | # load & process message: 34 | filename = input("\nEnter full filename for message to translate: ") 35 | try: 36 | loaded_message = load_text(filename) 37 | except IOError as e: 38 | print("{}. Terminating program.".format(e), file=sys.stderr) 39 | sys.exit(1) 40 | print("\nORIGINAL MESSAGE =") 41 | print("{}".format(loaded_message), "\n") 42 | print("\nList of punctuation marks to check = {}". 43 | format(string.punctuation), "\n") 44 | 45 | # remove whitespace: 46 | message = ''.join(loaded_message.split()) 47 | 48 | # get range of possible cipher keys from user: 49 | while True: 50 | lookahead = input("\nNumber of letters to check after " \ 51 | "punctuation mark: ") 52 | if lookahead.isdigit(): 53 | lookahead = int(lookahead) 54 | break 55 | else: 56 | print("Please input a number.", file=sys.stderr) 57 | print() 58 | 59 | # run function to decode cipher: 60 | solve_null_cipher(message, lookahead) 61 | 62 | if __name__ == '__main__': 63 | main() 64 | -------------------------------------------------------------------------------- /Chapter_5/save_Mary_practice.py: -------------------------------------------------------------------------------- 1 | """Hide a null cipher within a list of names using a variable pattern.""" 2 | import load_dictionary 3 | 4 | # write a short message and use no punctuation or numbers! 5 | message = "Give your word and we rise" 6 | message = "".join(message.split()) 7 | 8 | # open name file 9 | names = load_dictionary.load('supporters.txt') 10 | 11 | name_list = [] 12 | 13 | # start list with null word not used in cipher 14 | name_list.append(names[0]) 15 | 16 | # add letter of null cipher to 2nd letter of name, then 3rd, then repeat 17 | count = 1 18 | for letter in message: 19 | for name in names: 20 | if len(name) > 2 and name not in name_list: 21 | if count % 2 == 0 and name[2].lower() == letter.lower(): 22 | name_list.append(name) 23 | count += 1 24 | break 25 | elif count % 2 != 0 and name[1].lower() == letter.lower(): 26 | name_list.append(name) 27 | count += 1 28 | break 29 | 30 | # add two null words early in message to throw-off cryptanalysts 31 | name_list.insert(3, 'Stuart') 32 | name_list.insert(6, 'Jacob') 33 | 34 | # display cover letter and list with null cipher 35 | print(""" 36 | Your Royal Highness: \n 37 | It is with the greatest pleasure I present the list of noble families who 38 | have undertaken to support your cause and petition the usurper for the 39 | release of your Majesty from the current tragical circumstances. 40 | """) 41 | 42 | print(*name_list, sep='\n') 43 | 44 | 45 | -------------------------------------------------------------------------------- /Chapter_5/trevanion.txt: -------------------------------------------------------------------------------- 1 | Worthie Sir John: Hope, that is the beste comfort of the afflicted, cannot much, I fear me, help you now. 2 | That I would saye to you, is this only: if ever I may be able to requite that I do owe you, stand not upon 3 | asking me. 'Tis not much I can do: but what I can do, bee you verie sure I wille. I knowe that, if deathe 4 | comes, if ordinary men fear it, it frights not you, accounting for it for a high honour, to have such a 5 | rewarde of your loyalty. Pray yet that you may be spared this soe bitter, cup. I fear not that you will 6 | grudge any sufferings; onlie if bie submission you can turn them away, 'tis the part of a wise man. Tell 7 | me, an if you can, to do for you anythinge that you wolde have done. The general goes back on Wednesday. 8 | Restinge your servant to command. R.T. -------------------------------------------------------------------------------- /Chapter_6/ciphertext_message_letterhead.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_6/ciphertext_message_letterhead.docx -------------------------------------------------------------------------------- /Chapter_6/elementary_ink.py: -------------------------------------------------------------------------------- 1 | """ Hide a message in a docx document using a white font.""" 2 | import docx 3 | from docx.shared import RGBColor, Pt 4 | 5 | # get text from a fake message & make each line a list item 6 | fake_text = docx.Document('fakeMessage.docx') 7 | fake_list = [] 8 | for paragraph in fake_text.paragraphs: 9 | fake_list.append(paragraph.text) 10 | 11 | # get text from real message & make each line a list item 12 | real_text = docx.Document('realMessage_Vig.docx') 13 | real_list = [] 14 | for paragraph in real_text.paragraphs: 15 | if len(paragraph.text) != 0: # remove blank lines 16 | real_list.append(paragraph.text) 17 | 18 | # load template that sets style, font, margins, etc. 19 | doc = docx.Document('template.docx') 20 | 21 | # add letterhead: 22 | doc.add_heading('Morland Holmes', 0) 23 | subtitle = doc.add_heading('Global Consultanting & Negotiations', 1) 24 | subtitle.alignment = 1 25 | doc.add_heading('', 1) 26 | doc.add_paragraph('December 17, 2015') 27 | doc.add_paragraph('') 28 | 29 | def set_spacing(paragraph): 30 | """Use docx to set line spacing between paragraphs.""" 31 | paragraph_format = paragraph.paragraph_format 32 | paragraph_format.space_before = Pt(0) 33 | paragraph_format.space_after = Pt(0) 34 | 35 | length_real = len(real_list) 36 | count_real = 0 # index of current line in real (hidden) message 37 | 38 | # interleave real and fake message lines 39 | for line in fake_list: 40 | if count_real < length_real and line == "": 41 | paragraph = doc.add_paragraph(real_list[count_real]) 42 | paragraph_index = len(doc.paragraphs) - 1 43 | 44 | # set real message color to white 45 | run = doc.paragraphs[paragraph_index].runs[0] 46 | font = run.font 47 | font.color.rgb = RGBColor(255, 255, 255) # make it red to test 48 | count_real += 1 49 | 50 | else: 51 | paragraph = doc.add_paragraph(line) 52 | 53 | set_spacing(paragraph) 54 | 55 | doc.save('ciphertext_message_letterhead.docx') 56 | 57 | print("Done") 58 | -------------------------------------------------------------------------------- /Chapter_6/elementary_ink_practice.py: -------------------------------------------------------------------------------- 1 | """Add code to check blank lines in fake message vs lines in real message.""" 2 | import sys 3 | import docx 4 | from docx.shared import RGBColor, Pt 5 | 6 | 7 | # get text from fake message & make each line a list item 8 | fake_text = docx.Document('fakeMessage.docx') 9 | fake_list = [] 10 | for paragraph in fake_text.paragraphs: 11 | fake_list.append(paragraph.text) 12 | 13 | # get text from real message & make each line a list item 14 | real_text = docx.Document('realMessageChallenge.docx') 15 | real_list = [] 16 | for paragraph in real_text.paragraphs: 17 | if len(paragraph.text) != 0: # remove blank lines 18 | real_list.append(paragraph.text) 19 | 20 | # define function to check available hiding space: 21 | def line_limit(fake, real): 22 | """Compare number of blank lines in fake vs lines in real and 23 | warn user if there are not enough blanks to hold real message. 24 | 25 | NOTE: need to import 'sys' 26 | 27 | """ 28 | num_blanks = 0 29 | num_real = 0 30 | for line in fake: 31 | if line == '': 32 | num_blanks += 1 33 | num_real = len(real) 34 | diff = num_real - num_blanks 35 | print("\nNumber of blank lines in fake message = {}".format(num_blanks)) 36 | print("Number of lines in real message = {}\n".format(num_real)) 37 | if num_real > num_blanks: 38 | print("Fake message needs {} more blank lines." 39 | .format(diff), file=sys.stderr) 40 | sys.exit() 41 | 42 | line_limit(fake_list, real_list) 43 | 44 | # load template that sets style, font, margins, etc. 45 | doc = docx.Document('template.docx') 46 | 47 | # add letterhead 48 | doc.add_heading('Morland Holmes', 0) 49 | subtitle = doc.add_heading('Global Consultanting & Negotiations', 1) 50 | subtitle.alignment = 1 51 | doc.add_heading('', 1) 52 | doc.add_paragraph('December 17, 2015') 53 | doc.add_paragraph('') 54 | 55 | def set_spacing(paragraph): 56 | """Use docx to set line spacing between paragraphs.""" 57 | paragraph_format = paragraph.paragraph_format 58 | paragraph_format.space_before = Pt(0) 59 | paragraph_format.space_after = Pt(0) 60 | 61 | length_real = len(real_list) 62 | count_real = 0 # index of current line in real (hidden) message 63 | 64 | # interleave real and fake message lines 65 | for line in fake_list: 66 | if count_real < length_real and line == "": 67 | paragraph = doc.add_paragraph(real_list[count_real]) 68 | paragraph_index = len(doc.paragraphs) - 1 69 | 70 | # set real message color to white 71 | run = doc.paragraphs[paragraph_index].runs[0] 72 | font = run.font 73 | font.color.rgb = RGBColor(255, 255, 255) # make it red to test 74 | count_real += 1 75 | 76 | else: 77 | paragraph = doc.add_paragraph(line) 78 | 79 | set_spacing(paragraph) 80 | 81 | doc.save('ciphertext_message_letterhead.docx') 82 | 83 | print("Done") 84 | -------------------------------------------------------------------------------- /Chapter_6/example_template_prep.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_6/example_template_prep.docx -------------------------------------------------------------------------------- /Chapter_6/fakeMessage.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_6/fakeMessage.docx -------------------------------------------------------------------------------- /Chapter_6/realMessage.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_6/realMessage.docx -------------------------------------------------------------------------------- /Chapter_6/realMessageChallenge.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_6/realMessageChallenge.docx -------------------------------------------------------------------------------- /Chapter_6/realMessage_Vig.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_6/realMessage_Vig.docx -------------------------------------------------------------------------------- /Chapter_6/template.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/ff9065e94430dc4ecf76d2c9e78f05fae499213e/Chapter_6/template.docx -------------------------------------------------------------------------------- /Chapter_7/brute_force_cracker.py: -------------------------------------------------------------------------------- 1 | """Use brute force to crack a lock combination.""" 2 | import time 3 | from itertools import product 4 | 5 | start_time = time.time() 6 | 7 | combo = (9, 9, 7, 6, 5) 8 | 9 | # use Cartesian product to generate permutations with repetition 10 | for perm in product([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], repeat=len(combo)): 11 | if perm == combo: 12 | print("Cracked! {} {}".format(combo, perm)) 13 | 14 | end_time = time.time() 15 | print("\nRuntime for this program was {} seconds.".format 16 | (end_time - start_time)) 17 | -------------------------------------------------------------------------------- /Chapter_7/safe_cracker.py: -------------------------------------------------------------------------------- 1 | """Use hill climbing to crack a lock combination.""" 2 | import time 3 | from random import randint, randrange 4 | 5 | def fitness(combo, attempt): 6 | """Compare items in two lists and count number of matches.""" 7 | grade = 0 8 | for i, j in zip(combo, attempt): 9 | if i == j: 10 | grade += 1 11 | return grade 12 | 13 | def main(): 14 | """Enter lock combination & run hill climbing algorithm to find solution.""" 15 | combination = '6822858902' 16 | print("Combination = {}".format(combination)) 17 | # convert combination to list: 18 | combo = [int(i) for i in combination] 19 | 20 | # generate guess at combination & grade fitness: 21 | best_attempt = [0] * len(combo) 22 | best_attempt_grade = fitness(combo, best_attempt) 23 | 24 | count = 0 25 | 26 | # evolve guess 27 | while best_attempt != combo: 28 | # crossover 29 | next_try = best_attempt[:] 30 | # mutate 31 | lock_wheel = randrange(0, len(combo)) 32 | next_try[lock_wheel] = randint(0, 9) 33 | # grade & select 34 | next_try_grade = fitness(combo, next_try) 35 | if next_try_grade > best_attempt_grade: 36 | best_attempt = next_try[:] 37 | best_attempt_grade = next_try_grade 38 | print(next_try, best_attempt) 39 | count += 1 40 | 41 | print() 42 | print("Cracked! {}".format(best_attempt), end=' ') 43 | print("in {} tries!".format(count)) 44 | 45 | if __name__ == '__main__': 46 | start_time = time.time() 47 | main() 48 | end_time = time.time() 49 | duration = end_time - start_time 50 | print("\nRuntime for this program was {:.5f} seconds.".format(duration)) 51 | -------------------------------------------------------------------------------- /Chapter_7/super_rats.py: -------------------------------------------------------------------------------- 1 | """Use genetic algorithm to simulate breeding race of super rats.""" 2 | 3 | import time 4 | import random 5 | import statistics 6 | 7 | # CONSTANTS (weights in grams) 8 | GOAL = 50000 9 | NUM_RATS = 20 # number of adult breeding rats in each generation 10 | INITIAL_MIN_WT = 200 11 | INITIAL_MAX_WT = 600 12 | INITIAL_MODE_WT = 300 13 | MUTATE_ODDS = 0.01 14 | MUTATE_MIN = 0.5 15 | MUTATE_MAX = 1.2 16 | LITTER_SIZE = 8 17 | LITTERS_PER_YEAR = 10 18 | GENERATION_LIMIT = 500 19 | 20 | # ensure even-number of rats for breeding pairs: 21 | if NUM_RATS % 2 != 0: 22 | NUM_RATS += 1 23 | 24 | def populate(num_rats, min_wt, max_wt, mode_wt): 25 | """Initialize a population with a triangular distribution of weights.""" 26 | return [int(random.triangular(min_wt, max_wt, mode_wt))\ 27 | for i in range(num_rats)] 28 | 29 | def fitness(population, goal): 30 | """Measure population fitness based on an attribute mean vs target.""" 31 | ave = statistics.mean(population) 32 | return ave / goal 33 | 34 | def select(population, to_retain): 35 | """Cull a population to contain only a specified number of members.""" 36 | sorted_population = sorted(population) 37 | to_retain_by_sex = to_retain//2 38 | members_per_sex = len(sorted_population)//2 39 | females = sorted_population[:members_per_sex] 40 | males = sorted_population[members_per_sex:] 41 | selected_females = females[-to_retain_by_sex:] 42 | selected_males = males[-to_retain_by_sex:] 43 | return selected_males, selected_females 44 | 45 | def breed(males, females, litter_size): 46 | """Crossover genes among members of a population.""" 47 | random.shuffle(males) 48 | random.shuffle(females) 49 | children = [] 50 | for male, female in zip(males, females): 51 | for child in range(litter_size): 52 | child = random.randint(female, male) 53 | children.append(child) 54 | return children 55 | 56 | def mutate(children, mutate_odds, mutate_min, mutate_max): 57 | """Randomly alter rat weights using input odds & fractional changes.""" 58 | for index, rat in enumerate(children): 59 | if mutate_odds >= random.random(): 60 | children[index] = round(rat * random.uniform(mutate_min, 61 | mutate_max)) 62 | return children 63 | 64 | def main(): 65 | """Initialize population, select, breed, and mutate, display results.""" 66 | generations = 0 67 | 68 | parents = populate(NUM_RATS, INITIAL_MIN_WT, INITIAL_MAX_WT, 69 | INITIAL_MODE_WT) 70 | print("initial population weights = {}".format(parents)) 71 | popl_fitness = fitness(parents, GOAL) 72 | print("initial population fitness = {}".format(popl_fitness)) 73 | print("number to retain = {}".format(NUM_RATS)) 74 | 75 | ave_wt = [] 76 | 77 | while popl_fitness < 1 and generations < GENERATION_LIMIT: 78 | selected_males, selected_females = select(parents, NUM_RATS) 79 | children = breed(selected_males, selected_females, LITTER_SIZE) 80 | children = mutate(children, MUTATE_ODDS, MUTATE_MIN, MUTATE_MAX) 81 | parents = selected_males + selected_females + children 82 | popl_fitness = fitness(parents, GOAL) 83 | print("Generation {} fitness = {:.4f}".format(generations, 84 | popl_fitness)) 85 | ave_wt.append(int(statistics.mean(parents))) 86 | generations += 1 87 | 88 | print("average weight per generation = {}".format(ave_wt)) 89 | print("\nnumber of generations = {}".format(generations)) 90 | print("number of years = {}".format(int(generations / LITTERS_PER_YEAR))) 91 | 92 | if __name__ == '__main__': 93 | start_time = time.time() 94 | main() 95 | end_time = time.time() 96 | duration = end_time - start_time 97 | print("\nRuntime for this program was {} seconds.".format(duration)) 98 | -------------------------------------------------------------------------------- /Chapter_8/count_syllables.py: -------------------------------------------------------------------------------- 1 | """American English syllable counter using NLTK cmudict corpus.""" 2 | import sys 3 | from string import punctuation 4 | import json 5 | from nltk.corpus import cmudict 6 | 7 | # load dictionary of words in haiku corpus but not in cmudict 8 | with open('missing_words.json') as f: 9 | missing_words = json.load(f) 10 | 11 | cmudict = cmudict.dict() 12 | 13 | def count_syllables(words): 14 | """Use corpora to count syllables in English word or phrase.""" 15 | # prep words for cmudict corpus 16 | words = words.replace('-', ' ') 17 | words = words.lower().split() 18 | num_sylls = 0 19 | for word in words: 20 | word = word.strip(punctuation) 21 | if word.endswith("'s")or word.endswith("’s"): 22 | word = word[:-2] 23 | if word in missing_words: 24 | num_sylls += missing_words[word] 25 | else: 26 | for phonemes in cmudict[word][0]: 27 | for phoneme in phonemes: 28 | if phoneme[-1].isdigit(): 29 | num_sylls += 1 30 | return num_sylls 31 | 32 | def main(): 33 | while True: 34 | print("Syllable Counter") 35 | word = input("Enter word or phrase else press Enter to Exit: ") 36 | if word == '': 37 | sys.exit() 38 | try: 39 | num_syllables = count_syllables(word) 40 | print("number of syllables in {} is: {}" 41 | .format(word, num_syllables)) 42 | print() 43 | except KeyError: 44 | print("Word not found. Try again.\n", file=sys.stderr) 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /Chapter_8/missing_words.json: -------------------------------------------------------------------------------- 1 | {"petaled": 2, "sake": 2, "storks": 1, "spiritless": 3, "foregather": 3, "deepener": 3, "asakura": 4, "moonrise": 2, "fie": 1, "nursemaid": 2, "watersplash": 3, "paperweights": 3, "samuri": 3, "skims": 1, "battlers": 2, "tendrils": 2, "lichened": 2, "cloudbanks": 2, "beholders": 3, "samisen": 3, "carven": 2, "morningglory": 4, "persimmons": 3, "dusky": 2, "froglings": 2, "archways": 2, "oranged": 2, "treehouse": 2, "atsuta": 3, "camellia": 4, "yowl": 1, "wisteria": 4, "whippoorwill": 3, "inuyasha": 4, "dragonfly": 3, "nightingales": 3, "woodcutter": 3, "colour": 2, "dewdrop": 2, "hibiscus": 3, "ridgelines": 2, "swordhand": 2, "tendrilled": 2, "treeline": 2, "mooing": 2, "evenfall": 3, "priestling": 2, "scatters": 2, "bathwater": 3, "furue": 2, "windless": 2, "wintery": 3, "cloudbank": 2, "cumulus": 3, "pattering": 3, "shadeless": 2, "stretchings": 2, "windblown": 2, "creepers": 2} 2 | -------------------------------------------------------------------------------- /Chapter_8/missing_words_finder.py: -------------------------------------------------------------------------------- 1 | """Find words in haiku corpus missing from cmudict & build exceptions dict.""" 2 | import sys 3 | from string import punctuation 4 | import pprint 5 | import json 6 | from nltk.corpus import cmudict 7 | 8 | cmudict = cmudict.dict() # Carnegie Mellon University Pronouncing Dictionary 9 | 10 | def main(): 11 | haiku = load_haiku('train.txt') 12 | exceptions = cmudict_missing(haiku) 13 | build_dict = input("\nManually build an exceptions dictionary (y/n)? \n") 14 | if build_dict.lower() == 'n': 15 | sys.exit() 16 | else: 17 | missing_words_dict = make_exceptions_dict(exceptions) 18 | save_exceptions(missing_words_dict) 19 | 20 | def load_haiku(filename): 21 | """Open and return training corpus of haiku as a set.""" 22 | with open(filename) as in_file: 23 | haiku = set(in_file.read().replace('-', ' ').split()) 24 | return haiku 25 | 26 | def cmudict_missing(word_set): 27 | """Find and return words in word set missing from cmudict.""" 28 | exceptions = set() 29 | for word in word_set: 30 | word = word.lower().strip(punctuation) 31 | if word.endswith("'s") or word.endswith("’s"): 32 | word = word[:-2] 33 | if word not in cmudict: 34 | exceptions.add(word) 35 | print("\nexceptions:") 36 | print(*exceptions, sep='\n') 37 | print("\nNumber of unique words in haiku corpus = {}".format(len(word_set))) 38 | print("Number of words in corpus not in cmudict = {}" 39 | .format(len(exceptions))) 40 | membership = (1 - (len(exceptions) / len(word_set))) * 100 41 | print("cmudict membership = {:.1f}{}".format(membership, '%')) 42 | return exceptions 43 | 44 | def make_exceptions_dict(exceptions_set): 45 | """Return dictionary of words and syllable counts from set of words.""" 46 | missing_words = {} 47 | print("Input # syllables in word. Mistakes can be corrected at end. \n") 48 | for word in exceptions_set: 49 | while True: 50 | num_sylls = input("Enter number syllables in {}: ".format(word)) 51 | if num_sylls.isdigit(): 52 | break 53 | else: 54 | print(" Not a valid answer!", file=sys.stderr) 55 | missing_words[word] = int(num_sylls) 56 | print() 57 | pprint.pprint(missing_words, width=1) 58 | 59 | print("\nMake Changes to Dictionary Before Saving?") 60 | print(""" 61 | 0 - Exit & Save 62 | 1 - Add a Word or Change a Syllable Count 63 | 2 - Remove a Word 64 | """) 65 | 66 | while True: 67 | choice = input("\nEnter choice: ") 68 | if choice == '0': 69 | break 70 | elif choice == '1': 71 | word = input("\nWord to add or change: ") 72 | missing_words[word] = int(input("Enter number syllables in {}: " 73 | .format(word))) 74 | elif choice == '2': 75 | word = input("\nEnter word to delete: ") 76 | missing_words.pop(word, None) 77 | 78 | print("\nNew words or syllable changes:") 79 | pprint.pprint(missing_words, width=1) 80 | 81 | return missing_words 82 | 83 | def save_exceptions(missing_words): 84 | """Save exceptions dictionary as json file.""" 85 | json_string = json.dumps(missing_words) 86 | f = open('missing_words.json', 'w') 87 | f.write(json_string) 88 | f.close() 89 | print("\nFile saved as missing_words.json") 90 | 91 | if __name__ == '__main__': 92 | main() 93 | -------------------------------------------------------------------------------- /Chapter_8/test_count_syllables_w_dict.py: -------------------------------------------------------------------------------- 1 | """Load a dictionary file, pick random words, run syllable-counting module.""" 2 | import sys 3 | import random 4 | from count_syllables import count_syllables 5 | 6 | def load(file): 7 | """Open a text file & return list of lowercase strings.""" 8 | with open(file) as in_file: 9 | loaded_txt = in_file.read().strip().split('\n') 10 | loaded_txt = [x.lower() for x in loaded_txt] 11 | return loaded_txt 12 | 13 | try: 14 | word_list = load('2of4brif.txt') 15 | except IOError as e: 16 | print("{}\nError opening file. Terminating program.".format(e), 17 | file=sys.stderr) 18 | sys.exit(1) 19 | 20 | test_data = [] 21 | num_words = 100 22 | test_data.extend(random.sample(word_list, num_words)) 23 | 24 | for word in test_data: 25 | try: 26 | num_syllables = count_syllables(word) 27 | print(word, num_syllables, end='\n') 28 | except KeyError: 29 | print(word, end='') 30 | print(" not found", file=sys.stderr) 31 | -------------------------------------------------------------------------------- /Chapter_8/test_count_syllables_w_full_corpus.py: -------------------------------------------------------------------------------- 1 | """Check syllable-counting program against training corpus for haiku.""" 2 | import sys 3 | import count_syllables 4 | 5 | with open('train.txt.') as in_file: 6 | words = set(in_file.read().split()) 7 | 8 | missing = [] 9 | 10 | for word in words: 11 | try: 12 | num_syllables = count_syllables.count_syllables(word) 13 | ## print(word, num_syllables, end='\n') # uncomment to see word counts 14 | except KeyError: 15 | missing.append(word) 16 | 17 | print("Missing words:", missing, file=sys.stderr) 18 | -------------------------------------------------------------------------------- /Chapter_9/count_syllables.py: -------------------------------------------------------------------------------- 1 | """American English syllable counter using NLTK cmudict corpus.""" 2 | import sys 3 | from string import punctuation 4 | import json 5 | from nltk.corpus import cmudict 6 | 7 | # load dictionary of words in haiku corpus but not in cmudict 8 | with open('missing_words.json') as f: 9 | missing_words = json.load(f) 10 | 11 | cmudict = cmudict.dict() 12 | 13 | def count_syllables(words): 14 | """Use corpora to count syllables in English word or phrase.""" 15 | # prep words for cmudict corpus 16 | words = words.replace('-', ' ') 17 | words = words.lower().split() 18 | num_sylls = 0 19 | for word in words: 20 | word = word.strip(punctuation) 21 | if word.endswith("'s")or word.endswith("’s"): 22 | word = word[:-2] 23 | if word in missing_words: 24 | num_sylls += missing_words[word] 25 | else: 26 | for phonemes in cmudict[word][0]: 27 | for phoneme in phonemes: 28 | if phoneme[-1].isdigit(): 29 | num_sylls += 1 30 | return num_sylls 31 | 32 | def main(): 33 | while True: 34 | print("Syllable Counter") 35 | word = input("Enter word or phrase else press Enter to Exit: ") 36 | if word == '': 37 | sys.exit() 38 | try: 39 | num_syllables = count_syllables(word) 40 | print("number of syllables in {} is: {}" 41 | .format(word, num_syllables)) 42 | print() 43 | except KeyError: 44 | print("Word not found. Try again.\n", file=sys.stderr) 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /Chapter_9/missing_words.json: -------------------------------------------------------------------------------- 1 | {"sake": 2, "lichened": 2, "jagged": 2, "dewdrop": 2, "ridgelines": 2, "morningglory": 4, "oranged": 2, "evenfall": 3, "cumulus": 3, "woodcutter": 3, "priestling": 2, "nightingales": 3, "yowl": 1, "whippoorwill": 3, "scatters": 2, "mooing": 2, "fie": 1, "creepers": 2, "beholders": 3, "watersplash": 3, "moonrise": 2, "persimmons": 3, "wisteria": 4, "battlers": 2, "pattering": 3, "archways": 2, "wintery": 3, "petaled": 2, "colour": 2, "samisen": 3, "deepener": 3, "spiritless": 3, "skims": 1, "dragonfly": 3, "tendrils": 2, "tendrilled": 2, "swordhand": 2, "treehouse": 2, "hibiscus": 3, "foregather": 3, "camellia": 4, "furue": 3, "stretchings": 2, "storks": 1, "windblown": 2, "windless": 2, "dusky": 2, "samuri": 3, "shadeless": 2, "treeline": 2, "inuyasha": 4, "atsuta": 3, "nursemaid": 2, "froglings": 2, "cloudbanks": 2, "cloudbank": 2, "asakura": 4, "bathwater": 3, "carven": 2, "paperweights": 3} 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Impractical Python Projects 2 | 3 | This repository contains the source code and supporting files for the book *Impractical Python Projects: Playful Programming Activities to Make You Smarter* by Lee Vaughan. The files are organized by chapter. Each code listing in the book references a corresponding file name in this repository. 4 | 5 | ![image](https://user-images.githubusercontent.com/31315095/86491992-2382a380-bd32-11ea-82c2-e26febc82187.png) 6 | 7 | ## Get the Book 8 | The book is available at retail bookstores like Barnes & Noble and from online sellers like https://www.amazon.com/. 9 | A print/eBook bundle can be purchased directly from the publisher at https://nostarch.com/. 10 | 11 | ## Download the Chapters 12 | To download the chapter folders, use the green “Code” button near the top of the repository code page. 13 | 14 | ![image](https://user-images.githubusercontent.com/31315095/86492104-9c81fb00-bd32-11ea-97d0-96efd5863c49.png) 15 | 16 | ## Version 17 | The book uses Python 3.5. 18 | 19 | ## Errata 20 | For updates and errata, visit https://nostarch.com/impracticalpythonprojects. Please report typos and mistakes to errata@nostarch.com. 21 | 22 | ## Additonal Books by Author 23 | [*Real World Python: A Hacker's Guide to Solving Problems with Code*](https://nostarch.com/real-world-python) 24 | --------------------------------------------------------------------------------