Apuntes DAM
Volver al inicio

Cajero automático con PIN, límite diario y billetes

Ejercicio de JavaMedioUnos 50 minutos

Simula la sesión de un cajero: tres intentos de PIN, consultas, ingresos y retiradas con límite diario, desglose en billetes de 50, 20 y 10 y cantidades en céntimos para no perder ni un céntimo. Condicionales, bucles, switch y validación de la entrada.

  • if / else if
  • switch con flechas
  • while y lectura con Scanner
  • Expresiones regulares con matches
  • División entera y resto
  • Céntimos en long

Enunciado

Un banco quiere probar la lógica de sus cajeros antes de instalarla en las máquinas. En lugar de pulsar botones, el programa recibe por teclado lo que hace el cliente, una orden por línea, y responde con lo que aparecería en la pantalla del cajero.

La primera línea no la escribe el cliente: es lo que el cajero lee de la tarjeta, el saldo de la cuenta y el PIN correcto. Mientras el cliente no acierte el PIN no puede hacer nada más, y si falla tres veces el cajero se queda la tarjeta.

El cajero solo tiene billetes de 50, 20 y 10 €, así que solo se puede sacar dinero en múltiplos de 10, y nunca más de 600 € al día entre todas las retiradas. Como cualquier programa que maneja dinero, trabaja en céntimos enteros: sumar 49,95 en coma flotante acaba dando 49,94999… y el saldo dejaría de cuadrar.

Qué tiene que hacer el programa

  1. La primera línea es saldo pin, por ejemplo 850.50 1234. Después, cada línea es una orden (en mayúsculas o minúsculas) con su argumento separado por un espacio. Las líneas vacías se ignoran.
  2. Hasta acertar el PIN, cualquier orden que no sea PIN responde Introduzca el PIN. PIN n responde PIN correcto si acierta; si falla, PIN incorrecto. Quedan 2 intentos o PIN incorrecto. Queda 1 intento, y al tercer fallo PIN incorrecto. Tarjeta retenida y el programa termina sin leer nada más.
  3. Con el PIN correcto: SALDO escribe Saldo: X; PIN escribe Ya ha introducido el PIN; cualquier orden desconocida, Operación desconocida.
  4. INGRESAR cantidad: la cantidad es un número positivo con punto decimal y como mucho dos decimales. Escribe Ingresados X. Saldo: Y, o Cantidad no válida.
  5. RETIRAR cantidad, comprobando en este orden: que sea un múltiplo de 10 € mayor que 0 (si no, Importe no válido: debe ser múltiplo de 10 €), que haya saldo (Saldo insuficiente) y que con lo ya retirado hoy no se pasen los 600 € (Supera el límite diario: hoy puede retirar hasta X). Si todo va bien: Retirados X: desglose. Saldo: Y, con el desglose en el menor número de billetes, de mayor a menor y sin los que no se usan (por ejemplo 3x50 1x20 1x10).
  6. SALIR escribe Operaciones: N · Retirado hoy: X (N cuenta los ingresos y retiradas hechos) y Retire su tarjeta, y termina: lo que venga después no se procesa. Si la entrada se acaba sin SALIR y el cliente se había identificado, el cajero escribe esas dos mismas líneas.
  7. Los importes se escriben con punto de millar, coma decimal y dos decimales, seguidos de un espacio y €: 1.012.345,60 €.

Entrada

Línea 1: saldo pin. Resto: una orden por línea (PIN, SALDO, INGRESAR, RETIRAR, SALIR).

Ejemplos de ejecución

Tu programa debe escribir exactamente esta salida para estas entradas. Las pruebas del editor incluyen estos ejemplos y otros casos ocultos.

Una sesión completa

Entrada

850.50 1234
SALDO
PIN 1111
PIN 1234
SALDO
RETIRAR 180
INGRESAR 49.95
RETIRAR 75
RETIRAR 500
RETIRAR 420
CONSULTAR
SALIR
SALDO

Salida por consola

Introduzca el PIN
PIN incorrecto. Quedan 2 intentos
PIN correcto
Saldo: 850,50 €
Retirados 180,00 €: 3x50 1x20 1x10. Saldo: 670,50 €
Ingresados 49,95 €. Saldo: 720,45 €
Importe no válido: debe ser múltiplo de 10 €
Supera el límite diario: hoy puede retirar hasta 420,00 €
Retirados 420,00 €: 8x50 1x20. Saldo: 300,45 €
Operación desconocida
Operaciones: 3 · Retirado hoy: 600,00 €
Retire su tarjeta

Tres fallos de PIN

Entrada

2000 9876
PIN 1
PIN 2
PIN 3
SALDO

Salida por consola

PIN incorrecto. Quedan 2 intentos
PIN incorrecto. Queda 1 intento
PIN incorrecto. Tarjeta retenida

Guía paso a paso

Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.

1. Todo en céntimos

Convierte cada cantidad a céntimos al leerla y guárdala en un long. Con una expresión regular compruebas a la vez que sea un número y que no tenga más de dos decimales; después separas la parte entera y la decimal.

java
if (!texto.matches("\\d{1,7}(\\.\\d{1,2})?")) return -1;
String[] partes = texto.split("\\.");
long resultado = Long.parseLong(partes[0]) * 100;
if (partes.length == 2) resultado += Long.parseLong((partes[1] + "0").substring(0, 2));
2. El estado de la sesión

Además del saldo, el programa tiene que recordar si el cliente ya se ha identificado, cuántos fallos de PIN lleva, cuántas operaciones ha hecho y cuánto ha retirado hoy. Son variables que se declaran antes del bucle y que cada orden va actualizando.

3. Primero el PIN

Al principio de cada vuelta, si el cliente no se ha identificado, trata la línea aparte y usa continue para no llegar al switch. Así el resto de las órdenes no tienen que comprobar nada del PIN.

4. Retirar: comprobaciones en orden

Encadena las comprobaciones con if / else if en el orden del enunciado: la primera que falle decide el mensaje. cantidad % 1000 != 0 (en céntimos) dice si no es múltiplo de 10 €.

5. El desglose en billetes

Con billetes de 50, 20 y 10, coger siempre el billete más grande que quepa da el menor número de billetes. La división entera dice cuántos billetes de cada tipo y el resto, lo que queda por dar.

java
for (int billete : BILLETES) {
    long n = euros / billete;
    euros %= billete;
    ...
}

Resuélvelo aquí

El editor trae el esqueleto del programa. Pulsa «Ejecutar» para comprobarlo con los ejemplos y con 2 casos ocultos que buscan los errores típicos.

☕JavaCajero automático con PIN, límite diario y billetesMedio

Ejemplo

Entrada (lo que se escribe por teclado)
850.50 1234
SALDO
PIN 1111
PIN 1234
SALDO
RETIRAR 180
INGRESAR 49.95
RETIRAR 75
RETIRAR 500
RETIRAR 420
CONSULTAR
SALIR
SALDO
Salida esperada
Introduzca el PIN
PIN incorrecto. Quedan 2 intentos
PIN correcto
Saldo: 850,50 €
Retirados 180,00 €: 3x50 1x20 1x10. Saldo: 670,50 €
Ingresados 49,95 €. Saldo: 720,45 €
Importe no válido: debe ser múltiplo de 10 €
Supera el límite diario: hoy puede retirar hasta 420,00 €
Retirados 420,00 €: 8x50 1x20. Saldo: 300,45 €
Operación desconocida
Operaciones: 3 · Retirado hoy: 600,00 €
Retire su tarjeta
⏳
Test oculto #3
⏳
Test oculto #4
0/4 tests pasados · pulsa un test para ver su entrada y su salida esperada

Solución explicada

Ver la solución completa
java
1import java.util.Locale;
2import java.util.Scanner;
3
4public class Main {
5    static final long LIMITE_DIARIO = 60000;      // 600 € en céntimos
6    static final int[] BILLETES = {50, 20, 10};
7
8    /** Céntimos → «1.234,50 €». */
9    static String euros(long centimos) {
10        String entero = String.format(Locale.ROOT, "%,d", centimos / 100).replace(',', '.');
11        return entero + "," + String.format("%02d", centimos % 100) + " €";
12    }
13
14    /** «49.95» → 4995 céntimos; -1 si no es una cantidad válida (hasta 2 decimales). */
15    static long centimos(String texto) {
16        if (!texto.matches("\\d{1,7}(\\.\\d{1,2})?")) return -1;
17        String[] partes = texto.split("\\.");
18        long resultado = Long.parseLong(partes[0]) * 100;
19        if (partes.length == 2) resultado += Long.parseLong((partes[1] + "0").substring(0, 2));
20        return resultado;
21    }
22
23    /** Desglose en billetes de 50, 20 y 10 con el menor número de billetes: «3x50 1x20 1x10». */
24    static String desglose(long euros) {
25        StringBuilder sb = new StringBuilder();
26        for (int billete : BILLETES) {
27            long n = euros / billete;
28            euros %= billete;
29            if (n > 0) sb.append(sb.length() > 0 ? " " : "").append(n).append("x").append(billete);
30        }
31        return sb.toString();
32    }
33
34    public static void main(String[] args) {
35        Scanner sc = new Scanner(System.in);
36        String[] inicio = sc.nextLine().trim().split("\\s+");
37        long saldo = centimos(inicio[0]);
38        String pin = inicio[1];
39
40        boolean identificado = false;
41        int fallos = 0, operaciones = 0;
42        long retiradoHoy = 0;
43
44        while (sc.hasNextLine()) {
45            String linea = sc.nextLine().trim();
46            if (linea.isEmpty()) continue;
47            String[] partes = linea.split("\\s+");
48            String orden = partes[0].toUpperCase();
49            String argumento = partes.length > 1 ? partes[1] : "";
50
51            // Sin el PIN correcto solo se acepta la orden PIN
52            if (!identificado) {
53                if (!orden.equals("PIN")) {
54                    System.out.println("Introduzca el PIN");
55                } else if (argumento.equals(pin)) {
56                    identificado = true;
57                    System.out.println("PIN correcto");
58                } else if (++fallos == 3) {
59                    System.out.println("PIN incorrecto. Tarjeta retenida");
60                    return;
61                } else {
62                    int quedan = 3 - fallos;
63                    System.out.println("PIN incorrecto. " + (quedan == 1 ? "Queda 1 intento" : "Quedan " + quedan + " intentos"));
64                }
65                continue;
66            }
67
68            switch (orden) {
69                case "PIN" -> System.out.println("Ya ha introducido el PIN");
70                case "SALDO" -> System.out.println("Saldo: " + euros(saldo));
71                case "INGRESAR" -> {
72                    long cantidad = centimos(argumento);
73                    if (cantidad <= 0) {
74                        System.out.println("Cantidad no válida");
75                    } else {
76                        saldo += cantidad;
77                        operaciones++;
78                        System.out.println("Ingresados " + euros(cantidad) + ". Saldo: " + euros(saldo));
79                    }
80                }
81                case "RETIRAR" -> {
82                    long cantidad = centimos(argumento);
83                    if (cantidad <= 0 || cantidad % 1000 != 0) {
84                        System.out.println("Importe no válido: debe ser múltiplo de 10 €");
85                    } else if (cantidad > saldo) {
86                        System.out.println("Saldo insuficiente");
87                    } else if (retiradoHoy + cantidad > LIMITE_DIARIO) {
88                        System.out.println("Supera el límite diario: hoy puede retirar hasta " + euros(LIMITE_DIARIO - retiradoHoy));
89                    } else {
90                        saldo -= cantidad;
91                        retiradoHoy += cantidad;
92                        operaciones++;
93                        System.out.println("Retirados " + euros(cantidad) + ": " + desglose(cantidad / 100) + ". Saldo: " + euros(saldo));
94                    }
95                }
96                case "SALIR" -> {
97                    System.out.println("Operaciones: " + operaciones + " · Retirado hoy: " + euros(retiradoHoy));
98                    System.out.println("Retire su tarjeta");
99                    return;
100                }
101                default -> System.out.println("Operación desconocida");
102            }
103        }
104        // Se acabó la entrada sin SALIR: el cajero cierra la sesión igualmente
105        if (identificado) {
106            System.out.println("Operaciones: " + operaciones + " · Retirado hoy: " + euros(retiradoHoy));
107            System.out.println("Retire su tarjeta");
108        }
109    }
110}

El programa es un bucle que lee una orden por vuelta y un pequeño estado (identificado, fallos, saldo, retirado hoy, operaciones) que las órdenes van cambiando. Tratar el PIN antes del switch separa las dos fases de la sesión: identificarse y operar.

El dinero va siempre en céntimos dentro de un long: la entrada se convierte una sola vez con centimos y la salida pasa siempre por euros. Así no hay redondeos y el formato español (punto de millar, coma decimal) está en un único sitio.

Las comprobaciones de la retirada siguen el orden del enunciado con if / else if: es la forma natural de expresar «el primer problema que encuentres es el que se muestra».

El desglose es un algoritmo voraz: con 50, 20 y 10 coger siempre el billete mayor da el mínimo de billetes. Con otros billetes no siempre es así (con 25, 20 y 5, para 40 € daría 25+5+5+5 en lugar de 20+20): por eso conviene saber por qué funciona y no solo que funciona.

Para ir más allá

  • El cajero tiene un número limitado de billetes de cada tipo: si no puede dar una cantidad con los que le quedan, que lo diga (y busca un desglose alternativo antes de rendirte).
  • Añade TRANSFERIR cantidad IBAN comprobando el dígito de control del IBAN.
  • Guarda un histórico de movimientos y añade la orden MOVIMIENTOS que muestre los cinco últimos.

Dónde se explica