Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import os | |
| import subprocess | |
| import time | |
| from collections import deque | |
| import shutil | |
| import re | |
| def format_colors(text): | |
| """Remove todos os códigos de cor e deixa apenas texto puro""" | |
| import re | |
| # Remover qualquer código ANSI | |
| ansi_pattern = r'\x1b\[[0-9;]*m|\[([0-9;]*)m' | |
| text = re.sub(ansi_pattern, '', text) | |
| # Remover códigos unicode de escape | |
| text = text.replace("\\u001B[31m", "") | |
| text = text.replace("\\u001B[34m", "") | |
| text = text.replace("\\u001B[32m", "") | |
| text = text.replace("\\u001B[0m", "") | |
| # Remover códigos sem escape | |
| text = text.replace("[31m", "") | |
| text = text.replace("[34m", "") | |
| text = text.replace("[32m", "") | |
| text = text.replace("[0m", "") | |
| # NÃO alterar os símbolos das aeronaves - deixar como estão | |
| # > e < são as aeronaves e devem aparecer normalmente | |
| return text | |
| def check_and_install_java(): | |
| """Verifica se Java está instalado e tenta instalar se necessário""" | |
| try: | |
| # Verificar se javac está disponível | |
| result = subprocess.run(["javac", "-version"], capture_output=True, text=True) | |
| if result.returncode == 0: | |
| return True, "Java já está instalado" | |
| except FileNotFoundError: | |
| pass | |
| try: | |
| # Tentar instalar Java usando apt-get | |
| print("🔧 Instalando Java...") | |
| subprocess.run(["apt-get", "update"], check=True, capture_output=True) | |
| subprocess.run(["apt-get", "install", "-y", "default-jdk"], check=True, capture_output=True) | |
| # Verificar se a instalação funcionou | |
| result = subprocess.run(["javac", "-version"], capture_output=True, text=True) | |
| if result.returncode == 0: | |
| return True, "Java instalado com sucesso" | |
| except Exception as e: | |
| pass | |
| # Se chegou até aqui, não conseguiu instalar | |
| return False, "Não foi possível instalar Java automaticamente" | |
| # Verificar Java na inicialização | |
| java_available, java_message = check_and_install_java() | |
| print(f"Status do Java: {java_message}") | |
| # Criar pasta para armazenar as classes Java | |
| os.makedirs("combat_classes", exist_ok=True) | |
| # Templates de código para as aeronaves com altura dinâmica e novos atributos | |
| TEAM1_TEMPLATE = '''import java.util.ArrayList; | |
| import java.util.Random; | |
| /** | |
| * Time 1 - Configure sua aeronave! | |
| * | |
| * SISTEMA DE PONTOS: | |
| * - Você tem 100 pontos para distribuir entre os atributos | |
| * - Escolha com sabedoria para criar uma aeronave competitiva | |
| * | |
| * ATRIBUTOS: | |
| * - speed: Velocidade da aeronave (1-10) - Afeta quão rápido sua aeronave pode se mover | |
| * - fireRate: Taxa de disparo (1-10) - Controla com que frequência sua aeronave pode atirar | |
| * - maneuverability: Manobrabilidade (1-10) - Facilita mudanças de altitude e esquivas | |
| * - shotPower: Poder do tiro normal (5-20) - Dano causado por tiros normais | |
| * - supersonicPower: Poder do tiro supersônico (10-30) - Dano causado por tiros supersônicos | |
| * - missilePower: Poder do míssil (15-40) - Dano causado pelo míssil especial | |
| * - defense: Defesa (5-25) - Reduz o dano recebido | |
| * - stealthChance: Chance de furtividade (0-20) - Probabilidade de evitar ataques | |
| * - radar: Radar (0-10) - Capacidade de detectar projéteis inimigos | |
| * - doubleShot: Tiro duplo (0-10) - Permite disparar em duas altitudes diferentes | |
| * - nuclearPower: Poder nuclear (0-10) - Poder do míssil nuclear (dano massivo) | |
| * | |
| * SÍMBOLOS: | |
| * - Aeronave: T1 (Time 1) | |
| * - Tiro normal: -> | |
| * - Tiro supersônico: >> | |
| * - Míssil especial: => | |
| * - Tiro duplo: => | |
| * - Míssil nuclear: -N-> | |
| */ | |
| public class Team1Aircraft extends Aircraft { | |
| private Random random = new Random(); | |
| private int maxAltitude; // Armazena a altura máxima do campo | |
| public Team1Aircraft() { | |
| super( | |
| // DISTRIBUA 100 PONTOS ENTRE ESSES ATRIBUTOS | |
| 5, // Velocidade (1-10) | |
| 5, // Taxa de fogo (1-10) | |
| 5, // Manobrabilidade (1-10) | |
| 15, // Dano do tiro normal (5-20) | |
| 20, // Dano do tiro supersônico (10-30) | |
| 25, // Dano do míssil (15-40) | |
| 15, // Defesa (5-25) | |
| 5, // Chance de furtividade (0-20) | |
| 2, // Radar (0-10) | |
| 2, // Tiro duplo (0-10) | |
| 1, // Poder nuclear (0-10) | |
| "▶" // Símbolo da aeronave (não altere) | |
| ); | |
| // IMPORTANTE: A soma de todos os atributos deve ser <= 100 | |
| // Exemplo: 5+5+5+15+20+25+15+5+2+2+1 = 100 | |
| // Verifica dinamicamente a altura do campo (será definida pelo BattleMain) | |
| try { | |
| String heightEnv = System.getProperty("battlefield.height", "3"); | |
| maxAltitude = Integer.parseInt(heightEnv) - 1; | |
| } catch (Exception e) { | |
| maxAltitude = 2; // Valor padrão se não conseguir ler | |
| } | |
| } | |
| /** | |
| * Controla o movimento da aeronave. | |
| * Valor padrão: Movimento aleatório baseado na velocidade | |
| * Dica: Você pode personalizar para criar padrões de movimento mais inteligentes | |
| */ | |
| @Override | |
| public int move() { | |
| // Retorna um número entre -speed/2 e +speed | |
| return random.nextInt(speed + 1) - speed / 2; | |
| } | |
| /** | |
| * Controla a mudança de altitude da aeronave. | |
| * Valor padrão: Mudança aleatória entre subir, descer ou manter altitude | |
| * Dica: Uma boa estratégia pode aumentar suas chances de esquiva | |
| */ | |
| @Override | |
| public int changeAltitude() { | |
| int direction = random.nextInt(3) - 1; // -1 (descer), 0 (manter), 1 (subir) | |
| this.posY = Math.max(0, Math.min(maxAltitude, posY + direction)); | |
| return direction; | |
| } | |
| /** | |
| * Tiro normal - mais frequente, menos dano | |
| */ | |
| @Override | |
| public Projectile shoot(int posX, int direction) { | |
| return new Projectile(posX, this.posY, direction, 1, "->"); | |
| } | |
| /** | |
| * Tiro supersônico - mais rápido, mais dano | |
| */ | |
| @Override | |
| public Projectile shootSupersonic(int posX, int direction) { | |
| return new Projectile(posX, this.posY, direction, 2, ">>"); | |
| } | |
| /** | |
| * Míssil especial - muito dano, com cooldown | |
| */ | |
| @Override | |
| public Projectile specialMissile(int posX, int direction) { | |
| if (missileCooldown == 0) { | |
| missileCooldown = 3; // Espera 3 turnos para usar novamente | |
| return new Projectile(posX, this.posY, direction, 1, "=>"); | |
| } | |
| missileCooldown--; | |
| return null; | |
| } | |
| /** | |
| * Tiro duplo - ataca em duas altitudes diferentes | |
| */ | |
| @Override | |
| public Projectile doubleShot(int posX, int direction) { | |
| // Define a segunda altitude para o tiro (diferente da atual) | |
| int currentAlt = this.posY; | |
| int secondAlt = (currentAlt + 1) % (maxAltitude + 1); | |
| // Guarda essa altitude para ser usada pelo BattleMain | |
| this.secondShotAltitude = secondAlt; | |
| // Retorna o projétil principal | |
| return new Projectile(posX, this.posY, direction, 1, "=>"); | |
| } | |
| /** | |
| * Míssil nuclear - dano massivo | |
| */ | |
| @Override | |
| public Projectile nuclearMissile(int posX, int direction) { | |
| if (missileCooldown == 0) { | |
| missileCooldown = 5; // Longo cooldown para o poder nuclear | |
| return new Projectile(posX, this.posY, direction, 1, "-]=>"); | |
| } | |
| return null; | |
| } | |
| /** | |
| * Radar - detecta projéteis inimigos | |
| */ | |
| @Override | |
| public void radarScan(ArrayList<Projectile> projectiles, int enemyPosX, int enemyPosY) { | |
| // Implementação básica: apenas detecta projéteis próximos | |
| // Em uma implementação mais avançada, você poderia usar essas informações | |
| // para ajustar seu movimento e evitar projéteis | |
| } | |
| }''' | |
| TEAM2_TEMPLATE = '''import java.util.ArrayList; | |
| import java.util.Random; | |
| /** | |
| * Time 2 - Configure sua aeronave! | |
| * | |
| * SISTEMA DE PONTOS: | |
| * - Você tem 100 pontos para distribuir entre os atributos | |
| * - Escolha com sabedoria para criar uma aeronave competitiva | |
| * | |
| * ATRIBUTOS: | |
| * - speed: Velocidade da aeronave (1-10) - Afeta quão rápido sua aeronave pode se mover | |
| * - fireRate: Taxa de disparo (1-10) - Controla com que frequência sua aeronave pode atirar | |
| * - maneuverability: Manobrabilidade (1-10) - Facilita mudanças de altitude e esquivas | |
| * - shotPower: Poder do tiro normal (5-20) - Dano causado por tiros normais | |
| * - supersonicPower: Poder do tiro supersônico (10-30) - Dano causado por tiros supersônicos | |
| * - missilePower: Poder do míssil (15-40) - Dano causado pelo míssil especial | |
| * - defense: Defesa (5-25) - Reduz o dano recebido | |
| * - stealthChance: Chance de furtividade (0-20) - Probabilidade de evitar ataques | |
| * - radar: Radar (0-10) - Capacidade de detectar projéteis inimigos | |
| * - doubleShot: Tiro duplo (0-10) - Permite disparar em duas altitudes diferentes | |
| * - nuclearPower: Poder nuclear (0-10) - Poder do míssil nuclear (dano massivo) | |
| * | |
| * SÍMBOLOS: | |
| * - Aeronave: T2 (Time 2) | |
| * - Tiro normal: <- | |
| * - Tiro supersônico: << | |
| * - Míssil especial: <= | |
| * - Tiro duplo: <= | |
| * - Míssil nuclear: <-N- | |
| */ | |
| public class Team2Aircraft extends Aircraft { | |
| private Random random = new Random(); | |
| private int maxAltitude; // Armazena a altura máxima do campo | |
| public Team2Aircraft() { | |
| super( | |
| // DISTRIBUA 100 PONTOS ENTRE ESSES ATRIBUTOS | |
| 5, // Velocidade (1-10) | |
| 5, // Taxa de fogo (1-10) | |
| 5, // Manobrabilidade (1-10) | |
| 15, // Dano do tiro normal (5-20) | |
| 20, // Dano do tiro supersônico (10-30) | |
| 25, // Dano do míssil (15-40) | |
| 15, // Defesa (5-25) | |
| 5, // Chance de furtividade (0-20) | |
| 2, // Radar (0-10) | |
| 2, // Tiro duplo (0-10) | |
| 1, // Poder nuclear (0-10) | |
| "◀" // Símbolo da aeronave (não altere) | |
| ); | |
| // IMPORTANTE: A soma de todos os atributos deve ser <= 100 | |
| // Exemplo: 5+5+5+15+20+25+15+5+2+2+1 = 100 | |
| // Verifica dinamicamente a altura do campo (será definida pelo BattleMain) | |
| try { | |
| String heightEnv = System.getProperty("battlefield.height", "3"); | |
| maxAltitude = Integer.parseInt(heightEnv) - 1; | |
| } catch (Exception e) { | |
| maxAltitude = 2; // Valor padrão se não conseguir ler | |
| } | |
| } | |
| /** | |
| * Controla o movimento da aeronave. | |
| * Valor padrão: Movimento aleatório baseado na velocidade | |
| * Dica: Você pode personalizar para criar padrões de movimento mais inteligentes | |
| */ | |
| @Override | |
| public int move() { | |
| // Retorna um número entre -speed/2 e +speed | |
| return random.nextInt(speed + 1) - speed / 2; | |
| } | |
| /** | |
| * Controla a mudança de altitude da aeronave. | |
| * Valor padrão: Mudança aleatória entre subir, descer ou manter altitude | |
| * Dica: Uma boa estratégia pode aumentar suas chances de esquiva | |
| */ | |
| @Override | |
| public int changeAltitude() { | |
| int direction = random.nextInt(3) - 1; // -1 (descer), 0 (manter), 1 (subir) | |
| this.posY = Math.max(0, Math.min(maxAltitude, posY + direction)); | |
| return direction; | |
| } | |
| /** | |
| * Tiro normal - mais frequente, menos dano | |
| */ | |
| @Override | |
| public Projectile shoot(int posX, int direction) { | |
| return new Projectile(posX, this.posY, direction, 1, "<-"); | |
| } | |
| /** | |
| * Tiro supersônico - mais rápido, mais dano | |
| */ | |
| @Override | |
| public Projectile shootSupersonic(int posX, int direction) { | |
| return new Projectile(posX, this.posY, direction, 2, "<<"); | |
| } | |
| /** | |
| * Míssil especial - muito dano, com cooldown | |
| */ | |
| @Override | |
| public Projectile specialMissile(int posX, int direction) { | |
| if (missileCooldown == 0) { | |
| missileCooldown = 3; // Espera 3 turnos para usar novamente | |
| return new Projectile(posX, this.posY, direction, 1, "<="); | |
| } | |
| missileCooldown--; | |
| return null; | |
| } | |
| /** | |
| * Tiro duplo - ataca em duas altitudes diferentes | |
| */ | |
| @Override | |
| public Projectile doubleShot(int posX, int direction) { | |
| // Define a segunda altitude para o tiro (diferente da atual) | |
| int currentAlt = this.posY; | |
| int secondAlt = (currentAlt + 1) % (maxAltitude + 1); | |
| // Guarda essa altitude para ser usada pelo BattleMain | |
| this.secondShotAltitude = secondAlt; | |
| // Retorna o projétil principal | |
| return new Projectile(posX, this.posY, direction, 1, "<="); | |
| } | |
| /** | |
| * Míssil nuclear - dano massivo | |
| */ | |
| @Override | |
| public Projectile nuclearMissile(int posX, int direction) { | |
| if (missileCooldown == 0) { | |
| missileCooldown = 5; // Longo cooldown para o poder nuclear | |
| return new Projectile(posX, this.posY, direction, 1, "<[=-"); | |
| } | |
| return null; | |
| } | |
| /** | |
| * Radar - detecta projéteis inimigos | |
| */ | |
| @Override | |
| public void radarScan(ArrayList<Projectile> projectiles, int enemyPosX, int enemyPosY) { | |
| // Implementação básica: apenas detecta projéteis próximos | |
| // Em uma implementação mais avançada, você poderia usar essas informações | |
| // para ajustar seu movimento e evitar projéteis | |
| } | |
| }''' | |
| # Código base das classes Aircraft e Projectile | |
| aircraft_code = """ | |
| import java.util.ArrayList; | |
| public abstract class Aircraft { | |
| protected int health; // Agora definido externamente | |
| protected int speed; | |
| protected int fireRate; | |
| protected int maneuverability; | |
| protected int shotPower; | |
| protected int supersonicPower; | |
| protected int missilePower; | |
| protected int defense; | |
| protected int stealthChance; | |
| protected int radar; | |
| protected int doubleShot; | |
| protected int doubleShotPower; | |
| protected int nuclearPower; | |
| protected int secondShotAltitude = -1; | |
| protected int missileCooldown = 0; | |
| protected int posY = 1; | |
| protected String symbol; | |
| protected static final int TOTAL_POINTS = 100; | |
| public Aircraft(int speed, int fireRate, int maneuverability, int shotPower, int supersonicPower, | |
| int missilePower, int defense, int stealthChance, int radar, int doubleShot, | |
| int nuclearPower, String symbol) { | |
| this.health = 100; // Valor padrão que será substituído | |
| this.speed = speed; | |
| this.fireRate = fireRate; | |
| this.maneuverability = maneuverability; | |
| this.shotPower = shotPower; | |
| this.supersonicPower = supersonicPower; | |
| this.missilePower = missilePower; | |
| this.defense = defense; | |
| this.stealthChance = stealthChance; | |
| this.radar = radar; | |
| this.doubleShot = doubleShot; | |
| this.doubleShotPower = doubleShot; | |
| this.nuclearPower = nuclearPower; | |
| this.symbol = symbol; | |
| validateAttributes(); | |
| } | |
| public void setInitialHealth(int health) { | |
| this.health = health; | |
| } | |
| private void validateAttributes() { | |
| int total = speed + fireRate + maneuverability + shotPower + supersonicPower + | |
| missilePower + defense + stealthChance + radar + doubleShot + nuclearPower; | |
| if (total > TOTAL_POINTS) { | |
| throw new IllegalArgumentException("Erro: A soma dos atributos excede " + TOTAL_POINTS + " pontos! Total: " + total); | |
| } | |
| } | |
| public abstract int move(); | |
| public abstract int changeAltitude(); | |
| public abstract Projectile shoot(int posX, int direction); | |
| public abstract Projectile shootSupersonic(int posX, int direction); | |
| public abstract Projectile specialMissile(int posX, int direction); | |
| public abstract Projectile doubleShot(int posX, int direction); | |
| public abstract Projectile nuclearMissile(int posX, int direction); | |
| public abstract void radarScan(ArrayList<Projectile> projectiles, int enemyPosX, int enemyPosY); | |
| public int getHealth() { | |
| return health; | |
| } | |
| public void takeDamage(int damage) { | |
| this.health -= Math.max(0, damage - (defense / 10)); | |
| } | |
| public boolean isAlive() { | |
| return health > 0; | |
| } | |
| public int getPositionY() { | |
| return posY; | |
| } | |
| public int getSecondShotAltitude() { | |
| int alt = secondShotAltitude; | |
| secondShotAltitude = -1; // Reset após uso | |
| return alt; | |
| } | |
| } | |
| """ | |
| projectile_code = """ | |
| public class Projectile { | |
| int posX; | |
| int posY; | |
| int direction; | |
| int speed; | |
| String symbol; | |
| int power = 0; // Poder do projétil, usado para dano personalizado | |
| public Projectile(int posX, int posY, int direction, int speed, String symbol) { | |
| this.posX = posX; | |
| this.posY = posY; | |
| this.direction = direction; | |
| this.speed = speed; | |
| this.symbol = symbol; | |
| } | |
| public Projectile(int posX, int posY, int direction, int speed, String symbol, int power) { | |
| this(posX, posY, direction, speed, symbol); | |
| this.power = power; | |
| } | |
| public void move() { | |
| posX += direction * speed; | |
| } | |
| public boolean isOutOfBounds(int screenWidth) { | |
| return (posX < 0 || posX >= screenWidth); | |
| } | |
| public int getPower() { | |
| return power; | |
| } | |
| } | |
| """ | |
| def run_battle(code1, code2, screen_width, battlefield_height, p1_start_pos, p2_start_pos, team1_health, team2_health): | |
| # Verificar se Java está disponível | |
| if not java_available: | |
| yield f""" | |
| <div style="padding:20px; background-color:#ffe6e6; border:2px solid #ff4444; border-radius:5px; margin:10px;"> | |
| <h3 style="color:#cc0000;">❌ Java não está disponível</h3> | |
| <p>Este aplicativo requer Java para compilar e executar as aeronaves.</p> | |
| <p><strong>Soluções:</strong></p> | |
| <ul> | |
| <li>Execute localmente com Java instalado</li> | |
| <li>Use um ambiente Docker com Java</li> | |
| <li>Aguarde enquanto tentamos instalar Java automaticamente...</li> | |
| </ul> | |
| <p><em>Status: {java_message}</em></p> | |
| </div> | |
| """ | |
| return | |
| # Caminhos dos arquivos Java | |
| aircraft_path = "combat_classes/Aircraft.java" | |
| projectile_path = "combat_classes/Projectile.java" | |
| class1_path = "combat_classes/Team1Aircraft.java" | |
| class2_path = "combat_classes/Team2Aircraft.java" | |
| main_path = "combat_classes/BattleMain.java" | |
| # Gerar o código do BattleMain com os parâmetros configuráveis | |
| battle_main_code = f""" | |
| import java.util.ArrayList; | |
| import java.util.Iterator; | |
| import java.util.Random; | |
| public class BattleMain {{ | |
| public static void main(String[] args) {{ | |
| // Definir a altura do campo como propriedade do sistema | |
| System.setProperty("battlefield.height", "{battlefield_height}"); | |
| Aircraft team1 = new Team1Aircraft(); | |
| Aircraft team2 = new Team2Aircraft(); | |
| // Definir a vida inicial de cada aeronave | |
| team1.setInitialHealth({team1_health}); | |
| team2.setInitialHealth({team2_health}); | |
| Random random = new Random(); | |
| int p1PosX = {p1_start_pos}; | |
| int p2PosX = {p2_start_pos}; | |
| int screenWidth = {screen_width}; | |
| int battlefieldHeight = {battlefield_height}; | |
| ArrayList<Projectile> projectiles = new ArrayList<>(); | |
| // Inicializar as altitudes das naves em uma posição média do campo | |
| team1.posY = battlefieldHeight / 2; | |
| team2.posY = battlefieldHeight / 2; | |
| while (team1.isAlive() && team2.isAlive()) {{ | |
| System.out.println("\\n=== NOVO TURNO ==="); | |
| System.out.flush(); | |
| String[][] battlefield = new String[battlefieldHeight][screenWidth]; | |
| for (int row = 0; row < battlefieldHeight; row++) {{ | |
| for (int i = 0; i < screenWidth; i++) {{ | |
| battlefield[row][i] = " "; | |
| }} | |
| }} | |
| // Radar scan para detectar projéteis | |
| team1.radarScan(projectiles, p2PosX, team2.getPositionY()); | |
| team2.radarScan(projectiles, p1PosX, team1.getPositionY()); | |
| // Movimento das aeronaves | |
| p1PosX += team1.move(); | |
| p2PosX += team2.move(); | |
| p1PosX = Math.max(0, Math.min(screenWidth - 1, p1PosX)); | |
| p2PosX = Math.max(0, Math.min(screenWidth - 1, p2PosX)); | |
| // Mudança de altitude | |
| team1.changeAltitude(); | |
| team2.changeAltitude(); | |
| // Garantir que a altitude não exceda o novo tamanho do campo de batalha | |
| team1.posY = Math.min(team1.posY, battlefieldHeight - 1); | |
| team2.posY = Math.min(team2.posY, battlefieldHeight - 1); | |
| // Atirar para Time 1 | |
| if (random.nextInt(10) < team1.fireRate) {{ | |
| Projectile shot = null; | |
| int shotType = random.nextInt(100); | |
| // Escolha aleatória do tipo de tiro baseado na probabilidade | |
| if (shotType < 5 && team1.nuclearPower > 0) {{ | |
| // Tiro nuclear (baixa probabilidade) | |
| shot = team1.nuclearMissile(p1PosX, 1); | |
| if (shot != null) {{ | |
| System.out.println("!!! Time 1 lançou um MISSIL NUCLEAR!"); | |
| }} | |
| }} else if (shotType < 15 && team1.doubleShot > 0) {{ | |
| // Tiro duplo | |
| shot = team1.doubleShot(p1PosX, 1); | |
| if (shot != null) {{ | |
| System.out.println(">>> Time 1 disparou um TIRO DUPLO!"); | |
| // Adicionar o segundo projétil em uma altitude diferente | |
| int secAlt = team1.getSecondShotAltitude(); | |
| if (secAlt >= 0 && secAlt < battlefieldHeight) {{ | |
| projectiles.add(new Projectile(p1PosX, secAlt, 1, 1, "->", team1.doubleShotPower)); | |
| }} | |
| }} | |
| }} else if (shotType < 30) {{ | |
| // Míssil especial | |
| shot = team1.specialMissile(p1PosX, 1); | |
| }} else if (shotType < 60) {{ | |
| // Tiro supersônico | |
| shot = team1.shootSupersonic(p1PosX, 1); | |
| }} else {{ | |
| // Tiro normal | |
| shot = team1.shoot(p1PosX, 1); | |
| }} | |
| if (shot != null) {{ | |
| // Garantir que a altitude do projétil não exceda o campo de batalha | |
| shot.posY = Math.min(shot.posY, battlefieldHeight - 1); | |
| projectiles.add(shot); | |
| }} | |
| }} | |
| // Atirar para Time 2 | |
| if (random.nextInt(10) < team2.fireRate) {{ | |
| Projectile shot = null; | |
| int shotType = random.nextInt(100); | |
| // Escolha aleatória do tipo de tiro baseado na probabilidade | |
| if (shotType < 5 && team2.nuclearPower > 0) {{ | |
| // Tiro nuclear (baixa probabilidade) | |
| shot = team2.nuclearMissile(p2PosX, -1); | |
| if (shot != null) {{ | |
| System.out.println("!!! Time 2 lançou um MISSIL NUCLEAR!"); | |
| }} | |
| }} else if (shotType < 15 && team2.doubleShot > 0) {{ | |
| // Tiro duplo | |
| shot = team2.doubleShot(p2PosX, -1); | |
| if (shot != null) {{ | |
| System.out.println("<<< Time 2 disparou um TIRO DUPLO!"); | |
| // Adicionar o segundo projétil em uma altitude diferente | |
| int secAlt = team2.getSecondShotAltitude(); | |
| if (secAlt >= 0 && secAlt < battlefieldHeight) {{ | |
| projectiles.add(new Projectile(p2PosX, secAlt, -1, 1, "<-", team2.doubleShotPower)); | |
| }} | |
| }} | |
| }} else if (shotType < 30) {{ | |
| // Míssil especial | |
| shot = team2.specialMissile(p2PosX, -1); | |
| }} else if (shotType < 60) {{ | |
| // Tiro supersônico | |
| shot = team2.shootSupersonic(p2PosX, -1); | |
| }} else {{ | |
| // Tiro normal | |
| shot = team2.shoot(p2PosX, -1); | |
| }} | |
| if (shot != null) {{ | |
| // Garantir que a altitude do projétil não exceda o campo de batalha | |
| shot.posY = Math.min(shot.posY, battlefieldHeight - 1); | |
| projectiles.add(shot); | |
| }} | |
| }} | |
| // Posicionar aeronaves no campo de batalha sem cores | |
| battlefield[team1.getPositionY()][p1PosX] = team1.symbol; // Time 1 | |
| battlefield[team2.getPositionY()][p2PosX] = team2.symbol; // Time 2 | |
| // Mover projéteis e verificar colisões | |
| Iterator<Projectile> iterator = projectiles.iterator(); | |
| while (iterator.hasNext()) {{ | |
| Projectile p = iterator.next(); | |
| p.move(); | |
| // Verificar colisões com Time 1 | |
| if (p.posX == p1PosX && p.posY == team1.getPositionY()) {{ | |
| int damage = 0; | |
| // Verificar se o projétil tem poder personalizado | |
| if (p.getPower() > 0) {{ | |
| damage = p.getPower(); | |
| }} else if (p.symbol.contains("<-N-")) {{ // Míssil nuclear do Time 2 | |
| damage = team2.nuclearPower * 2; | |
| System.out.println("!!! MISSIL NUCLEAR do Time 2 atingiu o Time 1!"); | |
| }} else if (p.symbol.contains("<=")) {{ // Tiro duplo do Time 2 | |
| damage = team2.doubleShotPower; | |
| }} else if (p.symbol.equals("<=")) {{ | |
| damage = team2.missilePower; | |
| }} else if (p.symbol.equals("<<")) {{ | |
| damage = team2.supersonicPower; | |
| }} else {{ | |
| damage = team2.shotPower; | |
| }} | |
| if (random.nextInt(100) >= team1.stealthChance) {{ | |
| team1.takeDamage(damage); | |
| System.out.println("*** Aeronave do Time 1 atingida! -" + damage + " pontos"); | |
| }} else {{ | |
| System.out.println("--- Aeronave do Time 1 esquivou!"); | |
| if (team1.radar > 0) {{ | |
| System.out.println("... Radar do Time 1 detectou o projétil!"); | |
| }} | |
| }} | |
| iterator.remove(); | |
| continue; | |
| }} | |
| // Verificar colisões com Time 2 | |
| if (p.posX == p2PosX && p.posY == team2.getPositionY()) {{ | |
| int damage = 0; | |
| // Verificar se o projétil tem poder personalizado | |
| if (p.getPower() > 0) {{ | |
| damage = p.getPower(); | |
| }} else if (p.symbol.contains("-N->")) {{ // Míssil nuclear do Time 1 | |
| damage = team1.nuclearPower * 2; | |
| System.out.println("!!! MISSIL NUCLEAR do Time 1 atingiu o Time 2!"); | |
| }} else if (p.symbol.contains("=>")) {{ // Tiro duplo do Time 1 | |
| damage = team1.doubleShotPower; | |
| }} else if (p.symbol.equals("=>")) {{ | |
| damage = team1.missilePower; | |
| }} else if (p.symbol.equals(">>")) {{ | |
| damage = team1.supersonicPower; | |
| }} else {{ | |
| damage = team1.shotPower; | |
| }} | |
| if (random.nextInt(100) >= team2.stealthChance) {{ | |
| team2.takeDamage(damage); | |
| System.out.println("*** Aeronave do Time 2 atingida! -" + damage + " pontos"); | |
| }} else {{ | |
| System.out.println("--- Aeronave do Time 2 esquivou!"); | |
| if (team2.radar > 0) {{ | |
| System.out.println("... Radar do Time 2 detectou o projétil!"); | |
| }} | |
| }} | |
| iterator.remove(); | |
| continue; | |
| }} | |
| // Remover projéteis fora dos limites | |
| if (p.isOutOfBounds(screenWidth)) {{ | |
| iterator.remove(); | |
| continue; | |
| }} | |
| // Mostrar projéteis no campo de batalha sem cores | |
| if (p.posX >= 0 && p.posX < screenWidth && p.posY >= 0 && p.posY < battlefieldHeight) {{ | |
| battlefield[p.posY][p.posX] = p.symbol; | |
| }} | |
| }} | |
| // Mostrar campo de batalha | |
| for (int row = 0; row < battlefieldHeight; row++) {{ | |
| for (int i = 0; i < screenWidth; i++) {{ | |
| System.out.print(battlefield[row][i]); | |
| }} | |
| System.out.println(); | |
| }} | |
| // Mostrar status de vida e posições das aeronaves | |
| System.out.println("Vida Time 1: " + team1.getHealth() + " | Vida Time 2: " + team2.getHealth()); | |
| System.out.println("Posições - Time 1: (" + p1PosX + "," + team1.getPositionY() + ") | Time 2: (" + p2PosX + "," + team2.getPositionY() + ")"); | |
| System.out.flush(); | |
| // Pausa para visualização | |
| try {{ | |
| Thread.sleep(200); | |
| }} catch (InterruptedException e) {{ | |
| System.err.println("Erro na pausa: " + e.getMessage()); | |
| }} | |
| }} | |
| if (team1.isAlive()) {{ | |
| System.out.println("*** Time 1 venceu! ***"); | |
| }} else {{ | |
| System.out.println("*** Time 2 venceu! ***"); | |
| }} | |
| System.out.flush(); | |
| }} | |
| }}""" | |
| # Salvar os arquivos Java | |
| with open(aircraft_path, "w") as f: | |
| f.write(aircraft_code) | |
| with open(projectile_path, "w") as f: | |
| f.write(projectile_code) | |
| with open(class1_path, "w") as f1: | |
| f1.write(code1) | |
| with open(class2_path, "w") as f2: | |
| f2.write(code2) | |
| with open(main_path, "w") as f_main: | |
| f_main.write(battle_main_code) | |
| try: | |
| # Compilar os arquivos Java | |
| for java_file in [aircraft_path, projectile_path, class1_path, class2_path, main_path]: | |
| compile_result = subprocess.run(["javac", "-cp", "combat_classes", "-d", "combat_classes", java_file], capture_output=True, text=True) | |
| if compile_result.returncode != 0: | |
| return f"❌ Erro na compilação:\\n{compile_result.stderr}" | |
| # Executar a simulação | |
| process = subprocess.Popen( | |
| ["java", "-cp", "combat_classes", "BattleMain"], | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.PIPE, | |
| text=True, | |
| bufsize=1 | |
| ) | |
| # Usar deque para manter os últimos turnos na visualização | |
| turnos = deque(maxlen=4) # Últimos 4 turnos para visualização fluida | |
| # Guardar saída completa | |
| saida_completa = "" | |
| turno_atual = "" | |
| coletando_turno = False | |
| resultado_final = None | |
| for line in iter(process.stdout.readline, ""): | |
| saida_completa += line | |
| # Detectar novo turno | |
| if "=== NOVO TURNO ===" in line: | |
| if turno_atual: | |
| turnos.append(turno_atual) | |
| turno_atual = line | |
| coletando_turno = True | |
| elif coletando_turno: | |
| turno_atual += line | |
| # Verificar se é o resultado final | |
| if "venceu" in line: | |
| resultado_final = line | |
| # Atualizar a cada turno | |
| texto = "".join(list(turnos)) + turno_atual | |
| # Conversão de cores usando função robusta | |
| formatted_texto = format_colors(texto) | |
| # Script de scroll | |
| scroll_js = """ | |
| <script> | |
| (function() { | |
| function forceScrollBottom() { | |
| const container = document.getElementById('battle-container'); | |
| if (container) { | |
| container.scrollTop = container.scrollHeight * 10; | |
| setTimeout(() => { | |
| container.scrollTop = container.scrollHeight * 10; | |
| }, 50); | |
| } | |
| } | |
| forceScrollBottom(); | |
| const scrollInterval = setInterval(forceScrollBottom, 100); | |
| setTimeout(() => { clearInterval(scrollInterval); }, 2000); | |
| })(); | |
| </script> | |
| """ | |
| html_output = f""" | |
| <div id="battle-container" style="height:400px; overflow:auto; border:1px solid #ccc; padding:10px; | |
| font-family:monospace; white-space:pre; background-color:#f8f8f8;"> | |
| {formatted_texto} | |
| </div> | |
| {scroll_js} | |
| """ | |
| yield html_output | |
| time.sleep(0.05) | |
| # Ao final, mostrar resultado destacado | |
| if resultado_final: | |
| # Usar função robusta para formatar cores | |
| formatted_saida = format_colors(saida_completa) | |
| vencedor = "Time 1" if "Time 1 venceu" in resultado_final else "Time 2" | |
| cor = "blue" if vencedor == "Time 1" else "red" | |
| final_scroll_js = """ | |
| <script> | |
| (function() { | |
| function finalForceScroll() { | |
| const container = document.querySelector('#historico-completo'); | |
| if (container) { | |
| container.scrollTop = container.scrollHeight * 20; | |
| } | |
| } | |
| finalForceScroll(); | |
| for (let i = 1; i <= 20; i++) { | |
| setTimeout(finalForceScroll, i * 100); | |
| } | |
| const scrollInterval = setInterval(finalForceScroll, 200); | |
| setTimeout(() => { clearInterval(scrollInterval); }, 5000); | |
| })(); | |
| </script> | |
| """ | |
| final_html = f""" | |
| <div> | |
| <div style="padding:15px; background-color:#e9f7e9; border:2px solid #4CAF50; | |
| margin:15px 0; text-align:center; border-radius:5px;"> | |
| <h3 style="color:{cor}; margin:0; font-size:24px;">🏆 {vencedor} VENCEU! 🏆</h3> | |
| </div> | |
| <h4>Histórico Completo da Batalha:</h4> | |
| <div id="historico-completo" style="height:500px; overflow:auto; border:1px solid #ccc; padding:10px; | |
| font-family:monospace; white-space:pre; background-color:#f8f8f8;"> | |
| {formatted_saida} | |
| </div> | |
| {final_scroll_js} | |
| </div> | |
| """ | |
| yield final_html | |
| except Exception as e: | |
| yield f"⚠ Erro inesperado: {str(e)}" | |
| # Funções para carregar templates | |
| def load_team1_template(): | |
| return TEAM1_TEMPLATE | |
| def load_team2_template(): | |
| return TEAM2_TEMPLATE | |
| # Função para preparar e executar a batalha | |
| def prepare_battle(code1, code2, width, height, p1_pos, p2_auto, p2_pos, t1_health, t2_health): | |
| # Se a posição do Time 2 é automática, calcule-a com base na largura | |
| final_p2_pos = width - 2 if p2_auto else p2_pos | |
| # Executar a simulação e retornar o iterador | |
| for output in run_battle(code1, code2, width, height, p1_pos, final_p2_pos, t1_health, t2_health): | |
| yield output | |
| # Interface Gradio | |
| with gr.Blocks(title="Java Air Combat", theme=gr.themes.Soft()) as app: | |
| gr.Markdown("# ⯐🛦🛧 JAVA-Aircraft-Combat - Time 1 vs Time 2") | |
| gr.Markdown(""" | |
| ## Instruções Rápidas | |
| 1. Use os botões "Carregar Template" para obter um modelo editável para cada time | |
| 2. Personalize os atributos da aeronave (máximo de 100 pontos) | |
| 3. Configure a arena no painel de configurações abaixo se desejar | |
| 4. Clique em "🔥 Combate!" para iniciar a batalha | |
| **NOVIDADES**: Agora suas aeronaves podem ter radar para detectar projéteis, tiro duplo para atacar em duas altitudes diferentes e até mísseis nucleares para dano massivo! | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| team1_code = gr.Textbox(label="🟦 Código Time 1", lines=20, placeholder="Clique em 'Carregar Template Time 1' para começar") | |
| team1_template_btn = gr.Button("📝 Carregar Template Time 1", variant="secondary") | |
| with gr.Column(): | |
| team2_code = gr.Textbox(label="🟥 Código Time 2", lines=20, placeholder="Clique em 'Carregar Template Time 2' para começar") | |
| team2_template_btn = gr.Button("📝 Carregar Template Time 2", variant="secondary") | |
| # Conectar botões às funções | |
| team1_template_btn.click(load_team1_template, inputs=[], outputs=team1_code) | |
| team2_template_btn.click(load_team2_template, inputs=[], outputs=team2_code) | |
| with gr.Accordion("⚙️ Configurações da Arena", open=False): | |
| with gr.Row(): | |
| screen_width = gr.Slider(minimum=50, maximum=200, value=100, step=10, | |
| label="Largura da Tela", info="Define a largura do campo de batalha") | |
| battlefield_height = gr.Slider(minimum=3, maximum=7, value=3, step=1, | |
| label="Altura do Campo", info="Define o número de linhas no campo de batalha") | |
| with gr.Row(): | |
| p1_start_pos = gr.Slider(minimum=0, maximum=49, value=2, step=1, | |
| label="Posição Inicial Time 1", info="Define onde o Time 1 começa na arena") | |
| p2_start_pos_auto = gr.Checkbox(label="Posicionar Time 2 automaticamente", | |
| info="Se marcado, o Time 2 será posicionado no lado oposto", value=True) | |
| p2_start_pos = gr.Slider(minimum=51, maximum=200, value=98, step=1, | |
| label="Posição Inicial Time 2", info="Define onde o Time 2 começa na arena", | |
| visible=False) | |
| with gr.Row(): | |
| team1_health = gr.Slider(minimum=50, maximum=500, value=100, step=10, | |
| label="❤️ Vida Time 1", info="Define a vida inicial da aeronave do Time 1") | |
| team2_health = gr.Slider(minimum=50, maximum=500, value=100, step=10, | |
| label="❤️ Vida Time 2", info="Define a vida inicial da aeronave do Time 2") | |
| # Botão para equalizar as vidas | |
| def equalize_health(health_value): | |
| return health_value, health_value | |
| equalize_btn = gr.Button("🔄 Igualar Vidas", variant="secondary") | |
| equalize_btn.click(fn=equalize_health, inputs=team1_health, outputs=[team1_health, team2_health]) | |
| # Lógica para mostrar/esconder a posição do Time 2 | |
| def toggle_p2_pos(auto_checked): | |
| return {"visible": not auto_checked} | |
| p2_start_pos_auto.change(toggle_p2_pos, inputs=p2_start_pos_auto, outputs=p2_start_pos) | |
| # Atualizar posição do Time 2 automaticamente com base na largura da tela | |
| def update_p2_pos(width): | |
| return width - 2 | |
| screen_width.change(update_p2_pos, inputs=screen_width, outputs=p2_start_pos) | |
| btn = gr.Button("🔥 Combate!", variant="primary", size="lg") | |
| # Configurar o componente HTML | |
| css = """ | |
| <style> | |
| #battle-result .prose { | |
| padding: 0 !important; | |
| margin: 0 !important; | |
| display: block !important; | |
| visibility: visible !important; | |
| } | |
| </style> | |
| """ | |
| output = gr.HTML(label="Resultado do Combate", elem_id="battle-result", value=css) | |
| # Conectar o botão à função | |
| btn.click(fn=prepare_battle, | |
| inputs=[team1_code, team2_code, screen_width, battlefield_height, | |
| p1_start_pos, p2_start_pos_auto, p2_start_pos, | |
| team1_health, team2_health], | |
| outputs=output) | |
| # Adicionar informações de rodapé | |
| gr.Markdown(""" | |
| ### Dicas | |
| - Equilibre seus pontos! Uma distribuição balanceada geralmente é melhor. | |
| - Lembre que cada atributo tem limites (indicados nos comentários). | |
| - O radar permite detectar projéteis inimigos e realizar manobras evasivas automáticas. | |
| - O tiro duplo ataca em duas altitudes ao mesmo tempo, aumentando suas chances de acerto. | |
| - O míssil nuclear causa dano massivo, mas tem um longo cooldown de 5 turnos. | |
| - Use toda a altura do campo (configure nas opções) para estratégias mais interessantes! | |
| - Agora você pode ajustar a vida inicial das aeronaves para batalhas mais longas ou equilibrar times desiguais! | |
| Desenvolvido para a disciplina de Programação. Bons combates! | |
| """) | |
| if __name__ == "__main__": | |
| app.launch() |