Introducción al Problema de Simulación
El problema POJ 3760, conocido como "Warcraft", presenta un desafío clásico de programación competitiva que simula batallas y gestión de recursos entre dos cuarteles generales (rojo y azul) en un mapa lineal de ciudades. El objetivo es implementar con precisión las reglas de un juego simplificado, donde diferentes tipos de unidades con habilidades únicas se crean, mueven, recolectan recursos y combaten para capturar el cuartel enemigo o dominar las ciudades.
La simulación transcurre en intervalos de tiempo, con acciones específicas ocurriendo en minutos designados (0, 10, 20, 30, 40, 50). La correcta implementación del orden de los eventos y las interacciones entre las unidades y el entorno es crucial para obtener la salida esperada.
Mecánicas del Juego y Unidades
El juego presenta cinco tipos de unidades, cada una con propiedades de vida y ataque base, así como habilidades especiales:
- Dragón (Dragon): Grita al ganar una batalla.
- Ninja: No contraataca.
- Hombre de Hielo (Iceman): Pierde 9 HP y gana 20 de ataque cada dos movimientos. Si su HP cae a 0 o menos, se establece en 1.
- León (Lion): Muere después de atacar si no hay un enemigo que contraataque. Si muere, transfiere su HP restante al atacante (después del daño recibido por el león).
- Lobo (Wolf): Duplica su HP y ataque si mata a un enemigo y este es su segundo o posterior asesinato.
Cada cuartel general (rojo y azul) produce unidades en un ciclo fijo. Las unidades se mueven por las ciudades, recolectan recursos de las ciudades desocupadas y luchan contra unidades enemigas. El orden de ataque en una ciudad se determina por una bandera de la ciudad o la paridad del índice de la ciudad.
La victoria se logra cuando un cuartel general es invadido por dos unidades enemigas. Los recursos se recolectan de las ciudades desocupadas, y las unidades victoriosas en combate pueden recibir recursos adicionales de su cuartel general.
Estructuras de Datos Clave
Para gestionar el estado de la simulación, se utilizan varias estructuras de datos:
PropiedadesUnidad: Almacena la vida y el ataque base de cada tipo de unidad.Guerrero: Representa una unidad individual con su tipo, vida actual, ataque actual, posición, número de movimientos y bajas.Ciudad: Contiene el estado de una ciudad, incluyendo su bandera, recursos, los IDs de los guerreros rojo y azul presentes, y los contadores de ocupación consecutiva.CuartelGeneral: Mantiene el total de elementos, los IDs de los invasores enemigos, el número de invasores y una lista de victorias recientes para recompensas.
El Ciclo de Simulación
El programa principal itera a través del tiempo, ejecutando diferentes acciones en momentos específicos:
- Minuto 0 (
get_birth): Ambos cuarteles generales intentan producir una nueva unidad si tienen suficiantes recursos. - Minuto 10 (
step): Todas las unidades avanzan una ciudad. Las unidades que llegan a un cuartel enemigo se registran como invasores. - Minuto 20 (
spawn): Cada ciudad neutral genera 10 elementos. - Minuto 30 (
get_elements): Las unidades en ciudades desocupadas recolectan recursos para su cuartel general. - Minuto 40 (
war): Se resuelven las batallas en todas las ciudades donde hay unidades de ambos bandos. Las reglas de ataque y contraataque se aplican, y se actualizan las banderas de las ciudades si un bando ocupa la misma ciudad consecutivamente. Las unidades victoriosas pueden ser recompensadas. - Minuto 50 (
print_element): Se reporta la cantidad de elementos en cada cuartel general.
La simulación termina si un cuartel general es invadido por dos unidades enemigas o si se alcanza el tiempo máximo de simulación.
Consideraciones de Implementación
Durante la implementación de este tipo de problemas, es común encontrar desafíos como:
- Gestión de tipos de datos: Asegurarse de que las variables puedan almacenar los valores máximos requeridos. Para este problema, el tipo
intes generalmente suficiente, evitando errores por el uso innecesario delong longque podría ocultar problemas o ser menos eficiente. - Errores tipográficos: Los pequeños errores al nombrar unidades o variables pueden llevar a comportamientos incorrectos difíciles de depurar, como confundir "ninja" con "ninjn".
- Orden de eventos: La secuencia precisa de cada acción en cada intervalo de tiempo es vital.
- Reglas de combate: Las interacciones complejas entre unidades (ataque, contraataque, habilidades especiales) deben codificarse con cuidado para reflejar fielmente las especificaciones.
- Condiciones de victoria y fin de partida: El estado de la simulación debe ser evaluado correctamente en cada paso.
Generador de Datos de Prueba
Un generador de datos aleatorios es una herramienta indispensable para probar la robustez de la solución. El siguiente código proporciona un ejemplo sencillo para generar casos de prueba con parámetros aleatorios para el problema.
#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <chrono> // Para semilla aleatoria
// Función auxiliar para generar números aleatorios en un rango
int generarAleatorio(int minVal, int maxVal, std::mt19937& generador) {
std::uniform_int_distribution<int> distribucion(minVal, maxVal);
return distribucion(generador);
}
int main() {
// Configurar generador de números aleatorios
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
std::mt19937 generador(seed);
int numCasos = generarAleatorio(500, 500, generador); // Generar 500 casos
std::cout << numCasos << std::endl;
while (numCasos--) {
// M: recursos iniciales, N: número de ciudades, T: tiempo máximo
int m = generarAleatorio(100, 200, generador);
int n = generarAleatorio(20, 20, generador); // Fijo en 20 ciudades
int t = generarAleatorio(990, 1000, generador) * 10; // Tiempo en minutos (múltiplos de 10)
std::cout << m << " " << n << " " << t << std::endl;
// HP base de las 5 unidades
for (int i = 0; i < 5; ++i) {
std::cout << generarAleatorio(10, 100, generador) << (i == 4 ? "" : " ");
}
std::cout << std::endl;
// Ataque base de las 5 unidades
for (int i = 0; i < 5; ++i) {
std::cout << generarAleatorio(0, 0, generador) << (i == 4 ? "" : " "); // Ataque 0, según el problema original
}
std::cout << std::endl;
}
return 0;
}
Solución Completa
A continuación se presenta una implementación en C++ que aborda las mecánicas y el flujo del problema POJ 3760. Se han utilizado estructuras de datos claras y nombres de variables descriptivos para mejorar la legibilidad y el mantenimiento.
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// Constante para el tamaño máximo de arrays (o IDs)
const int MAX_ENTIDADES = 100100;
// Tipos de unidades (para mejor legibilidad que números mágicos)
enum TipoUnidad {
NINGUNO = 0, // 0 si no hay unidad
DRAGON = 1,
NINJA = 2,
ICEMAN = 3,
LEON = 4,
LOBO = 5
};
// Nombres de las unidades, indexados por TipoUnidad
vector<string> NOMBRES_UNIDADES = {
"", "dragon", "ninja", "iceman", "lion", "wolf"
};
// Propiedades base de cada tipo de unidad
struct PropiedadesUnidad {
int vidaBase;
int ataqueBase;
};
PropiedadesUnidad propiedadesUnidades[6]; // Índices 1-5 corresponden a TipoUnidad
// Representación de un guerrero individual
struct Guerrero {
TipoUnidad tipo;
int vidaActual;
int ataqueActual;
int posicionActual;
int movimientosRealizados;
int bajasConfirmadas;
Guerrero() : tipo(NINGUNO), vidaActual(0), ataqueActual(0), posicionActual(0),
movimientosRealizados(0), bajasConfirmadas(0) {}
// Constructor para inicialización
Guerrero(TipoUnidad t, int hp, int atk, int pos) :
tipo(t), vidaActual(hp), ataqueActual(atk), posicionActual(pos),
movimientosRealizados(0), bajasConfirmadas(0) {}
};
Guerrero ejercitoRojo[MAX_ENTIDADES];
Guerrero ejercitoAzul[MAX_ENTIDADES];
// Representación de una ciudad
struct Ciudad {
int idBandera; // 0: neutral, 1: rojo, 2: azul
int recursosElementos;
int idGuerreroRojo; // ID del guerrero rojo en la ciudad (0 si ninguno)
int idGuerreroAzul; // ID del guerrero azul en la ciudad (0 si ninguno)
int contadorVictoriasRojo; // Victorias consecutivas de rojo en esta ciudad
int contadorVictoriasAzul; // Victorias consecutivas de azul en esta ciudad
};
Ciudad ciudades[MAX_ENTIDADES]; // ciudades[0] es HQ rojo, ciudades[N+1] es HQ azul
// Representación de un Cuartel General
struct CuartelGeneral {
int elementosAlmacenados;
int invasoresEnemigos[3]; // IDs de los guerreros enemigos que han llegado (máx 2)
int numInvasores;
int elementosParaRecoger; // Recursos pendientes de añadir al total
vector<pair<int, int>> victoriasRecientes; // (idCiudad, idGuerrero) para recompensas
CuartelGeneral() : elementosAlmacenados(0), numInvasores(0), elementosParaRecoger(0) {
memset(invasoresEnemigos, 0, sizeof(invasoresEnemigos));
}
};
CuartelGeneral cuartelRojo, cuartelAzul;
int maxElementosInicial, numCiudades, tiempoMaximo;
int totalGuerrerosRojos, totalGuerrerosAzules;
// Orden de nacimiento de unidades para cada cuartel
vector<TipoUnidad> ordenNacimientoRojo = {ICEMAN, LEON, LOBO, NINJA, DRAGON};
vector<TipoUnidad> ordenNacimientoAzul = {LEON, DRAGON, ICEMAN, NINJA, LOBO};
// Función para lectura rápida de enteros
int leerEntero() {
int s = 0, w = 1;
char ch = getchar();
while (ch > '9' || ch < '0') {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
s = (s << 1) + (s << 3) + ch - '0';
ch = getchar();
}
return s * w;
}
// Inicializa nombres de unidades y orden de nacimiento (ya manejado con vector<string> y vector<TipoUnidad>)
void configurarUnidades() {
// Los nombres están en NOMBRES_UNIDADES
// El orden de nacimiento está en ordenNacimientoRojo y ordenNacimientoAzul
}
// Prepara el estado inicial para un nuevo caso de prueba
void inicializarJuego() {
maxElementosInicial = leerEntero();
numCiudades = leerEntero();
tiempoMaximo = leerEntero();
totalGuerrerosRojos = totalGuerrerosAzules = 0;
cuartelRojo = CuartelGeneral(); // Reinicializar structs
cuartelAzul = CuartelGeneral();
cuartelRojo.elementosAlmacenados = cuartelAzul.elementosAlmacenados = maxElementosInicial;
for (int i = 0; i <= numCiudades + 1; ++i) {
ciudades[i].idBandera = 0;
ciudades[i].recursosElementos = 0;
ciudades[i].idGuerreroRojo = 0;
ciudades[i].idGuerreroAzul = 0;
ciudades[i].contadorVictoriasRojo = 0;
ciudades[i].contadorVictoriasAzul = 0;
}
for (int i = 1; i <= 5; ++i) propiedadesUnidades[i].vidaBase = leerEntero();
for (int i = 1; i <= 5; ++i) propiedadesUnidades[i].ataqueBase = leerEntero();
}
// Verifica si algún cuartel general ha sido capturado
bool verificarVictoria(pair<int, int> tiempoActual) {
if (cuartelRojo.numInvasores == 2 || cuartelAzul.numInvasores == 2) {
return true;
}
return false;
}
// Crea una unidad roja
void producirGuerreroRojo(pair<int, int> tiempoActual) {
TipoUnidad tipoParaNacer = ordenNacimientoRojo[totalGuerrerosRojos % 5];
int costeHP = propiedadesUnidades[tipoParaNacer].vidaBase;
if (cuartelRojo.elementosAlmacenados < costeHP) return;
cuartelRojo.elementosAlmacenados -= costeHP;
printf("%03d:%02d red %s %d born\n", tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[tipoParaNacer].c_str(), ++totalGuerrerosRojos);
ejercitoRojo[totalGuerrerosRojos] = Guerrero(
tipoParaNacer, propiedadesUnidades[tipoParaNacer].vidaBase,
propiedadesUnidades[tipoParaNacer].ataqueBase, 0
);
ciudades[0].idGuerreroRojo = totalGuerrerosRojos;
}
// Crea una unidad azul
void producirGuerreroAzul(pair<int, int> tiempoActual) {
TipoUnidad tipoParaNacer = ordenNacimientoAzul[totalGuerrerosAzules % 5];
int costeHP = propiedadesUnidades[tipoParaNacer].vidaBase;
if (cuartelAzul.elementosAlmacenados < costeHP) return;
cuartelAzul.elementosAlmacenados -= costeHP;
printf("%03d:%02d blue %s %d born\n", tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[tipoParaNacer].c_str(), ++totalGuerrerosAzules);
ejercitoAzul[totalGuerrerosAzules] = Guerrero(
tipoParaNacer, propiedadesUnidades[tipoParaNacer].vidaBase,
propiedadesUnidades[tipoParaNacer].ataqueBase, numCiudades + 1
);
ciudades[numCiudades + 1].idGuerreroAzul = totalGuerrerosAzules;
}
// Produce guerreros para ambos bandos
void generarGuerreros(pair<int, int> tiempoActual) {
producirGuerreroRojo(tiempoActual);
producirGuerreroAzul(tiempoActual);
}
// Aplica la habilidad especial del iceman rojo
void activarIcemanRojo(int idGuerrero) {
if (ejercitoRojo[idGuerrero].movimientosRealizados > 0 &&
ejercitoRojo[idGuerrero].movimientosRealizados % 2 == 0) {
ejercitoRojo[idGuerrero].vidaActual -= 9;
if (ejercitoRojo[idGuerrero].vidaActual <= 0) ejercitoRojo[idGuerrero].vidaActual = 1;
ejercitoRojo[idGuerrero].ataqueActual += 20;
}
}
// Aplica la habilidad especial del iceman azul
void activarIcemanAzul(int idGuerrero) {
if (ejercitoAzul[idGuerrero].movimientosRealizados > 0 &&
ejercitoAzul[idGuerrero].movimientosRealizados % 2 == 0) {
ejercitoAzul[idGuerrero].vidaActual -= 9;
if (ejercitoAzul[idGuerrero].vidaActual <= 0) ejercitoAzul[idGuerrero].vidaActual = 1;
ejercitoAzul[idGuerrero].ataqueActual += 20;
}
}
// Imprime el mensaje de marcha de un guerrero rojo
void imprimirMarchaRojo(pair<int, int> tiempoActual, int idGuerrero) {
printf("%03d:%02d red %s %d", tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoRojo[idGuerrero].tipo].c_str(), idGuerrero);
if (ejercitoRojo[idGuerrero].tipo == ICEMAN) activarIcemanRojo(idGuerrero);
}
// Imprime el mensaje de marcha de un guerrero azul
void imprimirMarchaAzul(pair<int, int> tiempoActual, int idGuerrero) {
printf("%03d:%02d blue %s %d", tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoAzul[idGuerrero].tipo].c_str(), idGuerrero);
if (ejercitoAzul[idGuerrero].tipo == ICEMAN) activarIcemanAzul(idGuerrero);
}
// Simula el movimiento de todas las unidades
void moverUnidades(pair<int, int> tiempoActual) {
// Mover unidades azules hacia la izquierda (pos-1)
for (int i = 1; i <= numCiudades + 1; ++i) {
if (ciudades[i].idGuerreroAzul) {
ciudades[i-1].idGuerreroAzul = ciudades[i].idGuerreroAzul;
ejercitoAzul[ciudades[i-1].idGuerreroAzul].movimientosRealizados++;
ciudades[i].idGuerreroAzul = 0;
ejercitoAzul[ciudades[i-1].idGuerreroAzul].posicionActual = i-1;
}
}
// Mover unidades rojas hacia la derecha (pos+1)
for (int i = numCiudades; i >= 0; --i) {
if (ciudades[i].idGuerreroRojo) {
ciudades[i+1].idGuerreroRojo = ciudades[i].idGuerreroRojo;
ejercitoRojo[ciudades[i+1].idGuerreroRojo].movimientosRealizados++;
ciudades[i].idGuerreroRojo = 0;
ejercitoRojo[ciudades[i+1].idGuerreroRojo].posicionActual = i+1;
}
}
// Reportar llegada a cuarteles
if (ciudades[0].idGuerreroAzul != 0 && cuartelRojo.invasoresEnemigos[0] != ciudades[0].idGuerreroAzul) {
int idGuerrero = ciudades[0].idGuerreroAzul;
imprimirMarchaAzul(tiempoActual, idGuerrero);
printf(" reached red headquarter with %d elements and force %d\n", ejercitoAzul[idGuerrero].vidaActual, ejercitoAzul[idGuerrero].ataqueActual);
cuartelRojo.invasoresEnemigos[++cuartelRojo.numInvasores] = idGuerrero;
if (cuartelRojo.numInvasores == 2) printf("%03d:%02d red headquarter was taken\n", tiempoActual.first, tiempoActual.second);
}
for (int i = 1; i <= numCiudades; ++i) {
if (ciudades[i].idGuerreroRojo) {
int idGuerrero = ciudades[i].idGuerreroRojo;
imprimirMarchaRojo(tiempoActual, idGuerrero);
printf(" marched to city %d with %d elements and force %d\n", i, ejercitoRojo[idGuerrero].vidaActual, ejercitoRojo[idGuerrero].ataqueActual);
}
if (ciudades[i].idGuerreroAzul) {
int idGuerrero = ciudades[i].idGuerreroAzul;
imprimirMarchaAzul(tiempoActual, idGuerrero);
printf(" marched to city %d with %d elements and force %d\n", i, ejercitoAzul[idGuerrero].vidaActual, ejercitoAzul[idGuerrero].ataqueActual);
}
}
if (ciudades[numCiudades + 1].idGuerreroRojo != 0 && cuartelAzul.invasoresEnemigos[0] != ciudades[numCiudades + 1].idGuerreroRojo) {
int idGuerrero = ciudades[numCiudades + 1].idGuerreroRojo;
imprimirMarchaRojo(tiempoActual, idGuerrero);
printf(" reached blue headquarter with %d elements and force %d\n", ejercitoRojo[idGuerrero].vidaActual, ejercitoRojo[idGuerrero].ataqueActual);
cuartelAzul.invasoresEnemigos[++cuartelAzul.numInvasores] = idGuerrero;
if (cuartelAzul.numInvasores == 2) printf("%03d:%02d blue headquarter was taken\n", tiempoActual.first, tiempoActual.second);
}
}
// Genera recursos en ciudades neutrales
void generarRecursosCiudad() {
for (int i = 1; i <= numCiudades; ++i) ciudades[i].recursosElementos += 10;
}
// Imprime mensaje de recolección de elementos para guerrero rojo
void recolectarRojo(pair<int, int> tiempoActual, int idGuerrero, int idCiudad) {
printf("%03d:%02d red %s %d earned %d elements for his headquarter\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoRojo[idGuerrero].tipo].c_str(),
idGuerrero, ciudades[idCiudad].recursosElementos);
cuartelRojo.elementosParaRecoger += ciudades[idCiudad].recursosElementos;
ciudades[idCiudad].recursosElementos = 0;
}
// Imprime mensaje de recolección de elementos para guerrero azul
void recolectarAzul(pair<int, int> tiempoActual, int idGuerrero, int idCiudad) {
printf("%03d:%02d blue %s %d earned %d elements for his headquarter\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoAzul[idGuerrero].tipo].c_str(),
idGuerrero, ciudades[idCiudad].recursosElementos);
cuartelAzul.elementosParaRecoger += ciudades[idCiudad].recursosElementos;
ciudades[idCiudad].recursosElementos = 0;
}
// Recolecta recursos de ciudades desocupadas
void recolectarRecursos(pair<int, int> tiempoActual) {
for (int i = 1; i <= numCiudades; ++i) {
if (ciudades[i].idGuerreroRojo && (!ciudades[i].idGuerreroAzul)) {
recolectarRojo(tiempoActual, ciudades[i].idGuerreroRojo, i);
}
if ((!ciudades[i].idGuerreroRojo) && ciudades[i].idGuerreroAzul) {
recolectarAzul(tiempoActual, ciudades[i].idGuerreroAzul, i);
}
}
}
// Transfiere elementos pendientes a los cuarteles generales
void actualizarElementosCuarteles() {
cuartelRojo.elementosAlmacenados += cuartelRojo.elementosParaRecoger;
cuartelRojo.elementosParaRecoger = 0;
cuartelAzul.elementosAlmacenados += cuartelAzul.elementosParaRecoger;
cuartelAzul.elementosParaRecoger = 0;
}
// Limpia las listas de victorias recientes
void limpiarVictorias() {
cuartelRojo.victoriasRecientes.clear();
cuartelAzul.victoriasRecientes.clear();
}
// Mensaje de grito del dragón rojo
void gritoDragonRojo(pair<int, int> tiempoActual, int idGuerrero, int idCiudad) {
printf("%03d:%02d red dragon %d yelled in city %d\n", tiempoActual.first, tiempoActual.second, idGuerrero, idCiudad);
}
// Mensaje de grito del dragón azul
void gritoDragonAzul(pair<int, int> tiempoActual, int idGuerrero, int idCiudad) {
printf("%03d:%02d blue dragon %d yelled in city %d\n", tiempoActual.first, tiempoActual.second, idGuerrero, idCiudad);
}
// Ataque activo de rojo a azul
void ataqueActivoRojo(pair<int, int> tiempoActual, int idRojo, int idAzul, int idCiudad) {
printf("%03d:%02d red %s %d attacked blue %s %d in city %d with %d elements and force %d\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoRojo[idRojo].tipo].c_str(), idRojo,
NOMBRES_UNIDADES[ejercitoAzul[idAzul].tipo].c_str(), idAzul, idCiudad,
ejercitoRojo[idRojo].vidaActual, ejercitoRojo[idRojo].ataqueActual);
ejercitoAzul[idAzul].vidaActual -= ejercitoRojo[idRojo].ataqueActual;
if (ejercitoAzul[idAzul].vidaActual <= 0) {
printf("%03d:%02d blue %s %d was killed in city %d\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoAzul[idAzul].tipo].c_str(), idAzul, idCiudad);
ejercitoRojo[idRojo].bajasConfirmadas++;
if (ejercitoRojo[idRojo].tipo == LOBO && ejercitoRojo[idRojo].bajasConfirmadas % 2 == 0) {
ejercitoRojo[idRojo].vidaActual *= 2;
ejercitoRojo[idRojo].ataqueActual *= 2;
}
if (ejercitoAzul[idAzul].tipo == LEON) {
ejercitoRojo[idRojo].vidaActual += ejercitoAzul[idAzul].vidaActual + ejercitoRojo[idRojo].ataqueActual;
}
cuartelRojo.victoriasRecientes.push_back(make_pair(idCiudad, idRojo));
ciudades[idCiudad].idGuerreroAzul = 0; // Unidad azul eliminada
ciudades[idCiudad].contadorVictoriasRojo++;
ciudades[idCiudad].contadorVictoriasAzul = 0;
} else {
if (ejercitoAzul[idAzul].tipo == NINJA) { // Ninja no contraataca
ciudades[idCiudad].contadorVictoriasRojo = 0;
ciudades[idCiudad].contadorVictoriasAzul = 0;
}
}
}
// Ataque activo de azul a rojo
void ataqueActivoAzul(pair<int, int> tiempoActual, int idRojo, int idAzul, int idCiudad) {
printf("%03d:%02d blue %s %d attacked red %s %d in city %d with %d elements and force %d\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoAzul[idAzul].tipo].c_str(), idAzul,
NOMBRES_UNIDADES[ejercitoRojo[idRojo].tipo].c_str(), idRojo, idCiudad,
ejercitoAzul[idAzul].vidaActual, ejercitoAzul[idAzul].ataqueActual);
ejercitoRojo[idRojo].vidaActual -= ejercitoAzul[idAzul].ataqueActual;
if (ejercitoRojo[idRojo].vidaActual <= 0) {
printf("%03d:%02d red %s %d was killed in city %d\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoRojo[idRojo].tipo].c_str(), idRojo, idCiudad);
ejercitoAzul[idAzul].bajasConfirmadas++;
if (ejercitoAzul[idAzul].tipo == LOBO && ejercitoAzul[idAzul].bajasConfirmadas % 2 == 0) {
ejercitoAzul[idAzul].vidaActual *= 2;
ejercitoAzul[idAzul].ataqueActual *= 2;
}
if (ejercitoRojo[idRojo].tipo == LEON) {
ejercitoAzul[idAzul].vidaActual += ejercitoRojo[idRojo].vidaActual + ejercitoAzul[idAzul].ataqueActual;
}
cuartelAzul.victoriasRecientes.push_back(make_pair(idCiudad, idAzul));
ciudades[idCiudad].idGuerreroRojo = 0; // Unidad roja eliminada
ciudades[idCiudad].contadorVictoriasAzul++;
ciudades[idCiudad].contadorVictoriasRojo = 0;
} else {
if (ejercitoRojo[idRojo].tipo == NINJA) { // Ninja no contraataca
ciudades[idCiudad].contadorVictoriasRojo = 0;
ciudades[idCiudad].contadorVictoriasAzul = 0;
}
}
}
// Contraataque de rojo a azul
void contraAtaqueRojo(pair<int, int> tiempoActual, int idRojo, int idAzul, int idCiudad) {
printf("%03d:%02d red %s %d fought back against blue %s %d in city %d\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoRojo[idRojo].tipo].c_str(), idRojo,
NOMBRES_UNIDADES[ejercitoAzul[idAzul].tipo].c_str(), idAzul, idCiudad);
ejercitoAzul[idAzul].vidaActual -= (ejercitoRojo[idRojo].ataqueActual / 2);
if (ejercitoAzul[idAzul].vidaActual <= 0) {
printf("%03d:%02d blue %s %d was killed in city %d\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoAzul[idAzul].tipo].c_str(), idAzul, idCiudad);
if (ejercitoAzul[idAzul].tipo == LEON) {
ejercitoRojo[idRojo].vidaActual += ejercitoAzul[idAzul].vidaActual + (ejercitoRojo[idRojo].ataqueActual / 2);
}
cuartelRojo.victoriasRecientes.push_back(make_pair(idCiudad, idRojo));
ciudades[idCiudad].idGuerreroAzul = 0; // Unidad azul eliminada
ciudades[idCiudad].contadorVictoriasRojo++;
ciudades[idCiudad].contadorVictoriasAzul = 0;
} else {
ciudades[idCiudad].contadorVictoriasRojo = 0;
ciudades[idCiudad].contadorVictoriasAzul = 0;
}
}
// Contraataque de azul a rojo
void contraAtaqueAzul(pair<int, int> tiempoActual, int idRojo, int idAzul, int idCiudad) {
printf("%03d:%02d blue %s %d fought back against red %s %d in city %d\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoAzul[idAzul].tipo].c_str(), idAzul,
NOMBRES_UNIDADES[ejercitoRojo[idRojo].tipo].c_str(), idRojo, idCiudad);
ejercitoRojo[idRojo].vidaActual -= (ejercitoAzul[idAzul].ataqueActual / 2);
if (ejercitoRojo[idRojo].vidaActual <= 0) {
printf("%03d:%02d red %s %d was killed in city %d\n",
tiempoActual.first, tiempoActual.second,
NOMBRES_UNIDADES[ejercitoRojo[idRojo].tipo].c_str(), idRojo, idCiudad);
if (ejercitoRojo[idRojo].tipo == LEON) {
ejercitoAzul[idAzul].vidaActual += ejercitoRojo[idRojo].vidaActual + (ejercitoAzul[idAzul].ataqueActual / 2);
}
cuartelAzul.victoriasRecientes.push_back(make_pair(idCiudad, idAzul));
ciudades[idCiudad].idGuerreroRojo = 0; // Unidad roja eliminada
ciudades[idCiudad].contadorVictoriasAzul++;
ciudades[idCiudad].contadorVictoriasRojo = 0;
} else {
ciudades[idCiudad].contadorVictoriasRojo = 0;
ciudades[idCiudad].contadorVictoriasAzul = 0;
}
}
// Bandera roja se levanta
void levantarBanderaRoja(pair<int, int> tiempoActual, int idCiudad) {
ciudades[idCiudad].idBandera = 1;
printf("%03d:%02d red flag raised in city %d\n", tiempoActual.first, tiempoActual.second, idCiudad);
}
// Bandera azul se levanta
void levantarBanderaAzul(pair<int, int> tiempoActual, int idCiudad) {
ciudades[idCiudad].idBandera = 2;
printf("%03d:%02d blue flag raised in city %d\n", tiempoActual.first, tiempoActual.second, idCiudad);
}
// Simula las batallas en todas las ciudades con unidades de ambos bandos
void resolverCombates(pair<int, int> tiempoActual) {
actualizarElementosCuarteles();
limpiarVictorias();
for (int i = 1; i <= numCiudades; ++i) {
if (ciudades[i].idGuerreroRojo && ciudades[i].idGuerreroAzul) {
int idRojo = ciudades[i].idGuerreroRojo;
int idAzul = ciudades[i].idGuerreroAzul;
// Determinar quién ataca primero
bool atacaRojoPrimero = (ciudades[i].idBandera == 1 || (i % 2 == 1 && ciudades[i].idBandera != 2));
bool atacaAzulPrimero = (ciudades[i].idBandera == 2 || (i % 2 == 0 && ciudades[i].idBandera != 1));
if (atacaRojoPrimero) {
ataqueActivoRojo(tiempoActual, idRojo, idAzul, i);
if (ciudades[i].idGuerreroAzul && ejercitoAzul[ciudades[i].idGuerreroAzul].tipo != NINJA) {
contraAtaqueAzul(tiempoActual, idRojo, idAzul, i);
}
} else if (atacaAzulPrimero) {
ataqueActivoAzul(tiempoActual, idRojo, idAzul, i);
if (ciudades[i].idGuerreroRojo && ejercitoRojo[ciudades[i].idGuerreroRojo].tipo != NINJA) {
contraAtaqueRojo(tiempoActual, idRojo, idAzul, i);
}
}
// Gritos de dragones
if (ciudades[i].idGuerreroRojo && ejercitoRojo[ciudades[i].idGuerreroRojo].tipo == DRAGON && !ciudades[i].idGuerreroAzul) {
gritoDragonRojo(tiempoActual, ciudades[i].idGuerreroRojo, i);
}
if (ciudades[i].idGuerreroAzul && ejercitoAzul[ciudades[i].idGuerreroAzul].tipo == DRAGON && !ciudades[i].idGuerreroRojo) {
gritoDragonAzul(tiempoActual, ciudades[i].idGuerreroAzul, i);
}
}
}
// Recolectar recursos después de la batalla si queda una unidad
for (int i = 1; i <= numCiudades; ++i) {
if (ciudades[i].idGuerreroRojo && !ciudades[i].idGuerreroAzul && ciudades[i].recursosElementos > 0) {
recolectarRojo(tiempoActual, ciudades[i].idGuerreroRojo, i);
} else if (ciudades[i].idGuerreroAzul && !ciudades[i].idGuerreroRojo && ciudades[i].recursosElementos > 0) {
recolectarAzul(tiempoActual, ciudades[i].idGuerreroAzul, i);
}
}
// Actualizar banderas
for (int i = 1; i <= numCiudades; ++i) {
if (ciudades[i].idGuerreroRojo && !ciudades[i].idGuerreroAzul && ciudades[i].contadorVictoriasRojo >= 2 && ciudades[i].idBandera != 1) {
levantarBanderaRoja(tiempoActual, i);
} else if (ciudades[i].idGuerreroAzul && !ciudades[i].idGuerreroRojo && ciudades[i].contadorVictoriasAzul >= 2 && ciudades[i].idBandera != 2) {
levantarBanderaAzul(tiempoActual, i);
}
}
// Ordenar victorias para recompensas (prioridad a ciudades más lejanas/cercanas al cuartel)
sort(cuartelRojo.victoriasRecientes.begin(), cuartelRojo.victoriasRecientes.end(), [](const pair<int,int>& a, const pair<int,int>& b){
return a.first > b.first; // Mayor ID de ciudad primero para rojo
});
sort(cuartelAzul.victoriasRecientes.begin(), cuartelAzul.victoriasRecientes.end(), [](const pair<int,int>& a, const pair<int,int>& b){
return a.first < b.first; // Menor ID de ciudad primero para azul
});
// Recompensar a los ganadores con 8 elementos
for (auto const& victoria : cuartelRojo.victoriasRecientes) {
if (cuartelRojo.elementosAlmacenados >= 8) {
cuartelRojo.elementosAlmacenados -= 8;
ejercitoRojo[victoria.second].vidaActual += 8;
} else break;
}
for (auto const& victoria : cuartelAzul.victoriasRecientes) {
if (cuartelAzul.elementosAlmacenados >= 8) {
cuartelAzul.elementosAlmacenados -= 8;
ejercitoAzul[victoria.second].vidaActual += 8;
} else break;
}
}
// Imprime el total de elementos en cada cuartel
void imprimirElementosCuarteles(pair<int, int> tiempoActual) {
actualizarElementosCuarteles();
printf("%03d:%02d %d elements in red headquarter\n", tiempoActual.first, tiempoActual.second, cuartelRojo.elementosAlmacenados);
printf("%03d:%02d %d elements in blue headquarter\n", tiempoActual.first, tiempoActual.second, cuartelAzul.elementosAlmacenados);
}
int main() {
// Optimización de I/O (no necesario con getchar pero buena práctica)
// ios::sync_with_stdio(false);
// cin.tie(NULL);
int numCasosPrueba = leerEntero();
configurarUnidades(); // No hace nada aquí ya que se usan vectores globales
for (int o = 1; o <= numCasosPrueba; ++o) {
printf("Case:%d\n", o);
inicializarJuego();
for (int i = 0; i <= tiempoMaximo; i += 10) {
pair<int, int> tiempoActual = make_pair(i / 60, i % 60);
if (tiempoActual.second == 0) generarGuerreros(tiempoActual);
if (tiempoActual.second == 10) moverUnidades(tiempoActual);
if (verificarVictoria(tiempoActual)) break; // Termina si hay victoria
if (tiempoActual.second == 20) generarRecursosCiudad();
if (tiempoActual.second == 30) recolectarRecursos(tiempoActual);
if (tiempoActual.second == 40) resolverCombates(tiempoActual);
if (tiempoActual.second == 50) imprimirElementosCuarteles(tiempoActual);
}
}
return 0;
}