0
\$\begingroup\$

I made this easy game to improve my C++.

The functions like NoFlicker and ShowCursor are copied from the Internet, but I made AlignString by myself and I'm proud of it.

Function changeP() swaps players. Before I created this function and edited my code, I had player1pick and player2pick; now it's more clear. Function Result(char p) takes p from winCheck(pChar). pChar is symbol of active player and winCheck() checks if there are 4 blocks in one of the ways and then returns p as result.

The first thing in main rescales console also from internet. Setup() sets everything up before new game. Pick() asks active player for column. draw() draws field and the tagged lines are about coloring it but it's more complicated than I thought.

#include <iostream>
#include <string>
#include <Windows.h>
#include <conio.h>

using namespace std;


bool gameOver, p1t, p2t;
char p1[25], p2[25], player[25];// , winner[25];
const unsigned short int X = 9, Y=7;
char field[7][8];//7 - lower border, 8 - 2 side borders
int temp[7] = {6,6,6,6,6,6,6};//to know on what lvl next block will be placed
int cPick, pColor;//column pick
char pChar;//player char for later change by players desire


void NoFlicker(short int x, short int y) {  //No flickering fix (Internet)
    COORD pos = { x, y };
    HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(output, pos);
}//End of NoFlicker
void ShowCursor(bool showFlag) {                //Show the cursor while drawing (Internet)
    HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);

    CONSOLE_CURSOR_INFO     cursorInfo;

    GetConsoleCursorInfo(out, &cursorInfo);
    cursorInfo.bVisible = showFlag; // set the cursor visibility
    SetConsoleCursorInfo(out, &cursorInfo);
}//End of ShowCursor
void AlignString(string text, int y) {      //Aligns strings to the Middle (by me)
    int size;
    size = 60 - (text.length() / 2);
    for (int ent = 0; ent <= y; ent++) {
        cout << "\n";
    }
    for (int gap = 0; gap < size; gap++) {
        cout << " ";
    }
    cout << text;
}//End of AlignString

void draw() {


    NoFlicker(0, 40);
    cout << "\t\t\t\t\t\t\t 1234567 \n";
    for (int i = 0; i < Y; i++) {
        cout << "\t\t\t\t\t\t\t";
        for (int j = 0; j < X; j++) {
            if (i == Y-1 || j == 0 || j == X-1) {
                //SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 9);
                field[i][j] = 219;
                //cout << field[i][j];
            }else if (cPick == j) {
                //SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), pColor);
                field[temp[cPick - 1]][j] = pChar;
                //cout << field[i][j];
            } /*else {
                if (field[i][j] == 1){
                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 14);
                    cout << field[i][j];
                } else if(field[i][j] == 2) {
                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4);
                    cout << field[i][j];
                } else {
                    field[i][j] = ' ';
                    cout << field[i][j];
                }*/

            cout << field[i][j];
        }cout << endl;
    }

    /*cout << endl;
    for (int i = 0; i < 7; i++) {
        cout <<temp[i];
    }cout << endl;*/
}

void changeP() {//changes players
    if (p1t) {
        pChar = 1; //current player's symbol
        pColor = 14;
        strcpy_s(player, p1);//googled this function
        p1t = false; //player's 1 turn
        p2t = true;//player's 2 turn
    }
    else if (p2t) {
        pChar = 2;
        pColor = 4;
        strcpy_s(player, p2);
        p1t = true;
        p2t = false;
    }
}

void result(char p) {
    if (p == 1) {
        gameOver = true;
        AlignString(p1, 1);
        AlignString("Won the game! It was fair match", 1);
        AlignString("New game will start in less than 10 seconds!", 10);
    }
    else if (p == 2) {
        gameOver = true;
        AlignString(p2, 1);
        AlignString("Won the game! It was fair match!", 1);
        //strcpy_s(winner, p1);
        AlignString("New game will start in less than 10 seconds!",10);
    }

}

char winCheck(char p) {
    for (int i = 0; i < 7; i++) {
        for (int j = 0; j < 9; j++) {
            if (field[i][j] == p && field[i+1][j + 1] == p && field[i+2][j + 2] == p && field[i+3][j + 3] == p) {//Diagonal Right Down Check
                return p;
            }
            if (field[i][j] == p && field[i][j + 1] == p && field[i][j + 2] == p && field[i][j + 3] == p) {//Horizontal Check
                return p;
            }
            if (field[i][j] == p && field[i-1][j + 1] == p && field[i-2][j + 2] == p && field[i-3][j + 3] == p) {//Diagonal Right Up Check
                return p;
            }
            if (field[i][j] == p && field[i - 1][j] == p && field[i - 2][j] == p && field[i - 3][j] == p) {//Vertical Check
                return p;
            }
        }
    }
}

void pick() {
    AlignString(player, 1);
    AlignString("you can pick your column --> ",1);
    cin >> cPick;
    if (cPick > 7 || cPick < 1) {
        AlignString("nIllegal pick. Try again!", 1);
        pick();
    } else {
        temp[cPick - 1] -= 1;
        if (temp[cPick - 1] < 0) {
            AlignString("This column is full. Try another one!",1);
            pick();
        }
        system("cls");
    }
}

void setup() {
    ShowCursor(false);
    gameOver = false;
    p1t = true;
    p2t = false;
    AlignString("Player 1, write your name or how you wanna be called here --> ",5);
    //cout << "\n\t\t\t\";
    cin.getline(p1, 25);
    AlignString("Ok now, Player 2, write your name or how you wanna be called here --> ",1);
    cin.getline(p2, 25);

    system("cls");
    draw();
}

void main() {

    HWND console = GetConsoleWindow();
    RECT r;
    GetWindowRect(console, &r); //stores the console's current dimensions
    MoveWindow(console, r.left, r.top, 900, 900, TRUE); // 800 width, 100 height

    setup();
    while (!gameOver) {
        changeP();
        pick();
        draw();
        result(winCheck(pChar));
    }
    Sleep(10000);
    main();

}
\$\endgroup\$
  • 1
    \$\begingroup\$ I know nothing of <Windows.h> or <conio.h>, so not qualified to review this, but you probably ought to eliminate using namespace std; and all those globals, as well as the call of main(). \$\endgroup\$ – Toby Speight Jul 4 '19 at 12:36
  • \$\begingroup\$ I've updated my answer to cover one of your additional questions. \$\endgroup\$ – pacmaninbw Jul 5 '19 at 15:49
1
\$\begingroup\$

Usage of main()
In C++ main() should only be called by the operating system, never by the program itself, it is the entry point into the program from the operating system. The definition of main() is integer so that it can return the status of the program to whatever is calling it.

Due to the recursive nature of the use of main() in this program it is possible that a stack overflow will occur and that may cause security risks on the computer running this program. A stack overflow may have other undesired side affects as well.

EDIT As pointed out by @TobySpeight calling main() from inside a c++ program results in Undefined Behavior. This means that it could do almost anything and none of what would do can be expected and is probably a bad thing. For example data within the program can be corrupted. Back when I started programming it could have shut down the computer by causing a Kernel Panic.

There is a perfectly good loop in main() for executing the game, to run another game put the game loop and the sleep statement into an outer loop.

This is an example of main() without calling itself, it compiles, I don't know if it works:

int main() {

    HWND console = GetConsoleWindow();
    RECT r;
    GetWindowRect(console, &r); //stores the console's current dimensions
    MoveWindow(console, r.left, r.top, 900, 900, TRUE); // 800 width, 100 height
    string playAgain("no");

    do {
        setup();
        while (!gameOver) {
            changeP();
            pick();
            draw();
            result(winCheck(pChar));
        }
        std::cout << "Enter yes to play again";
        std::getline(std::cin, playAgain);
    } while (playAgain.compare("yes") == 0);

}

using namespace std
The use of this statement may cause collisions of function names and variables, it would be much better to use std::cin, std::cout and std::string rather than having this statement in the code. Having this statement in a header file can cause even more confusion. As you write more complex program that are object oriented you may find yourself defining cin and cout for your objects so that they can be input or output.

Global Variables
The use of global variables makes writing and debugging code more difficult. Global variables may be modified anywhere in the program and changes to a variable can be difficult to track down.

Magic Numbers
Numeric constants in code are sometimes referred to as Magic Numbers, since it isn't clear what they are or mean.

There are a number of numeric constants used in the code, such as 0, 1, 2, 4, 14, 40, and 219. This makes the code harder to read and understand. It isn't clear what pColor = 4 or pColor = 14 are doing and it really isn't clear what this statement is doing field[i][j] = 219;

You can use const int PLAYER_ONE = 1; or const int FIELD_HEIGHT = 7; const int FIELD_WIDTH = 8; to define symbolic constants rather than numbers. This will make the code easier to read, and if you need to change the size of field you only need to edit in one place rather than multiple places. It will also make easier to understand any for loops that move through the field matrix.

String Versus C Style String
The code already includes the C++ string class, it might be better if p1, p2 and player were defined as string rather C style character arrays. The built in std::cin and std::cout already know how to handle the string class.

Use struct or class
There could be a struct or class that represents a player. It could have the fields int id;, string name; and int color;. This would reduce the number of variables for each player.

    std::cin >> p1.name;

    std::cin >> p2.name;

or the struct or class could contain a function that gets the user name.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Hi, thank you for your feedback. main(): when i shouldn't call in prograb but only OS how would I make it repeat? using namespace std; what collisions? If I don't have two same names then they can't collide and I can't see what std has to do with it. Global variables: Where should I put them then? When I use them in multiple functions. Magic numbers: I don't get it. and the 0, 1,2...are numbers from ascii except 4 and 14 they are colors and how should I do it without using them? when I tried to use string something didnt worked so I switched to char field idr what didnt work \$\endgroup\$ – Dalibor Trampota Jul 4 '19 at 16:09
  • \$\begingroup\$ and about the structs or classes I dont know what struct is and classes I haven't learned yet. Like I know how they work, whats their syntax creating objects then but Idk how to use or when to use them \$\endgroup\$ – Dalibor Trampota Jul 4 '19 at 16:16
  • 1
    \$\begingroup\$ Calling main() is Undefined Behaviour, so anything could happen. not limited to the normal effects of infinite recursion. \$\endgroup\$ – Toby Speight Jul 4 '19 at 17:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.