-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Mastermind project (#17) #32
base: main
Are you sure you want to change the base?
Changes from 13 commits
cd8ceb2
f26fc72
80cf0b1
6313cc5
199f29a
9eedf82
5b2d319
a8d8323
d2fd59e
a65b124
c617b46
47608b0
6bc015e
8c09b12
a5fdb94
cf405c4
5090ba2
8d2d980
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,226 @@ | ||||||
--- | ||||||
title: Create Mastermind Game with Python | ||||||
author: 3t8 | ||||||
datePublished: 2022-11-06 | ||||||
description: Learn how to create Mastermind game with Python in this project tutorial | ||||||
header: URL | ||||||
tags: | ||||||
- beginner | ||||||
- python | ||||||
--- | ||||||
|
||||||
# Create Mastermind Game with Python | ||||||
|
||||||
<AuthorAvatar author_name="3t8" author_avatar="https://avatars.githubusercontent.com/u/62209650" /> | ||||||
|
||||||
![Header image](URL) | ||||||
|
||||||
**Prerequisites:** Python fundamentals | ||||||
**Versions:** Python 3.10 | ||||||
**Read Time:** 30 minutes | ||||||
|
||||||
## [#](#-introduction) Introduction | ||||||
|
||||||
With more than 55 million units sold, [Mastermind](https://en.wikipedia.org/wiki/Mastermind_(board_game)) is a one of the world’s most popular strategy games ever. The idea behind it is to guess an unknown sequence of colored pegs in the fewest number of attempts possible. Despite having simple rules the game is still very difficult and entertaining. | ||||||
3t8 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
Because representing colors in a terminal is not an easy task, we are going to create a more *modern* version of the Mastermind game. In 1977 Invicta released an electronic version of the game where the colored pegs were replaced by an array of up to five digits. In this tutorial we are going to recreate it in Python. | ||||||
3t8 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
![Invicta Electronic Master Mind](https://tomsk3000.com/wp-content/uploads/2020/07/200707_Electronic-Mastermind_08_lres.jpg) | ||||||
|
||||||
## [#](#-rules) Rules | ||||||
|
||||||
The rules are really simple: | ||||||
- A random 4 digit secret code is generated | ||||||
- The player will input a 4 digit number trying to guess the secret code | ||||||
3t8 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
- The number of digits that are correct and in the right position is returned | ||||||
- The number of digits that are correct but in the wrong position is returned | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick, but could you add a small note here about what "right position" and "correct" means? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 |
||||||
|
||||||
## [#](#-generate-a-random-number) Step 1. Generate a Random Number | ||||||
|
||||||
In order to generate a random number we need to use the `random` module. More specifically we are going to use `randrange` method. | ||||||
|
||||||
```python | ||||||
from random import randrange | ||||||
``` | ||||||
|
||||||
The `randrange` method returns a random number within the specified range. To generate a 4 digit number we need our range to be between 0 and 10000 (the largest number generated would be 9999) | ||||||
|
||||||
```python | ||||||
number = randrange(10000) | ||||||
``` | ||||||
|
||||||
There is only a small problem. If the generated number is smaller than 1000 we will end up with a secret code that has less than 4 digits. | ||||||
|
||||||
To fix that we need to add zeroes at the beginning of the number until it reaches 4 digits. | ||||||
|
||||||
The easiest way to do it is converting the number to a string and justify it to the right with `rjust` by adding `0` until it has `4` digits. | ||||||
|
||||||
``` python | ||||||
number = str(number).rjust(4, '0') | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we rename this |
||||||
``` | ||||||
|
||||||
We now have our random secret code. | ||||||
|
||||||
## [#](#-read-users-guess) Step 2. Read user's guess | ||||||
|
||||||
The next step is getting user's guess. To do that we will use the `input` function. | ||||||
3t8 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
We use `isnumeric` to check that all the characters taken as input are numeric and `len(guess)` to make sure that the guess has 4 digits. | ||||||
|
||||||
If any of those conditions are not met, we will ask again the user for a guess. | ||||||
3t8 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
We set `guess = 0` to execute the instructions inside the while the first time. | ||||||
|
||||||
|
||||||
```python | ||||||
guess = 0 | ||||||
while (guess.isnumeric() == False or len(guess) != 4): | ||||||
guess = input('Guess the four digit number: ') | ||||||
``` | ||||||
The `guess` read with `input` is treated as a string, same as the secret code `number`. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Please change |
||||||
|
||||||
## [#](#-additional-variables) Step 3. Additional variables | ||||||
|
||||||
We need a variable to count the correct digits in the right position | ||||||
|
||||||
```python | ||||||
correct_position = 0 | ||||||
``` | ||||||
|
||||||
and one to count the correct digits in the wrong position | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this to the previous line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The two variables can also be combined to one python field. |
||||||
|
||||||
```python | ||||||
correct_digit = 0 | ||||||
``` | ||||||
We will also use a copy of `number` in order to keep track of the digits that have already been matched and the ones that are left unmatched | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing a period. |
||||||
|
||||||
```python | ||||||
unmatched = number | ||||||
``` | ||||||
|
||||||
## [#](#-count-guessed-digits-in-the-right-position) Step 4. Count guessed digits in the right position | ||||||
|
||||||
To count how many digits of the `guess` match the ones in `number` we need to check all of them in pairs. | ||||||
Because there are 4 digits we need to iterate the check 4 times so we use `range(4)`. | ||||||
|
||||||
If both `guess` and `number` have the same digits in the same positions, we increment the `correct_position` counter. | ||||||
|
||||||
To keep track of the matched digits and exclude them later when we count the right digits in the wrong positions, we conventionally set `unmatched` to `f` at the position of the matched digit. This way `f` will never match with any digit from 0 to 9 | ||||||
|
||||||
```python | ||||||
for i in range(4): | ||||||
if (guess[i] == number[i]): | ||||||
unmatched = unmatched.replace(guess[i], 'f', 1) | ||||||
correct_position += 1 | ||||||
``` | ||||||
|
||||||
## [#](#-count-guessed-digits-in-the-wrong-position) Step 5. Count guessed digits in the wrong position | ||||||
|
||||||
There is no need to count the right digits in the wrong position if we already matched all 4. | ||||||
|
||||||
```python | ||||||
if (correct_position < 4): | ||||||
``` | ||||||
|
||||||
We will only execute the next instructions if the condition above is true. | ||||||
|
||||||
Once again we use `range(4)` to iterate over all 4 digits. | ||||||
This time the digits are compared between `guess` and `unmatched`. | ||||||
We check if `guess[i] in unmatched` and if that's true we increment the `correct_digit` counter. | ||||||
|
||||||
The `guess` variable has not been changed at all, but we **must not** check the digits that have already been matched in the right position. | ||||||
That is done by also checking if `guess[i] != number[i]` (or if `unmathced[i] != 'f'`). | ||||||
|
||||||
```python | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing the aforementioned |
||||||
if (guess[i] in unmatched and guess[i] != number[i]): | ||||||
unmatched = unmatched.replace(guess[i], 'f', 1) | ||||||
correct_digit += 1 | ||||||
``` | ||||||
Like the last time, the matched digits in `unmatched` are set to `f` so they will be excluded in the next iteration. | ||||||
|
||||||
## [#](#-printing-guessed-digits-and-winning) Step 6. Printing guessed digits | ||||||
|
||||||
At this point both `correct_position` and `correct_digit` counters should have the right values. We just need to print them. | ||||||
|
||||||
```python | ||||||
print('Digits in right position: ' + str(correct_position)) | ||||||
print('Digits in wrong position: ' + str(correct_digit)) | ||||||
``` | ||||||
|
||||||
## [#](#-counting-the-number-of-guesses-and-winning) Step 7. Counting the number of guesses and winning | ||||||
|
||||||
The whole code after the `number` generation needs to be inside a | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to finish this sentence with a colon or " |
||||||
|
||||||
```python | ||||||
while (guess != number): | ||||||
``` | ||||||
|
||||||
Each time the code is executed we need to increment the `tries` variable | ||||||
|
||||||
```python | ||||||
tries += 1 | ||||||
``` | ||||||
|
||||||
If the `while` condition is not satisfied that means `guess == number` so the secret number has been guessed. | ||||||
|
||||||
We tell the user that he/she won and we also tell him/her how many attempts it took | ||||||
|
||||||
```python | ||||||
print('You are a mastermind! You guessed the number in ' + str(tries) + ' tries') | ||||||
``` | ||||||
|
||||||
## [#](#-code) Code | ||||||
|
||||||
The whole code should look like this: | ||||||
|
||||||
```python | ||||||
from random import randrange | ||||||
|
||||||
# Generate a random number from 0 to 10000 | ||||||
number = randrange(10000) | ||||||
# Add padding to the number if it doesn't have four digits | ||||||
number = str(number).rjust(4, '0') | ||||||
tries = 0 | ||||||
guess = 0 | ||||||
|
||||||
while (guess != number): | ||||||
tries += 1 | ||||||
guess = '' | ||||||
# unmatched will keep track of the digits that have not been guessed | ||||||
unmatched = number | ||||||
correct_digit = 0 | ||||||
correct_position = 0 | ||||||
# Read a four digit number | ||||||
while (guess.isnumeric() == False or len(guess) != 4): | ||||||
guess = input('Guess the four digit number: ') | ||||||
# Count the guessed digits in the right postion | ||||||
for i in range(4): | ||||||
if (guess[i] == number[i]): | ||||||
# Set the guessed digit to 'f' in unmatched | ||||||
unmatched = unmatched.replace(guess[i], 'f', 1) | ||||||
correct_position += 1 | ||||||
# Count the guessed digits in the wrong position | ||||||
if (correct_position < 4): | ||||||
for i in range(4): | ||||||
if (guess[i] in unmatched and guess[i] != number[i]): | ||||||
# Set the guessed digit to 'f' in unmatched | ||||||
unmatched = unmatched.replace(guess[i], 'f', 1) | ||||||
correct_digit += 1 | ||||||
print('Digits in right position: ' + str(correct_position)) | ||||||
print('Digits in wrong position: ' + str(correct_digit)) | ||||||
print('You are a mastermind! You guessed the number in ' + str(tries) + ' tries') | ||||||
``` | ||||||
|
||||||
## [#](#-improvements) Improvements | ||||||
|
||||||
To improve this project you could try to: | ||||||
|
||||||
- Increase the number of digits the user has to guess. With 4 digits the possible permutations are 10000. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
- Allow the user to use a [custom seed](https://docs.python.org/3/library/random.html#random.seed) for the secret number. This could allow multiple users to compete against each other and try and guess the same number. | ||||||
- Try to code your own solver by implementing [Knuth's algorithm](https://en.wikipedia.org/wiki/Mastermind_(board_game)#Worst_case:_Five-guess_algorithm). | ||||||
|
||||||
## [#](#-more-resources) More Resources | ||||||
|
||||||
- [Solution on GitHub](https://github.com/codedex-io/projects/blob/main/projects/create-mastermind-game-with-python/mastermind.py) | ||||||
- [Documentation: random](https://docs.python.org/3/library/random.html) | ||||||
- [Mastermind solver](https://nebupookins.github.io/JS-Mastermind-Solver/) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from random import randrange | ||
# Generate a random number from 0 to 10000 | ||
number = randrange(10000) | ||
# Add padding to the number if it doesn't have four digits | ||
number = str(number).rjust(4, '0') | ||
tries = 0 | ||
guess = 0 | ||
|
||
while (guess != number): | ||
tries += 1 | ||
guess = '' | ||
# unmatched will keep track of the digits that have not been guessed | ||
unmatched = number | ||
correct_digit = 0 | ||
correct_position = 0 | ||
# Read a four digit number | ||
while (guess.isnumeric() == False or len(guess) != 4): | ||
guess = input('Guess the four digit number: ') | ||
# Count the guessed digits in the right postion | ||
for i in range(4): | ||
if (guess[i] == number[i]): | ||
# Set the guessed digit to 'f' in unmatched | ||
unmatched = unmatched.replace(guess[i], 'f', 1) | ||
correct_position += 1 | ||
# Count the guessed digits in the wrong position | ||
if (correct_position < 4): | ||
for i in range(4): | ||
if (guess[i] in unmatched and guess[i] != number[i]): | ||
# Set the guessed digit to 'f' in unmatched | ||
unmatched = unmatched.replace(guess[i], 'f', 1) | ||
correct_digit += 1 | ||
print('Digits in right position: ' + str(correct_position)) | ||
print('Digits in wrong position: ' + str(correct_digit)) | ||
print('You are a mastermind! You guessed the number in ' + str(tries) + ' tries') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you ok with using your real name here?
If so please update your name here