Cajero automático con PIN, límite diario y billetes
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
- La primera línea es
saldo pin, por ejemplo850.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. - Hasta acertar el PIN, cualquier orden que no sea
PINrespondeIntroduzca el PIN.PIN nrespondePIN correctosi acierta; si falla,PIN incorrecto. Quedan 2 intentosoPIN incorrecto. Queda 1 intento, y al tercer falloPIN incorrecto. Tarjeta reteniday el programa termina sin leer nada más. - Con el PIN correcto:
SALDOescribeSaldo: X;PINescribeYa ha introducido el PIN; cualquier orden desconocida,Operación desconocida. INGRESAR cantidad: la cantidad es un número positivo con punto decimal y como mucho dos decimales. EscribeIngresados X. Saldo: Y, oCantidad no válida.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 ejemplo3x50 1x20 1x10).SALIRescribeOperaciones: N · Retirado hoy: X(N cuenta los ingresos y retiradas hechos) yRetire su tarjeta, y termina: lo que venga después no se procesa. Si la entrada se acaba sinSALIRy el cliente se había identificado, el cajero escribe esas dos mismas líneas.- 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.
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.
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.
Ejemplo
850.50 1234 SALDO PIN 1111 PIN 1234 SALDO RETIRAR 180 INGRESAR 49.95 RETIRAR 75 RETIRAR 500 RETIRAR 420 CONSULTAR SALIR SALDO
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
Solución explicada
Ver la solución completa
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 IBANcomprobando el dígito de control del IBAN. - Guarda un histórico de movimientos y añade la orden
MOVIMIENTOSque muestre los cinco últimos.