Crear un juego de 2048 en C

CBeginner
Practicar Ahora

Introducción

2048 es un popular juego de rompecabezas numérico en el que el objetivo es alcanzar la ficha de 2048 combinando fichas adyacentes con el mismo número. En este proyecto, aprenderás cómo crear un sencillo juego de 2048 en C. Proporcionaremos instrucciones paso a paso para construir el juego, desde la inicialización del tablero hasta la implementación de la lógica del juego y la ejecución del juego.

👀 Vista previa

2048 Game

🎯 Tareas

En este proyecto, aprenderás:

  • Cómo crear los archivos del proyecto
  • Cómo definir constantes para el juego
  • Cómo implementar la función main() para ejecutar el bucle del juego
  • Cómo inicializar el tablero del juego
  • Cómo implementar funciones para comprobar el estado del juego
  • Cómo crear la lógica para mover las fichas
  • Cómo mostrar el tablero del juego
  • Cómo compilar y probar el juego

🏆 Logros

Después de completar este proyecto, podrás:

  • Crear un programa en C para un juego
  • Usar matrices para representar el tablero del juego
  • Implementar la lógica del juego para combinar fichas
  • Mostrar el tablero del juego
  • Manejar la entrada del jugador
  • Comprobar las condiciones de fin de juego y victoria

Crear los archivos del proyecto

Primero, crea un nuevo archivo llamado 2048.c y ábrelo en tu editor de código preferido.

cd ~/project
touch 2048.c
✨ Revisar Solución y Practicar

Definir constantes

Primero, necesitamos escribir el código en C. El primer paso es incluir los archivos de cabecera:

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

Antes de escribir la función main(), completemos algunas tareas básicas para definir algunas constantes:

#define SIZE 4
#define WIN_SCORE 2048

int board[SIZE][SIZE];
int score = 0;
✨ Revisar Solución y Practicar

Función 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 llamada a función inicializa el tablero del juego, establece todas las celdas del tablero en 0 y luego genera dos números aleatorios iniciales (2 o 4) en ubicaciones aleatorias.
  • print_board(): Esta función se utiliza para mostrar el estado actual del tablero del juego, incluyendo la puntuación actual y el número en cada celda.
  • while (1): Este es un bucle infinito que mantendrá en ejecución el juego hasta que se cumpla la condición de finalización del juego.

El flujo principal del juego 2048 se implementa aquí, incluyendo la inicialización del tablero del juego, el movimiento de los bloques de números, la evaluación de la victoria o derrota del juego y la espera de la entrada del jugador para controlar el progreso del juego.

✨ Revisar Solución y Practicar

Inicializar el tablero del juego

Para inicializar el tablero del juego, crearemos una función init_board que configura el tablero y genera dos números aleatorios iniciales.

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;
    }
}

Lo que hace esta función es, al comienzo del juego, vaciar todas las celdas del tablero del juego y generar dos bloques de números aleatorios iniciales en ubicaciones aleatorias, proporcionando al jugador un estado inicial del juego.

✨ Revisar Solución y Practicar

Implementar funciones para verificar el estado del juego

Necesitamos funciones para verificar si el jugador ha ganado, si el tablero está lleno y si quedan movimientos válidos. Aquí están las funciones:

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 función se utiliza para verificar si el tablero está lleno, es decir, si todas las celdas están ocupadas.

  • int is_won(): Esta función verifica si el jugador ha ganado, es decir, si hay una celda con el valor de WIN_SCORE (generalmente 2048) para ganar el juego.

  • int can_move(): Esta función se utiliza para verificar si todavía quedan movimientos válidos para garantizar que el juego pueda continuar.

✨ Revisar Solución y Practicar

Crear la lógica para mover las fichas

Implementa la lógica para mover las fichas en la función move. Esta función maneja los movimientos del jugador en cuatro direcciones: arriba, abajo, izquierda y derecha. También verifica si el movimiento es válido, actualiza la puntuación y genera un nuevo número aleatorio.

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;
    }
}
  • Primero, se define una variable moved para marcar si se ha producido un movimiento. El valor inicial es 0, lo que indica que no se ha producido ningún movimiento. Se crea una matriz bidimensional temporal llamada prev_board para almacenar el estado actual del tablero del juego, de modo que se pueda restaurar al estado anterior si el movimiento falla.

  • Se copia el estado actual del tablero del juego a prev_board, guardando el estado actual del tablero del juego en caso de que el movimiento falle.

  • Según el valor del parámetro dir (0 para arriba, 1 para abajo, 2 para izquierda, 3 para derecha), se realiza la operación de movimiento correspondiente.

  • Si se produce un movimiento o una fusión, la bandera moved se establece en 1, lo que indica que el estado del juego ha cambiado. Si la operación de movimiento o fusión se realiza con éxito, se genera un nuevo número aleatorio, que se utiliza para generar un nuevo bloque de números en un espacio en blanco del tablero del juego. Finalmente, si se produce un movimiento (moved es 1), la función devuelve 1, lo que indica que el movimiento fue exitoso. Si el movimiento falla (no hay operación de movimiento o fusión), el tablero del juego se restaura a su estado anterior, devolviendo 0, lo que indica que el movimiento falló.

✨ Revisar Solución y Practicar

Mostrar el tablero del juego

Para mostrar el tablero del juego, crearemos una función print_board que limpia la terminal y muestra el estado actual del tablero.

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");
    }
}

El objetivo principal aquí es limpiar la pantalla de la terminal, luego mostrar la puntuación actual en la parte superior de la pantalla y, a continuación, imprimir el estado del tablero del juego en la pantalla para que el jugador pueda ver claramente los bloques de números en el tablero del juego y la puntuación. Esto ayuda a proporcionar una interfaz amigable para el usuario que permita a los jugadores conocer el estado actual del juego.

✨ Revisar Solución y Practicar

Compilar y probar

Ingrese el siguiente comando en la terminal para compilar y ejecutar:

cd ~/project
gcc -o 2048 2048.c
./2048
2048 Game
✨ Revisar Solución y Practicar

Resumen

En este proyecto, has aprendido cómo crear un juego básico de 2048 en C. Has inicializado el tablero del juego, implementado funciones para verificar el estado del juego, creado la lógica para mover las fichas, mostrado el tablero del juego y ejecutado el juego. ¡Diviértete jugando y mejorando aún más tu juego de 2048!