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

🎯 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
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;
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.
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.
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 deWIN_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.
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
movedpara marcar se um movimento ocorreu. O valor inicial é 0, indicando que nenhum movimento ocorreu. Crie uma matriz 2D temporária chamadaprev_boardpara 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.
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.
Compilar e Testar
Insira o seguinte comando no terminal para compilar e executar:
cd ~/project
gcc -o 2048 2048.c
./2048

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!



