Este artículo va del lado de ingeniería de los image slide puzzles: cómo una foto arbitraria se convierte en un puzzle N×N jugable. Es la parte que los jugadores no ven y a la que los desarrolladores dedican más tiempo del esperado.
El pipeline
Tres etapas, en orden:
1. Recorte cuadrado
La mayoría de fotos no son cuadradas. Los HEIC del iPhone son 4:3; los retratos son 3:4. El tablero es 1:1. Paso uno: elegir la región cuadrada de la foto que se juega.
Dos enfoques:
Recorte centrado. Toma el cuadrado más grande centrado. Rápido, predecible, a veces equivocado (sujeto fuera del centro).
Recorte interactivo. Muestra al usuario un cuadrado arrastrable. Que elija. Más lento, pero el usuario siempre obtiene lo que quería.
Slide Puzzle usa recorte interactivo, centrado por defecto. El recorte no es destructivo — el original en la fotolibrería no se modifica nunca.
2. Reducir a resolución de trabajo
Tras el recorte, la imagen suele seguir en 3000×3000 o más (según la cámara). Para jugar al puzzle es exceso. Un tablero 6×6 mostrado a 320pt en una pantalla 3× de iPhone renderiza cada ficha a unos 53pt = 160 píxeles físicos. Una resolución de origen 1024×1024 da unos 170 píxeles por ficha — más nítido de lo que muestra la pantalla.
Reducir a 1024 (a veces 2048) es estándar. Orígenes mayores gastan memoria y ralentizan la carga sin mejora visible.
3. Renderizado a demanda
Aquí es donde la mayoría se equivoca al principio. La imagen no se corta de verdad en 16 archivos separados. Sería lento, derrochador e innecesario.
En su lugar, la app pinta cada ficha dibujando la misma imagen origen en un rectángulo del tamaño de la ficha, con el rectángulo de origen desplazado por la posición de la ficha en la imagen objetivo. CSS lo llama un truco de background-position; iOS lo llama clip rectangle de CGImage; SwiftUI usa Rectangle().clipped() sobre un Image.
En pseudocódigo, para pintar una ficha en posición (fila, col) de un tablero N×N con imagen de lado S:
dibujar imagen en (-col * S/N, -fila * S/N)
dentro de un clip rectangle de (S/N × S/N)
Eso es todo. Dieciséis fichas para un 4×4 son 16 llamadas a pintar la misma imagen con 16 offsets distintos. La app nunca corta el archivo.
Por eso cortar a 6×6 es instantáneo: son 36 llamadas al mismo renderer, no 36 operaciones de fichero.
Y la animación
Deslizar una ficha es un translate transform de la ficha renderizada. El contenido dentro no cambia — solo cambia la posición del marco recortado en pantalla. Esto hace que la animación sea trivial para la GPU y vaya a 120 Hz en pantallas ProMotion.
Tres detalles que importan:
- Clip y contenido son hermanos, no padre/hijo. Anidados, el contenido viaja con el clip y el deslizamiento se rompe.
- Usa transform, no layout. Animar x/y por CSS transform o SwiftUI offset es GPU-acelerado; animar left/top no.
- Pre-rasteriza la primera vez. El primer frame de pintar una ficha puede ser lento porque hay que decodificar la imagen origen. Renderiza todas las fichas una vez al iniciar la partida para calentarlas.
Almacenamiento
¿Dónde vive realmente la imagen importada?
En una app respetuosa, en el sandbox de la app, cifrada por iOS en reposo. La fotolibrería sigue teniendo el original; la app guarda una copia de trabajo de 1024×1024 en su carpeta Documents. Al borrar la app, la copia se va con ella.
En una app en la nube, la copia vive en un servidor en algún sitio. Las implicaciones son muy distintas — para privacidad, para juego sin conexión, y para qué ocurre cuando el servidor desaparece. Slide Puzzle es del tipo sandbox.
Presupuesto de memoria
Un image slide puzzle 4×4 típico:
- Imagen origen: ~3 MB JPEG.
- Imagen decodificada en memoria: 1024 × 1024 × 4 bytes = 4 MB.
- Una copia por partida activa.
Aceptable. A 6×6 origen y buffer decodificado son del mismo tamaño. El tablero no necesita más memoria.
Donde aprieta es en las bibliotecas: 300 imágenes × 4 MB = 1,2 GB si se decodifican todas. Las apps lo evitan con decodificación bajo demanda (solo cuando una imagen se muestra) y liberación al ir a background (solo la imagen del juego activo se mantiene).
Casos límite
Tres cosas que se rompen en la práctica:
Fotos con etiqueta de orientación EXIF. Una foto tomada en vertical pero guardada en horizontal con etiqueta de rotación se ve bien en la fotolibrería y mal en el puzzle si la app se olvida de aplicar la rotación. La primera versión de toda app de fotopuzzle tiene este bug.
Imágenes origen muy grandes. Algunos HEIC son 6000×8000 píxeles. Cargarlos a resolución completa cuelga la app en iPhones menores. La solución: decodificación streaming con tamaño objetivo reducido — ImageIO de Apple lo soporta. Decodifica a 2048×2048 directamente desde disco; nunca a tamaño completo.
Renderizado sub-pixel. Tamaños de ficha que no caen sobre la cuadrícula de píxeles producen una columna fraccionaria en un lado de cada ficha. Con clipping puro, eso se ve como un hilo de hueco. Soluciona o ajustando los tamaños a píxeles enteros (jitter visible a tamaños no estándar) o dejando que las fichas se solapen 0,5 px (sin hueco, sin jitter).
Ninguno es un problema profundo, pero todos los olvidan al principio.
Resumen
El pipeline: recorte → reducir → clip-y-translate para pintar fichas → translate transforms para animar deslizamientos. En una app respetuosa, la imagen nunca sale del dispositivo. El pipeline entero cabe en unas 200 líneas, más otras 50 para el manejo EXIF que todos olvidan al principio.
Los image slide puzzles son más sencillos de lo que parecen por fuera. La imagen sigue siendo una imagen; las fichas son solo vistas enmarcadas sobre ella.