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() |