Search code examples
ctic-tac-toe

getch() replacement in C


I want and searching for an alternative that could replace the getch() function of C language. I want help if any any function which can replace the getch() function from the code below or an appropriate alternative. The conio.h header file is unavailable because I used the GNU GCC compiler but I don't want to change compilers because it will be super long to wait.

#include <stdio.h>
#include <stdlib.h>
#include <conio.h> // No such file or directory

char square[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
int choice, player;

int checkForWin();
void displayBoard();
void markBoard(char mark);

int checkForWin() {
    int returnValue = 0;

    if (square[1] == square[2] && square[2] == square[3]) {
        returnValue = 1;
    } else if (square[4] == square[5] && square[5] == square[6]) {
        returnValue = 1;
    } else if (square[7] == square[8] && square[8] == square[9]) {
        returnValue = 1;
    } else if (square[1] == square[4] && square[4] == square[7]) {
        returnValue = 1;
    } else if (square[2] == square[5] && square[5] == square[8]) {
        returnValue = 1;
    } else if (square[3] == square[6] && square[6] == square[9]) {
        returnValue = 1;
    } else if (square[1] == square[5] && square[5] == square[9]) {
        returnValue = 1;
    } else if (square[3] == square[5] && square[5] == square[7]) {
        returnValue = 1;
    } else if (square[1] != '1' && square[2] != '2' && square[3] != '3' && square[4] != '4' && square[5] != '5'&& square[6] != '6' && square[7] != '7' && square[8] != '8' && square[9] != '9') {
        returnValue = 0;
    } else {
        returnValue = -1;
    }
    return returnValue;
}

void displayBoard() {
    system("cls");

    printf("\n\n\tTic Tac Toe\n\n");

    printf("Player 1 (X) - Player (O)\n\n\n");

    printf("   |   |   \n");
    printf(" %c | %c | %c \n", square[1], square[2], square[3]);

    printf("___|___|___\n");
    printf("   |   |   \n");

    printf(" %c | %c | %c \n", square[4], square[5], square[6]);

    printf("___|___|___\n");
    printf("   |   |   \n");

    printf(" %c | %c | %c \n", square[7], square[8], square[9]);

    printf("   |   |   \n\n");
}

void markBoard(char mark) {
    if (choice == 1 && square[1] == '1') {
        square[1] = mark;
    } else if (choice == 2 && square[2] == '2') {
        square[2] = mark;
    } else if (choice == 3 && square[3] == '3') {
        square[3] = mark;
    } else if (choice == 4 && square[4] == '4') {
        square[4] = mark;
    } else if (choice == 5 && square[5] == '5') {
        square[5] = mark;
    } else if (choice == 6 && square[6] == '6') {
        square[6] = mark;
    } else if (choice == 7 && square[7] == '7') {
        square[7] = mark;
    } else if (choice == 8 && square[8] == '8') {
        square[8] = mark;
    } else if (choice == 9 && square[9] == '9') {
        square[9] = mark;
    } else {
        printf("Invalid move ");

        player--;
        getch(); // error
    }
}

int main()
{
    int gameStatus;

    char mark;

    player = 1;

    do {
        displayBoard();

        player = (player % 2) ? 1 : 2;

        printf("Player %d, enter a number: ", player);
        scanf("%d", &choice);

        mark = (player == 1) ? 'X' : 'O';

        markBoard(mark);

        gameStatus = checkForWin;

        player++;
    } while (gameStatus == -1);

    if (gameStatus == 1) {
        printf("==>\aPlayer %d wins! ", --player);
    } else {
        printf("==>\aGame draw!");
    }
}

I'm just simply creating a Tic-Tac-Toe game, you need to write a number from 1 to 9 for each squares, if any of the players wins, it will print "==>Player __ wins!", if the game is a tie, it will print out "==>Game draw!"

I tried the getchar() function but it keeps on printing "==>Game draw!" and "sh: line 1: cls: command not found" I tried the getche() and putch() function also but it also alerts an error


Solution

  • conio.h will not be available if you moved to a different OS. See also Why the use of "conio.h" is not good habit of programming? and Why must we refrain from using conio.h? Is it obsolete?

    But in your actual case you should not need to use getch. In your code you call it at a moment that the user enters an invalid move, but in that case you don't really have to wait for a key. The user anyway needs a new opportunity to enter a valid move, so just go ahead and ask for their move again without further delay.

    There are some other problems in your code:

    • gameStatus = checkForWin; assigns a function to an int variable. A good IDE will complain about that. Your intention was to call checkForWin, but you didn't. For that you need to append parentheses.

    • Don't use system("cls"). It makes your code OS dependent; it will give an error on OS that do not have a cls command. Instead you can use a VT-100 escape sequence.

    Not a real problem, but:

    • It is odd that player can temporarily get a value that is neither 1 nor 2, but 3, which then gets corrected with another assignment. You can avoid this by realising that the opposite player has number 3 - player.

    • markBoard should not have to deal with printing a message. Nor should it alter the player variable. Leave that to the main function. markBoard should just do what its name suggest. You could have it return a success/failure indication so that the caller can do what is needed to deal with an invalid move.

    • Avoid global variables. Instead declare these variables in main and pass them as arguments to the other functions as needed.

    • Avoid code repetition. Although markBoard correctly adjusts the squares, you could shorten the code by using choice as an index in square and compare with the character that is expected there, which is '0' + choice.

    • Also in checkForWin you could avoid some code repetition and work with a table of winning lines. The check for a draw could also be done with a loop instead of having 9 conditions in one if statement.

    • When the game ends, the last state of the board is not printed. It might be good to add that.

    Here is your code adapted with these remarks:

    #include <stdio.h>
    #include <stdlib.h>
    
    int checkForWin(char square[]);
    void displayBoard(char square[]);
    int markBoard(char square[], char mark, int choice);
    
    // Avoid globals: moved to main.
    
    int checkForWin(char square[]) {
        // Avoid code repetition
        static int wins[8][3] = {
            { 1, 2, 3 },
            { 4, 5, 6 },
            { 7, 8, 9 },
            { 1, 4, 7 },
            { 2, 5, 8 },
            { 3, 6, 9 },
            { 1, 5, 9 },
            { 3, 5, 7 }
        };
        for (int i = 0; i < 8; i++) {
            if (square[wins[i][0]] == square[wins[i][1]] && square[wins[i][0]] == square[wins[i][2]]) {
                return 1; // A win detected
            }
        }
        for (int i = 1; i <= 9; i++) {
            if (square[i] == '0' + i) return -1; // Still a square available for a move
        }
        return 0; // A draw
    }
    
    void displayBoard(char square[]) {
        // Don't use system("cls"). It makes your code OS dependent. Use a VT100 escape sequence:
        puts("\033[2J\033[H"); // clear and home
        printf("\n\n\tTic Tac Toe\n\n");
    
        printf("Player 1 (X) - Player 2 (O)\n\n\n");
    
        printf("   |   |   \n");
        printf(" %c | %c | %c \n", square[1], square[2], square[3]);
    
        printf("___|___|___\n");
        printf("   |   |   \n");
    
        printf(" %c | %c | %c \n", square[4], square[5], square[6]);
    
        printf("___|___|___\n");
        printf("   |   |   \n");
    
        printf(" %c | %c | %c \n", square[7], square[8], square[9]);
    
        printf("   |   |   \n\n");
    }
    
    // Have the function return a boolean to indicate whether the move was performed
    int markBoard(char square[], char mark, int choice) {
        // Use dynamic indexing
        if (choice < 1 || choice > 9 || square[choice] != '0' + choice) return 0;
        square[choice] = mark;
        // Don't alter player here, nor display a message. This should be done by main.
        return 1;
    }
    
    int main()
    {
        // Avoid globals, define your variables in main, and pass them as arguments where relevant
        char square[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
        int choice;
        int gameStatus;
        char mark;
        int player = 2; // This will switch to 1 in the first iteration
        int wasValid = 1; // False when previously entered move was not valid
    
        do {
            displayBoard(square);
            if (wasValid) {
                player = 3 - player; // This is a way to toggle between 1 and 2.
            } else {
                printf("Invalid Move! "); // Don't wait for a key
            }
            mark = player == 1 ? 'X' : 'O';
            printf("Player %d, enter a number: ", player);
            scanf("%d", &choice);
            wasValid = markBoard(square, mark, choice); // Get the result
            gameStatus = checkForWin(square); // Must call the function!
        } while (gameStatus == -1);
    
        displayBoard(square); // Also display the board at the end
        if (gameStatus == 1) {
            printf("==> Player %d wins!", player);
        } else {
            printf("==> It's a draw!");
        }
    }