Use the skills taught on our Python course to help build a simple Python game using Lists and much more and if you are up for it maybe even your own AI bot to play
About this Tutorial –
Objectives –
Python is a powerful and popular object-oriented scripting language. This course provides a comprehensive introduction to the core syntax and functions provided by Python, including full coverage of its object-oriented features. The course also explores some of Python’s powerful APIs and techniques, including file handling, XML processing, object serialization, and Web service
Audience
This training course is aimed at new developers and experienced developers
Prerequisites
No previous experience in Python programming is required. But any experience you do have in programming will help. Also no experience in IDLE is required. But again any experience you do have with programming development environments will be a valuable.
Experience using a contemporary OO language such as C++ or C# would be useful but is not required.
Contents
The Python course covers these topics and more:
- Strings and Regular Expressions: Overview of strings in Python; Basic string manipulation; Introduction to regular expressions
- XML Processing: XML essentials; Parsing XML documents; Searching for XML content; Generating XML data
- Web Services: Overview of Web services; Implementing Web services using Python; Caching; Compression; Handling redirects
Overview
Estimated Time – 10+ hours
Lab 1: Understanding the game
- Why are we making a game?
- To help teach and consolidate your Python skills.
- Create a program that is fast and fun to do.
- Learn how all your Python skills come together to make a project.
- The idea of the game:
- The game is based on a board-game (https://en.wikipedia.org/wiki/Mastermind_(board_game))
- You can have a choice between three to eight pegs
- The game will choose a random combination of pegs to start with
- Your challenge is to guess the correct combination of coloured pegs e.g. r,b,g,y – would be red, blue, green, yellow pegs in that order
- You are given two pieces of information after each guess – a black number (right colour peg in the correct position) and a white number (right colour in the wrong position)
- You have 12 guesses
- It gets harder when you select more pegs
- The components of the game:
- Basic AI
- Use of Classes
- Basic use of Lists and Dictionaries
-
The game should allow you to test the skills you have learnt from the course. Then put them into practice. If you wanted to go above and beyond you could attempt a multiplayer game and
introduce some new functionality to play with a friends over the internet. Or create a Graphical User Interface.
Lab 2: Basic Setup – Create the files and Structure
- What IDE? Which version of Python?
- I am going to be using IDLE that comes with Python download for windows.
- This tutorial will be using Python 3.5.1.
- To define our project we are going to split the work up into classes. This will separate the functions into logical groupings – For this we have four files. A main script that is the Python script that we run to start everything off; there is then a service layer that contains the logic that manipulate the lists. Similar to the service layer we then have a UI class. This is used to update the command line interface. Lastly we have an AI class that contains all the functions used by the AI bot in the game.
- Create a new folder where we will add all our files – I called this “MasterMind”; we will now add the four files above “Main.py”, “ServiceLayer.py”, “GUI.py” and finally “AI.py”.
- We will just set up the files to start and then in the next lab it will be easier to start coding – For the Main script we will just add a quick command line graphic to make it look better and more like a game.
print("Welcome to:")
View code file.
print(" ")
print(" __ __ _ ____ _____ _____ ____")
print(" | \/ | / \ / __ | |_ _| | ___ | | _ \ ")
print(" | |\/| | / _ \ \___ \ | | | _| | |_) |")
print(" | | | | / ___ \ ___) | | | | |___ | _ > ")
print(" |_| |_| /_/ \_\|____/ |_| |_____| |_| \_\ ")
print(" ")
print(" __ __ _ _ ____ ")
print(" | \/ | ___ | \ | | | _ \ ")
print(" | |\/| | |_ _| | \| | | | | | ")
print(" | | | | | | | |\ | | |_| | ")
print(" |_| |_| | | |_| \_| |____/ ")
print(" |___|")
print(" ")
- Next we will add the classes to our 3 files – remember any functions after this must be indented:
- ServiceLayer
class MasterServiceLayer(object):
View code file.
- GUI
class MasterMindGUI(object):
View code file.
- AI
class MasterMindAI(object):
View code file.
- ServiceLayer
- Now we are ready to start coding in the next labs :)
Lab 3: Write the Main Script
- First we are going to create our main script. This will initialize the game. It also keeps everything running. What features does our main script need?
- Take in the number of colours the user wants – this allows them decide the difficulty.
- Take in the number of pegs the user wants- and again decide the difficulty.
- Both of the above need to be validated before starting the game.
- Decide if they want to play against the computer or only watch the computer play itself? (why do this? It allows the user to see how the game should be played if confused, and allows you to practice some more complex coding).
- Once a game type is chosen, ensure it runs correctly until there is a winner (either the computer or player).
- To start, let’s open up our Main.py script with IDLE. Then write the basics for taking user input for pegs, colours and number of players. Only when this is done will we start to play the game.
- We need to create a loop construct that will constantly repeat until the user has entered all the correct information. We do this by using a Boolean variable and a while loop. This will continue until the variable equals false. Remember when coding in Python that indentation is very important. This effects the flow of statement execution.
check = True
View code file.
while check == True:
...
- Next we add a try construct with catches for exceptions – The reason for this is that we are taking in user input and sometimes people make mistakes and click the wrong buttons; We don’t want these mistakes to cause the application to crash so we catch them and deal with them.
check = True # intalise variable
View code file.
while check == True: # ensure that values are entered before the game continues
try:
...
except TypeError:
print("Enter a number")
except ValueError:
print("Enter a number")
- Now to add the code to take the user input for number of pegs, the number of colours and what play type they want. We need to validate these inputs. The number of pegs and colours will be in a range of three to eight. They can only have a minimum of three pegs and three colours and a maximum of eight pegs and eight colours. For the play type it will be a choice between one and two. When validation checks are passed we set the Boolean variable.
try:
View code file.
NoOfColours = int(input("How many colours do you want to use 3-8: "))
NoOfPegs = int(input("How many pegs do you want to use 3-8: "))
Players = int(input("How many players 1 or 2? (1 = (1 vs Comp) or 2 = (comp Vs comp)): "))
if NoOfColours in [3,4,5,6,7,8] and NoOfPegs in [3,4,5,6,7,8]:
if Players == 1 or Players ==2:
check = False
else:
print("Enter a number")
else:
print("Enter a number")
- Now we can run this code and check that it works. Also that with the correct input the application will exit.
- Now we are going to add some structure and comments. What happen after all the validation has been done? Simply enough we want the game to start. Then depending on the play type take an actions; either comp vs player or comp vs comp.
except ValueError: # print error message
View code file.
print("Enter a number")
# call start function in the service layer
if Players == 1:
# Player vs Comp
# Loop until ?
# get Player guess
# Get black and white clues from guess
# See if they have won
else:
# Comp Vs Comp
# Loop until ?
# get Computer guess
# Get black and white clues from guess
# See if they have won
- Now we need to create the looping functionality that sits inside the player if function. For this we will want to loop until a win or the maximum number of guesses. For this we need another Boolean variable called answer and an integer variable called guessNo. This we will initialize at the start.
guessNo = 0
View code file.
answer = False
if Players == 1:
while answer == False and guessNo < 12:
# get Player guess
# Get black and white clues from guess
# See if they have won
continue
if guessNo == 12 and answer == False:
print("You have lost")
print("The Answer was: ", peglst)
else:
while answer == False and guessNo < 12:
# get Computer guess
# Get black and white clues from guess
# See if they have won
continue
if guessNo == 12 and answer == False:
print("You have lost")
print("The Answer was: ", peglst)
- Later we are going to be removing these comments and adding in functions in our service layer to build our functionality.
Lab 4: Write the Game Initialization function
- Now we will add in our service layer functionality. To start we will create the function that we will call from Main.py to initialize the game. We will import the service layer into our Main.py first using the command below.
from ServiceLayer import MasterServiceLayer
View code file.
- In Main.py we will add the code to call the start function; this will pass in the number of pegs to use and the number of colours. Another bit of information is that we will pass in an empty list. This is in the service layer will get populated with the choice made by the opponent e.g. the computer such as r,b,g,y.
peglst = [] # Holds the pegs that need to be guessed
View code file.
....
except ValueError:
print("Enter a number")
MasterServiceLayer.start(NoOfPegs, NoOfColours, peglst)
if Players == 1:
- Now in our ServiceLayer.py we can add our functions under the class MasterServiceLayer(object). For the start function we want it to print the colours available to the player, display any information on start up, draw the board and calculate the pegs that have to be guessed. We will call MasterMindGUI’s StartGame function to print starting information (this takes in an parameter that will be a string of colours that can be guessed) and then DrawGUI to draw the board that the user sees when playing the game. Lastly it calls a function on itself to calculate the pegs to be guessed.
def start(NoOfPegs, NoOfColours, peglst):
View code file.
MasterMindGUI.StartGame(MasterServiceLayer.colourlst(NoOfColours))
MasterMindGUI.DrawGUI(NoOfPegs) # draw the text based user interface
MasterServiceLayer.calculatepegs(NoOfPegs, NoOfColours, peglst) # calculate the pegs colours to be guessed
- Let’s look into the start function and add any other calls. I’m going to look at MasterServiceLayer.colourlst() first as this is passed as a parameter. This should return a string dictating what colours are available to choose from depending on what the user entered at the start of the game. What the code does is first set up a string that will be returned; it then gets the list of colours and then iterating over the dictionary gets the colours full name, afterwards it appends this to the string to be returned.
def colourlst(number): # function used to make a string with the names of all the colours being used for when the program starts.
View code file.
colourString = "The colour avaliable are: " # intalise variables
names = ""
Colourlst = list(MasterServiceLayer.getcolours(number)) # get Colourlst e.g. [r,b,g]
for letters in Colourlst:
names = MasterServiceLayer.colournames[letters] # refrence dictionary to use key and get tehe value returned
colourString = colourString + names + "," #concatenate the string together
names = ""
return colourString
- The code above calls a function called MasterServiceLayer.getcolours(). We will need to build this. All it does is return the list of values from a tuple up to a point in that tuple using the notation :number e.g. :4 will give you the first four values from the tuple.
def getcolours(number): # gets a list of colours depending on how many colours they select at the start.
View code file.
return MasterServiceLayer.colours[:number]
- You may have also noticed that the two functions above use variables called colournames and colours so we will need to add these as class variables to MasterServiceLayer.
class MasterServiceLayer(object):
View code file.
colours = ('r', 'b', 'g', 'y', 'p', 'o', 'w', 'v') # tuple containing all possible colour values
colournames = {'r' : 'Red', 'b' : 'Blue', 'g' : 'Green', 'y' : 'Yellow', 'p' : 'Purple', 'o' : 'Orange', 'w' : 'White', 'v' : 'Violet'} # dictionary to identify colours
- Now MasterServiceLayer.colourlst() has been done, we need to do the MasterMindGUI.StartGame() function. But we have yet to import this. At the top of the file import this class from the module GUI.
from GUI import MasterMindGUI
View code file.
- Let’s open up GUI.py and under our class add the StartGame function that will print the list of available colours and some instructions.
def StartGame(coloursAvaliable):
View code file.
print(coloursAvaliable)
print("Please use the first letter of the colour in the program e.g. p for purple or r for red")
print("please enter in the format r,b,g,y")
print("You have 12 guess's to get the correct answer")
print("\n")
- Now we just need to draw the board on the CLI(Command Line Interface) using the function DrawGUI that is in GUI.py – This just needs a variable for the number of pegs to be given it so it knows how many blank spaces to put for the user guess. The will just loop from 1 to 12 by using range() starting at one stopping at 13 going in steps of one.
def DrawGUI(NoOfPegs): # creates the GUI for when the game is first played
View code file.
for i in range(1,13,1):
text = " _,"
print("Attempt Number ", i,)
print("Your input :",text*NoOfPegs, ": Black Number = _ : White Number = _ ")
print("------------------------------------------------------------------------")
- Lastly before the game can start the computer needs to choose the pegs that the player or computer will have to guess depending on what game type the user selects. This is a call made to itself inside MasterServiceLayer for the function calculatepegs(). This will manipulate the list past to it that is stored inside Main.py. The below uses a module called random to choose pegs at random for the guess so you need to make sure this is imported at the top of the file.
import random
View code file.
....
def calculatepegs(NoOfPegs, NoOfColours, peglst): # calculate the pegs to be guessed
Colourlst = list(MasterServiceLayer.getcolours(NoOfColours)) # gets the colourlst e.g. [r,b,g]
for i in range(NoOfPegs): # for the number of pegs
peglst.append(random.choice(Colourlst)) # append a random colour
- Now you can run the application and see what it looks like; as you can see it will start the game and draw the board but will then just stop and not exit. This is because we have while loops that will not terminate. The code is not yet in place. So the next lab will be starting to add some of this game logic.
Lab 5: Write the Player Vs Computer Logic
- For this lab we are going to be adding in the game logic for when the player is against the computer e.g. when they select option one for play type in Main.py. Currently we have the basic structure for the code with some comments on what we are likely to need to do; so the purpose of this lab is to turn those comments into code.
if Players == 1:
View code file.
while answer == False and guessNo < 12:
# get Player guess
# Get black and white clues from guess
# See if they have won
continue
if guessNo == 12 and answer == False:
print("You have lost")
print("The Answer was: ", peglst)
- Starting with the first comment, we need to get the players guess. For this we can make a call to the service layer. We will call this getGuess() and the only parameter it will need is the number of pegs being used so it can validate the users guess and what it return is the user guess so we will need to add a variable to hold this. We can now also remove the continue as there is code inside the while loop.
userGuess = []
View code file.
....
if Players == 1:
while answer == False and guessNo < 12:
userGuess = MasterServiceLayer.getGuess(NoOfPegs) # get the guess
# Get black and white clues from guess
# See if they have won
if guessNo == 12 and answer == False:
- Inside MasterServiceLayer we can now add our function getGuess() – This will get the user guess to start and will then check to see if the value is “quit”. In the case of the user entering quit we should stop the application and hence we call exit(). Otherwise the user has entered a guess so we need to split this by commas; the first thing we do is check that the size is the same as the number of pegs. If it turns out they have entered the incorrect number of pegs then we need to inform the user and we will return an exception that we can catch and deal with in Main.py. We can then check the that for each peg it is in the colour list that was made available to them and if not then we will raise and exception.
def getGuess(NoOfPegs): # get the users guess
View code file.
guess = input("Guess: ") # get input from user
if guess == 'quit': # if quit
print("quit")
exit() # close aplication
else:
guess = guess.split(',') # split the expression by user using commas
if len(guess) != NoOfPegs: # if to small or to large a guess from pegs they decided
MasterServiceLayer.invalidMove() # call error
else:
for value in range(0,len(guess),1): # for the values in their guess
if guess[value] in MasterServiceLayer.getcolours(NoOfPegs): # if the letters are in teh colour list
continue
else:
MasterServiceLayer.invalidMove() # error value
return guess # return guess
- As you can see we reference an invalidMove() function, we have yet to build this so we will do that now. All it is going to do is tell the user that they have entered an invalid input and raise an exception so that Main.py can then deal with it.
def invalidMove(): # if an error comes up this is what is displayed
View code file.
print("Invalid Input")
raise RuntimeError()
- In Main.py we need to now catch this exception when we deal with the getGuess function. We do this by using try and except, when the exception is caught we will just continue as normal and it will just loop again and ask for their input until they get it right.
try:
View code file.
userGuess = MasterServiceLayer.getGuess(NoOfPegs) # get the guess
except RuntimeError:
continue
- We can now test to see if this is working – This will still be an infinite loop . But this is now easier to see.
- Now the user is able to have their guess we need to increment the number of guesses. This is used to exit the while loop when it’s 12. We also need to add the users guess to a list so that we can display this on the GUI.
userGuessList = []
View code file.
...
if Players == 1:
while answer == False and guessNo < 12:
try:
userGuess = MasterServiceLayer.getGuess(NoOfPegs) # get the guess
except RuntimeError:
continue
guessNo = guessNo + 1 # increment guess
userGuessList.append(userGuess) # append their guess to the list
# Get black and white clues from guess
# See if they have won
if guessNo == 12 and answer == False:
print("You have lost")
print("The Answer was: ", peglst)
else:
- After we have the user guess we need to check how many black and white pegs it needs to inform the player how correct there answer may be. For this we will call a function in the service layer to calculate this for us. We will then append this result to a list for blacks and white so it can be displayed in the GUI.
black = 0
View code file.
white = 0
blacklst = []
whitelst = []
....
black, white = MasterServiceLayer.checkGuess(userGuess, peglst) # work out how many blacks and whites they get for their guess
blacklst.append(black)
whitelst.append(white)
- Now to implement this in MasterServiceLayer adding the function checkGuess() – This will need to have passed into it the user’s guess and the correct peg sequence and will return two integers representing black and white. This will move through the user’s guess if a peg matches the same as the correct peg sequence then it will increment black else it will add it a white list. It will then move through the white list, if the peg is in the correct peg sequence but not already in the black list then it will increment the white number. At the end it will then return these back to Main.
def checkGuess(usrlst, complst): # checks the guess to see how many black and whites should be awarded
View code file.
blacks = 0 # intalise variables
whites = 0
lstblack = []
lstwhite = []
for value in range(0,len(usrlst),1): # for the values form 0 to the legnth of the userlst-1
if usrlst[value] == complst[value]: # if they are the same
blacks = blacks + 1 # Inidcates black pegs, right colour in right spot, increment it
lstblack.append(usrlst[value]) # add it to the list
else:
lstwhite.append(usrlst[value]) # append it to the white list
for letters in lstwhite: # for each letter in the white list
if letters in complst and not letters in lstblack: # if the letter is in the computer generated list and is not in the black list
whites = whites + 1 # increment the white value, white is right colour wrong spot.
return blacks,whites
- Finally we need to check if the player has won. We do this by calling a function from Main to the MasterServiceLayer, we need to pass in a lot of variables encase the user has not won so that we can update the GUI with this information. We can check if the player has won by seeing if the number of black pegs is the same as the number of pegs in the correct peg sequence that we are trying to guess.
while answer == False and guessNo < 12:
View code file.
try:
userGuess = MasterServiceLayer.getGuess(NoOfPegs) # get the guess
except RuntimeError:
continue
guessNo = guessNo + 1 # increment guess
userGuessList.append(userGuess) # apped their guess to the list
black, white = MasterServiceLayer.checkGuess(userGuess, peglst) # work out how many blacks and whites they get for their guess
blacklst.append(black)
whitelst.append(white)
answer = MasterServiceLayer.haveWon(black, white, guessNo, NoOfPegs, userGuessList, blacklst, whitelst) # see if they have won
if guessNo == 12 and answer == False:
...
- Now implementing this into MasterServiceLayer with the function haveWon(). – If the black pegs and peg size are the same then they have won as described above. Else we need to make a call to MasterMindGUI to update the GUI with the new board displaying the players input and what black and white pegs it has received. It returns true or false so that if the player has won it will break out of the while loop in Main and end the application.
def haveWon(blackpeg, whitepeg, guessNumber, NoOfPegs,UserGuesslistload, blacklstload, whitelstload): # check to see if you have won
View code file.
if blackpeg == NoOfPegs: # if number of black pegs is the same as number of pegs then print that they win and return true
print("you have WON")
return True
else:
MasterMindGUI.updateGUI(blackpeg, whitepeg, guessNumber, NoOfPegs,UserGuesslistload, blacklstload, whitelstload) # update the gui with the necessry information
return False
- As you can probably guess we now need to implement updateGUI() in MasterMindGUI. This is the function that will update the GUI after the players turn so they can see their previous guesses and their current guess and what it has scored in black and white pegs. The function will print some empty lines to the GUI so that it clears the previous page ready for the new one; then it enters the first loop that will loop through each of the previous guesses adding the attempt number e.g. 1- 12 and then adding the user input with the blacks and whites scored for that guess. The second for loop will print through any un-guessed guesses so the user can see how many they have left until the game is over.
def updateGUI(blackpeg, whitepeg, guessNumber, NoOfPegs, UserGuesslistload, blacklstload,whitelstload): # Updates the textual based interface providied to the user
View code file.
for i in range(25):
print("\n") # prints some new lines so it looks neater
text = "" # intalise text to string
emptytext = " _," #
counter = 0 # initalise counter, used for creating the attempt number and getting the black and white number form their lists
for lists in UserGuesslistload: # for each list in a list
counter = counter + 1
text = "" # set text to nothing everytime it goes through list loop
for values in lists:
text = text + " " + values + "," # create a string version of the list
print("Attempt Number ", counter,) # print for each list the attempt and what was entered and the black and white number
print("Your input :",text, ": Black Number = ",blacklstload[counter-1]," : White Number = ", whitelstload[counter-1])
print("------------------------------------------------------------------------") # seperator
for l in range(1+counter,13,1): # for any guess that has not be done yet
print("Attempt Number ", l,)
print("Your input :",emptytext*NoOfPegs, ": Black Number = _ : White Number = _ ") # print this to screen with the guess being unknnown
print("------------------------------------------------------------------------")
- Now let’s run our code and see if it works and if we can play a game end to end. In the next lab we will be using the same sort of idea but will be building an AI version of a player.
Lab 6: Write the AI
- For this part of the tutorial we will be building the AI version where a computer will have to try and beat a mastermind. This will allow you to learn some more complex techniques and so don’t worry if it doesn’t all make sense yet.
- To solve the problem we will be using an algorithm called the five-guess algorithm but made more generic to work out any combination of possibilities like this game allows. The description to this can be found here: https://en.wikipedia.org/wiki/Mastermind_(board_game).
- You can read the description on wikipedia, but here is a cutdown version:
- Create a list of all possible combinations.
- As the first guess choose half and half as best as possible e.g rrbb for four pegs or rrrbb for five pegs etc…
- Score this against the hidden sequence – return a number of blacks and whites.
- If its correct then you win.
- Else (this is where it starts to get complex), remove any combination from the possible combinations that would not result in that score.
- Now this is where it really gets interesting, but simply put; for your next guess you want to select the combination that is going to remove the highest number of possible combinations therefore making your possible combinations smaller and smaller.
- Repeat – until the set gets so small that its very easy to work out the answer just from the blacks and whites scored.
- For the first step in our code we need to do a bit of manipulation on our possible colour values. Plus some secret code that is generated so it is easier to work with. All this will do is join all the values in the lists to create a strings that represent both the possible colours and the secret peg list.
if Players == 1: # if there is only one player
View code file.
...
else: # if they have selected 2 players
possiblePegValues = ''.join(MasterServiceLayer.getcolours(NoOfColours)) # 'rbgy'
secret = ''.join(peglst) # 'rrgg'
while answer == False and guessNo < 12:
...
if guessNo == 12 and answer == False: # if they hit max guesses or the last value of answer is false
print("You have lost")
print("The Answer was: ", peglst)
- Next we will create a list and a set of all the possible values – why create both? This will become more obvious later where we want a set that we can update to hold a set of combinations, one of which is the answer. And also a list of all possible combinations to work out the best combinations to remove as many possibilities as possible. As you can see from the code we obviously need to import MasterMindAI as well, so make sure you remember this.
from AI import MasterMindAI
View code file.
...
else: # if they have selected 2 players
possiblePegValues = ''.join(MasterServiceLayer.getcolours(NoOfColours)) # 'rbgy'
secret = ''.join(peglst) # 'rrgg'
allPossibleValues = MasterMindAI.calcPossibleValues(possiblePegValues, NoOfPegs) # large collection of all possible value e.g rrrr, rrrb etc...
possibleValues = set(MasterMindAI.calcPossibleValues(possiblePegValues, NoOfPegs)) # new object but a set
- Now we need to implement our first method in MasterMindAI called calcPossibleValues(); this uses a function called product that does all the hard work for us, we just need to import it. All it requires is the string that we need every different combination for e.g rbgy and to what level this should be repeated e.g. repeat = 5 would give rrrrr, rrrrb etc…
from itertools import product
View code file.
...
def calcPossibleValues(possibleValueString, repeatTime):
return [''.join(p) for p in product(possibleValueString, repeat=repeatTime)]
- Now we will create a list of all the possible results for whites and blacks that can be returned as tuples encoded as (black, white). This is used to generate the best possible combination to remove as many combinations as possible from a list.
else: # if they have selected 2 players
View code file.
possiblePegValues = ''.join(MasterServiceLayer.getcolours(NoOfColours)) # 'rbgy'
secret = ''.join(peglst) # 'rrgg'
allPossibleValues = MasterMindAI.calcPossibleValues(possiblePegValues, NoOfPegs) # large collection of all possible value e.g rrrr, rrrb etc...
possibleValues = set(MasterMindAI.calcPossibleValues(possiblePegValues, NoOfPegs)) # new object but a set
resultValues = MasterMindAI.calcResultValues(NoOfPegs) # set of all possible blacks and whites e.g. (0,1) ....
- Implementing the method calcResultValues(), this just needs the number of pegs as a parameter so as to create the correct sized list. The code will go through a for loop creating the list of all possible values except the case where they are all black except one being white as this is just impossible in the game of mastermind.
def calcResultValues(pegListSize):
View code file.
return [(right, wrong) for right in range(pegListSize+1) for wrong in range((pegListSize + 1) - right) if not (right == (pegListSize - 1) and wrong == 1)]
- Now to get on with the guts of the AI for mastermind, we need to have the computer take a guess at each turn – Hence we will make a call out to AI to calculate this given all the possible combinations, the combination of all results and the peg colour combination chosen.
resultValues = MasterMindAI.calcResultValues(NoOfPegs) # set of all possible blacks and whites e.g. (0,1) ....
View code file.
while answer == False and guessNo < 12:
userGuess = MasterMindAI.attempt(possibleValues, MasterServiceLayer.getcolours(NoOfPegs), possiblePegValues, resultValues, allPossibleValues, guessNo == 0) # get the AI guess
if guessNo == 12 and answer == False:
print("You have lost")
print("The Answer was: ", peglst)
- Now we can implement the function attempt() in MasterMindAI. If this is the first guess then we come up with the best first guess which will be half and half of the first two colour sequences. If it is four pegs then it will be rrbb and five pegs rrrbb etc…. If the set of all possible combinations is of size one then this will be the guess to choose as it is likely to be the answer. Lastly, if it is none of the above then we need to calculate the next best possible combination using minimax – This means taking the max value that is made from totaling the score of all possible values with the results to find the combination that is going to remove most combinations per guess and get you closer to the answer.
def attempt(S, pegList, stringPegList, results, possible, first=False):
View code file.
if first:
if (len(pegList) % 2) == 0:
a = pegList[0] * int(len(pegList)/2) + pegList[1] * int(len(pegList)/2)
else:
a = pegList[0] * int((len(pegList)+1) / 2) + pegList[1] * int((len(pegList)-1) / 2)
elif len(S) == 1:
a = S.pop()
else:
a = max(possible, key=lambda x: min(sum(1 for p in S if MasterMindAI.score(p, x, stringPegList) != res) for res in results))
return a
- The above may be confusing but just spend 10 minutes to try and understand it and hopefully the code will make a bit of sense.
- Now we have the computer guess we need can increment the guess number and also print to the GUI the guess that the computer has taken.
while answer == False and guessNo < 12:
View code file.
userGuess = MasterMindAI.attempt(possibleValues, MasterServiceLayer.getcolours(NoOfPegs), possiblePegValues, resultValues, allPossibleValues, guessNo == 0) # get the AI
print("UserGuess", userGuess)
guessNo = guessNo + 1
- We will get the black and white pegs from the computers guess and we will append the results so that we can update these in the GUI.
while answer == False and guessNo < 12:
View code file.
userGuess = MasterMindAI.attempt(possibleValues, MasterServiceLayer.getcolours(NoOfPegs), possiblePegValues, resultValues, allPossibleValues, guessNo == 0) # get the AI
print("UserGuess", userGuess)
guessNo = guessNo + 1
blacksAndWhites = MasterMindAI.score(secret, userGuess, possiblePegValues) # see how well this guess scored
userGuessList.append(userGuess)
whitelst.append(blacksAndWhites[0])
blacklst.append(blacksAndWhites[1])
- Creating the function score() in MasterMindAI so that it returns the black and white values in the form (black, white) given a users guess.
def score(secret, other, stringPegList):
View code file.
first = len([speg for speg, opeg in zip(secret, other) if speg == opeg])
return first, sum([min(secret.count(j), other.count(j)) for j in stringPegList]) - first
- We then check to see if the user has won as we would normally do in the player type gam. If they have; tell the user else we need to update the GUI.
while answer == False and guessNo < 12: # while they don't have it right and guess is less than 12
View code file.
userGuess = MasterMindAI.attempt(possibleValues, MasterServiceLayer.getcolours(NoOfPegs), possiblePegValues, resultValues, allPossibleValues, guessNo == 0) # get the AI guess
print("UserGuess", userGuess)
guessNo = guessNo + 1
blacksAndWhites = MasterMindAI.score(secret, userGuess, possiblePegValues) # see how well this guess scored
userGuessList.append(userGuess)
whitelst.append(blacksAndWhites[0])
blacklst.append(blacksAndWhites[1])
answer = MasterServiceLayer.haveWon(blacksAndWhites[0], blacksAndWhites[1], guessNo, NoOfPegs, userGuessList, blacklst, whitelst) # see if they have won
- The last thing now we know the computer has not won, is to update the possibilities for the winning combination to ensure this gets smaller and smaller. All we need to know to perform this is the current possible values, the black and white values, the users guess and what combination of coloured pegs can be used.
while answer == False and guessNo < 12: # while they don't have it right and guess is less than 12
View code file.
userGuess = MasterMindAI.attempt(possibleValues, MasterServiceLayer.getcolours(NoOfPegs), possiblePegValues, resultValues, allPossibleValues, guessNo == 0) # get the AI guess
print("UserGuess", userGuess)
guessNo = guessNo + 1
blacksAndWhites = MasterMindAI.score(secret, userGuess, possiblePegValues) # see how well this guess scored
userGuessList.append(userGuess)
whitelst.append(blacksAndWhites[0])
blacklst.append(blacksAndWhites[1])
answer = MasterServiceLayer.haveWon(blacksAndWhites[0], blacksAndWhites[1], guessNo, NoOfPegs, userGuessList, blacklst, whitelst) # see if they have won
MasterMindAI.updatePossibilties(possibleValues, blacksAndWhites, userGuess, possiblePegValues) # update possibilities that are now only relevant
if guessNo == 12 and answer == False:
...
- Now in MasterMindAI we just need to implement the function updatePossibilties(), this will modify the current set e.g. the set of possible values and hence it doesn’t have a return result. All it does is remove any combinations that would not have resulted in giving the score that was returned by the guess.
def updatePossibilties(possibleValues, blacksAndWhites, userGuess, stringPegList):
View code file.
possibleValues.difference_update(set(p for p in possibleValues if MasterMindAI.score(p, userGuess, stringPegList) != blacksAndWhites))
- Now you should be able to run the application in game mode type two where it is comp vs comp – depending on the difficulty the results can vary on time – e.g. four pegs and four colours take five seconds where as six colours and four pegs take 30 seconds. The more you increase colours and pegs the more time it will take in an exponential fashion.
- That is the game finished – If you want to make things more difficult, maybe consider using web sockets to create a multiplayer game where it is player vs player or adding a real GUI to the application instead of what is just a CLI.
If you would like to see more content like this in the future, please fill-in our quick survey.