Criando um Jogo 2048 em C

CBeginner
Pratique Agora

Introdução

2048 é um popular jogo de quebra-cabeça numérico onde o objetivo é alcançar o bloco 2048 fundindo blocos adjacentes com o mesmo número. Neste projeto, você aprenderá como criar um jogo 2048 simples em C. Forneceremos instruções passo a passo para construir o jogo, desde a inicialização do tabuleiro até a implementação da lógica do jogo e a execução do jogo.

👀 Pré-visualização

2048 Game

🎯 Tarefas

Neste projeto, você aprenderá:

  • Como criar os arquivos do projeto
  • Como definir constantes para o jogo
  • Como implementar a função main() para executar o loop do jogo
  • Como inicializar o tabuleiro do jogo
  • Como implementar funções para verificar o estado do jogo
  • Como criar a lógica para mover os blocos
  • Como exibir o tabuleiro do jogo
  • Como compilar e testar o jogo

🏆 Conquistas

Após concluir este projeto, você será capaz de:

  • Criar um programa em C para um jogo
  • Usar arrays para representar o tabuleiro do jogo
  • Implementar a lógica do jogo para fundir blocos
  • Exibir o tabuleiro do jogo
  • Lidar com a entrada do jogador
  • Verificar as condições de fim de jogo e vitória

Criar os Arquivos do Projeto

Primeiro, crie um novo arquivo chamado 2048.c e abra-o no seu editor de código preferido.

cd ~/project
touch 2048.c
✨ Verificar Solução e Praticar

Definir Constantes

Primeiro, precisamos escrever o código C. O primeiro passo é incluir os arquivos de cabeçalho:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

Antes de escrever a função main(), vamos completar algumas tarefas básicas para definir algumas constantes:

#define SIZE 4
#define WIN_SCORE 2048

int board[SIZE][SIZE];
int score = 0;
✨ Verificar Solução e Praticar

Função main

int main() {
    srand(time(NULL));
    init_board();
    print_board();

    while (1) {
        if (is_won()) {
            printf("You won!\n");
            break;
        }

        if (is_full() && !can_move()) {
            printf("Game over!\n");
            break;
        }

        int direction;
        printf("Enter the move direction (0-Up, 1-Down, 2-Left, 3-Right): ");
        scanf("%d", &direction);

        if (move(direction)) {
            // Print the board after the move
            print_board();
        }
    }

    return 0;
}
  • init board(): Esta chamada de função inicializa o tabuleiro do jogo, define todas as células do tabuleiro do jogo como 0 e, em seguida, gera dois números aleatórios iniciais (2 ou 4) em locais aleatórios.
  • print_board(): Esta função é usada para exibir o estado atual do tabuleiro do jogo, incluindo a pontuação atual e o número em cada célula.
  • while (1): Este é um loop infinito que continuará executando o jogo até que a condição de término do jogo seja atendida.

O fluxo principal do jogo 2048 é implementado aqui, incluindo a inicialização do tabuleiro do jogo, a movimentação dos blocos de números, o julgamento da vitória ou derrota do jogo e a espera pela entrada do jogador para controlar o progresso do jogo.

✨ Verificar Solução e Praticar

Inicializar o Tabuleiro do Jogo

Para inicializar o tabuleiro do jogo, criaremos uma função init_board que configura o tabuleiro e gera dois números aleatórios iniciais.

void init_board() {
    // Initialize the board by setting all cells to 0
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            board[i][j] = 0;
        }
    }

    // Generate two initial random numbers
    for (int k = 0; k < 2; k++) {
        int i = rand() % SIZE;
        int j = rand() % SIZE;
        int value = (rand() % 2 + 1) * 2; // Generate 2 or 4 randomly
        board[i][j] = value;
    }
}

O que esta função faz é, no início do jogo, esvaziar todas as células do tabuleiro do jogo e gerar dois blocos de números aleatórios iniciais em locais aleatórios, fornecendo ao jogador um estado inicial do jogo.

✨ Verificar Solução e Praticar

Implementar Funções para Verificar o Estado do Jogo

Precisamos de funções para verificar se o jogador venceu, se o tabuleiro está cheio e se restam movimentos válidos. Aqui estão as funções:

int is_full() {
    // Check if the board is full
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (board[i][j] == 0) {
                return 0; // Board is not full
            }
        }
    }
    return 1; // Board is full
}

int is_won() {
    // Check if the player has won
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (board[i][j] == WIN_SCORE) {
                return 1; // Player has won
            }
        }
    }
    return 0; // Player has not won
}

int can_move() {
    // Check if there are any valid moves left
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (board[i][j] == 0) {
                return 1; // There are still empty cells to move
            }
            if (j > 0 && board[i][j] == board[i][j - 1]) {
                return 1; // Can move left
            }
            if (j < SIZE - 1 && board[i][j] == board[i][j + 1]) {
                return 1; // Can move right
            }
            if (i > 0 && board[i][j] == board[i - 1][j]) {
                return 1; // Can move up
            }
            if (i < SIZE - 1 && board[i][j] == board[i + 1][j]) {
                return 1; // Can move down
            }
        }
    }
    return 0; // No valid moves left
}
  • int is_full(): Esta função é usada para verificar se o tabuleiro está cheio, ou seja, se todas as células estão ocupadas.

  • int is_won(): Esta função verifica se o jogador venceu, ou seja, se existe uma célula com o valor de WIN_SCORE (geralmente 2048) para a vitória no jogo.

  • int can_move(): Esta função é usada para verificar se ainda existem movimentos válidos para garantir que o jogo possa continuar.

✨ Verificar Solução e Praticar

Criar a Lógica para Mover as Peças

Implemente a lógica para mover as peças na função move. Esta função lida com o movimento do jogador em quatro direções: para cima, para baixo, para a esquerda e para a direita. Ela também verifica se o movimento é válido, atualiza a pontuação e gera um novo número aleatório.

int move(int dir) {
    int moved = 0;

    // Store the current board state
    int prev_board[SIZE][SIZE];
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            prev_board[i][j] = board[i][j];
        }
    }

    // Move upwards
    if (dir == 0) {
        for (int j = 0; j < SIZE; j++) {
            for (int i = 1; i < SIZE; i++) {
                if (board[i][j] != 0) {
                    int k = i;
                    while (k > 0 && board[k - 1][j] == 0) {
                        board[k - 1][j] = board[k][j];
                        board[k][j] = 0;
                        k--;
                        moved = 1;
                    }
                    if (k > 0 && board[k - 1][j] == board[k][j]) {
                        board[k - 1][j] *= 2;
                        score += board[k - 1][j];
                        board[k][j] = 0;
                        moved = 1;
                    }
                }
            }
        }
    }

    // Move downwards
    else if (dir == 1) {
        for (int j = 0; j < SIZE; j++) {
            for (int i = SIZE - 2; i >= 0; i--) {
                if (board[i][j] != 0) {
                    int k = i;
                    while (k < SIZE - 1 && board[k + 1][j] == 0) {
                        board[k + 1][j] = board[k][j];
                        board[k][j] = 0;
                        k++;
                        moved = 1;
                    }
                    if (k < SIZE - 1 && board[k + 1][j] == board[k][j]) {
                        board[k + 1][j] *= 2;
                        score += board[k + 1][j];
                        board[k][j] = 0;
                        moved = 1;
                    }
                }
            }
        }
    }

    // Move left
    else if (dir == 2) {
        for (int i = 0; i < SIZE; i++) {
            for (int j = 1; j < SIZE; j++) {
                if (board[i][j] != 0) {
                    int k = j;
                    while (k > 0 && board[i][k - 1] == 0) {
                        board[i][k - 1] = board[i][k];
                        board[i][k] = 0;
                        k--;
                        moved = 1;
                    }
                    if (k > 0 && board[i][k - 1] == board[i][k]) {
                        board[i][k - 1] *= 2;
                        score += board[i][k - 1];
                        board[i][k] = 0;
                        moved = 1;
                    }
                }
            }
        }
    }

    // Move right
    else if (dir == 3) {
        for (int i = 0; i < SIZE; i++) {
            for (int j = SIZE - 2; j >= 0; j--) {
                if (board[i][j] != 0) {
                    int k = j;
                    while (k < SIZE - 1 && board[i][k + 1] == 0) {
                        board[i][k + 1] = board[i][k];
                        board[i][k] = 0;
                        k++;
                        moved = 1;
                    }
                    if (k < SIZE - 1 && board[i][k + 1] == board[i][k]) {
                        board[i][k + 1] *= 2;
                        score += board[i][k + 1];
                        board[i][k] = 0;
                        moved = 1;
                    }
                }
            }
        }
    }

    // Check if the move was successful
    if (moved) {
        // Generate a new random number
        int i = rand() % SIZE;
        int j = rand() % SIZE;
        while (board[i][j] != 0) {
            i = rand() % SIZE;
            j = rand() % SIZE;
        }
        board[i][j] = (rand() % 2 + 1) * 2; // Generate 2 or 4

        // Print the board after the move
        print_board();
    }

    // Check if the move was successful
    if (moved) {
        return 1;
    } else {
        // If the move failed, restore the previous board state
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                board[i][j] = prev_board[i][j];
            }
        }
        return 0;
    }
}
  • Primeiro, defina uma variável moved para marcar se um movimento ocorreu. O valor inicial é 0, indicando que nenhum movimento ocorreu. Crie uma matriz 2D temporária chamada prev_board para armazenar o estado atual do tabuleiro do jogo, para que ele possa ser restaurado ao estado anterior se o movimento falhar.

  • Copie o estado atual do tabuleiro do jogo para prev_board, salvando o estado atual do tabuleiro do jogo em caso de falha no movimento.

  • De acordo com o valor do parâmetro dir (0 para cima, 1 para baixo, 2 para a esquerda, 3 para a direita), a operação de movimento correspondente é executada.

  • Se um movimento ou mesclagem ocorrer, a flag moved é definida como 1, indicando que o estado do jogo foi alterado. Se a operação de movimento ou mesclagem for executada com sucesso, um novo número aleatório é gerado, que é usado para gerar um novo bloco de números em um ponto em branco no tabuleiro do jogo. Finalmente, se um movimento ocorrer (moved é 1), a função retorna 1, indicando que o movimento foi bem-sucedido. Se o movimento falhar (não houver operação de movimento ou mesclagem), o tabuleiro do jogo é restaurado ao seu estado anterior, retornando 0, indicando que o movimento falhou.

✨ Verificar Solução e Praticar

Exibir o Tabuleiro do Jogo

Para exibir o tabuleiro do jogo, criaremos uma função print_board que limpa o terminal e imprime o estado atual do tabuleiro.

void print_board() {
    // Clear the terminal
    system("clear");

    printf("Score: %d\n", score);

    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            printf("%4d", board[i][j]);
        }
        printf("\n");
    }
}

O objetivo principal aqui é limpar a tela do terminal, exibir a pontuação atual no topo da tela e, em seguida, imprimir o status do tabuleiro do jogo na tela para que o jogador possa ver claramente os blocos de números no tabuleiro e a pontuação. Isso ajuda a fornecer uma interface amigável que permite aos jogadores saber o estado atual do jogo.

✨ Verificar Solução e Praticar

Compilar e Testar

Insira o seguinte comando no terminal para compilar e executar:

cd ~/project
gcc -o 2048 2048.c
./2048
2048 Game
✨ Verificar Solução e Praticar

Resumo

Neste projeto, você aprendeu como criar um jogo 2048 básico em C. Você inicializou o tabuleiro do jogo, implementou funções para verificar o estado do jogo, criou a lógica para mover as peças, exibiu o tabuleiro do jogo e executou o jogo. Divirta-se jogando e aprimorando seu jogo 2048 ainda mais!