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, 27 de julio de 2024

Juegos VI: The Marconix Codebreaker (Juego de Posiciones)

The Marconix Codebreaker

Descripción del juego:
Este es un juego de ordenamiento en el que el jugador debe organizar una serie de casillas, cada una representada por una letra (A-F), en el orden correcto. Cada letra tiene asociado un número oculto (1-6) y el objetivo es ordenar las letras de manera que sus números asociados estén en orden ascendente.

Reglas del juego:
El juego comienza con 6 casillas (A-F) en orden alfabético y cada letra tiene un número oculto (1-6) asignado aleatoriamente.
El jugador tiene una puntuación inicial de 1000 puntos.
En cada turno, el jugador puede:
  . Pedir una pista (cuesta 50 puntos)
  . Reorganizar las casillas (cuesta 100 puntos)
  . Intentar salir del juego
Las pistas proporcionan información sobre el orden relativo de tres letras.
Al reorganizar, el jugador debe ingresar un nuevo orden para todas las letras.
Después de cada acción, el juego verifica si las casillas están en el orden correcto.
El juego termina cuando:
  .El jugador ordena correctamente las casillas (victoria)
  .El jugador decide salir del juego
La puntuación final se basa en los puntos restantes después de restar el costo de las pistas y reorganizaciones utilizadas.


Estructura jerárquica

Marconix
├── Variables estáticas
│   ├── NUM_CASILLAS
│   ├── letras
│   ├── numeros
│   ├── letraANumero
│   ├── pistasUsadas
│   └── puntuacion

├── main()
│   ├── inicializarJuego()
│   └── jugar()

├── inicializarJuego()

├── jugar()
│   ├── mostrarCasillas()
│   ├── darPista()
│   │   └── generarPistaCompleja()
│   ├── reorganizarCasillas()
│   │   └── darFeedback()
│   └── estaOrdenado()

├── mostrarCasillas()

├── darPista()
│   └── generarPistaCompleja()

├── generarPistaCompleja()

├── reorganizarCasillas()
│   └── darFeedback()

├── darFeedback()

└── estaOrdenado()


Diagrama de funcionamiento

┌─► Inicio
├─► Inicializar juego
├◄───────────────────────────────────────────────────────┐
├─► Mostrar casillas actuales                            │
├─► Mostrar puntuación                                   │
└─► Preguntar por pista                                  │
    ├─► Sí                                               │
    │   └─► Dar pista                                    │
    │       Restar 50 puntos ───────────────────────────►┤
    └─► No                                               │
        └─► Preguntar por reorganizar                    │
            ├─► Sí                                       │
            │   └─► Reorganizar casillas                 │
            │       Restar 100 puntos ──────────────────►┤
            └─► No                                       │
                └─► Preguntar por salir                  │
                    ├─► Sí                               │
                    │   └─► Terminar juego*              │
                    └─► No                               │
                        └─► Verificar si está ordenado   │
                            ├─► Sí                       │
                            │   └─► Mostrar victoria     │
                            │       Terminar juego*      │
                            └─► No ─────────────────────►┘



Código Java (Marconix.java):

package Marconix;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

public class Marconix {

    private static final int NUM_CASILLAS = 6;
    private static final List<Character> letras = new ArrayList<>();
    private static final List<Integer> numeros = new ArrayList<>();
    private static final Map<Character, Integer> letraANumero = new HashMap<>();
    private static final Set<String> pistasUsadas = new HashSet<>();
    private static int puntuacion = 1000;

    public static void main(String[] args) {
        inicializarJuego();
        jugar();
    }

    private static void inicializarJuego() {
        for (char c = 'A'; c < 'A' + NUM_CASILLAS; c++) {
            letras.add(c);
        }
        for (int i = 1; i <= NUM_CASILLAS; i++) {
            numeros.add(i);
        }
        Collections.shuffle(numeros);
        for (int i = 0; i < NUM_CASILLAS; i++) {
            letraANumero.put(letras.get(i), numeros.get(i));
        }
    }

    private static void jugar() {
        try (Scanner scanner = new Scanner(System.in)) {
            while (true) {
                mostrarCasillas();
                System.out.println("Puntuación actual: " + puntuacion);
                System.out.println("¿Quieres una pista? (s/n)");
                String respuesta = scanner.nextLine().toLowerCase();
                if (respuesta.equals("s")) {
                    darPista();
                    puntuacion -= 50;
                } else if (respuesta.equals("n")) {
                    System.out.println("¿Quieres reorganizar las casillas? (s/n)");
                    respuesta = scanner.nextLine().toLowerCase();
                    if (respuesta.equals("s")) {
                        reorganizarCasillas(scanner);
                        puntuacion -= 100;
                    } else if (respuesta.equals("n")) {
                        System.out.println("¿Quieres salir del juego? (s/n)");
                        if (scanner.nextLine().toLowerCase().equals("s")) {
                            break;
                        }
                    }
                }
                if (estaOrdenado()) {
                    System.out.println("¡Felicidades! Has ordenado correctamente las casillas.");
                    System.out.println("Puntuación final: " + puntuacion);
                    break;
                }
            }
        }
    }

    private static void mostrarCasillas() {
        System.out.println("Orden actual de las casillas:");
        letras.forEach(letra -> System.out.print(letra + " "));
        System.out.println();
    }

    private static void darPista() {
        String pista;
        do {
            pista = generarPistaCompleja();
        } while (pistasUsadas.contains(pista));
        pistasUsadas.add(pista);
        System.out.println("Pista: " + pista);
    }

    private static String generarPistaCompleja() {
        List<Character> letrasAleatorias = new ArrayList<>(letras);
        Collections.shuffle(letrasAleatorias);
        char letra1 = letrasAleatorias.get(0);
        char letra2 = letrasAleatorias.get(1);
        char letra3 = letrasAleatorias.get(2);
        int num1 = letraANumero.get(letra1);
        int num2 = letraANumero.get(letra2);
        int num3 = letraANumero.get(letra3);

        if (num1 < num2 && num2 < num3) {
            return letra1 + " < " + letra2 + " < " + letra3;
        } else if (num1 < num2 && num2 > num3 && num1 < num3) {
            return letra1 + " < " + letra3 + " < " + letra2;
        } else if (num2 < num1 && num1 < num3) {
            return letra2 + " < " + letra1 + " < " + letra3;
        } else if (num2 < num3 && num3 < num1) {
            return letra2 + " < " + letra3 + " < " + letra1;
        } else if (num3 < num1 && num1 < num2) {
            return letra3 + " < " + letra1 + " < " + letra2;
        } else {
            return letra3 + " < " + letra2 + " < " + letra1;
        }
    }

    private static void reorganizarCasillas(Scanner scanner) {
        System.out.println("Ingresa el nuevo orden de las letras (sin espacios):");
        String nuevoOrden = scanner.nextLine().toUpperCase();
        if (nuevoOrden.length() == NUM_CASILLAS && nuevoOrden.chars().distinct().count() == NUM_CASILLAS) {
            letras.clear();
            for (char c : nuevoOrden.toCharArray()) {
                letras.add(c);
            }
            darFeedback();
        } else {
            System.out.println("Orden inválido. Asegúrate de incluir todas las letras una vez.");
        }
    }

    private static void darFeedback() {
        int posicionesCorrectas = 0;
        for (int i = 0; i < NUM_CASILLAS; i++) {
            if (letraANumero.get(letras.get(i)) == i + 1) {
                posicionesCorrectas++;
            }
        }
        System.out.println("Tienes " + posicionesCorrectas + " letra(s) en la posición correcta.");
    }

    private static boolean estaOrdenado() {
        for (int i = 1; i < NUM_CASILLAS; i++) {
            if (letraANumero.get(letras.get(i - 1)) > letraANumero.get(letras.get(i))) {
                return false;
            }
        }
        return true;
    }
}


Resultado:

run:
Orden actual de las casillas:
A B C D E F
Puntuación actual: 1000
¿Quieres una pista? (s/n)
s
Pista: D < F < A
Orden actual de las casillas:
A B C D E F
Puntuación actual: 950
¿Quieres una pista? (s/n)
n
¿Quieres reorganizar las casillas? (s/n)
s
Ingresa el nuevo orden de las letras (sin espacios):
BCDEFA
Tienes 3 letra(s) en la posición correcta.
Orden actual de las casillas:
B C D E F A
Puntuación actual: 850
¿Quieres una pista? (s/n)
n
¿Quieres reorganizar las casillas? (s/n)
n
¿Quieres salir del juego? (s/n)
s
BUILD SUCCESSFUL (total time: 2 minutes 11 seconds)


lunes, 22 de julio de 2024

Juegos V.2: Approximate(GUI): El Juego de Números y Operaciones.

Este algoritmo proporciona una interfaz clara y funcional para la aplicación 'Approximate', permitiendo al usuario ingresar o generar números, realizar cálculos y ver los resultados de manera intuitiva mediante interfaz de usuario (GUI).

Desde NetBeans y en modo "Design" creamos la interfaz de usuario (GUI) siguiendo la siguiente nomenclatura:


Estructura GUI:

MainApproximate

└─── jPanel_Main
    │
    ├─── jPanel_Botonera
    │    ├─── jButtonRandom
    │    ├─── jButtonLimpiar
    │    └─── jButtonCalcular
    │
    ├─── jPanel_NumeroObjetivo
    │    └─── jTextField_NumObjetivo
    │
    ├─── jPanel_NumerosDisponibles
    │    ├─── jTextField1
    │    ├─── jTextField2
    │    ├─── jTextField3
    │    ├─── jTextField4
    │    ├─── jTextField5
    │    └─── jTextField6
    │
    ├─── jSeparator1
    │
    ├─── jPanel_MejorAproximacion
    │    └─── jTextField_MejorAproximacion
    │
    └─── jPanel_Expresion
         └─── jTextField_Expresion




Código Java (MainApproximate.java):

package approximategui;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import javax.swing.JOptionPane;

public class MainApproximate extends javax.swing.JFrame {

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

    public MainApproximate() {
        initComponents();
        setLocationRelativeTo(null);
        limpiarDatos();
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code"> 
                         
    private void initComponents() { ... } // Código generado por Netbeans desde modo "Design"                      

    private void jButtonCalcularActionPerformed(java.awt.event.ActionEvent evt) {                                                
        calcularMejorAproximacion();
    }                                               

    private void jButtonRandomActionPerformed(java.awt.event.ActionEvent evt) {                                              
        generarNumerosAleatorios();
        // Limpiar los resultados
        jTextField_MejorAproximacion.setText("");
        jTextField_Expresion.setText("");
    }                                             

    private void jButtonLimpìarActionPerformed(java.awt.event.ActionEvent evt) {                                               
        limpiarDatos();
    }                                              

    private void limpiarDatos() {
        // Limpiar el número objetivo
        jTextField_NumObjetivo.setText("");

        // Limpiar los números disponibles
        jTextField1.setText("");
        jTextField2.setText("");
        jTextField3.setText("");
        jTextField4.setText("");
        jTextField5.setText("");
        jTextField6.setText("");

        // Limpiar los resultados
        jTextField_MejorAproximacion.setText("");
        jTextField_Expresion.setText("");

        // Resetear las variables globales si es necesario
        mejorResultado = OBJETIVO_NO_ALCANZADO;
        mejorExpresion = "";
    }

    private void generarNumerosAleatorios() {
        int objetivo = random.nextInt(999) + 1;
        jTextField_NumObjetivo.setText(String.valueOf(objetivo));

        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]);
        jTextField1.setText(String.valueOf(numeros[0]));
        jTextField2.setText(String.valueOf(numeros[1]));
        jTextField3.setText(String.valueOf(numeros[2]));
        jTextField4.setText(String.valueOf(numeros[3]));
        jTextField5.setText(String.valueOf(numeros[4]));
        jTextField6.setText(String.valueOf(numeros[5]));
    }

    private void calcularMejorAproximacion() {
        try {
            int objetivo = Integer.parseInt(jTextField_NumObjetivo.getText());
            Integer[] numeros = new Integer[6];
            numeros[0] = Integer.parseInt(jTextField1.getText());
            numeros[1] = Integer.parseInt(jTextField2.getText());
            numeros[2] = Integer.parseInt(jTextField3.getText());
            numeros[3] = Integer.parseInt(jTextField4.getText());
            numeros[4] = Integer.parseInt(jTextField5.getText());
            numeros[5] = Integer.parseInt(jTextField6.getText());

            mejorResultado = OBJETIVO_NO_ALCANZADO;
            mejorExpresion = "";

            generarCombinaciones(numeros, objetivo);

            if (mejorResultado != OBJETIVO_NO_ALCANZADO) {
                jTextField_MejorAproximacion.setText(String.valueOf(mejorResultado));
                jTextField_Expresion.setText(mejorExpresion);
            } else {
                jTextField_MejorAproximacion.setText("No encontrado");
                jTextField_Expresion.setText("N/A");
            }
        } catch (NumberFormatException e) {
            JOptionPane.showMessageDialog(this, "Por favor, ingrese números válidos en todos los campos.", "Error de entrada", JOptionPane.ERROR_MESSAGE);
        }
    }

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

    private 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 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 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 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 int evaluarExpresionAritmetica(String expresion) {
        List<String> tokens = tokenizarExpresion(expresion);
        return evaluarTokens(tokens);
    }

    private 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 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;
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(() -> {
            new MainApproximate().setVisible(true);
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JButton jButtonCalcular;
    private javax.swing.JButton jButtonLimpìar;
    private javax.swing.JButton jButtonRandom;
    private javax.swing.JPanel jPanel_Botonera;
    private javax.swing.JPanel jPanel_Expresion;
    private javax.swing.JPanel jPanel_Main;
    private javax.swing.JPanel jPanel_MejorAproximacion;
    private javax.swing.JPanel jPanel_NumeroObjetivo;
    private javax.swing.JPanel jPanel_NumerosDisponibles;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JTextField jTextField1;
    private javax.swing.JTextField jTextField2;
    private javax.swing.JTextField jTextField3;
    private javax.swing.JTextField jTextField4;
    private javax.swing.JTextField jTextField5;
    private javax.swing.JTextField jTextField6;
    private javax.swing.JTextField jTextField_Expresion;
    private javax.swing.JTextField jTextField_MejorAproximacion;
    private javax.swing.JTextField jTextField_NumObjetivo;
    // End of variables declaration                   
}


Resultado:


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.