diff --git a/projects/create-mastermind-game-with-python/create-mastermind-game-with-python.mdx b/projects/create-mastermind-game-with-python/create-mastermind-game-with-python.mdx new file mode 100644 index 00000000..88328687 --- /dev/null +++ b/projects/create-mastermind-game-with-python/create-mastermind-game-with-python.mdx @@ -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 + + + +![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. 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. + +Since 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 that version in Python. + +![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 to try to guess the secret code +- 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 + +## [#](#-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') +``` + +We now have our random secret code. + +## [#](#-read-users-guess) Step 2. Read user's guess + +The next step is getting the user's guess. To do that, we will use the `input` function. + +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 the user for a guess again. + +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`. + +## [#](#-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 + +```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 + +```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 +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 + +```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. +- 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/) diff --git a/projects/create-mastermind-game-with-python/mastermind.py b/projects/create-mastermind-game-with-python/mastermind.py new file mode 100644 index 00000000..d7a47a0d --- /dev/null +++ b/projects/create-mastermind-game-with-python/mastermind.py @@ -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')