PAUL GUERRERO software engineer
← retos

Euskal Encounter 34 · Solve It 1

Solve It 1 — Singular Calculus

Resuelto
  • #ctf
  • #criptografía
Las mates no parece que se le den muy bien a Begitxo, ¿o sí?
Enunciado del reto

Cómo se resolvió

  1. Manda la opacidad, no el gris

    Sumas de primaria mal resueltas no van de aritmética. El PNG está en modo LA: gris más canal alpha. El gris nunca pasa de 5, así que los dígitos casi invisibles lo son por transparencia.

  2. Los resultados caben en el alfabeto

    El canal de gris toma siete valores; el de transparencia, veintiséis. Los operandos miden entre 41 y 219, pero los ocho resultados valen 26 o menos: solo ellos caben en el alfabeto, y leerlos como letra da la clave.

  3. Los operandos suman, y hacen overflow

    El alpha izquierdo más el derecho da el del resultado, módulo 256. En pantalla las cuentas de Begitxo están mal; en el canal oculto son exactas, y lo que falla es que un byte no pasa de 255.

  4. El callejón del ranking

    Ordenar las 25 opacidades y numerarlas A–Y produce estructura de sobra: por rango los operandos suman 35 exacto, emparejados en espejo. Todo cierto y todo inútil. El valor crudo era el mensaje desde el principio.

Contraseña ODDMATHS

El PNG que lo acompaña son nueve sumas de primero de primaria: 1 + 1 = 3 y ocho filas más con interrogante. Solo la primera trae resultado, y está mal. Nadie pide ayuda con eso por la aritmética, así que la pregunta buena no es cuánto es 1 + 2: es dónde está escondido el mensaje.

El reto entero cabe en dos líneas de Python, y aun así nos costó veinte minutos de más. Por el camino encontramos una regularidad preciosa que no llevaba a ninguna parte, y nos empeñamos en que la solución tenía que ser complicada. Ese callejón se queda en el writeup, porque enseña más que la solución. Lo que nos sacó de él no fue una idea técnica: fue saber que otros equipos ya lo habían resuelto, o sea que no podía ser rebuscado.

El canal que no se ve

Nueve sumas en columna, «1 + N = ?», con cada dígito en un gris distinto: unos casi negros, otros apenas visibles. La primera fila, «1 + 1 = 3», casi no se ve

Lo primero, siempre, es mirar el fichero y no la imagen:

from PIL import Image
import numpy as np
im = Image.open('level.png')
print(im.mode, im.size)          # LA (800, 1200)
arr = np.array(im)
print(sorted(set(arr[:,:,0].flatten())))      # [0, 1, 2, 3, 4, 5, 255]
print(len(set(arr[:,:,1].flatten())))         # 26

Modo LA: escala de grises más canal alpha, la transparencia. Y ahí está la anomalía que decide el reto: el canal de gris toma solo 7 valores (0–5 y el 255 del fondo), pero el alpha toma 26 distintos. Que un canal tenga exactamente 26 niveles en un puzzle de contraseñas es demasiada coincidencia. Como el gris nunca pasa de 5, lo que ves en pantalla —dígitos casi negros junto a dígitos casi invisibles— lo fija la opacidad. Esa diferencia visual es el mensaje, no un efecto estético.

Cada dígito se segmenta como una mancha conexa de píxeles con alpha menor que 255 (el + y el = van a opacidad plena, así que la máscara los descarta sola), y se toma la mediana de alpha en cada una. Sale esta tabla:

filaizquierdaderecharesultado
1 + 1 = 3123
1 + 2 = ?2106115
1 + 3 = ?442164
1 + 4 = ?412194
1 + 5 = ?6320613
1 + 6 = ?671901
1 + 7 = ?15811820
1 + 8 = ?1621028
1 + 9 = ?13613919

El callejón: tratar la opacidad como un ranking

Con 25 opacidades distintas en los glifos y una fila de ejemplo cuyos tres valores son justo los tres más tenues (1, 2, 3), la lectura que se impone sola es: ordena las 25 y numéralas A=1 … Y=25. Y produce estructura inmediatamente. Por rango, en cada fila los dos operandos suman 35 exacto; los 16 operandos son exactamente las letras J–Y, emparejadas en espejo. Todo eso es verdad, es vistoso, y no significa nada.

Nos convenció de que los operandos eran relleno y el mensaje vivía en la columna de resultados, que por ranking da 3,7,4,4,6,1,9,5,8. Debería ser una permutación de 1–9, pero le falta el 2 y le sobra un 4, y es facilísimo contarse que ese defecto es el punto singular del reto. No lo era: era el artefacto de haber elegido mal la transformación. De ahí salieron barridos de anagramas sin un solo hit, matrices 3×3 buscando una singular (el título empuja a álgebra lineal; nada), y un rato mirando el canal de gris como si fuera un canal oculto (tampoco: seis niveles de ruido de cuantización, sin señal). Los metadatos del PNG, limpios. Una hipótesis equivocada también produce patrones, y cuanto más bonita es la anomalía que genera, más sospechosa debería ser.

La lectura correcta: no hay que rankear nada

Vuelve a la tabla y mira los valores crudos. En las filas 2–9 los operandos van de 41 a 219, todos por encima de 26; los resultados valen 15, 4, 4, 13, 1, 20, 8, 19, todos dentro del alfabeto. Son los únicos valores del canal que caben en él. Letra por número, sin ordenar nada:

15 → O    4 → D    4 → D    13 → M
 1 → A   20 → T    8 → H    19 → S

Odd maths, las mates raras de Begitxo. El «¿o sí?» del enunciado es el remate del chiste.

La fila de ejemplo encaja como leyenda: su resultado tiene alpha 3 y el dígito dibujado es un 3. Te está enseñando la regla —el alpha del resultado es el dato— con un caso donde alpha y dígito coinciden.

Y queda explicar los operandos, porque «son relleno» deja dieciséis valores sin función, y eso es demasiado residuo. No lo son. Suma los alphas crudos de cada fila:

fila 2: 210 + 61  = 271 = 256 + 15  → O
fila 3:  44 + 216 = 260 = 256 +  4  → D
fila 4:  41 + 219 = 260 = 256 +  4  → D
fila 5:  63 + 206 = 269 = 256 + 13  → M
fila 6:  67 + 190 = 257 = 256 +  1  → A
fila 7: 158 + 118 = 276 = 256 + 20  → T
fila 8: 162 + 102 = 264 = 256 +  8  → H
fila 9: 136 + 139 = 275 = 256 + 19  → S

alpha(izq) + alpha(der) ≡ alpha(res) (mod 256), exacto en las nueve filas; la primera es el caso base sin overflow, 1 + 2 = 3. Ahí está el chiste completo: en pantalla las cuentas de Begitxo están mal, pero en el canal alpha la aritmética es perfecta — lo que falla es que un byte hace overflow. Título, enunciado y mecanismo cuadran los tres, y cada valor de la imagen tiene función.

Lo que nos llevamos

Cabe en dos frases. Prueba la transformación identidad antes que cualquier otra: ante valores que ya caben en el alfabeto, el valor crudo va primero y los rankings después. Y un patrón bonito confirma que tu transformación es consistente, no que sea la correcta.

Solución:

ODDMATHS

El material — el enunciado original y el writeup técnico completo de este nivel están en euskal34/solve-it/1.