I am teaching myself Python by working through 2 books by the same author, Al Sweigart: Automate the Boring Stuff and The Big Book of Small Python Projects. My aim is to learn by doing. I’m partly inspired by Andrej Karpathy’s tweet:

“How to become expert at thing: 1 iteratively take on concrete projects and accomplish them depth wise, learning “on demand” (ie don’t learn bottom up breadth wise) 2 teach/summarize everything you learn in your own words 3 only compare yourself to younger you, never to others”.

I’ll channel his method by going through The Big Book project by project and learning the concepts on-demand through Automate the Boring Stuff (which is more of a textbook teaching Python). Additionally, The Big Book has its own learning methodology to increase its effectiveness:
1) Download the source code and run it yourself to see what happens and what it does.
2) Manually copy the code yourself, line by line.
3) Run your copy, go back and fix typos & errors.
4) Run the program under a debugger, learn what each line of code does.
5) Find comments within the code marked with (!) to modify and see cause & effect on how it runs.
6) Try to re-write the program from scratch.

What is bagels.py?
bagels.py is a game in which the user has 10 guesses to guess a random 3-digit number generated by the program. If the user guesses a correct digit in the wrong place, the program will return ‘Pico’ as a clue. If the user guesses a correct digit in the correct place, the program will return ‘Fermi’ as a clue. Example: If the number is 123 and I guess 243, the program will return ‘Fermi Pico’. The clue is put into alphabetical order so the user does not know which digit is in the correct place. If the user doesn’t guess a correct digit, the program returns ‘Bagels’ hence the name.

Concepts to Know:
Modules: Python comes with a standard library of modules. Each one is a Python program that contains a related group of functions that can be used in the program. These can be imported by, on it’s own line, typing ‘import name of module’
Variables: Variables are simple, they allow you to store a value in them via an ‘Assignment Statement’. The term is analogous to its an algebraic variable. x = 4 is an Assignment Statement that makes x always equal to 4. x + 1 is equal to 5 the same way 4 + 1 is equal to 5. Variables are case sensitive, seems like everything is in Python, and cannot be equal to Python Key Words.
Functions: Python has its own set of built-in functions, I won’t go through each one. You can also create your own function by using a ‘def statement’.
Flow Statements: Flow control statements like if, else, elif are key words that tell the program to do something if a condition is met, or if it’s not.
Loop Statements: Similar to flow statements, loop statements like while, for, continue will repeat or continue while a condition is true.
Lists: A value that contains multiple values that starts and ends with square brackets.

The above concepts are high level as the program was introductory, as I dive into further projects I will dissect sub-concepts within each such as adding a slice to a list to return multiple values, or docstrings.

Description of Code:
The final step before re-writing from scratch was to write down in plain language what each line of code did in order to be my source material to re-write without referring back to the source code. The original plan was to include that within this blog, but it’s overly dense and boring. But the end result was the code below, which I am content with as it only had 4 – 5 bugs to correct:

"""Bagels, by Rob D. al@inventwithpython.com
A deductive logic game where you must deduct a number based on clues.
View this code at https://nostarch.com/big-book-small-python-projects
A version of this game is featured in the book "Invent Your Own
Computer Games with Python" https://nostarch.com/inventwithpython
Tags: short, game, puzzle"""

import random

NUM_DIGITS = 3
MAX_GUESSES = 10

def main() :
    print('''Bagels a deductive logic game.
	By Al Sweigart al@inventwithpython.com
	
	I am thinking of a {}-digit number with no repeated digits.
	Try to guess what it is. Here are some clues:
	When I say:     That means:
	    Pico        One digit is correct but in the wrong position.
	    Fermi       One digit is correct and in the right position.
	    Bagels      No digit is correct.
	
	For example, if the secret number is 248 and your guess was 843, the
	clues would be Fermi Pico.'''.format(NUM_DIGITS))
    while True :
        secretNum = getSecretNum()
        print('I have thought up a number.')
        print('You have {}-guesses to get it.'.format(MAX_GUESSES))

        numGuesses = 1
        while numGuesses <= MAX_GUESSES :
            guess = ''
            while len(guess) != NUM_DIGITS or not guess.isdecimal() :
                print('Guess #{}'.format(numGuesses))
                guess = input('>')

            clues = getClues(guess, secretNum)
            print(clues)
            numGuesses += 1

            if guess == secretNum :
                break
            if numGuesses > MAX_GUESSES :
                print('You ran out of guesses.')
                print('The answer was {}'.format(secretNum))
        print('Do you want to play again? (yes or no)')
        if not input('>').lower().startswith('y') :
            break
        print('Thanks for playing.')

def getSecretNum() :
    """Returns a string made up of NUM_DIGITS
    unique random digits."""
    numbers = list('0123456789')
    random.shuffle(numbers)

    secretNum = ''

    for i in range(NUM_DIGITS) :
        secretNum += str(numbers[i])
    return secretNum

def getClues(guess, secretNum) :
    """Returns a string with the pico, fermi, bagels clues for a guess
    and secret number pair."""
    if guess == secretNum :
        return 'You got it.'

    clues = []

    for i in range(len(guess)) :
        if guess[i] == secretNum[i]:
            clues.append('Fermi')
        elif guess[i] in secretNum:
            clues.append('Pico')
    if len(clues) == 0:
        return 'Bagels'
    else:
        clues.sort()
        return ''.join(clues)

if __name__ == '__main__' :
    main()

Posted in

Leave a comment