3
\$\begingroup\$

Here is my game logic:

  • Tic Tac Toe - player Vs player
  • Player A (first player to enter his name) is getting 'O' and is starting
  • Players choose the desired spot to put their sign(X or O) using the arrows keys and pressing enter
  • Possible to change the board location via a menu
  • Possible to change the score area location via a menu

#include <stdio.h>

#include <Windows.h>
#include<conio.h>
enum key
{
    UP,
    DOWN,
    LEFT,
    RIGHT,
    INVALID,
    ENTER,
    NONE


};
typedef enum key key;
key get_key();


enum LOCATION
{
    MATRIX=0,
    SCORE
};
typedef enum LOCATION LOCATION;
void gotoxy(int x, int y);
void MoveMatrix(int (*locations)[2],char (*matrix)[3]);
void MoveScore(int *score, int(*locations)[2],char names[2][30]);
void PrintMatrix(int(*locations)[2],char *(*matrix)[3]);
void IntitiateMatrix(char(*matrix)[3]);
int PlayGame(char(*matrix)[3], int(*locations)[2],char names[2][30],int score[2]);
void PickBox(char(*matrix)[3], int(*locations)[2], int *box);
void gotoBox(int box, int(*locations)[2]);
void updateScreen(char(*matrix)[3], int(*locations)[2], char names[2][30], int score[2], int box);
char CheckWin(char(*matrix)[3], char symbol);
void PrintScore(int(*locations)[2], int *score, char names[2][30]);




void main()
{
    char names[2][30], matrix[3][3],YorN;
    int locations[2][2] = { {0,0},{9,0} };
    int score[2] = { 0 };
    int i = 0, started = 0, won;


    //Just to intitiate the names array and the matrix, incase the player chose to change their location using Move Matrix and MoveScore functions, without them firstly inttiated.
    IntitiateMatrix(matrix);
    strcpy(names[0], "Player A");
    strcpy(names[1], "Player B");
    //
    while (i != 5)
    {

        system("cls");
        printf("1. New game\n");
        printf("2. Reset settings\n");
        printf("3. Change Matrix Location\n");
        printf("4. Change Scores bar location\n");
        printf("5. Exit\n");
        scanf("%d", &i);
        switch (i)
        {
        case 1:
            if (started == 0)
            {
                system("cls");
                printf("Enter player A name! He will use O: ");
                scanf("%s", names[0]);
                printf("Enter player B name! He will use X: ");
                scanf("%s", names[1]);
                score[0] = 0;
                score[1] = 0;
                started = 1;
            }

            won = PlayGame(matrix, locations, names, score);
            IntitiateMatrix(matrix);
            switch (won)
            {
            case 1:
                score[0]++;
                PrintScore(locations, score, names);
                break;
            case 2:
                score[1]++;
                PrintScore(locations, score, names);
                break;
            default:
                break;
            }
            getch();
            break;
        case 2:
            started = 0;
            locations[MATRIX][0] = 0;
            locations[MATRIX][1] = 0;
            locations[SCORE][0] = 9;
            locations[SCORE][1] = 0;
            strcpy(names[0], "Player A");
            strcpy(names[1], "Player B");
            score[0] = 0;
            score[1] = 0;
            break;
        case 3:

                MoveMatrix(locations, matrix);
                //sleep(100);

            break;
        case 4:
                MoveScore(score, locations, names);
                //sleep(100);
            break;
        case 5:
            break;

        }


    }

}





void gotoxy(int x, int y)
{
    COORD coord;
    coord.X = y;
    coord.Y = x;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
void MoveMatrix(int(*locations)[2], char(*matrix)[3])
{
    int *x = &locations[MATRIX][0], *y = &locations[MATRIX][1];
    key direction;

    system("cls");
    PrintMatrix(locations, matrix);
    gotoxy(*x, *y);
    while ((direction=get_key()) != ENTER)
    {
        Sleep(50);
        switch (direction)
        {
        case ENTER:
            PrintMatrix(locations, matrix);
            break;
        case UP:
            if (*x == 0)
                break;
            (*x)--;
            system("cls");
            PrintMatrix(locations,matrix);
            gotoxy(*x, *y);
            break;
        case DOWN:
            (*x)++;
            system("cls");
            PrintMatrix(locations, matrix);
            gotoxy(*x, *y);
            break;
        case LEFT:
            if (*y == 0)
                break;
            (*y)--;
            system("cls");
            PrintMatrix(locations, matrix);
            gotoxy(*x, *y);
            break;
        case RIGHT:
            ++(*y);
            system("cls");
            PrintMatrix(locations, matrix);
            gotoxy(*x, *y);
            break;

        default:
            break;
        }
    }
    return;
}
void PrintMatrix(int (*locations)[2], char (*matrix)[3])
{
    int i, j;

    for (i = 0; i < 5; i++)
    {
        gotoxy(locations[MATRIX][0] + i, locations[MATRIX][1]);
        if (i % 2 == 0)
        {
            for (j = 0; j < 3; j++)
            {
                if (j == 1)
                {
                    printf("|%c|", matrix[i / 2][j]);
                }
                else
                {
                    printf("%c", matrix[i / 2][j]);
                }
            }
        }
        else
        {
            printf("-----");
        }

    }
}
void IntitiateMatrix(char(*matrix)[3])
{
    int i, j;
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
            matrix[i][j] = ' ';
    }
}
void PickBox(char(*matrix)[3], int(*locations)[2], int *box)
{
    int picked = 0,tempbox=*box;
    gotoBox(*box, locations);
    while (picked == 0)
    {

        switch (get_key())
        {
        case UP:
            if (tempbox <= 3)
                break;
            else
            {

                tempbox -= 3;
                gotoBox(tempbox, locations);
                break;
            }
            break;
        case DOWN:
            if (tempbox >= 7)
                break;
            else
            {
                tempbox += 3;
                gotoBox(tempbox, locations);
                break;
            }
            break;
        case LEFT:
            if (tempbox == 1 || tempbox == 4 || tempbox == 7)
                break;
            else
            {
                tempbox--;
                gotoBox(tempbox, locations);
                break;
            }
            break;
        case RIGHT:
            if (tempbox == 3 || tempbox == 6 || tempbox == 9)
                break;
            else
            {
                tempbox++;
                gotoBox(tempbox, locations);
                break;
            }
            break;
        case ENTER:
            if (matrix[(tempbox - 1) / 3][(tempbox - 1) % 3] != ' ')
                break;
            else
            {
                *box = tempbox;
                picked = 1;
                break;

            }
        default:
            break;
        }
    }
    return;
}
void updateScreen(char(*matrix)[3], int(*locations)[2], char names[2][30], int score[2], int box)
{
    system("cls");
    PrintMatrix(locations, matrix);
    PrintScore(locations, score, names);

}
char CheckWin(char(*matrix)[3], char symbol)
{
    int i, j;
    //rows win
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            if (matrix[i][j] != symbol)
                break;
        }
        if (j == 3)
            return symbol;
    }
    //colummns win
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            if (matrix[j][i] != symbol)
                break;
        }
        if (j == 3)
            return symbol;
    }
    //main diagontal
    for (i = 0; i < 3; i++)
    {
        if (matrix[i][i] != symbol)
            break;
    }
    if (i == 3)
        return symbol;
    //second diagontal
    for (i = 0; i < 3; i++)
        if (matrix[i][3 - i - 1] != symbol)
            break;
    if (i == 3)
        return symbol;

    //no win
    return 'T';
}
void MoveScore(int *score, int(*locations)[2], char names[2][30])
{
    int *x = &locations[SCORE][0], *y = &locations[SCORE][1];
    key direction;

    //system("cls");
    PrintScore(locations, score,names);
    gotoxy(*x, *y);
    while ((direction = get_key()) != ENTER)
    {
        Sleep(50);
        switch (direction)
        {
        case ENTER:
            break;
        case UP:
            if (*x == 0)
                break;
            (*x)--;
            system("cls");
            PrintScore(locations, score,names);
            gotoxy(*x, *y);
            break;
        case DOWN:
            (*x)++;
            system("cls");
            PrintScore(locations, score,names);
            gotoxy(*x, *y);
            break;
        case LEFT:
            if (*y == 0)
                break;
            (*y)--;
            system("cls");
            PrintScore(locations, score,names);
            gotoxy(*x, *y);
            break;
        case RIGHT:
            ++(*y);
            system("cls");
            PrintScore(locations, score,names);
            gotoxy(*x, *y);
            break;

        default:
            break;
        }
    }
    gotoxy(0, 0);
    return;
}
void PrintScore(int(*locations)[2], int *score, char names[2][30])
{
    gotoxy(locations[SCORE][0], locations[SCORE][1]);
    printf("%s : %d", names[0], score[0]);

    gotoxy(locations[SCORE][0]+1, locations[SCORE][1]);
    printf("%s : %d", names[1], score[1]);
}

int PlayGame(char(*matrix)[3], int(*locations)[2], char names[2][30], int score[2])
{
    int box = 5, i;
    char status = 'T';
    IntitiateMatrix(matrix);

    /*Just for fun
    gotoxy(5, 5);
    printf("********************************");
    gotoxy(6, 5);
    printf("*Initiating protocol!.....     *");
    gotoxy(7, 5);
    printf("********************************");
    sleep(2000);
    gotoxy(6, 5);
    printf("*Creating Random Number.....   *");
    sleep(2000);
    gotoxy(6, 5);
    printf("*Ultilizing Game Board..   ..  *");
    sleep(2000);
    */

    updateScreen(matrix, locations, names, score,box);
    for (i = 0; i < 9&&status=='T'; i++)
    {
        if (i % 2 == 0)
        {
            PickBox(matrix, locations, &box);
            matrix[(box - 1) / 3][(box - 1) % 3] = 'O';
            updateScreen(matrix, locations, names, score, box);
            status = CheckWin(matrix, 'O');


        }
        else
        {
            PickBox(matrix, locations, &box);
            matrix[(box - 1) / 3][(box - 1) % 3] = 'X';
            updateScreen(matrix, locations, names, score, box);
            status = CheckWin(matrix, 'X');
        }

    }
    switch (status)
    {
    case 'O':
        gotoxy(5, 5);
        printf("*****************");
        gotoxy(6, 5);
        printf("*%s WINS!!",names[0]);
        gotoxy(6, 21);
        printf("*");
        gotoxy(7, 5);
        printf("****************");
        return 1;
    case 'X':
        gotoxy(5, 5);
        printf("*****************");
        gotoxy(6, 5);
        printf("*%s WINS!!", names[1]);
        gotoxy(6, 21);
        printf("*");
        gotoxy(7, 5);
        printf("****************");
        return 2;
    case 'T':
        gotoxy(5, 5);
        printf("*****************");
        gotoxy(6, 5);
        printf("*Its A TIE!!..  *");
        gotoxy(6, 21);
        printf("*");
        gotoxy(7, 5);
        printf("****************");
        return 0;
    default:
        return 0;
        break;
    }

}
void gotoBox(int box, int(*locations)[2])
{
    int Ibox = (box - 1) / 3, Jbox = (box - 1) % 3;
    if (Ibox == 1)
        Ibox += 1;
    else if (Ibox == 2)
        Ibox += 2;
    if (Jbox == 1)
        Jbox += 1;
    else if (Jbox == 2)
        Jbox += 2;
    /* to test if the additions are correct, since there are walls we need to increament x and y through those walls to their cells
    gotoxy(10, 10); ///just test
    printf("Pixel row [%d][%d] - box %d", locations[MATRIX][0] + Ibox, locations[MATRIX][1] + Jbox,box);
    */
    gotoxy(locations[MATRIX][0] + Ibox, locations[MATRIX][1] + Jbox);

}

key get_key()
{
    int ch;
    if (_kbhit())
    {
        ch = _getch();
        ch == 0 || ch == 224;
        switch (ch)
        {
        case 0:
        case 224:
            switch (_getch())
            {

            case 72:
                return UP;
            case 80:
                return DOWN;
            case 75:
                return LEFT;
            case 77:
                return RIGHT;
            default:
                return INVALID;
            }
        case 56:
            return UP;
        case 50:
            return DOWN;
        case 52:
            return LEFT;
        case 54:
            return RIGHT;
        case 13:
            return ENTER;
        default:
            return INVALID;
        }
    }
    return NONE;

}

Few notes:

  1. The array locations is a 2 dimensional array used to store the matrix and the score bar location.
  2. The function PickBox gets the matrix of the game, the locations and a random box that it receive, and it used to navigate in the matrix, visually for the player. If a player hit enter on an empty box. The function will return that box via the pointer to the main function which will assign the corresponding value (X or O) to it.

If you are having some hard time figuring out my meaning about the game, try to run it and play with the menu that will pop up.

\$\endgroup\$
  • \$\begingroup\$ I'd like to run the game. However, the posted code is not portable due to windows.h and conio.h; (using ncurses for screen manipulation could have resulted in a portable game.) Note: I'm on ubuntu Linux. \$\endgroup\$ – user3629249 Aug 2 '15 at 16:21
  • \$\begingroup\$ @user3629249 I deliberly used those more "simple" libraries in order to gain experience, however, since I realized now it's not protable, i will try to make as much as portable programms in the future \$\endgroup\$ – Moses Aug 2 '15 at 16:37
2
\$\begingroup\$

Inclusions

You include Windows.h but that would generate an error, since the file is called windows.h. That isn't a real problem on Windows since it uses a case-insensitive filesystem anyway, but that's something to point out.

enums

You add NONE at the last of the enum making it the largest value in the enum. That's not a problem, but usually NONE-type enum values are zero (thus false as a boolean) for convenience.

Next, you typedef the enum right after you declare it. A shorthand would be to typedef it on declaration:

typedef enum { ... } key;

The same applies to enum Location.

Coding Style

Your coding style should be consistent and readable to be easy to follow and understand later on. For a consistent coding style, I highly recommend using a tool like indent.

Variable naming

You name an enum "LOCATION". All upper-case names are by practice used only for macros.

If you rely on SCORE to be 1, then explicitly write so, either for readability or if you plan to port the code to other non-POSIX compliant systems/compilers.

Function names are not consistent. Some of them are all lower case, some are lower camel case, some are upper camel case, and some contain underscores. See naming conventions

In Windows programming, the convention is to use upper camel case since it is used exclusively all Windows APIs.

Bracing Style

You use the K&R style, but your employment of braces seems to be random.
For example:

// InitiateMatrix()
...
for (...) {
    for (...)
        ...
}

You would either put braces after every one line loop, or you would omit it. You put it in one case and left it in the other. In case you are confused,

for (...)
   for (...)
       ...

is valid C, because the first for is technically one line because it encloses a block of code (the other loop).

Variable declaration placement

Declaring variables at the start of a block is so C89/Ansi that its not a rule anymore.

Program design

You use enum LOCATION values quite a lot in the program. However, MATRIX and SCORE are unrelated, so it would make more sense to #define them instead of using an enum since you're not using any of the benefits of an enum anyway.

I also noticed that you're not using any of C99 or C11 features, so you are basically coding like in the 80's.

For example ..

gotoxy()

You can reduce the four line code to one, while still being readable, like this:

SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), { .x = x, .y = y });

This is an example of designated initializers.

Also, since this is a simple function that gets called a lot, it is encouraged to mark it as inline so that the compiler can optimize it in any context it finds fit. This applies to other functions as well.

InitiateMatrix()

You could have used the enhanced for-loop scope, like this:

    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 3; ++j)
            matrix[i][j] = ' ';

which also has the advantage of narrowing down the scope of the variables used in the loop, which is always a good thing.

Summary

The code is functional, overall well-designed but unnecessarily long and seemingly outdated.

\$\endgroup\$

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.