Una forma sencilla y rápida de aprender JAVA, observando y deduciendo cómo se comporta el lenguaje a través de ejemplos prácticos.

Archivo del blog

sábado, 20 de julio de 2024

Juegos V: Approximate: El Juego de Números y Operaciones.

Caracteristicas del Juego:

    . Objetivo: Calcular un número específico (el objetivo) usando los números disponibles y las operaciones aritméticas básicas.
    . Números Disponibles: Se generan seis números aleatorios que pueden usarse cada uno una sola vez en los cálculos.
    . Operaciones Permitidas: Suma, resta, multiplicación y división(siempre que el resultado sea un número entero).
    . Uso de Números: Cada número puede usarse una sola vez.
    . Resultado Entero: Todas las operaciones deben producir resultados enteros.

El algoritmo explora exhaustivamente todas las combinaciones posibles de los seis números generados aleatoriamente y las operaciones básicas, buscando alcanzar el número objetivo o el más cercano posible. Devuelve el mejor resultado encontrado junto con la expresión utilizada para llegar a él.


Estructura del código Approximate:

approximate (paquete)

└── Approximate (clase pública)
    │
    ├── static fields (campos estáticos)
    │   ├── OPERADORES (array de caracteres)
    │   ├── mejorResultado (entero)
    │   ├── mejorExpresion (cadena)
    │   └── OBJETIVO_NO_ALCANZADO (constante entera)
    │
    ├── main() (método principal)
    │
    └── private static methods (métodos privados estáticos)
        ├── generarCombinaciones()
        ├── generarPermutaciones()
        ├── generarOperaciones()
        ├── evaluarExpresion()
        ├── construirExpresion()
        ├── evaluarExpresionAritmetica()
        ├── tokenizarExpresion()
        └── evaluarTokens()


Código Java (Approximate.java):

package approximate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

public class Approximate {

    private static final char[] OPERADORES = {'+', '-', '*', '/'};
    private static int mejorResultado;
    private static String mejorExpresion;
    private static final int OBJETIVO_NO_ALCANZADO = Integer.MAX_VALUE;

    public static void main(String[] args) {
        Random random = new Random();

        int objetivo = random.nextInt(999) + 1;

        Set<Integer> numerosSet = new HashSet<>();
        while (numerosSet.size() < 6) {
            int nuevoNumero = random.nextInt(100) + 1;
            if (nuevoNumero != objetivo) {
                numerosSet.add(nuevoNumero);
            }
        }

        Integer[] numeros = numerosSet.toArray(new Integer[0]);

        System.out.println("Número objetivo: " + objetivo);
        System.out.println("Números disponibles: " + Arrays.toString(numeros));

        mejorResultado = OBJETIVO_NO_ALCANZADO;
        mejorExpresion = "";

        generarCombinaciones(numeros, objetivo);

        if (mejorResultado != OBJETIVO_NO_ALCANZADO) {
            System.out.println("Mejor aproximación: " + mejorResultado);
            System.out.println("Expresión: " + mejorExpresion);
        } else {
            System.out.println("No se encontró una solución válida.");
        }
    }

    private static void generarCombinaciones(Integer[] numeros, int objetivo) {
        for (int i = 1; i <= numeros.length; i++) {
            generarPermutaciones(Arrays.asList(numeros), new ArrayList<>(), i, objetivo);
        }
    }

    private static void generarPermutaciones(List<Integer> numeros, List<Integer> permutacion, int longitud, int objetivo) {
        if (permutacion.size() == longitud) {
            generarOperaciones(permutacion, new ArrayList<>(), objetivo);
            return;
        }

        for (int i = 0; i < numeros.size(); i++) {
            List<Integer> nuevaPermutacion = new ArrayList<>(permutacion);
            nuevaPermutacion.add(numeros.get(i));

            List<Integer> nuevosNumeros = new ArrayList<>(numeros);
            nuevosNumeros.remove(i);

            generarPermutaciones(nuevosNumeros, nuevaPermutacion, longitud, objetivo);
        }
    }

    private static void generarOperaciones(List<Integer> numeros, List<Character> operadores, int objetivo) {
        if (operadores.size() == numeros.size() - 1) {
            evaluarExpresion(numeros, operadores, objetivo);
            return;
        }

        for (char operador : OPERADORES) {
            List<Character> nuevosOperadores = new ArrayList<>(operadores);
            nuevosOperadores.add(operador);
            generarOperaciones(numeros, nuevosOperadores, objetivo);
        }
    }

    private static void evaluarExpresion(List<Integer> numeros, List<Character> operadores, int objetivo) {
        String expresion = construirExpresion(numeros, operadores);

        try {
            int resultado = evaluarExpresionAritmetica(expresion);
            int diferencia = Math.abs(resultado - objetivo);

            if (diferencia < Math.abs(mejorResultado - objetivo)) {
                mejorResultado = resultado;
                mejorExpresion = expresion;
            }
        } catch (ArithmeticException e) {
            // Ignorar divisiones inválidas
        }
    }

    private static String construirExpresion(List<Integer> numeros, List<Character> operadores) {
        StringBuilder expresion = new StringBuilder();
        expresion.append(numeros.get(0));

        for (int i = 0; i < operadores.size(); i++) {
            expresion.append(operadores.get(i));
            expresion.append(numeros.get(i + 1));
        }

        return expresion.toString();
    }

    private static int evaluarExpresionAritmetica(String expresion) {
        List<String> tokens = tokenizarExpresion(expresion);
        return evaluarTokens(tokens);
    }

    private static List<String> tokenizarExpresion(String expresion) {
        List<String> tokens = new ArrayList<>();
        StringBuilder numeroActual = new StringBuilder();

        for (char c : expresion.toCharArray()) {
            if (Character.isDigit(c)) {
                numeroActual.append(c);
            } else {
                if (numeroActual.length() > 0) {
                    tokens.add(numeroActual.toString());
                    numeroActual = new StringBuilder();
                }
                tokens.add(String.valueOf(c));
            }
        }

        if (numeroActual.length() > 0) {
            tokens.add(numeroActual.toString());
        }

        return tokens;
    }

    private static int evaluarTokens(List<String> tokens) {
        // Primero, evaluamos multiplicaciones y divisiones
        for (int i = 1; i < tokens.size() - 1; i += 2) {
            String operador = tokens.get(i);
            if (operador.equals("*") || operador.equals("/")) {
                int a = Integer.parseInt(tokens.get(i - 1));
                int b = Integer.parseInt(tokens.get(i + 1));
                int resultado;
                if (operador.equals("*")) {
                    resultado = a * b;
                } else {
                    if (b == 0 || a % b != 0) {
                        throw new ArithmeticException("División inválida");
                    }
                    resultado = a / b;
                }
                tokens.set(i - 1, String.valueOf(resultado));
                tokens.remove(i);
                tokens.remove(i);
                i -= 2;
            }
        }

        // Luego, evaluamos sumas y restas
        int resultado = Integer.parseInt(tokens.get(0));
        for (int i = 1; i < tokens.size(); i += 2) {
            String operador = tokens.get(i);
            int numero = Integer.parseInt(tokens.get(i + 1));
            if (operador.equals("+")) {
                resultado += numero;
            } else if (operador.equals("-")) {
                resultado -= numero;
            }
        }

        return resultado;
    }
}


Resultado:

run:
Número objetivo: 127
Números disponibles: [49, 4, 53, 7, 87, 76]
Mejor aproximación: 127
Expresión: 49*4+7-76
BUILD SUCCESSFUL (total time: 2 seconds)

Con la tecnología de Blogger.