WEBVTT

00:00.180 --> 00:04.380
Hello again! In this video, we are going to have a practical based on the "Mastermind(TM)" game.

00:05.370 --> 00:07.780
This is a board game from the 1970s.

00:07.800 --> 00:10.890
As you can probably guess from the rather dated photo.

00:11.940 --> 00:18.690
This was really popular - or, well, so my parents tell me :) It is a board game for two players.

00:19.260 --> 00:23.670
There are lots of coloured pegs which go in these holes, and some small, black and white pegs which

00:23.670 --> 00:24.660
go in these holes.

00:25.560 --> 00:31.080
The idea is that one of the players takes some pegs and makes a secret code, which is hidden behind

00:31.080 --> 00:32.010
this block.

00:33.180 --> 00:37.800
And then the second player tries to guess the code by putting coloured pegs in these holes.

00:39.480 --> 00:43.950
The first player will put black and white pegs in these holes at the sides, which will tell them how

00:43.950 --> 00:44.640
close they were.

00:48.150 --> 00:52.440
So this is what it looks like from the first player's point of view. Here is the secret code.

00:54.000 --> 00:59.910
The second player wins if he guesses the secret code. So all the correct coloured pegs in the correct holes.

01:00.600 --> 01:02.580
So this is where the second player won the game.

01:03.690 --> 01:08.160
If they get all the way to here and they have not guessed it, then they lose and the first player wins.

01:09.700 --> 01:15.220
The pegs at the side depend on how close the guess is. If there is the right colour in the right hole,

01:15.970 --> 01:17.740
then you get a black peg at the side.

01:18.370 --> 01:21.610
If there is the right colour in the wrong hole, then you get a white peg.

01:22.240 --> 01:24.430
And if you have the wrong colour, then you get nothing at all.

01:26.810 --> 01:34.850
So, for example, there is no yellow in the solution, so there is an empty peg on the right. In this

01:34.850 --> 01:37.370
one, all the coloured pegs are in the right holes.

01:37.400 --> 01:40.400
So there are four black pegs and the first player wins.

01:40.790 --> 01:42.890
In the previous attempts, they had all the colours right.

01:43.040 --> 01:48.170
But only one peg was in the write hole, so they had one black peg and three right pegs.

01:50.670 --> 01:57.130
We are going to make a simple command-line game based on this. The actual game is rather complicated and

01:57.130 --> 01:57.970
there are lots of colours.

01:58.390 --> 02:03.940
So we are just going to use three colours: red, green and blue. So the computer is going to generate

02:04.120 --> 02:06.010
a secret code from those characters.

02:06.850 --> 02:13.450
The user will enter their guess at the code, and the computer will tell them how many exact matches

02:13.450 --> 02:13.840
they got.

02:13.870 --> 02:20.590
So the right character in the right position. And the number of near misses or loose matches, so they

02:20.590 --> 02:26.670
have the right character but in the wrong position. And this will continue until the user guesses right.

02:26.680 --> 02:27.970
Or they run out of turns.

02:31.380 --> 02:37.160
So it will look like this. We get prompted, telling us what to do. So enter four characters which can be red

02:37.230 --> 02:37.860
green or blue.

02:40.170 --> 02:45.060
So let's try 4 reds. One exact match and zero near misses.

02:46.260 --> 02:48.720
So we have one peg in the right colour.

02:49.620 --> 02:50.550
So we have one red.

02:52.890 --> 02:54.270
Let's try 4 greens.

02:54.720 --> 02:59.520
So we have zero exact matches and zero near matches, so there are no greens in the solution at

02:59.550 --> 02:59.820
all.

03:01.860 --> 03:03.800
So the others must be three blues.

03:04.020 --> 03:05.280
Yep, three exact matches.

03:06.480 --> 03:10.650
So we know there is one red and three blues, but we need to work out what order they are in.

03:11.070 --> 03:17.370
So let's try red at the front. And we get 2 exact matches and 2 near matches.

03:18.870 --> 03:21.840
So the four colours are correct, but two of them are in the wrong place.

03:22.770 --> 03:29.160
So let's try swapping them round. And there we are. So, four exact matches and we have won!

03:38.920 --> 03:43.930
So the program logic is going to be like this. We are going to generate the secret code, then we are

03:43.930 --> 03:49.420
going to read the user's input. Then we are going to convert it to a normal form to make the processing

03:49.420 --> 03:49.990
a bit simpler.

03:50.470 --> 03:53.380
So we convert it to alphabetical only, and uppercase.

03:54.370 --> 04:00.820
We find the number of exact matches - the right character in the right place - and the number of loose matches.

04:01.210 --> 04:02.650
The right character in the wrong place.

04:03.250 --> 04:06.250
And then we display that to the user and we ask them for their next guess.

04:06.880 --> 04:11.530
And we carry on doing this until there four exact matches, or the user runs out of turns.

04:14.290 --> 04:15.730
So here is our main function.

04:16.150 --> 04:18.460
So this is going to generate the secret code.

04:18.970 --> 04:20.350
Or, if you are testing, you can

04:20.350 --> 04:21.010
hard-code it.

04:22.330 --> 04:25.210
We have a flag to keep track of whether the user has won

04:25.210 --> 04:27.100
yet. Or should that be lost

04:27.110 --> 04:30.940
yet? We loop over the allowed number of turns.

04:31.750 --> 04:34.210
We process the input from the player.

04:35.050 --> 04:37.630
We get the number of exact and loose matches.

04:38.560 --> 04:44.080
And the way that I have calculated the loose matches will also include the number of exact matches.

04:44.500 --> 04:47.350
So we need to subtract that, to avoid double counting.

04:48.520 --> 04:49.360
Then we display that.

04:49.720 --> 04:52.660
And if the users got it right, then we congratulate them.

04:54.190 --> 04:56.710
And if they run out of turns, we commiserate.

05:00.440 --> 05:06.590
When I am writing a game, I like to have a file which contains all the parameters for the game, in their

05:06.590 --> 05:07.490
own namespace.

05:08.090 --> 05:10.610
You could also use a namespace for the rest of the program, if you like.

05:11.330 --> 05:15.800
I have not bothered here, but perhaps I should. So we have four pegs in the code.

05:16.960 --> 05:23.410
The user is allowed a maximum of 10 guesses, and there are three colours in the combination.

05:24.800 --> 05:27.350
Then we create a couple of types which are going to be useful.

05:27.710 --> 05:32.600
So these are library arrays with the right number of elements, so we can never have an out-of-bounds

05:32.600 --> 05:33.020
error.

05:33.650 --> 05:35.930
We have one for the peg arrays.

05:35.930 --> 05:41.810
So that is going to be the secret code and the user's guesses. And another one for the potential colours,

05:41.930 --> 05:42.800
the allowed colours.

05:43.850 --> 05:46.460
And then, in our constants namespace, we created an array

05:47.180 --> 05:48.470
of these allowed colours.

05:55.730 --> 06:00.080
To generate the code, we have one of these peg arrays, which we are going to populate.

06:00.710 --> 06:07.580
We use the generate() algorithm, so this will populate each element in the array with the return value

06:07.580 --> 06:09.130
from this callable object.

06:10.010 --> 06:14.450
This lambda expression is going to pick a colour at random from the allowed colours.

06:15.200 --> 06:22.250
So we need to have a random number engine and a uniform distribution. And we need to make the distribution

06:22.250 --> 06:24.590
produce a number which can be a valid index.

06:25.040 --> 06:27.890
So it is between zero and the number of colours, minus one.

06:29.920 --> 06:36.250
We need to capture by reference, because we are going to modify the engine and the distribution. I have

06:36.250 --> 06:40.240
used the capture-all, but you can use separate capture statements if you like.

06:43.060 --> 06:50.260
For validating the input, we use get_line(). We print some prompts to the user, to tell them

06:50.260 --> 06:53.680
what to do. And then we call our normalize() function.

06:55.450 --> 06:56.800
So you have seen this before.

06:57.190 --> 07:02.020
This uses copy_if(), so it only keeps characters, which are letters of the alphabet. Then

07:02.020 --> 07:07.930
it does a transform() to convert everything to uppercase, and then it returns the normalized string.

07:11.930 --> 07:16.430
If the user enters the wrong number of characters, then we prompt them to do it again.

07:17.120 --> 07:23.780
Obviously, you could make this a bit more robust! And then we have to copy the result into the peg array.

07:24.110 --> 07:29.030
And we have to use a loop here, because there is a string on the right hand side and a library array on the

07:29.030 --> 07:31.570
left hand side, and they do not convert.

07:31.610 --> 07:32.360
Unfortunately.

07:37.410 --> 07:40.110
Then we need to work out how to do these matches.

07:42.800 --> 07:48.590
To find the number of exact matches. So this is where the colour of the peg in the user's guess matches the

07:48.590 --> 07:50.990
colour of the same peg in the solution.

07:52.300 --> 07:56.650
We are going to write to a temporary object. If they do match, then we write the colour.

07:57.040 --> 08:03.160
Otherwise we write 0. And then we return the number of non-zero elements in this temporary object.

08:07.990 --> 08:13.810
So this exact_matches() function, it takes two arguments: the user's guess and the actual solution.

08:15.940 --> 08:21.250
We have this temporary object we are going to populate. And then we use transform() to compare the elements

08:21.250 --> 08:26.170
from these two arguments, and write the results to a third array.

08:28.260 --> 08:33.870
If the corresponding elements are the same, then we write that elements to this array, otherwise

08:33.870 --> 08:42.180
we write 0. And then we use count_if() to find how many elements in this array are non-zero. And then

08:42.180 --> 08:45.810
for the loose matches, we make a back-up of the user's

08:45.850 --> 08:54.180
guess. Then we iterate over each peg in the solution, and we look for a peg in the user's guess which

08:54.180 --> 08:54.960
has the same colour.

08:55.500 --> 09:00.840
And if we find one, we increment a counter, and then we erase that element from the user's guess.

09:01.170 --> 09:05.010
So that is why we are working with a backup, because we need to erase elements.

09:07.370 --> 09:09.930
And then, at the end, we return the value of this counter.

09:09.950 --> 09:15.020
So this will give us all the elements for which the user has guessed the colour which is somewhere

09:15.020 --> 09:15.710
in the solution.

09:16.310 --> 09:21.260
This will also include pegs which are in the correct place, which are the exact matches, so we need

09:21.260 --> 09:25.490
to subtract the number of exact matches to get the correct answer.

09:28.140 --> 09:32.100
So here is our loose_matches() function again, with the same arguments.

09:34.700 --> 09:38.120
We have this backup string and our counter.

09:39.470 --> 09:45.890
We make a copy of the user's guess into our backup string, and then we iterate over all the elements

09:45.890 --> 09:46.850
of the solution.

09:49.130 --> 09:55.190
We look for an element in the user's guess which has the same colour as the colour we are looking at at the

09:55.190 --> 09:55.580
moment.

09:57.320 --> 10:04.070
If we find one, we increment our counter and then we remove the element from the array.

10:05.150 --> 10:08.930
Now, when you see iterators being erased inside a loop, you should get a bit suspicious.

10:09.800 --> 10:17.770
In this case, it's safe because we are erasing elements from this copy guess array, and the loop is

10:17.780 --> 10:19.910
looping over the colour array, which is a different array.

10:20.270 --> 10:21.580
So there is no possibility that

10:21.590 --> 10:25.010
any of the operators in this container could become invalidated.

10:27.090 --> 10:28.890
And then finally, we return our counter.

10:30.030 --> 10:31.020
Okay, so that is it.

10:38.980 --> 10:43.510
So as I said, one thing you can do is to improve the error checking for the input processing.

10:44.390 --> 10:48.340
Another thing you could do is to look up the complete rules of Mastermind, because they are quite a bit

10:48.400 --> 10:49.120
more complex.

10:49.570 --> 10:52.060
There is eight colours, I think, and not just three.

10:52.900 --> 10:58.450
And if you really fancy getting busy, you could write a graphical version which will look much nicer!

10:59.080 --> 11:00.490
But this is enough to get started.

11:01.030 --> 11:02.560
Okay, so that is it for this video.

11:02.950 --> 11:03.790
I will see you next time.

11:03.790 --> 11:05.950
But until then, keep coding!
