0:05

Let us resume the programming of our Connect Four game.

In the first episode, we have endeavoured to

represent the game, the colors,

namely the data structures used in our prgram.

Then, in the second episode, we have coded the

basic functions for doing the intialization and printing of the game.

Now, let us tackle the game itself.

In order to play, we will need to ask the

user to drop a disk into the grid.

We need to make sure that their answer is correct

and ensure that the data structures

will remain valid according to the player's move,

thus indeed playing this move in the grid.

Then, we will ask the other player to drop a disk, validate and so on.

Thus, we will loop like this until one player has won

or the grid has been filled.

In this sequence, we will focus on the second point,

that is, validate the player's move and, according to the data structure,

render the data structure valid,

effectively playing the move in the grid.

Let's decide to make it a function.

The first step consists of precisely knowing

what this function has to do; we here focus on the "what".

In this case, we already have a precise idea:

We need to receive a column number,

signifying where the player intends to drop his disk.

Then, we will verify if this move is valid and update the game

according to the move indicated by the player.

We can name this function

"joue" (TN: means "play"), for example.

Now, let us focus on this function's prototype.

What are the arguments to be passed to the function?

To that end, we will imagine what the typical call of this function

would look like. Typically, we would write "joue",

of course, we need a game to play with; thus, we will pass a grid as argument.

Then, we will indicate where we intend to drop the disk;

for example to drop a disk down column 3

Finally, we need the player's color;

is it the red or the yellow player's turn?

For example, we would inidicate that it is the red player's turn;

we would consequently

drop a red disk down column 3.

2:12

Then, it would be the yellow player's turn.

For example, the yellow player could wish to drop a disk down column 2.

Then, the red player, at turn again, would drop their disk down column 3.

Thus, we clearly see that the function needs to receive three parameters,

we must pass three arguments: the grid,

a column number and a color.

Now, we can start writing the function's prototype.

Thus, "joue" (the name of the function),

then a grid, called "grille", passed as argument.

(TN:"grille" means "grid").

Then, we will retrieve a column number,

typically an integer number.

Finally, we will pass the plalyer's color

for which we had already defined the type "Couleur".

Here is a first version of this prototype.

Let us begin by criticizing it a little.

Here, for the column number, we used the integer type.

However, a priori, the columns will go from 0 to 6

and are all positive integers.

Thus, here, we wish to force this number to be a positive integer.

Moreover, this column will serve

to indicate the position in an array. Typically, if we have the grid here,

as you have seen during the printing, we used the indices "i" and "j",

where j is precisely the column index

Thus, this column being used to index the array elements,

we will respect the previously established conventions, namely that,

for array sizes and indices, we use the "size_t" type.

Here, we will thus use the "size_t" type and correct this aspect in our function's prototype.

Now, another very important correction

regarding the grid.

Obviously, the fact that we are playing in the grid will modify it.

The grid will be modified!

If, for example, the red player drops his disk down column 3,

that means that a red disk will appear here;

the grid is indeed modified!

Therefore, we have to resort to

by-reference passing.

The grid being modified, we resort to by-reference passing thanks to this sign

which, we remind you, is used to signify that the grid is passed by reference.

Those were the arguments of our prototype.

Let us now focus on the return value.

We did not use any return value in our calls;

we did not write something like "z = joue(grille, 3)".

If we had, what would this z be exaclty?!

Since we do not need to return anything,

the return type will be the "void" type.

4:52

Now that the prototype is finished,

we can move on to the function definition

and start coding what we wish to accomplish.

Our goal is to drop a disk

down the column received as parameter.

For example, if we drop a red disk down column 3,

what we wish to do is to move to the third column,

and check what the first empty position is.

For example, let us suppose that we already have a red disk here

and a yellow disk just above.

So, we are dropping a red disk down column 3.

This disk will therefore be placed on the first empty position.

We will thus check: here it is already occupied,

here it is empty and we can thus place our red disk.

First of all, we should write a comment

explaining what we wish to accomplish in order to clarify our thoughts

and make it easier for future readers to understand our code.

We will run through the furnished column

from bottom to top, starting with the lowest point.

How will we write this in C++? Why, we will give ourselves an index

in order to run through the rows. Let us call this index "ligne" (TN: means "row").

With this index, we will start from the bottom.

By the way, please be reminded that the array is always numbered

from 0, at the top, to the bottom.

The bottom is the size of the array minus 1.

Therefore, we will start here, at "size -1".

Thus, we declare a "size_t"-type variable "ligne"

initialized to the size of the grid minus 1.

We run through this column,

starting from the bottom until we reach an empty square.

In C++, "until" means that we have a loop,

here a conditional loop.

Since the condition is : "as long we do not have an empty square",

that is, as long as the square of the column we are currently visiting

is not empty.

This is written as follows:

As long as (a "while loop")

the square, that is "grille[ligne]",

indeed, for a given column,

we are running through

7:49

of a given column),

as long as this square is not "vide" (TN: means "empty")

where "vide" is a previously defined value,

belonging to the "Couleur" type.

As long as the square of the indices "ligne" and "colonne" is not "vide";

at this point, what should we do?

Well, we go up one line,

subtracting 1 from "ligne".

Indeed, the grid, the array, is numbered

from 0 to "size - 1".

Since we are following this direction,

we must decrement 1 from line to line.

8:55

This concludes our "joue" function.

Now, as good programmers, we will test our function.

We are doing just that, writing the following "main".

We start, of course, by declaring a game, a grid.

We then initialize and print this grid here,

just as we had done in the previous episode.

Now we can test our "joue" function on our grid, column 3,

color rouge (TN: means "red"). This should correspond,

upon the printing of the grid, to a red disk at this position.

9:57

But is our program correct? Have we considered all the tests?

Let us reexamine our code

and imagine that a player decides to play all the time in the same column.

Both players will always drop their disks in the same column.

What will happen?

Here, in order to simplify,

we will always use the same color.

We will start by putting a disk here, another here,

another here and so on.

Eventually, we will put a disk here.

Now let us suppose we ask to drop another disk

down column 3.

What will happen? We will start from the lower row

and, until we encounter an empty square, execute the instruction "ligne --".

Here, we will thus go up our row until here.

10:44

Now we reach the row index 0

but we still have not encountered any empty square. What should we do?

We execute here the instruction "ligne--". The question is:

What happens now?

For now, it does not really matter; whatever happens here

is a conception error, an algorithm error.

What we need to do,

is correct our algorithm. We will research the fact that

the case is indeed empty in the game and that we have not exited.

Therefore, we will introduce a boolean indicating if yes or no,

the column is empty.

At the beginning, the column is not empty;

we will thus initialize our boolean to "false".

11:25

Therefore, during our process,

we will verify if we have not overflowed.

So that, if we ever reach the row 0, here,

that means we have overflowed

and that the array is full.

So, we are testing "ligne == 0"

If so, that means that our variable "pleine" (TN: means "full"),

which we have declared here,

is true since we reached the row 0; the column is thus full.

Indeed, the row 0 of a column is not empty

if and only if the column is full.

Else, at this point,

we will keep going as we did previously,

subtracting 1 from the row at every iteration.

12:18

Now, let us correct our loop so that is stops

as soon as the column is empty.

Here, we had a "while loop" which means "continue as long as".

Thus, here, we kept going as long as the column was not full.

So, we will add "is not full",

namely that the column is not full

in addition to the prevous condition, namely that we did not encounter an empty square.

This is written exactly like we had done previously;

we are not changing anything, simply adding the condition

that the column should not be full.

Here, we thus explain in a comment

that we have either found an empty square or that

we will stop for having reached the top of the column.

Finally, we will correct the end of our program.

If, at this point, the column was not full, then we will proceed

like previously, filling the empty square.

We could also take the chance to indicate a boolean return value,

indicating if we have been able to play.

In this case, we have indeed been able to drop the desired

colored disk in the selected column.

Therefore, we could, for example, decide to return

the value "true",

meaning that we have been able to play.

On the other hand, if the column was full,

we will return the value "false".

We thus take the chance to modify the return type of our function,

which now returns a boolean value instead of nothing.

In a comment, we explain that if the column was not full,

we have played and returned the value "true".

Otherwise, we have not been able to play and have return the value "false".

14:03

Since we have modified our "joue" function here,

in order to correct a possible mistake,

it is important to test this correction.

Indeed, it is always important to test your program regularly.

Thus, we now write another "main" here

which will allows us to test the exact same thing.

We declare, initialize and print a grid.

Here though, we will try to fill, let us say,

10 times the same column.

To that end, we will again call our brand new "joue" function,

this time playing constantly in the same column.

This "joue" function now returns

a boolean-type value.

We declare here a bool-type variable

in order to retrieve the return value.

In our loop, we will test at every turn

if the played move was valid at this point.

If it was not valid, we will print that it was impossible to play on this column.

Also, our loop will print the grid at every iteration.

What will happen in the "main"

is that we will try to play 10 times in the column 3.

Thus, the first 6 moves will occur without trouble.

The first 6 moves will be played here.

Now, let us see what happens

upon the 7th move.

At this point, we will our function : " joue(grille, 3 rouge) ",

thus asking to drop a red disk down the column 3.

We will initialize "ligne" to the soze of the grid minus 1

and initialize the "pleine" value to "false".

Indeed, before the test, we suppose that the column is not full,

for it is precisely what we wish to verify.

Obviously here, the test is true and we will check this part of the test.

Here, the square on the row "size - 1"

and on the column 3, here

is not empty; the test is correct.

We are not in the case where "ligne" is 0

16:10

and will thus decrement the row.

This condition will still be true as long as the tested square is not empty.

We will thus decrement, decrement, decrement

until we arrive here, on the row 0.

At this point, we will reach this test phase.

"pleine" is still false and the square is not empty.

We thus enter the loop once more

except this time, "ligne" is 0. Since "ligne" is 0,

we will assing "true" to the variable "pleine".

Also, we will not execute the decrement statement, thus staying on the row 0.

Therefore, we will loop one last time inside our "while loop".

This time, the condition "not pleine" is false

and we will therefore exit the "while loop".

We now reach this "if".

This time,

the condition "not pleine" is still false

and we will enter the "else" block.

Ultimatley, we see that the grid has not been modified

and that we have return "false" here.

Consequently, the first 6 valid moves were correct

and the loop in the "main"

(the "for loop" here) has looped 6 times.

However, upon the 7th move, this "valide" has become "false"

because of this returned "false".

The 7th move is thus invalid

and we will print "impossible d'ajouter un pion" (TN: means "impossible do drop a disk").

17:42

Since, in our testing "main", we did not add any condition regarding

the validity of the game,

the "for loop" will be executed 10 times

and the message regarding the impossibility to play will be printed 4 times.

That is it four this "joue" function,

allowing us, given a grid, a column number and a grid,

to effectively play if the position is valid

and to indicate if the move was invalid.

There several different ways to write this function;

those will be presented in the next episode.