Level 1: Python Programming - Mastermind Game

Welcome!  You're about to start an exciting journey.  Programming can be amazing, engaging, and interesting, but most of all I think it should be fun.  If you've ever thought about learning programming, this is a good place to start.

This lesson is meant for beginners; they introduce basic programming concepts and allow a budding new developer to build a simple Mastermind game in Python.

I'd recommend that as you start coding, you experiment.  What happens if this variable is changed slightly?  What happens if these statements are rearranged?  Experimentation is key to learning, and you should convince yourself about the way code works by trying a few different things.  It can really help you to understand what's happening.

Remember, the best way to learn how to write code is to start writing code!

Installing Python
The first thing that you'll have to do is install Python.  You can get the Python installer here.  I'd recommend starting with the latest 2.7 version.

Our First Python Code
We'll work from the command line for this exercise.  If you need help finding it, on a Mac, open the "Applications" folder and go to the bottom.  You'll want to open the "Utilities" folder here.

In the "Utilities" folder, open the "Terminal" application.

Your terminal application should look something like this (but the colors might be different - you can change them in the Terminal => Preferences menu).

Nano is built-in on Mac OS.  Windows users: you'll want to open a command line by following the directions here.  We're going to use nano for these exercises, so you will want to download and install nano; alternatively, you can just use Notepad or another text editor, you'll just need to note where you save your files.

To get started, we'll write a very simple Python program.  Type the following into your Terminal:

nano hello.py

You should see a screen that looks something like this:

This is a simple text editor that will allow us to write basic Python code.  We need to use a text editor because it won't insert any special formatting or markup in our code - something like Word or TextEdit won't necessarily save our code in a plain text format.  

We're going to write a basic program here.  Type the following code into nano:

name = raw_input('What is your name?')
print 'Hello, ' + name

The code above is a simple way to start.  What is going on in our code:
  • On the first line, the first thing we do is define a variable called name.  A variable is simply a place where we will store some value - it can be a number, it can be text, it might be something even more complicated.  For now though, it's just going to contain text.
  • Next (still on the first line), we assign name - we're going to set its value.  We use raw_input here - raw_input is a function that is built-in to Python.  A function is like a little program - it does something particular.  The raw_input function outputs a message to the user, and collects the user's input.  This input will become the value of our name variable.  Note that the message starts and ends with single quotes (on your keyboard, this is directly to the right of the semicolon key).  This is how Python knows where our text starts and ends.
  • On the second line, we use the print statement in Python.  This is another built-in feature that we can use.  The print statement just outputs a message to the user - here, we're going to say hello.  We're going to also concatenate the text with the value that is now in the name variable.
To save this program, press Ctrl+O.  We can exit nano by pressing Ctrl+X. From here, we can run the program by typing the following:

python hello.py

This will run our program.  You should see something like this:

It doesn't do much yet, but it's a start.  

A Simple Guessing Game
We'll start by creating a very simple guessing game - this version will just be pure guessing with no hints.  First, type the following into your terminal:

nano mastermind.py

We'll start with the following code:

This is what our code is doing:
  • On the first line, we're just writing a message to the user using the print statement.
  • The second line is blank.  This doesn't really do anything - we don't need this, we're just doing it to make the code a bit more readable.
  • On the third line, we're creating a variable called code and assigning the value '1234' to it.
  • On the fourth line, we're creating a variable called attempts and assigning the value 1 to it.
    • Up until this point, we've been dealing with text values. h We call these strings.  Our attempts variable here is an integer.  This means that it's a positive or negative whole number (i.e., -143123, 1, 2, 929201, etc).
  • On the fifth line, we're creating a variable called guess and assigning the value '' to it.
  • The sixth line is also blank.  It's just for formatting.
  • On the seventh line, we're using a while statement.  The while statement continues to execute some code as long as some condition is met.  For our while statement here, we are going to execute some statements as long as the value of guess is not equal to (not equal is what the != symbols mean) the value of code.  
  • The seventh line ends with a colon.  This means that the following lines are going to be part of this statement.
  • The eighth and ninth lines both begin with two blank spaces.  This is important because Python uses indenting to determine which statements are part of our while loop.  Notice how the 8th and 9th lines look like the belong under the while loop - Python code looks nice and neat because of this requirement.
  • The eighth line sets our guess variable to the user's input.  We're also prompting the user here and telling the user which attempt number this is.
    • The string in the 8th line begins with \n - note that this means end line.  The user will not see the \n characters when the program runs, but each time we execute this statement, the text that we output will be preceded by a blank line.
    • Also on the 8th line, we concatenate the attempts variable with our string.  We first have to call the str function here.  str is a built-in function in Python that converts our integer into a string so that it can be concatenated with the rest of the string.  This is important because Python is type-safe - we'll talk more about this later.
  • The ninth line adds one to the attempts variable - this allows attempts to continue to increase once every time we run the while loop.
  • Note that the while loop will continue to run UNTIL the user inputs the correct guess.
  • On the eleventh line, we print a message for the user informing them that the game is over.
You can run this by typing:

python mastermind.py

You should see something like this:
This is a simple guessing game, but it's not very much fun to play over and over again.  We'll write some more code to build a more complete game (and introduce some other programming concepts).

Limiting the Number of Attempts
This game already has some limitations.  One problem: eventually, you're going to win.  We can solve the this problem pretty easily - we can limit the number of guesses that a user gets.  To do this, we'll add a new variable called max_attempts, and check it in our while loop.

  • Our changes are on line 6 and line 8.
  • On line 6, we declare the new variable - max_attempts, and initialize its value to 10.
  • On line 8, we enhance our while loop - we now check to see if the guess is not equal to the code, *and* if attempts is less than or equal to (this is what the <= symbols mean) max_attempts.  This will let us run the code within our loop no more than 10 times.
Of course this still doesn't get us as far as we need.  If you try to run your program now, it will let you guess 10 times, but will tell you you win regardless of whether or not you guessed the right code.  We'll need to add an if-else statement to output the right message.

  • On line 12, we start our if-else statement.  We'll start by checking if guess is equal to code (the == symbols are for checking equality).
  • If this is true, then we output "You guessed the code!"  Note that this line is indented 2 spaces to identify that it's part of the if statement.
  • If it's not true, we output "Sorry, you lose."  This line is also indented 2 spaces to show that it's part of the else statement.

Using a List
Right now, our code is a simple string, but at some point it would be good for us to check each of the values in our string individually.  To do this, we can use a Python list.  We can use lists in a few ways - on line 3 below, we'll declare a list and initialize it in one step.  It will contain individual values '1', '2', '3', and '4'.

We'll change some other code too to accommodate our new list.
  • On line 3, we change our code variable to a list.
  • On line 9, we change our guess variable to raw_guess because we're going to need to convert it.  We also change the message we're showing the user to clarify what we're expecting.
  • On line 10, we will take the user's input and convert it to a list.  We're splitting it apart by spaces.
  • On line 12, we declare a new variable, number_correct.  We'll use this to give the user some feedback on how many numbers they've guessed correctly.
  • On lines 13-20, we check each of the values in the user's entry and compare them to the values in our code list.  On line 13, we compare guess[0] to code[0] - these are the first values in the guess list and in the code list.  In Python (as in many other languages), our lists start with 0.
Now that we've determined how many the user has correct, we'll let the user know.  We'll add a statement on line 22 to let the user know how many they've got correct.

Note that the lines at the end of our mastermind.py file aren't gone - they're just not displayed.  If you continue to scroll down, you'll see them.

Using a For Loop
Of course, this repetition of statements on lines 13-20 is tedious and unnecessary.  Plus, we'll have to change it quite a bit if we want to make our code 5 digits or 3 digits.  Fortunately, we can use a for loop here. This is similar to a while loop; it's going to repeat a set of statements several times, but it's going to let us use a variable to count our way through those repetitions.

  • We've removed lines 13-20 and replaced them with a for loop here.
  • On line 13, we start the for loop.  We want to run the loop once for each item in code.  We start by declaring i as the counter variable here, and we run it from 0 until the length of the code list.  We use the len function in Python to determine how long code is, and we use the range function to iterate from 0 to that value.
  • On line 14 and 15, we execute the same logic as before, but now note that we are using i to identify which values we're comparing, instead of using the numbers 0, 1, 2, and 3.
  • Our for loop should execute 4 times; in those four executions, i will be 0, 1, 2, and 3.
There's one problem left we need to address; what happens if the user enters a code that doesn't have four values?  We should check this before we run into a problem.  The user might enter only 2 values, and you'll end up with something like this:
We could fix this with an if statement, and forfeit the user's turn if they don't enter 4 digits.  This would be simple, but we can also use a while loop to provide the user an option to keep entering a guess until they enter 4 digits.

You can see the code for this here:

  • On line 12, we check to see if the length of the user's guess is 4.  If it is, we won't run this while loop at all.
  • If the user's guess does not have 4 digits, then we will ask for another guess on line 13.
  • On line 14, we convert the raw_guess value into guess by splitting it based on spaces.
Now we should have a program that protects against a user entering bad inputs.

Using Randomness
Now we can look at addressing the fact that the game is the same every time.  To do this we'll use randomness.  Python has a built-in module called (appropriately enough) random.  We can use this to generate numbers.  We can write a simple program to test this: if you type

nano randtest.py

Then enter the following code:
You should be able to see exactly what happens with Python's random module when you run your program with 

python randtest.py

You should see something that looks like this if you run it a few times, but your numbers are likely to be different than these ones.
When we call random.random() in our program, it returns a random decimal number between 0 and 1.  Wha we want is an integer, though - not a decimal number.  Fortunately we can use a little math and some functions to generate a random integer.  To make this reusable, we'll use a function.  A function is like a tiny program that we can use over and over again.  Functions can use parameters so that they can operate differently based on some kind of input.  We use the def (for "define") keyword in Python to define a function.
  • On the very first line, we import Python's random module.  This will allow us to generate random numbers.
  • On the third line, we define a function called get_random.  This function will take one parameter - a size value.  We'll use this to tell the get_random function how large to make our result.
  • On the fourth line, we call random.random() and multiply by size.  This will give us a random decimal number between 0 and size.
  • We want our result to be an integer, so we'll use Python's int function to convert the random number.  This will round down, so our result will be a random number from 0 up to (but not including) size.
Now that we have a function, we can use it to generate our code randomly.  We'll define another function here to generate the code.
  • On line 7, we define a new function.  We call it generate_code and define two parameters for it: code_size and max_digit.  The code_size parameter will tell us how many values should be in our code; the max_digit parameter will tell us how large each digit should be.
  • On line 8, we declare our result.  It's going to be a list.
  • On line 9, we use a for loop to define the values in our code.  We will fill in a value for each value in the list, from 0 up to (but not including) code_size.  Note that this will give us a list that has exactly code_size values - while we don't include one for code_size, we include one for 0.
  • On line 10, we generate a code value.  This is going to use our get_random function, but since that returns a value from 0 to size (but not including size), and we want a value from 1 to size (including size), we add one to it.  We also use the str function to convert it to a string, because we're going to have to compare it to the user's input, which is also going to be a string.
  • On line 11, we use append to add our value to the list.
  • On line 12, after we've iterated code_size times, we return the result.
  • On line 16, we change our code variable to use the new function - now we'll generate a code that's 4 values long, with random digits from 1 to 4.
Now we have a real game - it's going to generate a different code each time and tell us how many we got right.  To be a bit more like the real Mastermind game though, we'll need to write a bit more code.

Checking the Code and Using Tuples
So far, we're able to let the user guess the code and tell the user how many of the digits were correct, but the original Mastermind game tells us when we have the right value in the wrong position.  Since we need to return 2 results, we can define a new function that will return a tuple.  A tuple is similar to a list, but they are immutable - they can't be changed after we declare them.  We'll use a tuple here to return the number of values in the user's guess that are in the correct position, as well as the number that are correct but not necessarily in the correct position.
  • On line 14, we define a new function.  This function is going to check code against guess.
  • On line 15, we declare a variable called correct_position.  This is going to tell us how many of the values in guess are in the right position in code.
  • On line 16, we declare a variable called correct_value.  We'll use this to track how many of the values in guess are in code, but not necessarily in the right position.
  • On line 17, we create a copy of code.  We do this because we have a challenge here.  We don't want to double-count values for correct_value.  For example, if our code is 1 2 1 1 and our guess is 3 1 3 3, we want to tell the user that *only* one of the values in guess is right, but in the wrong position.  In our code, we'll need to keep track of this and remove values as they're found.  We'll use this copy that we can modify easily without affecting code or guess.
  • On line 19, we check to make sure that code and guess have the same number of values.
  • On line 20, we use a for loop to iterate over the contents of guess.
  • On line 21, we check to see if the value in this position of guess is equal to the value in this position of code.  If it is, we add one to correct_position on line 22.
  • On line 23, we check to see if there are *any* values in code_copy that are equal to the value in this position of guess.  If there are, we add one to the value of correct_value on line 24, and on line 25, we remove the value from code_copy.  The call to remove here removes the *first* matching value from code_copy.
This function should allow us to check not only how many values are in the correct position in guess, but also how many are correct but *not* necessarily in the correct position.

Now that we've defined our function, we can replace some of our other code and improve the messages we're sending to the user.
  • On line 45, we are going to change the previous code - we want to use our function check_code to check the code now, instead of using the for loop we were using before.  We'll also declare two variables - number_correct and correct_values - these will take the values that we return from our function.
  • We will change line 47, where we printed the number the user had correct; now we will print the number of correct_values.
  • On line 48, we add another print statement - this one is going to tell the user the number of values that are in the correct position.
At this point we have a pretty functional game - it should be playable over and over again.

Finishing Up
Now that we have a real game, we can clean up our code in a few places.
  • On line 30, we were printing a message to the user, stating that we will use a 4 digit code.  We can provide more information here; let's move the print statement below our variables, so that we can use code to tell the user the number of digits.  This will be useful if you ever want to change the number of digits to 5 or 6 (for a harder game), or you want to make an easier game by changing the number of digits to 3.  Note that here we're using the len function to find the length of code, then we're using the str function to convert that number to a string.
  • We also define a new variable, max, to identify the maximum value of our digits.
  • We also add another print statement to tell the user how many digits we'll be using.
  • We change our raw_input statement; we add a print statement above so that we can tell the user the attempt number, and we add in the str(len(code)) call to tell the user how many digits to guess.
  • We change our while loop; instead of checking to see if len(guess) is not 4, we will use len(guess) != len(code).
  • We also change the line following our raw_input in the while loop, so that we can tell the user the number of digits to guess.
Now, we can easily modify the max variable and the call to the generate_code function to change the number of digits and the maximum digit.  If we try it with 3 for the maximum digit and 5 for code size, you can see how the game changes.
Note max = 3 and generate_code(5, max) above.

The game should now look something like this:
That should do it!  Now you have a functional Mastermind game that you made yourself.  Try experimenting with it!  See what you can modify to change the game's behavior.  Tell the user more information.  Be creative.  By testing out different ideas, you'll learn how they work and learn more about coding.

Thanks for making it this far, and check out some of the other examples and tutorials on the site!

Timothy James,
Jul 26, 2015, 10:29 AM