What i Learn from basic C++ Console Project
Tags: cpp, programming, beginners, assembly
Published: 2023-01-10 22:16:47

## <u>**Tombala**</u>

This will be a blog about what I learn during this project. First of all, this project is a console application and its written in C++. In the beginning it asks you how many bingo cards do you want and randomly creates cards which have 15 numbers in it and all the numbers are from 1 to 90. Every column, what I mean by column is, from 1 to 10 it is the 1st, from 11 – 19 the 2nd and from 81 – 90 the 9th, can have max 2 numbers. There are also 3 rows in a card and every row needs to have 5 numbers in it. After all cards have been randomly created, game starts. From 1 to 90 we put all numbers in a bag then we started choosing randomly and delete those numbers from cards which have those. The numbers we chose and deleted from the cards, were thrown away. First card wins 1st Cinko needs to remove all the numbers in 1 row before every other card does and the first card wins 2nd Cinko needs to remove all numbers in 2 row before others finally when a card’s all numbers were picked, that card wins the Tombala.


![gameStart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/11fyqqjwcv4l4elks99a.png)

![gameEnd](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oh05s3riylk9y685aut2.png)

Lets start!

---

## <u>Using “using” keyword to declare a type</u>


```
using basicCardType = std::array<int, constants::numberCountInCard>;

using advancedCardType = std::array <std::array<int, constants::columnCountCard>, constants::rowCountCard>;

using bingoDeckType = std::array <std::array<int, constants::columnCountBingo>, constants::rowCountBingo>; 

using winnerType = std::array<int, constants::winnerCount>;

```

These are the types in project that i used a lot times. For saving time and for the sake of readability it is better using “using” keyword to declare a type.  

Let’s look at `basicCardType` code above. 
It's type is `std::array<int, constants::numberCountInCard>`. 
The value of `constants::numberCountInCard` is 15 so it is an integer array stores 15 integers.

`basicCardType createBasicCard(){}`

- 
We can set that type as a return type from the function.

`basicCardType sortBasicCardNumbers(basicCardType basicCard) {}`

- 
We can use that type as a parameter in function and send it to function as an argument.

---


## <u>Adding optional parameters to function</u>

```
void printBingoDeck(bingoDeckType bingoDeck, int bingoNumber = 0, bool gameStarted = false)


```

In the `printBingoDeck()` function above we use 3 parameters.  
1. bingoDeck
2. bingoNumber
3. gameStarted

Normally we don’t assign a value to parameter in function like <u>_int bingoNumber = 0_</u>. This means, if there is no value send as an argument to _bingoNumber_ set its value to 0, and it is same in <u>_gameStarted = false_</u> too. If there is no value used in _gameStarted_ argument it takes value false. 
Lets check the code inside of the function.

```
     if (!gameStarted)
           std::cout << "Bingo game is ready!" << '\n' << '\n';
     else
           std::cout << "Bingo number is: " << bingoNumber << '\n' << '\n';

```
If _<u>gameStarted</u>_ did not send to function as an argument, function will set its value to <u>_false_</u> and get inside of an if statement.

`printBingoDeck(mainBingoDeck);`

`printBingoDeck(mainBingoDeck, randomNumber, gameStarted);`

Both function calls above are possible because the last 2 arguments are optional we do not need to use them as arguments.

There is a different way to use optional parameters in function. Which is `std::optional`. There is a link in references about `std::optional` for more information.

---


## <u>Understanding std::vector and std::vector::iterator</u>

Vectors are basically dynamic arrays which can be updated in runtime. In arrays it is not possible to change the size, after size has been set but in vectors you can change size by adding a new value or remove some of them in runtime.

In the beginning of this blog, we mentioned that, every column in card can have maximum 2 numbers in it. If we have <u>[21,27]</u> in 3rd column, we can not have <u>[21,24,27]</u> in there. So while we are creating our cards from randomly selected numbers, we need to eliminate numbers between 20 to 29 if we already have picked 2 numbers from that column. That’s where using std::vectors becomes handy. Lets check how we actually use them.

```
int getIndexValueFromVector(std::vector<int> numberVector, int startingNumber)
{
    std::vector<int>::iterator it;
    it = std::find(numberVector.begin(), numberVector.end(), startingValue);
    return static_cast<int>(it - numberVector.begin());
}
```

The function above returns the index value of the number that we need to start the removing process. For example if we need to remove 40 to 49, function will return us the index of value 40 from the values inside of that vector.

Before we get into that function lets understand `std::vector::iterator` 
Iterators job is basically starting from the beginning of the vector and going to the end to find the value and return the index of that value. Iterator starts from the `vector[0](index = 0)` and goes to the `vector[last + 1](index = last + 1)`.

But why it ends in `index[last + 1]` ?

`std::vector<int> myVector{ 11,12,13,14,15,16 };`
`myVector[0] = 11` -> iterator starts from here.
`myVector[5] = 16` -> iterator has not come to an end yet because there is still a value in 5th index.

Lets check the type of the value in **6th** index.

<u>_typeid(myVector[6].name()) = not initialized int_</u>

when iterator sees not initialized value, it understands that it comes to the end of the iteration and gets the value of `index[last + 1]` as its value. 

So when we get to the end of the iteration process, our <u>_iterator = 6_</u> which is the same as <u>_iterator = myVector.end()_</u>

It is important to remember when we don’t initialize an integer, it takes a value of 0 !

**std::cout << myVector[6];** will print 0.
so `myVector[6] = 0`. myVector's 6th index, the value 0 is iterators end but it is not iterators end in yourVector below

`std::vector yourVector {11,12,13,14,15,16,0,17}`

6th index in yourVector is also equals to 0 and <u>_std::cout << yourVector[6];_</u> will also print 0 but it is not the end of iterator when we iterate in yourVector.

Lets go back to myVector. `myVector.begin()` will return 0, `myVector.end()` will return 6(last+1) and the index of 14 is 3 `(myVector[3] = 14)` when we are iterating in myVector.

Now that we understand iterators beginning and end values, we can talk about 
`std::find(InputIterator first, InputIterator last, const T& val)` 

If we return **getIndexValueFromVector()** function above we saw,

```
std::vector<int>::iterator it; //iterator created
it = std::find(numberVector.begin(), numberVector.end(), startingNumber);
return static_cast<int>(it - numberVector.begin());

```
Applying `std::find()` function to the myVector to check value 14, we will get <u>_it = 3_</u>. Because of it is not letting us directly return the value of it, we will return <u>_it – myVector().begin_</u>


```
for (int k = 0; k < 10; k++)
{
    allNumbersInDeck.erase(allNumbersInDeck.begin() + startingIndex);
}
```
We can use `erase()` method to remove same index for 10 times. For example we want to remove values from 20 to 29. At first it will remove the value 20, then the same index's value will become 21 because of 20 is deleted.
**allNumbersInDeck** are also a vector.


```
std::vector<int> allNumbersInDeck{};
allNumbersInDeck.reserve(constants::totalBingoDeckNumbers);

for (int i = 0; i < constants::totalBingoDeckNumbers; i++)
{
    allNumbersInDeck.push_back(i + 1);
}
```
Another function is **reserve()**, if we know the size of vector it is a good practice to reserve the memory before assigning values for a better performance.

**push_back(value)** function adds a new value at the end of the vector.


```
int getIndexValueFromVector(std::vector<int> numberVector, int startingNumber)
{
    for (int i = 0; i < numberVector.size(); i++)
    {
        if (numberVector[i] == startingNumber)
            return i;
    }   
}
```

The way that we get Index value above, instead of using iterator is, i think more cheaper but it is not bad learning it.

---

## <u>Using reference parameters in functions</u>

```
int firstFunction(int parameter)
{   
  parameter += 1;
  return parameter;
}
int secondFunction(int& referenceParameter)
{
    referenceParameter += 2;
    return referenceParameter;
}
int thirdFunction(const int& constReferenceParameter)
{
    constReferenceParameter += 3;
    return constReferenceParameter;
}
int main()
{
    int firstVar{ 1 };
    int secondVar{ 1 };
    int thirdVar{ 1 };

    firstFunction(firstVar);
    std::cout << "After first Function : " << firstVar << '\n';

    secondFunction(secondVar);
    std::cout << "After second Function : " << secondVar << '\n';

    thirdFunction(thirdVar);
    std::cout << "After second Function : " << thirdVar << '\n';
}

```
We will read the code snippet one by one. 

Lets check the firstFunction(). It has an integer parameter. When we send firstVar to the firstFunction() our parameter will take the value of firstVar. Parameter will create another integer in memory and initialize it with the value of firstVar. Inside the function we add 1 to the new integer that we created and we return the value of new integer that we created inside firstFunction(). After function ends and when we turn back to main() function the new integer that has been created inside firstFunction() will destroy itself. Now in stack we only have 1 integer which is the first variable (firstVar) that we create inside of main().

So what will firstVar will print and does it changed after firstFunction() executed?

firstFunction() returned a value of (firstVar + 1) but we did not assign it to firstVar.So its value does not changed after firstFunction() executed. Basically we copy firstVar and used its value inside firstFunction() as another integers value and we return the value of (firstVar + 1).

Lets check the secondFunction. It has an integer reference parameter. We sent secondVar as an argument to secondFunction(), if we check the function parameter it is int& referenceParameter.

Lets check what happens under the hood and look at assembly code.

[Assembly Code in GodBolt](https://godbolt.org/z/v8K9xGrc3) 

ASM Code is compiled by x86-64 clang 15.0.0

```
main:                                   # @main
    1   push    rbp
    2   mov     rbp, rsp
    3   sub     rsp, 16
    4   mov     dword ptr [rbp - 4], 1
    5   lea     rdi, [rbp - 4]
    6   call    secondFunction(int&)
    7   mov     dword ptr [rbp - 8], eax
    8   xor     eax, eax
    9   add     rsp, 16
    10  pop     rbp
    11  ret
```
**<u>main:</u>**

<u>line 4</u> – move decimal 1 to the and store it inside of 4 byte memory which is **DWORD PTR [rbp - 4]**

<u>line 5</u> – move(lea acts like mov) value inside the memory [rbp - 4] which is decimal 1 to rdi(<u>64bit register that will carry value to secondFunction()</u>).

<u>line 6</u> – calls the function **secondFunction(int&)** 

```
secondFunction(int&):                   # @secondFunction(int&)
     1   push    rbp
     2   mov     rbp, rsp
     3   mov     qword ptr [rbp - 8], rdi
     4   mov     rax, qword ptr [rbp - 8]
     5   mov     ecx, dword ptr [rax]
     6   add     ecx, 2
     7   mov     dword ptr [rax], ecx
     8   mov     rax, qword ptr [rbp - 8]
     9   mov     eax, dword ptr [rax]
     10  pop     rbp
     11  ret
```

**<u>secondFunction(int&):</u>**

<u>line 3</u> - we get the **firstVar**’s memory address and value from **[rdi]** register comes from **main()** function and store it inside **QWORD PTR [rbp - 8]**. Normally integers are stored in 4 byte size but in here because of we use reference parameter (in ASM reference behaves and have a 8 byte memory like pointers) So the value inside **[rdi]** will store in **QWORD PTR [rbp – 8]**.

- 
**QWORD PTR[rbp – 8]** inside secondFunction(int&)'s adress is now equals to firstVar's adress.

<u>line 4 & 5</u> – these lines used same way in line 8 & 9. Because of we are using reference parameter and store it in 8 bytes of memory like in line 3 QWORD PTR[rbp – 8], when we want to move it we need to store it in 64 bit(8 byte) register which is [rax] as we see in line 4. We set **QWORD PTR [rbp -8]**'s adress to **[rax]** registers adress. 

**mov [rax], QWORD PTR [rbp -8]** 

but for moving the value of integer we need use 32 bit register because integer uses 4 bytes of memory but the size of [rax] is 64 bit. Then we use **[ecx]** which is a 32 bit register. It is also important to mention that we are moving 4 bytes of **[rax]** register. 

**mov [ecx], DWORD PTR [rax]**

If we use **QWORD PTR [rax]**, it will move the value inside of an 8 byte memory but because we move integer type, which is 4 byte, using **DWORD** and sending half of the **[rax]** registers memory, still can hold the value of integer.

- 
**DWORD PTR [rax]** and **[ecx]**, they both have the same values which are also equals to the value of **firstVar** inside main() function.

<u>line 6</u> – we add 2 to the value of [ecx] which is equals to decimal 1 like firstVar in main() function. Now [ecx]'s value becomes decimal 3. Because of **[ecx]** and **firstVar** have the same memory adresses now firstVar's value is equal to decimal 3.

<u>line 7</u> – we take the new value of **[ecx]** which is 3 and move it to the value of **DWORD PTR [rax]**. The value of **DWORD PTR [rax]** is now also 3.

<u>line 8&9</u> - in line 8&9 we will think more like **firstVar**.

**mov [rax], QWORD PTR [rbp -8]** 

because of **QWORD PTR [rbp -8]** stores the adress of **firstVar**, we now move the adress of firstVar to **[rax]** register.

**mov [eax], DWORD PTR [rax]**

because of now **[rax]** register has an adress of **firstVar**, the value inside firstVar adress is 3 and because we move that value to **[eax]**, **[eax]** register is now also have a value of decimal 3.

**<u>main:</u>**

<u>line 7</u> - **DWORD PTR [rbp - 8]**, because of we get out of secondFunction(int&) now our rbp(base pointer register)'s adress is different too..[rbp - 8] in secondFunction(int&) and [rbp - 8] in main() function are not the same adresses. In the last line of secondFunction(int&) we set [eax] a value of decimal 3. Now in line7 in main() we will assign its value to **DWORD PTR [rbp - 8]** which is 4 byte integer.

<u>What happened ?</u>

Our **firstVar**'s (int argument) value changed when we use it as reference parameter and change its value inside the function secondFunction(int&).

 
Lets check the third function. Because of we use const we can not compile this program it will throw error. Constant values can not be changed.

     `constReferenceParameter += 3;` // will throw error.

---

## <u>Using srand() and rand()</u>

`rand()` function creates series of numbers between 0 – 0x7FFF which is 524287. But every time that we run the program again we will get same values.

``` 
for (int i = 0; i < 5; i++)
{
     std::cout << rand() << '\n';
}
```
Console output will always be -> 41 18467 6334 26500 19169


In this case srand() will show himself. When we seed srand() function with a value it changes random values created.

```
srand(5);
for (int i = 0; i < 5; i++)
{
    std::cout << rand() << ' ';
}
```
But in this time because of its seed value is never changing.
Console output is always -> 54 28693 12255 24449 27660. 
So we need to seed that `srand()` function with a value that always changes. Time for example, is a good way to seed.

Lastly, to use it in algorithm we just need to take outputs modulus.
```
srand(time(0));
for (int i = 0; i < 5; i++)
{
    // we need values from 0 to 90
    std::cout << rand() % 91 << ' ';
}
```
We seed `srand()` with `time(0)` then take MOD91 of the values so we get values from 0 to 90 and we have a random function. This is not the safest and secure way to use random number generator but it works well in this project.

That's it.

Here is the Source Code of the project
[Tombala Project Source Code](https://github.com/UPinar/Tombala)

<u>**Useful Links**</u>

- 
[Type Alias](https://en.cppreference.com/w/cpp/language/type_alias)

- 
[Std::optional](https://en.cppreference.com/w/cpp/utility/optional)

- 
[optional parameter](https://stackoverflow.com/questions/3784114/how-to-pass-optional-arguments-to-a-method-in-c)

- 
[Vectors](https://iq.opengenus.org/find-element-in-vector-cpp-stl/)

- 
[reserve keyword](https://stackoverflow.com/questions/6525650/benefits-of-using-reserve-in-a-vector-c)

- 
[range based for loop](https://stackoverflow.com/questions/17032267/c11-range-based-for-loop-efficiency-const-auto-i-versus-auto-)

- 
[const auto](https://stackoverflow.com/questions/16809934/how-to-use-auto-with-const-and-in-c)