Создание простой игры в крестики-нолики на языке C

CCBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом проекте вы научитесь создавать простую игру "Крестики-нолики" на языке C. Игра будет проводиться между двумя игроками, которые по очереди ставят свои знаки в клетки 3x3 игровой сетки. Игрок, первым выстроивший три своих знака в ряд, по столбцу или по диагонали, побеждает. Если все клетки заполнены, а ни один игрок не выстроил три знака в ряд, игра заканчивается вничью.

👀 Предпросмотр

Предпросмотр игры "Крестики-нолики"

🎯 Задачи

В этом проекте вы научитесь:

  • Создавать игровое поле и инициализировать его пустыми клетками.
  • Реализовывать функции для очистки экрана, отображения игрового поля и проверки окончания игры.
  • Определять победителя игры.
  • Реализовывать основной игровой цикл, позволяющий игрокам по очереди взаимодействовать с игрой.

🏆 Достижения

После завершения этого проекта вы сможете:

  • Создавать и манипулировать массивами на языке C.
  • Использовать циклы и условные операторы для реализации игровой логики.
  • Взаимодействовать с пользователем через командную строку.
  • Организовывать код в функции для лучшей модульности и читаемости.

Создание файлов проекта

Сначала создайте новый файл с именем tictactoe.c и откройте его в вашем любимом текстовом редакторе кода.

cd ~/project
touch tictactoe.c
✨ Проверить решение и практиковаться

Определение констант

Теперь нам нужно написать код на языке C. Первым шагом является включение заголовочных файлов:

#include <stdio.h>
#include <stdbool.h>

Объявите размер игрового поля:

char board[3][3];
✨ Проверить решение и практиковаться

Инициализация игрового поля

Инициализируйте игровое поле пустыми клетками.

void initializeBoard() {
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            board[i][j] = ' ';
        }
    }
}

Первый цикл for является внешним и использует переменную i для итерации от 0 до 2, что представляет количество строк на игровом поле. Второй цикл for является внутренним и использует переменную j для итерации от 0 до 2, что представляет количество столбцов на игровом поле.

board[i][j] = ' 'Внутри внутреннего цикла устанавливает содержимое клетки в строке i и столбце j игрового поля в пробельный символ (' '). Это означает, что всё игровое поле инициализируется в пустое состояние, и ни один игрок ещё не сделал ходов на поле.

✨ Проверить решение и практиковаться

Очистка экрана после каждого хода

Реализуйте функцию для очистки экрана после каждого хода.

void clearScreen() {
    printf("\033[H\033[J");
}

Функция clearScreen в основном используется для очистки содержимого терминала или консольного экрана, чтобы удалить предыдущее состояние игры и вывод после каждого хода. Это достигается путем вывода специальной управляющей последовательности в стандартный поток вывода (обычно окно терминала), которая очищает текстовое содержимое на экране.

printf("\033[H\033[J") использует ANSI-управляющие последовательности:

  • \033 - это восьмеричное представление ASCII-символа Escape, которое обозначает начало последовательности.
  • [H указывает на перемещение курсора в левый верхний угол экрана, что эквивалентно позиционированию курсора в первую строку и первый столбец экрана.
  • [J означает очистку экрана. Она очистит все текстовое содержимое после текущей позиции курсора, включая предыдущее состояние игры и вывод.

Это означает, что каждый раз, когда вызывается функция clearScreen, она очистит содержимое текущего терминала или консольного экрана, чтобы отобразить новое состояние игры перед следующим игровым сеансом, что обеспечивает более чистый интерфейс.

✨ Проверить решение и практиковаться

Отображение игрового поля

Создайте функцию printBoard для визуального отображения текущего состояния игрового поля на экране, чтобы игрок мог понять расположение фигур на поле. Это часть игры, используемая в играх в крестики-нолики для отображения состояния игры.

void printBoard() {
    printf("\n");
    printf("  1   2   3\n");
    printf("1 %c | %c | %c\n", board[0][0], board[0][1], board[0][2]);
    printf("  ---------\n");
    printf("2 %c | %c | %c\n", board[1][0], board[1][1], board[1][2]);
    printf("  ---------\n");
    printf("3 %c | %c | %c\n", board[2][0], board[2][1], board[2][2]);
}
✨ Проверить решение и практиковаться

Проверка окончания игры

Реализуйте функцию isGameOver, которая определяет, закончилась ли игра в крестики-нолики, и возвращает булево значение (true или false).

bool isGameOver() {
    // Check rows
    for (int i = 0; i < 3; i++) {
        if (board[i][0]!= ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
            return true;
        }
    }

    // Check columns
    for (int j = 0; j < 3; j++) {
        if (board[0][j]!= ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
            return true;
        }
    }

    // Check diagonals
    if (board[0][0]!= ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
        return true;
    }
    if (board[0][2]!= ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
        return true;
    }

    // Check for a draw
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[i][j] == ' ') {
                return false;
            }
        }
    }

    return true;
}

Проверка наличия победителя в строке: Если все три клетки в строке заняты одним и тем же игроком, то есть победитель, и функция возвращает true.

Проверка наличия победителя в столбце: Если все три клетки в столбце заняты одним и тем же игроком, то есть победитель, и функция возвращает true.

Проверка наличия победителя по диагонали: Если все три клетки на любой диагонали заняты одним и тем же игроком, то есть победитель, и функция возвращает true.

Проверка на ничью: Наконец, функция проверяет, все ли клетки заняты, но при этом нет победителя. Если это так, игра заканчивается вничью, и функция возвращает true.

✨ Проверить решение и практиковаться

Определение победителя

Определена функция с именем getWinner, которая предназначена для определения, есть ли победитель в игре в крестики-нолики, и возврата флага победителя (X или O), или пробела (' '), если победителя нет.

char getWinner() {
    // Check rows
    for (int i = 0; i < 3; i++) {
        if (board[i][0]!= ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
            return board[i][0];
        }
    }

    // Check columns
    for (int j = 0; j < 3; j++) {
        if (board[0][j]!= ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
            return board[0][j];
        }
    }

    // Check diagonals
    if (board[0][0]!= ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
        return board[0][0];
    }
    if (board[0][2]!= ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
        return board[0][2];
    }

    return ' '; // No winner
}
✨ Проверить решение и практиковаться

Реализация основного игрового цикла

Напишите основной игровой цикл, чтобы игроки могли по очереди делать ходы и взаимодействовать с игрой.

int main() {
    initializeBoard();
    int currentPlayer = 1;

    while (1) {
        clearScreen();
        printf("Current board state:\n");
        printBoard();

        int row, col;
        printf("Player %d, please enter a row and column (e.g., 1 2):", currentPlayer);

        while (scanf("%d %d", &row, &col)!= 2) {
            printf("Invalid input, please try again: ");
            while (getchar()!= '\n');
        }

        if (row < 1 || row > 3 || col < 1 || col > 3 || board[row - 1][col - 1]!= ' ') {
            printf("Invalid move, please try again.\n");
        } else {
            if (currentPlayer == 1) {
                board[row - 1][col - 1] = 'X';
                currentPlayer = 2;
            } else {
                board[row - 1][col - 1] = 'O';
                currentPlayer = 1;
            }
        }

        if (isGameOver()) {
            clearScreen();
            printf("Game over!\n");
            printBoard();
            char winner = getWinner();
            if (winner!= ' ') {
                printf("Player %c wins!\n", winner);
            } else {
                printf("It's a draw!\n");
            }
            break;
        }
    }

    return 0;
}

Вызовите функцию initializeBoard для инициализации игрового поля, установив все клетки в пробелы.

Создайте целочисленную переменную currentPlayer, чтобы отслеживать, чей сейчас ход. Изначально она установлена в 1, что означает игрока 1.

В основном цикле while (1):

  • Вызовите функцию clearScreen для очистки экрана, чтобы экран обновлялся после каждого хода.
  • Вызовите функцию printBoard для вывода текущего состояния игрового поля.

Ввод игрока: Предложите текущему игроку ввести координаты строки и столбца с помощью функции scanf, например, Player 1, please enter a row and column (e.g., 1 2): . Если ввод некорректен (не два целых числа), выводится сообщение Invalid input, please try again: , и буфер ввода очищается до тех пор, пока не будет получен корректный ввод.

Проверка ввода: Затем проверьте, что введенные координаты строки и столбца корректны (в диапазоне от 1 до 3) и что выбранная позиция является пустой. Если ввод некорректен или выбранная позиция уже занята, выводится сообщение Invalid move, please try again., и игроку предлагается ввести данные снова.

Ход: Если ввод корректен, код ставит X или O в соответствующую позицию на игровом поле в зависимости от текущего игрока (1 или 2).

Проверка окончания игры: Затем код вызывает функцию isGameOver(), чтобы проверить, закончилась ли игра. Если игра закончилась, экран очищается, и выводится сообщение о конце игры, включая победителя (если он есть) или ничью.

Результат игры: Если есть победитель, код выведет Player X wins! или Player O wins!, в зависимости от того, какой игрок победил. Если победителя нет, код выведет It's a draw!, что означает ничью.

✨ Проверить решение и практиковаться

Компиляция и запуск проекта

Выполните команду gcc для компиляции:

cd ~/project
gcc -o tictactoe tictactoe.c
./tictactoe
Демонстрация игры в крестики-нолики
✨ Проверить решение и практиковаться

Итог

Поздравляем! Вы успешно создали простую игру в крестики-нолики на языке C. Игроки могут по очереди делать свои ходы и ставить свои метки на 3x3 игровом поле. После каждого хода игра проверяет наличие победителя или ничьей и соответствующим образом выводит результат. Этот простой проект демонстрирует основные концепции программирования на языке C и может служить отправной точкой для разработки более сложных игр.