In this episode, we will present the different alternative versions
of the "joue" (TN: means "plays") function that we have written in the previous episode.
Please be reminded that this function's goal is to place a disk
in the grid, given the number of the column
where we intend to drop our disk.
Also given is the player's color. By the way, do not forget
that this function also verifies the validity of our move.
Namely, if the column is full,
we will declare that the move is not valid.
No matter the implementing we choose for our alternate version
of the "joue" function, the prototype is not going to change.
Therefore, we will still have here a grid passed by reference,
a column passed as argument
and a color passed as argument aswell. Nothing has changed here.
However, the idea is to come up with a rather different algorithm.
We will thus start by saying that,
if the column is full,
we will not play.
The idea is to start by run the test here instead of doing it later.
This is written like this:
We are testing if the column is full,
Be reminded that a column is full if and only if
the uppermost level,
the level 0 is occupied.
Here, we will thus test if the square in the row 0,
this row here,
and in the column received as argument is not empty.
If this square is not empty, that means that the column is full
and we will immediately
return the fact that this move was invalid, that is, the value "false",
We are now released from this condition.
Since at this point we know the column is not empty,
we can calmly search the place where the disk will be placed.
We can freely seek the place where we will play.
To that end, we will run through the column, like we did in the previous version,
starting from the bottom. In C++, this is written like this.
We are declaring a variable
which will allow us to climb up the rows,
just like we did in the previous version of the "joue" function.
We initialize this variable to the size of the grid - 1
then to the current position.
That means that, if the square on the position "grille[ligne][colonne]" is not empty,
we will decrement the row by 1.
Finally, the last step
is to fill the square we have found
(indeed, thanks to our previous test, we know that such a square exists)
and finally to return "true"
since we have been able to play our move.
Now, we should test our brand new "joue" function;
we let you do this as an exercise.
Also, feel free to appreciate the differences between the two codes,
independently of the number of lines and the comments, by the way.
Now, let us see a third version of this "joue" function,
restarting with the beginning of the first version, which
we presented in the previous episode.
We will start by helping you recall the first version.
The idea is to focus on the troubles
we initially encountered, and brought us so many lines
with "pleine" (TN: means "full") here, also revised here aswell.
Those precisely pertained on this critical point
of decrementing "ligne" here when "ligne" was 0.
What happens then?
Also, is there a way to rewrite the code in
a slightly different way?
The idea is to do without this boolean "pleine"
and to replace it with another test.
Let us keep going from the intial problem.
and with a question that we had left unanswered,
that was: what happened when we were at the line 0.
Since we had not written
all this text here,
we still executed the instruction " ligne --"
Therefore, there was a risk, in this case, to exit the array.
So, let us go back to the type of "ligne",
which is of "size_t" type.
As we told you before, this type,
the "size_t" type is always a positive integer.
Here, we thus have a problem regarding the number representation.
Practically, what wil happen in the memory is that we will have
a very big number.
No matter the technical aspects though; this is not our focus here.
However, this very big number will certainly not be
a row number of our array.
And so,
what should we do here? Well, the systematic reflex
we should learn here
-another critique regarding our code here will thus arise-
is that we should systematically
test if the accesses to our array are correct.
We garantee this either by a demonstration of our algorithm
or by a true test. So, the conditions here
are that "ligne" should always be
less than "grille.size()".
Secondly, an array index cannot be
greater than the array size.
And, lastly, we should be able to verify that "colonne" (TN: means "column")
should always be less than
"grille[ligne.size()]"
Therefore, this value here "-1" which is the "size_t"-type 0
on which we execute the instruction " -- " will be represented one way or another,
for example "impossible", or a very big number,
no matter what, this number, " 0 -- ", that is, -1
cannot be represented as a "size_t".
Therefore, condition grid here, "less than grille.size() "
is not verified.
We will use precisely this condition
as the overflowing condition
of our array.
We will thus write,
along with a comment here,
that if the test "ligne" less than grille.size()
is false,
that means that the column is full.
We thus need to stop
since the move is invalid.
Now that we have understood and explained what we wish to accomplish,
we can correct our code
and supress the variable "pleine".
We supress here the test which involved it and replace it by this test :
"ligne" less than the size of the grid.
Please note the protection of the access index,
according to the size of the accessed array,
before we access it. This is an advice
you should systematically abide by. Of course, if such a condition is garanteed by "for loop",
or some other constraint, you will not add such a test at every step. However,
every time you have to access an array through an index,
you should at least ask yourself whether the condition
that it is less than
the array size is fulfilled.
So, we now see that we have
explicitly written this useful test,
tasked to test if the column is full.
Now, we can keep suppressing
everything involving "pleine"
and finally correct our program
wherever the variable "pleine"
is no longer needed.
Here, still the same test : if the row is less
than the grid size,
then we can play.
Otherwise, that means we could not play.
We thus modify our
code accordingly.
Our third version of "joue" function is now finished.
That being said, if you have followed our discourse so far,
then you should realize that
an error remains in all our "joue" functions.
Do you have any idea
what we should add to our three "joue" functions in order
to render them correct?
We will let you implement it yourself as an exercise
in the variant of the "joue" function you have choosen
to keep for your Connect Four.
So, at this point, you should have a correct "joue" function.
In the previous episodes, we have presented
the data structures necessary to build a Connect Four game,
simple functions such as "initialize" and "affiche".
Also, in these last two episodes, we have implement the "joue" function,
letting us a drop a disk down the indicated column (if this column is valid).
Now, we still have to ask the players where they intend to play.
We then validate their move -we have just done that.
Then, we just just need to iterate, alternating between players.
Asking the red player to play, the yellow player and so on.
Finally, we will terminate the program once a player wins
or once the grid is full.
This will occupy us for the next episodes.