Lenguaje de programación C

Arqueros en el bosque con C – Programación de juego

Hoy te mostraré la solución a un ejercicio propuesto en C que trata sobre hacer el juego de Arqueros en el bosque.

El juego consiste de un bosque representado mediante una cuadrícula, como el que se presenta a continuación (la vista es aérea):

Cuadrícula de bosque para arqueros – Juego programado en C

Donde X representa un árbol. El objetivo del juego consiste en que 2 jugadores (arqueros) ingresan al bosque por extremos opuestos (J1 en 9-A y J2 en 1-I) y cada uno debe de intentar eliminar al otro.

Para ello en cada turno un arquero podrá elegir entre moverse un lugar o disparar un flechazo. Los flechazos se mueven en línea recta, una distancia máxima de 5 metros (asuma cada celda 1 metro).

Por otro lado un jugador no puede moverse a una celda donde hay un árbol y las flechas no pueden traspasarlos. Al recibir un flechazo el jugador pierde el round, las partidas se componen de 3 round y las gana el que gane 2 de ellos.

Veamos entonces cómo hacer este juego en ANSI C, C, C 99 o como le quieras llamar. Obviamente es compatible con C++.

Funcionamiento general del juego

Todo el escenario del juego (el bosque) será una matriz cuadrada. Vamos a tener métodos que recibirán el bosque como argumento.

Las funciones que tendremos son varias, entre ellas las que limpian el bosque, colocan los árboles (generando coordenadas aleatorias) y colocan los jugadores.

Igualmente vamos a tener algunas variables que llevarán la ubicación de los jugadores así como los flechazos recibidos. Lo más interesante será simular el disparo de la flecha y comprobar si no colisiona con un árbol, o si termina su recorrido sin impactar.

Preparando el bosque

Al inicio de todo debemos limpiar el bosque y colocar los árboles así:

void limpiar_bosque(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    for (int y = 0; y < medidaGlobalBosque; y++) {
        for (int x = 0; x < medidaGlobalBosque; x++) {
            bosque[y][x] = ESPACIO_VACIO;
        }
    }
}

void colocar_arboles_en_bosque(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    int contador = 0;
    while (contador < medidaGlobalBosque) {
        int x = aleatorio_en_rango(0, medidaGlobalBosque - 1);
        int y = aleatorio_en_rango(0, medidaGlobalBosque - 1);
        // Colocar árboles solo si no hay nada (en especial no debemos colocarlos en donde haya jugadores)
        if (bosque[y][x] == ESPACIO_VACIO) {
            bosque[y][x] = ARBOL;
            contador++;
        }
    }
}

Como puedes observar, se coloca la misma cantidad de árboles que la medida del tablero. Todos son colocados de manera aleatoria y en espacios vacíos.

Por cierto, tenemos una función que va a imprimir el bosque, imprimiendo encabezados, letras, números, etcétera:

void imprimir_bosque(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    int contador = 0;
    // Un separador
    printf("\n\n");
    while (contador < medidaGlobalBosque) {
        printf("_____");
        contador++;
    }
    printf("\n");
    // Letras
    printf("|   ");
    char c = 'A';
    contador = 0;
    while (contador < medidaGlobalBosque) {
        printf("| %c ", c);
        c++;
        contador++;
    }
    printf("|\n");
    for (int y = 0; y < medidaGlobalBosque; y++) {
        printf("| %d |", y + 1);
        for (int x = 0; x < medidaGlobalBosque; x++) {
            printf(" %c |", bosque[y][x]);
        }
        printf("\n");
    }
}

Puedes ver la salida de esa función en la imagen que está al inicio de este post.

Solicitando datos

Solicitar tamaño de tablero, jugador y movimiento – Arqueros en el bosque con C

Veamos las funciones que van a preguntar el tamaño del tablero, solicitar direcciones, dar a elegir entre disparar o moverse, etcétera.

Para solicitar la dirección hacia donde se mueve el personaje usamos un ciclo while hasta que la dirección sea correcta. Este patrón se va a repetir en los siguientes métodos:

char solicitarMovimientoJugador(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador) {
    char direccion = DIRECCION_IZQUIERDA;
    while (1) {
        printf("%c - Arriba\n%c - Abajo\n%c - Derecha\n%c - Izquierda\nJugador %c. Ingrese la direccion para moverse: ",
               DIRECCION_ARRIBA, DIRECCION_ABAJO, DIRECCION_DERECHA, DIRECCION_IZQUIERDA, jugador);
        scanf("%c", &direccion);
        consumir_nueva_linea();
        if (jugador_puede_moverse(bosque, jugador, direccion)) {
            return direccion;
        } else {
            printf("No se puede mover hacia esa direccion. Intente de nuevo\n");
        }
    }
}

Por ejemplo, para solicitar la dirección del disparo:

char solicitarDireccionDisparo(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador) {
    char direccion = DIRECCION_IZQUIERDA;
    while (1) {
        printf("%c - Arriba\n%c - Abajo\n%c - Derecha\n%c - Izquierda\nJugador %c. Ingrese la direccion para disparar: ",
               DIRECCION_ARRIBA, DIRECCION_ABAJO, DIRECCION_DERECHA, DIRECCION_IZQUIERDA, jugador);
        scanf("%c", &direccion);
        consumir_nueva_linea();
        if (esDireccionValida(direccion)) {
            return direccion;
        } else {
            printf("No puede disparar en esa direccion. Intente de nuevo\n");
        }
    }
}

La medida del bosque:

int solicitarMedida() {
    int medida;
    while (1) {
        printf("Ingrese la medida del bosque. Puede ser de 5, 7 o 9: ");
        scanf("%d", &medida);
        consumir_nueva_linea();
        if (medida == 5 || medida == 7 || medida == 9) {
            return medida;
        } else {
            printf("La medida no es correcta\n");
        }
    }
}

Por cierto, como puedes ver la medida debe ser de 5, 7 o 9. Técnicamente puede ser de cualquier longitud, ya solo haría falta ajustar las letras para que cuando se llegue a la Z se reinicie o algo así.

Finalmente veamos la que pregunta cuál arquero inicia el juego y también la que pregunta si el jugador desea disparar o moverse.

char solicitar_jugador() {
    char jugador = ARQUERO_J1;
    while (1) {
        printf("%c - Arquero 1\n%c - Arquero 2. Seleccione quien empieza la partida: ", ARQUERO_J1, ARQUERO_J2);
        scanf("%c", &jugador);
        consumir_nueva_linea();
        if (jugador == ARQUERO_J1 || jugador == ARQUERO_J2) {
            return jugador;
        } else {
            printf("Jugador incorrecto\n");
        }
    }
}

char solicitar_eleccion(char jugador) {
    char eleccion = ' ';
    while (1) {
        printf("%c - Mover\n%c - Disparar. Jugador %c seleccione una opcion: ", OPCION_MOVER, OPCION_DISPARAR, jugador);
        scanf("%c", &eleccion);
        consumir_nueva_linea();
        if (eleccion == OPCION_MOVER || eleccion == OPCION_DISPARAR) {
            return eleccion;
        } else {
            printf("La opcion no es correcta\n");
        }
    }
}

Nueva partida y nuevo round

Iniciar nuevo round cuando el flechazo es acertado

Cada que haya una nueva partida o un nuevo round vamos a reiniciar ciertas variables. Por ejemplo, cuando se inicia un nuevo round, los jugadores se vuelven a colocar en las posiciones iniciales y los árboles se colocan aleatoriamente, pero los contadores de flechazos recibidos no se reinician.

void preparar_nuevo_round(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    limpiar_bosque(bosque);
    reiniciar_posicion_arqueros();
    colocar_jugadores_en_bosque(bosque);
    colocar_arboles_en_bosque(bosque);
}

void preparar_nueva_partida(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    preparar_nuevo_round(bosque);
    flechazos_recibidos_jugador_1 = 0;
    flechazos_recibidos_jugador_2 = 0;
}

En cambio, cuando se inicia una nueva partida se reinician los contadores de flechazos recibidos.

Mover jugador

Mover jugador en el bosque – Juego programado en C

Veamos uno de las dos funciones más importantes del juego arqueros en el bosque programado en C. En este caso para mover al jugador tenemos dos funciones: una que verifica si puede moverse y otra que mueve al jugador.

Recuerda que en este caso tenemos las coordenadas de manera global en variables separadas, por eso es que las modificamos:

int jugador_puede_moverse(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador, char direccion) {
    int x, y;
    if (jugador == ARQUERO_J1) {
        x = coordenada_x_jugador_1;
        y = coordenada_y_jugador_1;
    } else {
        x = coordenada_x_jugador_2;
        y = coordenada_y_jugador_2;
    }
    if (direccion == DIRECCION_ABAJO) {
        y++;
    } else if (direccion == DIRECCION_ARRIBA) {
        y--;
    } else if (direccion == DIRECCION_DERECHA) {
        x++;
    } else if (direccion == DIRECCION_IZQUIERDA) {
        x--;
    } else {
        // Si no es ninguna dirección válida, regresamos false
        return 0;
    }
    // Luego de modificar las coordenadas, vemos si son válidas. Comprobamos si están dentro del tablero
    if (x < 0 || x >= medidaGlobalBosque || y < 0 || y >= medidaGlobalBosque) {
        printf("Posicion fuera del tablero\n");
        return 0;
    }
    // Y comprobamos si no es un árbol u otro jugador
    if (bosque[y][x] == ESPACIO_VACIO) {
        return 1;
    } else {
        printf("Espacio no vacio\n");
        return 0;
    }
}

void mover(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador, char direccion) {
    printf("El jugador %c elige moverse hacia ", jugador);
    int xOriginal, yOriginal;
    if (jugador == ARQUERO_J1) {
        xOriginal = coordenada_x_jugador_1;
        yOriginal = coordenada_y_jugador_1;
    } else {
        xOriginal = coordenada_x_jugador_2;
        yOriginal = coordenada_y_jugador_2;
    }
    int nuevaX = xOriginal, nuevaY = yOriginal;
    if (direccion == DIRECCION_ABAJO) {
        printf(" abajo\n");
        nuevaY++;
    } else if (direccion == DIRECCION_ARRIBA) {
        printf(" arriba\n");
        nuevaY--;
    } else if (direccion == DIRECCION_DERECHA) {
        printf(" la derecha\n");
        nuevaX++;
    } else if (direccion == DIRECCION_IZQUIERDA) {
        printf(" la izquierda\n");
        nuevaX--;
    }
    // Limpiamos la posición anterior
    bosque[yOriginal][xOriginal] = ESPACIO_VACIO;
    // Y movemos al personaje
    bosque[nuevaY][nuevaX] = jugador;
    // Guardamos las coordenadas
    if (jugador == ARQUERO_J1) {
        coordenada_x_jugador_1 = nuevaX;
        coordenada_y_jugador_1 = nuevaY;
    } else {
        coordenada_x_jugador_2 = nuevaX;
        coordenada_y_jugador_2 = nuevaY;
    }
}

Disparar

Disparar flecha en el bosque – Cazadores

La otra función de este juego programado en C es la de disparar. Disfruté mucho programando esta función imaginando el recorrido de la flecha en mi mente para identificar si choca con un árbol, una pared, el jugador o termina su recorrido.

int disparar(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador, char direccion) {
    printf("El jugador %c dispara la flecha ", jugador);
    int acertado;
    switch (direccion) {
        case DIRECCION_IZQUIERDA:
            printf("a la izquierda\n");
            acertado = dispararIzquierda(bosque, jugador);
            break;
        case DIRECCION_DERECHA:
            printf("a la derecha\n");
            acertado = dispararDerecha(bosque, jugador);
            break;
        case DIRECCION_ARRIBA:
            printf("hacia arriba\n");
            acertado = dispararArriba(bosque, jugador);
            break;
        case DIRECCION_ABAJO:
        default:
            printf("hacia abajo\n");
            acertado = dispararAbajo(bosque, jugador);
            break;
    }
    // Si acertó, entonces aumentamos el conteo de flechas recibidas para el oponente
    if (acertado) {
        if (oponente_de(jugador) == ARQUERO_J1) {
            flechazos_recibidos_jugador_1++;
        } else {
            flechazos_recibidos_jugador_2++;
        }
    }
    // Y devolvemos el estado por si nuestro invocador lo requiere
    return acertado;
}

Por cierto, esta función aumenta el contador de los flechazos dependiendo del oponente. Ya que si dispara el jugador 1 y acierta, es porque le ha disparado al jugador 2 y viceversa.

Juego en C

Iniciar juego de arqueros en el bosque

Una vez que hemos visto las funciones más importantes veamos cómo es el ciclo principal del juego.

Tenemos la función principal que solo debe ser invocada una vez, ahí se pregunta el tamaño del bosque y, cuando se acaba de jugar, pregunta al usuario si quiere iniciar una nueva partida.

void jugar() {
    srand(getpid());
    char eleccion = 's';
    while (eleccion == 's') {
        medidaGlobalBosque = solicitarMedida();
        iniciar_nueva_partida();
        printf("Jugar de nuevo? [s/n]: ");
        scanf("%c", &eleccion);
        consumir_nueva_linea();
    }
}

Después tenemos la función que inicia una nueva partida en donde solicitamos al jugador el movimiento, el arquero que inicia el juego, etcétera y comenzamos a jugar preguntando las opciones al usuario mientras vamos imprimiendo el bosque.

void iniciar_nueva_partida() {
    char bosque[medidaGlobalBosque][medidaGlobalBosque];
    preparar_nueva_partida(bosque);
    char jugador = solicitar_jugador();
    while (1) {
        colocar_jugadores_en_bosque(bosque);
        imprimir_bosque(bosque);
        char eleccion = solicitar_eleccion(jugador);
        if (eleccion == OPCION_DISPARAR) {
            char direccion_del_disparo = solicitarDireccionDisparo(bosque, jugador);
            int acertado = disparar(bosque, jugador, direccion_del_disparo);
            // Si ha acertado, marcamos nuevo round
            if (acertado) {
                printf("El flechazo es acertado!. Inicia nuevo round\n");
                imprimir_estados_salud();
                preparar_nuevo_round(bosque);
            }
        } else if (eleccion == OPCION_MOVER) {
            char direccion_del_movimiento = solicitarMovimientoJugador(bosque, jugador);
            mover(bosque, jugador, direccion_del_movimiento);
        }
        if (es_ganador(jugador)) {
            printf("El jugador %c gana!\n", jugador);
            // Con el return terminamos el juego
            return;
        }
        jugador = oponente_de(jugador);
    }
}

La partida termina cuando un jugador ha recibido dos flechazos:

int es_ganador(char jugador) {
    if (jugador == ARQUERO_J1) {
        return flechazos_recibidos_jugador_2 >= ROUNDS_PARA_PERDER;
    } else {
        return flechazos_recibidos_jugador_1 >= ROUNDS_PARA_PERDER;
    }
}

Y así es como funciona este juego de arqueros o cazadores en el bosque programado con puro C.

Poniendo todo junto

Ya te he explicado el funcionamiento general del juego, las funciones más importantes y todo lo demás. Pero ahora te dejo aquí con el código completo.

Recuerda que todos mis programas son configurables así que puedes cambiar tamaños de tablero, rounds, etcétera.

Además, mis juegos están escritos (al menos los de C) pensando en la portabilidad, por lo que puedes ejecutar esto en Dev C++, CLion, compilarlo con GCC, clang, g++, etcétera.

/*
  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me               /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
            | IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define ARQUERO_J1 '1'
#define ARQUERO_J2 '2'
#define ARBOL 'X'
#define ESPACIO_VACIO ' '
#define DIRECCION_IZQUIERDA 'L'
#define DIRECCION_DERECHA 'R'
#define DIRECCION_ARRIBA 'U'
#define DIRECCION_ABAJO 'D'
#define OPCION_MOVER 'm'
#define OPCION_DISPARAR 'd'
#define DISTANCIA_MAXIMA_DISPARO 5
#define ROUNDS_PARA_PERDER 2
/*
 * Ajustes del juego
 * */int medidaGlobalBosque; // La medida del bosque
/*
 * Datos del juego
 * Llevar control de ubicación de arqueros y de los flechazos recibidos
 * */int coordenada_y_jugador_1 = 0,
        coordenada_x_jugador_1 = 0,
        coordenada_y_jugador_2 = 0,
        coordenada_x_jugador_2 = 0,
        flechazos_recibidos_jugador_1 = 0,
        flechazos_recibidos_jugador_2 = 0;
/*
 * Funciones que componen al juego
 * */
// Devuelve un número aleatorio entre minimo y maximo, incluyendo a minimo y maximo
int aleatorio_en_rango(int minimo, int maximo) {
    return minimo + rand() / (RAND_MAX / (maximo - minimo + 1) + 1);
}

void limpiar_bosque(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    for (int y = 0; y < medidaGlobalBosque; y++) {
        for (int x = 0; x < medidaGlobalBosque; x++) {
            bosque[y][x] = ESPACIO_VACIO;
        }
    }
}

void colocar_arboles_en_bosque(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    int contador = 0;
    while (contador < medidaGlobalBosque) {
        int x = aleatorio_en_rango(0, medidaGlobalBosque - 1);
        int y = aleatorio_en_rango(0, medidaGlobalBosque - 1);
        // Colocar árboles solo si no hay nada (en especial no debemos colocarlos en donde haya jugadores)
        if (bosque[y][x] == ESPACIO_VACIO) {
            bosque[y][x] = ARBOL;
            contador++;
        }
    }
}

void colocar_jugadores_en_bosque(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    bosque[coordenada_y_jugador_1][coordenada_x_jugador_1] = ARQUERO_J1;
    bosque[coordenada_y_jugador_2][coordenada_x_jugador_2] = ARQUERO_J2;
}

void imprimir_bosque(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    int contador = 0;
    // Un separador
    printf("\n\n");
    while (contador < medidaGlobalBosque) {
        printf("_____");
        contador++;
    }
    printf("\n");
    // Letras
    printf("|   ");
    char c = 'A';
    contador = 0;
    while (contador < medidaGlobalBosque) {
        printf("| %c ", c);
        c++;
        contador++;
    }
    printf("|\n");
    for (int y = 0; y < medidaGlobalBosque; y++) {
        printf("| %d |", y + 1);
        for (int x = 0; x < medidaGlobalBosque; x++) {
            printf(" %c |", bosque[y][x]);
        }
        printf("\n");
    }
}

int jugador_puede_moverse(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador, char direccion) {
    int x, y;
    if (jugador == ARQUERO_J1) {
        x = coordenada_x_jugador_1;
        y = coordenada_y_jugador_1;
    } else {
        x = coordenada_x_jugador_2;
        y = coordenada_y_jugador_2;
    }
    if (direccion == DIRECCION_ABAJO) {
        y++;
    } else if (direccion == DIRECCION_ARRIBA) {
        y--;
    } else if (direccion == DIRECCION_DERECHA) {
        x++;
    } else if (direccion == DIRECCION_IZQUIERDA) {
        x--;
    } else {
        // Si no es ninguna dirección válida, regresamos false
        return 0;
    }
    // Luego de modificar las coordenadas, vemos si son válidas. Comprobamos si están dentro del tablero
    if (x < 0 || x >= medidaGlobalBosque || y < 0 || y >= medidaGlobalBosque) {
        printf("Posicion fuera del tablero\n");
        return 0;
    }
    // Y comprobamos si no es un árbol u otro jugador
    if (bosque[y][x] == ESPACIO_VACIO) {
        return 1;
    } else {
        printf("Espacio no vacio\n");
        return 0;
    }
}

void mover(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador, char direccion) {
    printf("El jugador %c elige moverse hacia ", jugador);
    int xOriginal, yOriginal;
    if (jugador == ARQUERO_J1) {
        xOriginal = coordenada_x_jugador_1;
        yOriginal = coordenada_y_jugador_1;
    } else {
        xOriginal = coordenada_x_jugador_2;
        yOriginal = coordenada_y_jugador_2;
    }
    int nuevaX = xOriginal, nuevaY = yOriginal;
    if (direccion == DIRECCION_ABAJO) {
        printf(" abajo\n");
        nuevaY++;
    } else if (direccion == DIRECCION_ARRIBA) {
        printf(" arriba\n");
        nuevaY--;
    } else if (direccion == DIRECCION_DERECHA) {
        printf(" la derecha\n");
        nuevaX++;
    } else if (direccion == DIRECCION_IZQUIERDA) {
        printf(" la izquierda\n");
        nuevaX--;
    }
    // Limpiamos la posición anterior
    bosque[yOriginal][xOriginal] = ESPACIO_VACIO;
    // Y movemos al personaje
    bosque[nuevaY][nuevaX] = jugador;
    // Guardamos las coordenadas
    if (jugador == ARQUERO_J1) {
        coordenada_x_jugador_1 = nuevaX;
        coordenada_y_jugador_1 = nuevaY;
    } else {
        coordenada_x_jugador_2 = nuevaX;
        coordenada_y_jugador_2 = nuevaY;
    }
}

void consumir_nueva_linea(void) {
    int c;
    do {
        c = getchar();
    } while (c != EOF && c != '\n');
}

char solicitarMovimientoJugador(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador) {
    char direccion = DIRECCION_IZQUIERDA;
    while (1) {
        printf("%c - Arriba\n%c - Abajo\n%c - Derecha\n%c - Izquierda\nJugador %c. Ingrese la direccion para moverse: ",
               DIRECCION_ARRIBA, DIRECCION_ABAJO, DIRECCION_DERECHA, DIRECCION_IZQUIERDA, jugador);
        scanf("%c", &direccion);
        consumir_nueva_linea();
        if (jugador_puede_moverse(bosque, jugador, direccion)) {
            return direccion;
        } else {
            printf("No se puede mover hacia esa direccion. Intente de nuevo\n");
        }
    }
}

int esDireccionValida(char direccion) {
    if (direccion == DIRECCION_ARRIBA || direccion == DIRECCION_DERECHA || direccion == DIRECCION_ABAJO ||
        direccion == DIRECCION_IZQUIERDA) {
        return 1;
    }
    return 0;
}

char solicitarDireccionDisparo(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador) {
    char direccion = DIRECCION_IZQUIERDA;
    while (1) {
        printf("%c - Arriba\n%c - Abajo\n%c - Derecha\n%c - Izquierda\nJugador %c. Ingrese la direccion para disparar: ",
               DIRECCION_ARRIBA, DIRECCION_ABAJO, DIRECCION_DERECHA, DIRECCION_IZQUIERDA, jugador);
        scanf("%c", &direccion);
        consumir_nueva_linea();
        if (esDireccionValida(direccion)) {
            return direccion;
        } else {
            printf("No puede disparar en esa direccion. Intente de nuevo\n");
        }
    }
}

char oponente_de(char jugador) {
    if (jugador == ARQUERO_J1) {
        return ARQUERO_J2;
    } else {
        return ARQUERO_J1;
    }
}

int dispararIzquierda(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador) {
    int x, y;
    if (jugador == ARQUERO_J1) {
        x = coordenada_x_jugador_1;
        y = coordenada_y_jugador_1;
    } else {
        x = coordenada_x_jugador_2;
        y = coordenada_y_jugador_2;
    }
    int metrosAvance = 0;
    while (metrosAvance < DISTANCIA_MAXIMA_DISPARO && x > 0) {
        x--;
        metrosAvance++;
        // La flecha impactó con un árbol, así que no llegó al objetivo
        if (bosque[y][x] == ARBOL) {
            printf("Flecha impacta con un arbol");
            return 0;
        } else if (bosque[y][x] == oponente_de(jugador)) {
            // Significa que le ha disparado al oponente
            printf("Flecha impacta al jugador %c\n", oponente_de(jugador));
            return 1;
        }
    }
    // La flecha acabó su recorrido y no chocó con un árbol ni con el oponente
    printf("Flecha ha terminado su recorrido");
    return 0;
}

int dispararDerecha(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador) {
    int x, y;
    if (jugador == ARQUERO_J1) {
        x = coordenada_x_jugador_1;
        y = coordenada_y_jugador_1;
    } else {
        x = coordenada_x_jugador_2;
        y = coordenada_y_jugador_2;
    }
    int metrosAvance = 0;
    while (metrosAvance < DISTANCIA_MAXIMA_DISPARO && x < medidaGlobalBosque - 1) {
        x++;
        metrosAvance++;
        // La flecha impactó con un árbol, así que no llegó al objetivo
        if (bosque[y][x] == ARBOL) {
            printf("Flecha impacta con un arbol");
            return 0;
        } else if (bosque[y][x] == oponente_de(jugador)) {
            // Significa que le ha disparado al oponente
            printf("Flecha impacta al jugador %c\n", oponente_de(jugador));
            return 1;
        }
    }
    // La flecha acabó su recorrido y no chocó con un árbol ni con el oponente
    printf("Flecha ha terminado su recorrido");
    return 0;
}

int dispararArriba(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador) {
    int x, y;
    if (jugador == ARQUERO_J1) {
        x = coordenada_x_jugador_1;
        y = coordenada_y_jugador_1;
    } else {
        x = coordenada_x_jugador_2;
        y = coordenada_y_jugador_2;
    }
    int metrosAvance = 0;
    while (metrosAvance < DISTANCIA_MAXIMA_DISPARO && y > 0) {
        y--;
        metrosAvance++;
        // La flecha impactó con un árbol, así que no llegó al objetivo
        if (bosque[y][x] == ARBOL) {
            printf("Flecha impacta con un arbol");
            return 0;
        } else if (bosque[y][x] == oponente_de(jugador)) {
            // Significa que le ha disparado al oponente
            printf("Flecha impacta al jugador %c\n", oponente_de(jugador));
            return 1;
        }
    }
    // La flecha acabó su recorrido y no chocó con un árbol ni con el oponente
    printf("Flecha ha terminado su recorrido");
    return 0;
}

int dispararAbajo(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador) {
    int x, y;
    if (jugador == ARQUERO_J1) {
        x = coordenada_x_jugador_1;
        y = coordenada_y_jugador_1;
    } else {
        x = coordenada_x_jugador_2;
        y = coordenada_y_jugador_2;
    }
    int metrosAvance = 0;
    while (metrosAvance < DISTANCIA_MAXIMA_DISPARO && y < medidaGlobalBosque - 1) {
        y++;
        metrosAvance++;
        // La flecha impactó con un árbol, así que no llegó al objetivo
        if (bosque[y][x] == ARBOL) {
            printf("Flecha impacta con un arbol");
            return 0;
        } else if (bosque[y][x] == oponente_de(jugador)) {
            // Significa que le ha disparado al oponente
            printf("Flecha impacta al jugador %c\n", oponente_de(jugador));
            return 1;
        }
    }
    // La flecha acabó su recorrido y no chocó con un árbol ni con el oponente
    printf("Flecha ha terminado su recorrido");
    return 0;
}

int disparar(char bosque[medidaGlobalBosque][medidaGlobalBosque], char jugador, char direccion) {
    printf("El jugador %c dispara la flecha ", jugador);
    int acertado;
    switch (direccion) {
        case DIRECCION_IZQUIERDA:
            printf("a la izquierda\n");
            acertado = dispararIzquierda(bosque, jugador);
            break;
        case DIRECCION_DERECHA:
            printf("a la derecha\n");
            acertado = dispararDerecha(bosque, jugador);
            break;
        case DIRECCION_ARRIBA:
            printf("hacia arriba\n");
            acertado = dispararArriba(bosque, jugador);
            break;
        case DIRECCION_ABAJO:
        default:
            printf("hacia abajo\n");
            acertado = dispararAbajo(bosque, jugador);
            break;
    }
    // Si acertó, entonces aumentamos el conteo de flechas recibidas para el oponente
    if (acertado) {
        if (oponente_de(jugador) == ARQUERO_J1) {
            flechazos_recibidos_jugador_1++;
        } else {
            flechazos_recibidos_jugador_2++;
        }
    }
    // Y devolvemos el estado por si nuestro invocador lo requiere
    return acertado;
}

int es_ganador(char jugador) {
    if (jugador == ARQUERO_J1) {
        return flechazos_recibidos_jugador_2 >= ROUNDS_PARA_PERDER;
    } else {
        return flechazos_recibidos_jugador_1 >= ROUNDS_PARA_PERDER;
    }
}

void reiniciar_posicion_arqueros() {
    coordenada_x_jugador_1 = 0;
    coordenada_y_jugador_1 = medidaGlobalBosque - 1;
    coordenada_x_jugador_2 = medidaGlobalBosque - 1;
    coordenada_y_jugador_2 = 0;
}

void preparar_nuevo_round(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    limpiar_bosque(bosque);
    reiniciar_posicion_arqueros();
    colocar_jugadores_en_bosque(bosque);
    colocar_arboles_en_bosque(bosque);
}

void preparar_nueva_partida(char bosque[medidaGlobalBosque][medidaGlobalBosque]) {
    preparar_nuevo_round(bosque);
    flechazos_recibidos_jugador_1 = 0;
    flechazos_recibidos_jugador_2 = 0;
}

int solicitarMedida() {
    int medida;
    while (1) {
        printf("Ingrese la medida del bosque. Puede ser de 5, 7 o 9: ");
        scanf("%d", &medida);
        consumir_nueva_linea();
        if (medida == 5 || medida == 7 || medida == 9) {
            return medida;
        } else {
            printf("La medida no es correcta\n");
        }
    }
}

char solicitar_jugador() {
    char jugador = ARQUERO_J1;
    while (1) {
        printf("%c - Arquero 1\n%c - Arquero 2. Seleccione quien empieza la partida: ", ARQUERO_J1, ARQUERO_J2);
        scanf("%c", &jugador);
        consumir_nueva_linea();
        if (jugador == ARQUERO_J1 || jugador == ARQUERO_J2) {
            return jugador;
        } else {
            printf("Jugador incorrecto\n");
        }
    }
}

char solicitar_eleccion(char jugador) {
    char eleccion = ' ';
    while (1) {
        printf("%c - Mover\n%c - Disparar. Jugador %c seleccione una opcion: ", OPCION_MOVER, OPCION_DISPARAR, jugador);
        scanf("%c", &eleccion);
        consumir_nueva_linea();
        if (eleccion == OPCION_MOVER || eleccion == OPCION_DISPARAR) {
            return eleccion;
        } else {
            printf("La opcion no es correcta\n");
        }
    }
}

void imprimir_estados_salud() {
    printf("Jugador %c. Flechazos recibidos: %d\n", ARQUERO_J1, flechazos_recibidos_jugador_1);
    printf("Jugador %c. Flechazos recibidos: %d\n", ARQUERO_J2, flechazos_recibidos_jugador_2);
}


void iniciar_nueva_partida() {
    char bosque[medidaGlobalBosque][medidaGlobalBosque];
    preparar_nueva_partida(bosque);
    char jugador = solicitar_jugador();
    while (1) {
        colocar_jugadores_en_bosque(bosque);
        imprimir_bosque(bosque);
        char eleccion = solicitar_eleccion(jugador);
        if (eleccion == OPCION_DISPARAR) {
            char direccion_del_disparo = solicitarDireccionDisparo(bosque, jugador);
            int acertado = disparar(bosque, jugador, direccion_del_disparo);
            // Si ha acertado, marcamos nuevo round
            if (acertado) {
                printf("El flechazo es acertado!. Inicia nuevo round\n");
                imprimir_estados_salud();
                preparar_nuevo_round(bosque);
            }
        } else if (eleccion == OPCION_MOVER) {
            char direccion_del_movimiento = solicitarMovimientoJugador(bosque, jugador);
            mover(bosque, jugador, direccion_del_movimiento);
        }
        if (es_ganador(jugador)) {
            printf("El jugador %c gana!\n", jugador);
            // Con el return terminamos el juego
            return;
        }
        jugador = oponente_de(jugador);
    }
}

void jugar() {
    srand(getpid());
    char eleccion = 's';
    while (eleccion == 's') {
        medidaGlobalBosque = solicitarMedida();
        iniciar_nueva_partida();
        printf("Jugar de nuevo? [s/n]: ");
        scanf("%c", &eleccion);
        consumir_nueva_linea();
    }
}


int main() {
    jugar();
}

Por aquí te dejo con más tutoriales y ejemplos en C.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

3 días hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

3 días hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

3 días hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

3 días hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

3 días hace

Solución: Apache – Server unable to read htaccess file

Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…

4 días hace

Esta web usa cookies.