package es.upm.dit.adsw.cifrasyletras.juego;

import es.upm.dit.adsw.cifrasyletras.cifras.Cifras;
import es.upm.dit.adsw.cifrasyletras.cifras.CifrasPracticaCompetitiva;
import es.upm.dit.adsw.cifrasyletras.letras.Letras;
import es.upm.dit.adsw.cifrasyletras.letras.LetrasPracticaCompetitiva;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class JuegoCompetitivo {
    //Hay 2 jugadores, por lo que cada ronda devuelve dos soluciones con dos puntuaciones acumuladas
    private static final int NUM_LETRAS = 9;
    private static final int NUM_CIFRAS = 6;

    private final int pruebasCifras;
    private final int pruebasLetras;

    private final Letras jugadorLetras1;
    private final Letras jugadorLetras2;
    private final Cifras jugadorCifras1;
    private final Cifras jugadorCifras2;

    private final MonitorJuegoCompetitivo monitor;

    private final ValidadorCifrasConParentesis validadorCifras;
    private final ValidadorLetras validadorLetras;

    private int puntosJ1 = 0;
    private int puntosJ2 = 0;

    public static void main(String[] args) {
        MonitorJuegoCompetitivo monitor = new MonitorJuegoCompetitivo();
        JuegoCompetitivo juego = new JuegoCompetitivo(
                3, 3,
                new LetrasPracticaCompetitiva(1, monitor),
                new LetrasPracticaCompetitiva(2, monitor),
                new CifrasPracticaCompetitiva(1, monitor),
                new CifrasPracticaCompetitiva(2, monitor),
                monitor
        );
        juego.jugar();
    }

    public JuegoCompetitivo(int pruebasCifras, int pruebasLetras,
                             Letras jugadorLetras1, Letras jugadorLetras2,
                             Cifras jugadorCifras1, Cifras jugadorCifras2,
                             MonitorJuegoCompetitivo monitor) {
        this.pruebasCifras = pruebasCifras;
        this.pruebasLetras = pruebasLetras;
        this.jugadorLetras1 = jugadorLetras1;
        this.jugadorLetras2 = jugadorLetras2;
        this.jugadorCifras1 = jugadorCifras1;
        this.jugadorCifras2 = jugadorCifras2;
        this.monitor = monitor;
        this.validadorCifras = new ValidadorCifrasConParentesis();
        this.validadorLetras = new ValidadorLetras("data/es.txt");

        if (!(jugadorLetras1 instanceof Thread) || !(jugadorLetras2 instanceof Thread)) {
            System.err.println("Los solvers de letras deben extender Thread");
            System.exit(1);
        }
        if (!(jugadorCifras1 instanceof Thread) || !(jugadorCifras2 instanceof Thread)) {
            System.err.println("Los solvers de cifras deben extender Thread");
            System.exit(1);
        }
    }

    public void jugar() {
        System.out.println("=== COMENZANDO JUEGO COMPETITIVO ===");

        // Arrancar los cuatro hilos
        ((Thread) jugadorCifras1).start();
        ((Thread) jugadorCifras2).start();
        ((Thread) jugadorLetras1).start();
        ((Thread) jugadorLetras2).start();

        System.out.println("Pruebas de letras: " + pruebasLetras);
        System.out.println("Pruebas de cifras: " + pruebasCifras);
        System.out.println();

        jugarPruebasLetras();
        jugarPruebasCifras();

        imprimirResultadoFinal();
        System.exit(0);
    }

    private void jugarPruebasLetras() {
        System.out.println("--- PRUEBAS DE LETRAS ---");
        for (int i = 0; i < pruebasLetras; i++) {
            String letras = generarPruebaLetrasPorFrecuencias();
            System.out.println("\n=== RONDA " + (i + 1) + ": LETRAS ===");
            System.out.println("Letras disponibles: " + letras);

            String[] soluciones = monitor.publicarPruebaLetras(letras);
            String solJ1 = soluciones[0];
            String solJ2 = soluciones[1];

            System.out.println("J1: " + (solJ1 != null ? solJ1 : "(sin solución)"));
            System.out.println("J2: " + (solJ2 != null ? solJ2 : "(sin solución)"));

            int[] puntos = calcularPuntosLetras(solJ1, solJ2, letras);
            puntosJ1 += puntos[0];
            puntosJ2 += puntos[1];

            System.out.println("Puntos esta ronda → J1: " + puntos[0] + ", J2: " + puntos[1]);
            System.out.println("Acumulado → J1: " + puntosJ1 + ", J2: " + puntosJ2);
        }
    }

    private void jugarPruebasCifras() {
        System.out.println("\n--- PRUEBAS DE CIFRAS ---");
        for (int i = 0; i < pruebasCifras; i++) {
            int objetivo = generarObjetivoCifras();
            List<Integer> numeros = generarNumerosCifras();

            System.out.println("\n=== RONDA " + (i + 1) + ": CIFRAS ===");
            System.out.println("Números disponibles: " + numeros + ", objetivo: " + objetivo);

            String[] soluciones = monitor.publicarPruebaCifras(numeros, objetivo);
            String solJ1 = soluciones[0];
            String solJ2 = soluciones[1];

            System.out.println("J1: " + (solJ1 != null ? solJ1 : "(sin solución)"));
            System.out.println("J2: " + (solJ2 != null ? solJ2 : "(sin solución)"));

            int[] puntos = calcularPuntosCifras(solJ1, solJ2, numeros, objetivo);
            puntosJ1 += puntos[0];
            puntosJ2 += puntos[1];

            System.out.println("Puntos esta ronda → J1: " + puntos[0] + ", J2: " + puntos[1]);
            System.out.println("Acumulado → J1: " + puntosJ1 + ", J2: " + puntosJ2);
        }
    }

    /**
     * Calcula los puntos de cada jugador para una prueba de cifras.
     *
     * @param solJ1    solución del jugador 1 en formato "resultado = expresion" (puede ser null)
     * @param solJ2    solución del jugador 2
     * @param numeros  números usados en la prueba
     * @param objetivo valor objetivo
     * @return array {puntosJ1, puntosJ2}
     */
    private int[] calcularPuntosCifras(String solJ1, String solJ2,
                                        List<Integer> numeros, int objetivo) {
        int distJ1 = distanciaCifras(solJ1, numeros, objetivo);
        int distJ2 = distanciaCifras(solJ2, numeros, objetivo);

        return asignarPuntos(distJ1 == 0, distJ2 == 0, distJ1, distJ2); //debe de invocar a este metodo para asignar puntos de la ronda
    } // si las distancias es igual a cero es que son perfectas y el de menor distancia sera el que mas se acerque a este 

    /**
     * Calcula los puntos de cada jugador para una prueba de letras.
     *
     * @param solJ1  palabra del jugador 1 (puede ser null)
     * @param solJ2  palabra del jugador 2
     * @param letras letras disponibles en la prueba
     * @return array {puntosJ1, puntosJ2}
     */
    private int[] calcularPuntosLetras(String solJ1, String solJ2, String letras) {
        int lenJ1 = longitudLetras(solJ1, letras);
        int lenJ2 = longitudLetras(solJ2, letras);

        boolean perfectaJ1 = (solJ1 != null && lenJ1 == letras.length());
        boolean perfectaJ2 = (solJ2 != null && lenJ2 == letras.length());

        // Para letras, "mejor" = más larga, así que usamos como distancia la longitud negada
        // (menor distancia = mejor). Aqui adaptamos la logica: mayor longitud gana.
        return asignarPuntos(perfectaJ1, perfectaJ2, -lenJ1, -lenJ2);
    }

    /**
     * Aplica la tabla de puntuacion: perfecta=10, ninguna perfecta y mejor=7, empate=5/5.
     *
     * @param perfectaJ1 si el jugador 1 tiene solucion perfecta
     * @param perfectaJ2 si el jugador 2 tiene solucion perfecta
     * @param metricaJ1  cuanto mas pequena, mejor para J1 (distancia al objetivo, o -longitud)
     * @param metricaJ2  cuanto mas pequena, mejor para J2
     * @return {puntosJ1, puntosJ2}
     */
    
    //METODO PARA ASIGNAR PUNTOS
    private int[] asignarPuntos(boolean perfectaJ1, boolean perfectaJ2, int metricaJ1, int metricaJ2) {
    	int[] puntos = new int[2];
    	if(perfectaJ1 && perfectaJ2) {
    		puntos[0] = 10; // puntuacion del jugador uno
    		puntos[1] = 10; // puntuacion del jugador dos
    	}
    	else if(perfectaJ1) {
    		puntos[0] = 10;
    		puntos[1] = 0;
    	}
    	else if(perfectaJ2) {
    		puntos[0] = 0;
    		puntos[1] = 10;
    	}
    	else if(metricaJ1 > metricaJ2) {
    		puntos[0] = 0;
    		puntos[1] = 7;
    	}
    	else if(metricaJ2 > metricaJ1) {
    		puntos[0] = 7;
    		puntos[1] = 0;
    	}
    	else if(metricaJ1 == metricaJ2 && metricaJ1 != Integer.MAX_VALUE ) {
    		puntos[0] = 5;
    		puntos[1] = 5;
    	}
    	else {
    		puntos[0] = 0;
    		puntos[1] = 0;
    	}
    	return puntos;
    	
    }

    /**
     * Valida la solucion de cifras y devuelve la distancia al objetivo.
     * Devuelve Integer.MAX_VALUE si la solución es nula o inválida.
     */
    private int distanciaCifras(String sol, List<Integer> numeros, int objetivo) {
        if (sol == null || !validadorCifras.esValida(sol, numeros)) {
            return Integer.MAX_VALUE;
        }
        try {
            int resultado = Integer.parseInt(sol.trim().split("\\s*=\\s*")[0].trim());
            return Math.abs(resultado - objetivo);
        } catch (NumberFormatException e) {
            return Integer.MAX_VALUE;
        }
    }

    /**
     * Valida la palabra y devuelve su longitud. Devuelve 0 si es nula o inválida.
     */
    private int longitudLetras(String sol, String letras) {
        if (sol == null || !validadorLetras.esValida(sol, letras)) {
            return 0;
        }
        return sol.length();
    }

    private void imprimirResultadoFinal() {
        System.out.println("\n==============================");
        System.out.println("=== RESULTADO FINAL ===");
        System.out.println("Jugador 1: " + puntosJ1 + " puntos");
        System.out.println("Jugador 2: " + puntosJ2 + " puntos");
        if (puntosJ1 > puntosJ2) {
            System.out.println("¡Gana el Jugador 1!");
        } else if (puntosJ2 > puntosJ1) {
            System.out.println("¡Gana el Jugador 2!");
        } else {
            System.out.println("¡Empate!");
        }
        System.out.println("==============================");
    }

    // -------- Generadores de pruebas (igual que JuegoConcurrente) --------

    public String generarPruebaLetrasPorFrecuencias() {
        String letrasPonderadas =
                "AAAAAAAAAAAAEEEEEEEEEEEEOOOOOOOOOIIIIIIUUUUSSSSSSSNNNNNNRRRRRRLLLLLDDDDDCCCCTTTTMMMPPPBBGGVYQHFJZXÑ"
                .toLowerCase();
        StringBuilder mano = new StringBuilder();
        Random r = new Random();
        for (int i = 0; i < NUM_LETRAS; i++) {
            mano.append(letrasPonderadas.charAt(r.nextInt(letrasPonderadas.length())));
        }
        return mano.toString();
    }

    public int generarObjetivoCifras() {
        return Math.abs((int) (Math.random() * 899)) + 101;
    }

    public List<Integer> generarNumerosCifras() {
        List<Integer> numeros = new ArrayList<>();
        List<Integer> posibles = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 25, 50, 75, 100));
        for (int i = 0; i < NUM_CIFRAS; i++) {
            int n = posibles.get((int) (Math.random() * posibles.size()));
            if (n >= 25) {
                posibles.remove((Integer) n);
            }
            numeros.add(n);
        }
        return numeros;
    }

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: